fast-dev-cli 0.7.1__tar.gz → 0.7.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fast-dev-cli
3
- Version: 0.7.1
3
+ Version: 0.7.3
4
4
  Summary:
5
5
  Author: Waket Zheng
6
6
  Author-email: waketzheng@gmail.com
@@ -54,7 +54,7 @@ Description-Content-Type: text/markdown
54
54
 
55
55
  ## Requirements
56
56
 
57
- Python 3.11+
57
+ Python 3.10+
58
58
 
59
59
  ## Installation
60
60
 
@@ -94,4 +94,8 @@ fast sync
94
94
  ```bash
95
95
  fast upgrade
96
96
  ```
97
+ - Start a fastapi server in development mode
98
+ ```
99
+ fast dev
100
+ ```
97
101
 
@@ -31,7 +31,7 @@
31
31
 
32
32
  ## Requirements
33
33
 
34
- Python 3.11+
34
+ Python 3.10+
35
35
 
36
36
  ## Installation
37
37
 
@@ -71,3 +71,7 @@ fast sync
71
71
  ```bash
72
72
  fast upgrade
73
73
  ```
74
+ - Start a fastapi server in development mode
75
+ ```
76
+ fast dev
77
+ ```
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import importlib.metadata
2
4
  import os
3
5
  import re
@@ -6,7 +8,7 @@ import sys
6
8
  from functools import cached_property
7
9
  from pathlib import Path
8
10
  from subprocess import CompletedProcess
9
- from typing import Type
11
+ from typing import TYPE_CHECKING, Optional, Type
10
12
 
11
13
  if sys.version_info >= (3, 11):
12
14
  from enum import StrEnum
@@ -15,6 +17,9 @@ else: # pragma: no cover
15
17
  from strenum import StrEnum # type:ignore[no-redef,assignment]
16
18
  from typing_extensions import Self
17
19
 
20
+ if TYPE_CHECKING: # pragma: no cover
21
+ from typer.models import OptionInfo
22
+
18
23
 
19
24
  __version__ = importlib.metadata.version(Path(__file__).parent.name)
20
25
 
@@ -37,7 +42,7 @@ except ModuleNotFoundError:
37
42
  from click.core import Group as _Group
38
43
  from click.exceptions import Exit
39
44
 
40
- def Option(default, *shortcuts, help=None): # type:ignore[no-redef]
45
+ def Option(default, *shortcuts, **kw): # type:ignore[no-redef]
41
46
  return default
42
47
 
43
48
  def _command(self, *args, **kwargs):
@@ -93,6 +98,7 @@ def _run_shell(cmd: str, **kw) -> CompletedProcess:
93
98
 
94
99
 
95
100
  def run_and_echo(cmd: str, *, dry=False, verbose=True, **kw) -> int:
101
+ """Run shell command with subprocess and print it"""
96
102
  if verbose:
97
103
  echo(f"--> {cmd}")
98
104
  if dry:
@@ -115,16 +121,21 @@ def capture_cmd_output(command: list[str] | str, **kw) -> str:
115
121
  def get_current_version(verbose=False) -> str:
116
122
  cmd = ["poetry", "version", "-s"]
117
123
  if verbose:
118
- command = " ".join(cmd)
119
- echo(f"--> {command}")
124
+ echo(f"--> {' '.join(cmd)}")
120
125
  return capture_cmd_output(cmd)
121
126
 
122
127
 
128
+ def _ensure_bool(value: bool | "OptionInfo") -> bool:
129
+ if not isinstance(value, bool):
130
+ value = getattr(value, "default", False)
131
+ return value
132
+
133
+
123
134
  def exit_if_run_failed(
124
135
  cmd: str, env=None, _exit=False, dry=False, **kw
125
136
  ) -> CompletedProcess:
126
137
  run_and_echo(cmd, dry=True)
127
- if dry:
138
+ if _ensure_bool(dry):
128
139
  return CompletedProcess("", 0)
129
140
  if env is not None:
130
141
  env = {**os.environ, **env}
@@ -220,7 +231,7 @@ def bump_version(
220
231
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
221
232
  ) -> None:
222
233
  """Bump up version string in pyproject.toml"""
223
- return BumpUp(commit, part.value, dry=dry).run()
234
+ return BumpUp(_ensure_bool(commit), getattr(part, "value", part), dry=dry).run()
224
235
 
225
236
 
226
237
  def bump() -> None:
@@ -235,7 +246,8 @@ def bump() -> None:
235
246
  return BumpUp(commit, part, dry="--dry" in args).run()
