fast-dev-cli 0.6.4__tar.gz → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fast-dev-cli
3
- Version: 0.6.4
3
+ Version: 0.6.6
4
4
  Summary:
5
5
  Author: Waket Zheng
6
6
  Author-email: waketzheng@gmail.com
@@ -56,7 +56,7 @@ Python 3.11+
56
56
  ```console
57
57
  $ pip install "fast-dev-cli[all]"
58
58
  ---> 100%
59
- Successfully installed fast-dev-cli
59
+ Successfully installed fast-dev-cli isort black ruff mypy typer bumpversion pytest coverage
60
60
  ```
61
61
 
62
62
  </div>
@@ -67,6 +67,10 @@ Successfully installed fast-dev-cli
67
67
  ```bash
68
68
  fast lint /path/to/file-or-directory
69
69
  ```
70
+ - Check only
71
+ ```bash
72
+ fast check
73
+ ```
70
74
  - Bump up version in pyproject.toml
71
75
  ```bash
72
76
  fast bump
@@ -34,7 +34,7 @@ Python 3.11+
34
34
  ```console
35
35
  $ pip install "fast-dev-cli[all]"
36
36
  ---> 100%
37
- Successfully installed fast-dev-cli
37
+ Successfully installed fast-dev-cli isort black ruff mypy typer bumpversion pytest coverage
38
38
  ```
39
39
 
40
40
  </div>
@@ -45,6 +45,10 @@ Successfully installed fast-dev-cli
45
45
  ```bash
46
46
  fast lint /path/to/file-or-directory
47
47
  ```
48
+ - Check only
49
+ ```bash
50
+ fast check
51
+ ```
48
52
  - Bump up version in pyproject.toml
49
53
  ```bash
50
54
  fast bump
@@ -7,6 +7,7 @@ from enum import StrEnum
7
7
  from functools import cached_property
8
8
  from pathlib import Path
9
9
  from subprocess import CompletedProcess
10
+ from typing import Self, Type
10
11
 
11
12
  __version__ = importlib.metadata.version(Path(__file__).parent.name)
12
13
 
@@ -129,14 +130,14 @@ def exit_if_run_failed(
129
130
 
130
131
 
131
132
  class DryRun:
132
- def __init__(self, _exit=False, dry=False):
133
+ def __init__(self: Self, _exit=False, dry=False) -> None:
133
134
  self.dry = dry
134
135
  self._exit = _exit
135
136
 
136
- def gen(self) -> str:
137
+ def gen(self: Self) -> str:
137
138
  raise NotImplementedError
138
139
 
139
- def run(self) -> None:
140
+ def run(self: Self) -> None:
140
141
  exit_if_run_failed(self.gen(), _exit=self._exit, dry=self.dry)
141
142
 
142
143
 
@@ -146,7 +147,9 @@ class BumpUp(DryRun):
146
147
  minor = "minor"
147
148
  major = "major"
148
149
 
149
- def __init__(self, commit: bool, part: str, filename=TOML_FILE, dry=False):
150
+ def __init__(
151
+ self: Self, commit: bool, part: str, filename=TOML_FILE, dry=False
152
+ ) -> None:
150
153
  self.commit = commit
151
154
  self.part = part
152
155
  self.filename = filename
@@ -163,7 +166,7 @@ class BumpUp(DryRun):
163
166
  echo(f"Invalid part: {s!r}")
164
167
  raise Exit(1) from e
165
168
 
166
- def gen(self) -> str:
169
+ def gen(self: Self) -> str:
167
170
  _version = get_current_version()
168
171
  filename = self.filename
169
172
  echo(f"Current version(@{filename}): {_version}")
@@ -186,7 +189,7 @@ class BumpUp(DryRun):
186
189
  cmd += " --allow-dirty"
187
190
  return cmd
188
191
 
189
- def run(self) -> None:
192
+ def run(self: Self) -> None:
190
193
  super().run()
191
194
  if not self.commit and not self.dry:
192
195
  new_version = get_current_version(True)
@@ -196,7 +199,7 @@ class BumpUp(DryRun):
196
199
 
197
200
 
198
201
  @cli.command()
199
- def version():
202
+ def version() -> None:
200
203
  """Show the version of this tool"""
201
204
  echo(__version__)
202
205
 
@@ -208,11 +211,12 @@ def bump_version(
208
211
  False, "--commit", "-c", help="Whether run `git commit` after version changed"
209
212
  ),
210
213
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
211
- ):
214
+ ) -> None:
215
+ """Bump up version string in pyproject.toml"""
212
216
  return BumpUp(commit, part.value, dry=dry).run()
213
217
 
214
218
 
215
- def bump():
219
+ def bump() -> None:
216
220
  part, commit = "", False
217
221
  if args := sys.argv[2:]:
218
222
  if "-c" in args or "--commit" in args:
@@ -239,13 +243,17 @@ class Project:
239
243
  return None
240
244
 
241
245
  @classmethod
242
- def get_work_dir(cls, name=TOML_FILE, cwd: Path | None = None) -> Path:
246
+ def get_work_dir(
247
+ cls: Type[Self], name=TOML_FILE, cwd: Path | None = None, allow_cwd=False
248
+ ) -> Path:
243
249
  if d := cls.work_dir(name, cwd or Path.cwd(), cls.path_depth):
244
250
  return d
251
+ if allow_cwd:
252
+ return cls.get_root_dir(cwd)
245
253
  raise EnvError(f"{name} not found! Make sure this is a poetry project.")
246
254
 
247
255
  @classmethod
248
- def load_toml_text(cls):
256
+ def load_toml_text(cls: Type[Self]) -> str:
249
257
  toml_file = cls.get_work_dir().resolve() / TOML_FILE # to be optimize
250
258
  return toml_file.read_text("utf8")
251
259
 
@@ -254,7 +262,7 @@ class Project:
254
262
  return Path(sys.executable).parent
255
263
 
256
264
  @classmethod
257
- def get_root_dir(cls, cwd: Path | None = None) -> Path:
265
+ def get_root_dir(cls: Type[Self], cwd: Path | None = None) -> Path:
258
266
  root = cwd or Path.cwd()
259
267
  venv_parent = cls.python_exec_dir().parent.parent
260
268
  if root.is_relative_to(venv_parent):
@@ -313,7 +321,7 @@ class UpgradeDependencies(Project, DryRun):
313
321
 
314
322
  @classmethod
315
323
  def build_args(
316
- cls, package_lines: list[str]
324
+ cls: Type[Self], package_lines: list[str]
317
325
  ) -> tuple[list[str], dict[str, list[str]]]:
318
326
  args: list[str] = [] # ['typer[all]', 'fastapi']
319
327
  specials: dict[str, list[str]] = {} # {'--platform linux': ['gunicorn']}
@@ -347,7 +355,7 @@ class UpgradeDependencies(Project, DryRun):
347
355
  return args, specials
348
356
 
349
357
  @classmethod
350
- def should_with_dev(cls):
358
+ def should_with_dev(cls: Type[Self]) -> bool:
351
359
  text = cls.load_toml_text()
352
360
  return cls.DevFlag.new in text or cls.DevFlag.old in text
353
361
 
@@ -364,7 +372,7 @@ class UpgradeDependencies(Project, DryRun):
364
372
 
365
373
  @classmethod
366
374
  def get_args(
367
- cls, toml_text: str | None = None
375
+ cls: Type[Self], toml_text: str | None = None
368
376
  ) -> tuple[list[str], list[str], list[list[str]], str]:
369
377
  if toml_text is None:
370
378
  toml_text = cls.load_toml_text()
@@ -390,7 +398,7 @@ class UpgradeDependencies(Project, DryRun):
390
398
  return prod_packs, dev_packs, others, dev_flag
391
399
 
392
400
  @classmethod
393
- def gen_cmd(cls) -> str:
401
+ def gen_cmd(cls: Type[Self]) -> str:
394
402
  main_args, dev_args, others, dev_flags = cls.get_args()
395
403
  return cls.to_cmd(main_args, dev_args, others, dev_flags)
396
404
 