236
247
 
237
248
 
238
- class EnvError(Exception): ...
249
+ class EnvError(Exception):
250
+ """Raise this when the project is expected to be managed by poetry, but toml file not found."""
239
251
 
240
252
 
241
253
  class Project:
@@ -447,7 +459,8 @@ class GitTag(DryRun):
447
459
  self.message = message
448
460
  super().__init__(dry=dry)
449
461
 
450
- def has_v_prefix(self: Self) -> bool:
462
+ @staticmethod
463
+ def has_v_prefix() -> bool:
451
464
  return "v" in capture_cmd_output("git tag")
452
465
 
453
466
  def should_push(self: Self) -> bool:
@@ -530,6 +543,8 @@ class LintCode(DryRun):
530
543
  def lint(files=None, dry=False) -> None:
531
544
  if files is None:
532
545
  files = parse_files(sys.argv[1:])
546
+ if files and files[0] == "lint":
547
+ files = files[1:]
533
548
  LintCode(files, dry=dry).run()
534
549
 
535
550
 
@@ -546,7 +561,7 @@ def make_style(
546
561
  """Run: ruff check/format to reformat code and then mypy to check"""
547
562
  if isinstance(files, str):
548
563
  files = [files]
549
- if check_only:
564
+ if _ensure_bool(check_only):
550
565
  check(files, dry=dry)
551
566
  else:
552
567
  lint(files, dry=dry)
@@ -601,18 +616,11 @@ def _should_run_test_script(path: Path) -> bool:
601
616
  return path.exists()
602
617
 
603
618
 
604
- @cli.command()
605
- def test(
606
- dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
607
- ignore_script: bool = Option(False, "--ignore-script", "-i"),
608
- ) -> None:
609
- """Run unittest by pytest and report coverage"""
619
+ def test(dry: bool, ignore_script=False) -> None:
610
620
  cwd = Path.cwd()
611
621
  root = Project.get_work_dir(cwd=cwd, allow_cwd=True)
612
622
  test_script = root / "scripts" / "test.sh"
613
- if not isinstance(ignore_script, bool):
614
- ignore_script = getattr(ignore_script, "default", False)
615
- if not ignore_script and _should_run_test_script(test_script):
623
+ if not _ensure_bool(ignore_script) and _should_run_test_script(test_script):
616
624
  cmd = f"sh {test_script.relative_to(root)}"
617
625
  if cwd != root:
618
626
  cmd = f"cd {root} && " + cmd
@@ -624,6 +632,15 @@ def test(
624
632
  exit_if_run_failed(cmd, dry=dry)
625
633
 
626
634
 
635
+ @cli.command(name="test")
636
+ def coverage_test(
637
+ dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
638
+ ignore_script: bool = Option(False, "--ignore-script", "-i"),
639
+ ) -> None:
640
+ """Run unittest by pytest and report coverage"""
641
+ return test(dry, ignore_script)
642
+
643
+
627
644
  @cli.command()
628
645
  def upload(
629
646
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
@@ -633,5 +650,29 @@ def upload(
633
650
  exit_if_run_failed(cmd, dry=dry)
634
651
 
635
652
 
636
- if __name__ == "__main__":
637
- cli() # pragma: no cover
653
+ def dev(
654
+ port: int | None | "OptionInfo", host: str | None | "OptionInfo", dry=False
655
+ ) -> None:
656
+ cmd = "fastapi dev"
657
+ if (port := getattr(port, "default", port)) and port != 8000:
658
+ cmd += f" --port={port}"
659
+ if (host := getattr(host, "default", host)) and host not in (
660
+ "localhost",
661
+ "127.0.0.1",
662
+ ):
663
+ cmd += f" --host={host}"
664
+ exit_if_run_failed(cmd, dry=dry)
665
+
666
+
667
+ @cli.command(name="dev")
668
+ def runserver(
669
+ port: Optional[int] = Option(None, "-p", "--port"),
670
+ host: Optional[str] = Option(None, "-h", "--host"),
671
+ dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
672
+ ) -> None:
673
+ """Start a fastapi server(only for fastapi>=0.111.0)"""
674
+ dev(port, host, dry=dry)
675
+
676
+
677
+ if __name__ == "__main__": # pragma: no cover
678
+ cli()
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fast-dev-cli"
3
- version = "0.7.1"
3
+ version = "0.7.3"
4
4
  description = ""
5
5
  authors = ["Waket Zheng <waketzheng@gmail.com>"]
6
6
  readme = "README.md"
File without changes