@@ -413,30 +421,30 @@ class UpgradeDependencies(Project, DryRun):
413
421
  _upgrade += f" && poetry add {' '.join(single)}"
414
422
  return _upgrade
415
423
 
416
- def gen(self) -> str:
417
- return self.gen_cmd()
424
+ def gen(self: Self) -> str:
425
+ return self.gen_cmd() + " && poetry update"
418
426
 
419
427
 
420
428
  @cli.command()
421
429
  def upgrade(
422
430
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
423
- ):
431
+ ) -> None:
424
432
  """Upgrade dependencies in pyproject.toml to latest versions"""
425
433
  UpgradeDependencies(dry=dry).run()
426
434
 
427
435
 
428
436
  class GitTag(DryRun):
429
- def __init__(self, message, dry):
437
+ def __init__(self: Self, message: str, dry: bool) -> None:
430
438
  self.message = message
431
439
  super().__init__(dry=dry)
432
440
 
433
- def has_v_prefix(self) -> bool:
441
+ def has_v_prefix(self: Self) -> bool:
434
442
  return "v" in capture_cmd_output("git tag")
435
443
 
436
- def should_push(self) -> bool:
444
+ def should_push(self: Self) -> bool:
437
445
  return "git push" in self.git_status
438
446
 
439
- def gen(self):
447
+ def gen(self: Self) -> str:
440
448
  _version = get_current_version(verbose=False)
441
449
  if self.has_v_prefix():
442
450
  # Add `v` at prefix to compare with bumpversion tool
@@ -447,17 +455,17 @@ class GitTag(DryRun):
447
455
  return cmd
448
456
 
449
457
  @cached_property
450
- def git_status(self) -> str:
458
+ def git_status(self: Self) -> str:
451
459
  return capture_cmd_output("git status")
452
460
 
453
- def mark_tag(self) -> bool:
461
+ def mark_tag(self: Self) -> bool:
454
462
  if not re.search(r"working (tree|directory) clean", self.git_status):
455
463
  run_and_echo("git status")
456
464
  echo("ERROR: Please run git commit to make sure working tree is clean!")
457
465
  return False
458
466
  return bool(super().run())
459
467
 
460
- def run(self):
468
+ def run(self: Self) -> None:
461
469
  if self.mark_tag() and not self.dry:
462
470
  echo("You may want to publish package:\n poetry publish --build")
463
471
 
@@ -466,13 +474,13 @@ class GitTag(DryRun):
466
474
  def tag(
467
475
  message: str = Option("", "-m", "--message"),
468
476
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
469
- ):
477
+ ) -> None:
470
478
  """Run shell command: git tag -a <current-version-in-pyproject.toml> -m {message}"""
471
479
  GitTag(message, dry=dry).run()
472
480
 
473
481
 
474
482
  class LintCode(DryRun):
475
- def __init__(self, args, check_only=False, _exit=False, dry=False):
483
+ def __init__(self: Self, args, check_only=False, _exit=False, dry=False) -> None:
476
484
  self.args = args
477
485
  self.check_only = check_only
478
486
  super().__init__(_exit, dry)
@@ -482,7 +490,7 @@ class LintCode(DryRun):
482
490
  return check_call("black --version")
483
491
 
484
492
  @classmethod
485
- def to_cmd(cls, paths=".", check_only=False):
493
+ def to_cmd(cls: Type[Self], paths=".", check_only=False) -> str:
486
494
  cmd = ""
487
495
  tools = ["isort --profile=black", "black", "ruff check --fix", "mypy"]
488
496
  if check_only:
@@ -495,10 +503,7 @@ class LintCode(DryRun):
495
503
  tools = tools[:-1]
496
504
  lint_them = " && ".join("{0}{%d} {1}" % i for i in range(2, len(tools) + 2))
497
505
  current_path = Path.cwd()
498
- try:
499
- root = Project.get_work_dir(cwd=current_path)
500
- except EnvError:
501
- root = Project.get_root_dir(cwd=current_path)
506
+ root = Project.get_work_dir(cwd=current_path, allow_cwd=True)
502
507
  app_name = root.name.replace("-", "_")
503
508
  if (app_dir := root / app_name).exists() or (app_dir := root / "app").exists():
504
509
  if current_path == app_dir:
@@ -524,18 +529,18 @@ class LintCode(DryRun):
524
529
  cmd += lint_them.format(prefix, paths, *tools)
525
530
  return cmd
526
531
 
527
- def gen(self) -> str:
532
+ def gen(self: Self) -> str:
528
533
  paths = " ".join(self.args) if self.args else "."
529
534
  return self.to_cmd(paths, self.check_only)
530
535
 
531
536
 
532
- def lint(files=None, dry=False):
537
+ def lint(files=None, dry=False) -> None:
533
538
  if files is None:
534
539
  files = parse_files(sys.argv[1:])
535
540
  LintCode(files, dry=dry).run()
536
541
 
537
542
 
538
- def check(files=None, dry=False):
543
+ def check(files=None, dry=False) -> None:
539
544
  LintCode(files, check_only=True, _exit=True, dry=dry).run()
540
545
 
541
546
 
@@ -544,7 +549,7 @@ def make_style(
544
549
  files: list[str],
545
550
  check_only: bool = Option(False, "--check-only", "-c"),
546
551
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
547
- ):
552
+ ) -> None:
548
553
  """Run: isort+black+ruff to reformat code and then mypy to check"""
549
554
  if isinstance(files, str):
550
555
  files = [files]
@@ -557,13 +562,13 @@ def make_style(
557
562
  @cli.command(name="check")
558
563
  def only_check(
559
564
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
560
- ):
565
+ ) -> None:
561
566
  """Check code style without reformat"""
562
567
  check(dry=dry)
563
568
 
564
569
 
565
570
  class Sync(DryRun):
566
- def __init__(self, filename: str, extras: str, save: bool, dry=False):
571
+ def __init__(self: Self, filename: str, extras: str, save: bool, dry=False) -> None:
567
572
  self.filename = filename
568
573
  self.extras = extras
569
574
  self._save = save
@@ -594,20 +599,41 @@ def sync(
594
599
  False, "--save", "-s", help="Whether save the requirement file"
595
600
  ),
596
601
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
597
- ):
602
+ ) -> None:
598
603
  """Export dependencies by poetry to a txt file then install by pip."""
599
604
  Sync(filename, extras, save, dry=dry).run()
600
605
 
601
606
 
607
+ def _should_run_test_script(path: Path) -> bool:
608
+ return path.exists()
609
+
610
+
602
611
  @cli.command()
603
612
  def test(
604
613
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
605
- ):
614
+ ) -> None:
606
615
  """Run unittest by pytest and report coverage"""
607
- cmd = 'coverage run -m pytest -s && coverage report --omit="tests/*" -m'
608
- if not is_venv() or not check_call("coverage --version"):
609
- sep = " && "
610
- cmd = sep.join("poetry run " + i for i in cmd.split(sep))
616
+ cwd = Path.cwd()
617
+ root = Project.get_work_dir(cwd=cwd, allow_cwd=True)
618
+ test_script = root / "scripts" / "test.sh"
619
+ if _should_run_test_script(test_script):
620
+ cmd = f"sh {test_script.relative_to(root)}"
621
+ if cwd != root:
622
+ cmd = f"cd {root} && " + cmd
623
+ else:
624
+ cmd = 'coverage run -m pytest -s && coverage report --omit="tests/*" -m'
625
+ if not is_venv() or not check_call("coverage --version"):
626
+ sep = " && "
627
+ cmd = sep.join("poetry run " + i for i in cmd.split(sep))
628
+ exit_if_run_failed(cmd, dry=dry)
629
+
630
+
631
+ @cli.command()
632
+ def upload(
633
+ dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
634
+ ) -> None:
635
+ """Shortcut for package publish"""
636
+ cmd = "poetry publish --build"
611
637
  exit_if_run_failed(cmd, dry=dry)
612
638
 
613
639
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fast-dev-cli"
3
- version = "0.6.4"
3
+ version = "0.6.6"
4
4
  description = ""
5
5
  authors = ["Waket Zheng <waketzheng@gmail.com>"]
6
6
  readme = "README.md"
File without changes