fast-dev-cli 0.23.1__tar.gz → 0.24.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fast-dev-cli
3
- Version: 0.23.1
3
+ Version: 0.24.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.24.0"
@@ -12,7 +12,7 @@ import subprocess # nosec:B404
12
12
  import sys
13
13
  from functools import cached_property
14
14
  from pathlib import Path
15
- from typing import TYPE_CHECKING, Any, Literal, cast, get_args, overload
15
+ from typing import TYPE_CHECKING, Annotated, Any, Literal, cast, get_args, overload
16
16
 
17
17
  import typer
18
18
  from typer import Exit, Option, echo, secho
@@ -148,15 +148,15 @@ class Shell:
148
148
  if isinstance(command, str):
149
149
  cs = shlex.split(command)
150
150
  if "shell" not in self._kw and not (set(self._cmd) & {"|", ">", "&"}):
151
- command = self.extend_user(cs)
151
+ command = self.expand_user(cs)
152
152
  elif any(i.startswith("~") for i in cs):
153
153
  command = re.sub(r" ~", " " + os.path.expanduser("~"), command)
154
154
  else:
155
- command = self.extend_user(command)
155
+ command = self.expand_user(command)
156
156
  return command
157
157
 
158
158
  @staticmethod
159
- def extend_user(cs: list[str]) -> list[str]:
159
+ def expand_user(cs: list[str]) -> list[str]:
160
160
  if cs[0] == "echo":
161
161
  return cs
162
162
  for i, c in enumerate(cs):
@@ -255,28 +255,18 @@ def read_version_from_file(
255
255
  secho(f"WARNING: can not find 'version' item in {version_file}!")
256
256
  return "0.0.0"
257
257
  pattern = re.compile(r"__version__\s*=")
258
- for line in Path(version_file).read_text("utf-8").splitlines():
258
+ all_lines = Path(version_file).read_text("utf-8").strip().splitlines()
259
+ for line in all_lines:
259
260
  if pattern.match(line):
260
261
  return _parse_version(line, pattern)
261
- # TODO: remove or refactor the following lines.
262
- if work_dir is None:
263
- work_dir = Project.get_work_dir()
264
- package_dir = work_dir / package_name
265
- if (
266
- not (init_file := package_dir / "__init__.py").exists()
267
- and not (init_file := work_dir / "src" / package_name / init_file.name).exists()
268
- and not (init_file := work_dir / "app" / init_file.name).exists()
269
- ):
270
- secho("WARNING: __init__.py file does not exist!")
262
+ else:
263
+ pattern = re.compile(r"VERSION\s*=")
264
+ for line in all_lines:
265
+ if pattern.match(line):
266
+ return _parse_version(line, pattern)
267
+ secho(f"WARNING: can not find version pattern in {version_file}!")
271
268
  return "0.0.0"
272
269
 
273
- pattern = re.compile(r"__version__\s*=")
274
- for line in init_file.read_text("utf-8").splitlines():
275
- if pattern.match(line):
276
- return _parse_version(line, pattern)
277
- secho(f"WARNING: can not find '__version__' var in {init_file}!")
278
- return "0.0.0"
279
-
280
270
 
281
271
  def _get_frontend_version() -> tuple[Path, str] | None:
282
272
  try:
@@ -326,11 +316,7 @@ def get_current_version(
326
316
  check_version: bool = False,
327
317
  ) -> str | tuple[bool, str]:
328
318
  if is_poetry is True or Project.manage_by_poetry():
329
- cmd = ["poetry", "version", "-s"]
330
- if verbose:
331
- echo(f"--> {' '.join(cmd)}")
332
- if out := capture_cmd_output(cmd, raises=True):
333
- out = out.splitlines()[-1].strip().split()[-1]
319
+ out = _get_poetry_project_version(verbose)
334
320
  if check_version:
335
321
  return True, out
336
322
  return out
@@ -364,6 +350,15 @@ def get_current_version(
364
350
  return current_version
365
351
 
366
352
 
353
+ def _get_poetry_project_version(verbose: bool) -> str:
354
+ cmd = ["poetry", "version", "-s"]
355
+ if verbose:
356
+ echo(f"--> {' '.join(cmd)}")
357
+ if out := capture_cmd_output(cmd, raises=True):
358
+ out = out.splitlines()[-1].strip().split()[-1]
359
+ return out
360
+
361
+
367
362
  def _ensure_bool(value: bool | OptionInfo) -> bool:
368
363
  if isinstance(value, bool):
369
364
  return value
@@ -438,7 +433,7 @@ class BumpUp(DryRun):
438
433
 
439
434
  @staticmethod
440
435
  def parse_dynamic_version(
441
- toml_text: str,
436
+ toml_text: str | None,
442
437
  context: dict[str, Any],
443
438
  work_dir: Path | None = None,
444
439
  ) -> str | None:
@@ -452,9 +447,11 @@ class BumpUp(DryRun):
452
447
  or work_dir.joinpath(version_path).exists()
453
448
  ):
454
449
  return version_path
455
- # version = { source = "file", path = "fast_dev_cli/__init__.py" }
450
+ # e.g.: version = { source = "file", path = "fast_dev_cli/__init__.py" }
456
451
  v_key = "version = "
457
452
  p_key = 'path = "'
453
+ if toml_text is None:
454
+ toml_text = Project.load_toml_text()
458
455
  for line in toml_text.splitlines():
459
456
  if not line.startswith(v_key):
460
457
  continue
@@ -470,10 +467,20 @@ class BumpUp(DryRun):
470
467
  toml_text: str | None = None,
471
468
  work_dir: Path | None = None,
472
469
  package_name: str | None = None,
470
+ context: dict | None = None,
473
471
  ) -> str:
474
- if toml_text is None:
475
- toml_text = Project.load_toml_text()
476
- context = tomllib.loads(toml_text)
472
+ if context is None:
473
+ if toml_text is None:
474
+ toml_text = Project.load_toml_text()
475
+ context = tomllib.loads(toml_text)
476
+ is_dynamic_version = False
477
+ with contextlib.suppress(KeyError):
478
+ if "version" in context["project"]["dynamic"]:
479
+ is_dynamic_version = True
480
+ if is_dynamic_version:
481
+ if filename := cls.parse_dynamic_version(toml_text, context, work_dir):
482
+ return filename
483
+ yellow_warn("Failed to find version file for this dynamic version project.")
477
484
  by_version_plugin = False
478
485
  try:
479
486
  ver = context["project"]["version"]
@@ -497,7 +504,6 @@ class BumpUp(DryRun):
497
504
  by_version_plugin = version_value in ("0", "0.0.0", "init")
498
505
  if by_version_plugin:
499
506
  return cls.parse_plugin_version(context, package_name)
500
-
501
507
  return TOML_FILE
502
508
 
503
509
  @staticmethod
@@ -1704,13 +1710,21 @@ class MakeDeps(DryRun):
1704
1710
  tool: str,
1705
1711
  prod: bool = False,
1706
1712
  dry: bool = False,
1707
- active: bool = True,
1708
- inexact: bool = True,
1713
+ active: bool = False,
1714
+ inexact: bool = False,
1715
+ no_dev: bool = False,
1716
+ verbose: bool = False,
1717
+ no_extra: list[str] | None = None,
1718
+ no_group: list[str] | None = None,
1709
1719
  ) -> None:
1710
1720
  self._tool = tool
1711
1721
  self._prod = prod
1712
- self._active = active
1713
- self._inexact = inexact
1722
+ self._active = active or load_bool("FASTDEVCLI_DEPS_ACTIVE")
1723
+ self._inexact = inexact or load_bool("FASTDEVCLI_DEPS_INEXACT")
1724
+ self._verbose = verbose
1725
+ self._no_dev = no_dev
1726
+ self._no_extra = no_extra
1727
+ self._no_group = no_group
1714
1728
  super().__init__(dry=dry)
1715
1729
 
1716
1730
  def should_ensure_pip(self) -> bool:
@@ -1725,13 +1739,53 @@ class MakeDeps(DryRun):
1725
1739
  return ["dev"]
1726
1740
 
1727
1741
  def gen(self) -> str:
1742
+ cmd = self._gen()
1743
+ if self._verbose:
1744
+ cmd += " --verbose"
1745
+ if self._no_dev:
1746
+ opt = " --no-dev"
1747
+ if opt not in cmd:
1748
+ cmd += opt
1749
+ if self._no_extra:
1750
+ cmd += " " + " ".join(f"--no-extra {i}" for i in self._no_extra)
1751
+ if self._no_group:
1752
+ cmd += " " + " ".join(f"--no-group {i}" for i in self._no_group)
1753
+ if opts := os.getenv("FASTDEVCLI_DEPS_OPTS"):
1754
+ cmd += " " + opts.strip()
1755
+ return cmd
1756
+
1757
+ def get_package_name(self) -> str:
1758
+ with contextlib.suppress(FileNotFoundError, KeyError):
1759
+ try:
1760
+ toml_text = Project.load_toml_text()
1761
+ except EnvError:
1762
+ return ""
1763
+ doc = tomllib.loads(toml_text)
1764
+ tool_section = doc["tool"]
1765
+ uv_package = tool_section.get("uv", {}).get("package")
1766
+ if uv_package is not None:
1767
+ return doc["project"]["name"] if uv_package else ""
1768
+ match doc["build-system"]["build-backend"]:
1769
+ case "pdm.backend":
1770
+ if not tool_section.get("pdm", {}).get("distribution", True):
1771
+ return ""
1772
+ case x if x.startswith("poetry"):
1773
+ if not tool_section.get("poetry", {}).get("package-mode", True):
1774
+ return ""
1775
+ return doc["project"]["name"]
1776
+ return ""
1777
+
1778
+ def _gen(self) -> str:
1728
1779
  if self._tool == "pdm":
1729
1780
  return "pdm install --frozen " + ("--prod" if self._prod else "-G :all")
1730
1781
  elif self._tool == "uv":
1731
- uv_sync = "uv sync" + " --inexact" * self._inexact
1732
- if self._active:
1733
- uv_sync += " --active"
1734
- return uv_sync + ("" if self._prod else " --all-extras --all-groups")
1782
+ uv_sync = "uv sync"
1783
+ if project := self.get_package_name():
1784
+ uv_sync += f" --reinstall-package={project}"
1785
+ uv_sync += " --inexact" * self._inexact + " --active" * self._active
1786
+ return uv_sync + (
1787
+ " --no-dev" if self._prod else " --all-extras --all-groups"
1788
+ )
1735
1789
  elif self._tool == "poetry":
1736
1790
  return "poetry install " + (
1737
1791
  "--only=main" if self._prod else "--all-extras --all-groups"
@@ -1761,11 +1815,15 @@ def make_deps(
1761
1815
  use_pip: bool = Option(False, "--pip", help="Use `pip` to install deps"),
1762
1816
  use_poetry: bool = Option(False, "--poetry", help="Use `poetry` to install deps"),
1763
1817
  active: bool = Option(
1764
- True, help="Add `--active` to uv sync command(Only work for uv project)"
1818
+ False, help="Add `--active` to uv sync command(Only work for uv project)"
1765
1819
  ),
1766
1820
  inexact: bool = Option(
1767
- True, help="Add `--inexact` to uv sync command(Only work for uv project)"
1821
+ False, help="Add `--inexact` to uv sync command(Only work for uv project)"
1768
1822
  ),
1823
+ no_dev: bool = Option(False, "--no-dev"),
1824
+ no_extra: Annotated[list[str] | None, Option()] = None,
1825
+ no_group: Annotated[list[str] | None, Option()] = None,
1826
+ verbose: bool = Option(False, "--verbose"),
1769
1827
  dry: bool = DryOption,
1770
1828
  ) -> None:
1771
1829
  """Run: ruff check/format to reformat code and then mypy to check"""
@@ -1784,7 +1842,14 @@ def make_deps(
1784
1842
  tool = "poetry"
1785
1843
  elif tool == ToolOption.default:
1786
1844
  tool = Project.get_manage_tool(cache=True) or "pip"
1787
- MakeDeps(tool, prod, active=active, inexact=inexact, dry=dry).run()
1845
+ bool_opts = {
1846
+ "active": active,
1847
+ "inexact": inexact,
1848
+ "no_dev": no_dev,
1849
+ "verbose": verbose,
1850
+ "dry": dry,
1851
+ }
1852
+ MakeDeps(tool, prod, no_extra=no_extra, no_group=no_group, **bool_opts).run()
1788
1853
 
1789
1854
 
1790
1855
  class UvPypi(DryRun):
@@ -29,7 +29,7 @@ dependencies = [
29
29
  "typer>=0.24.0,<1",
30
30
  "tomli >=2.0.1,<3; python_version < '3.11'",
31
31
  ]
32
- version = "0.23.1"
32
+ version = "0.24.0"
33
33
 
34
34
  [project.urls]
35
35
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -52,7 +52,9 @@ fast = "fast_dev_cli.cli:main"
52
52
 
53
53
  [dependency-groups]
54
54
  dev = [
55
- "asynctor>=0.10.0",
55
+ "asynctor>=0.13.0",
56
+ "httpx2>=2.5.0",
57
+ "tomli-w>=1.2.0",
56
58
  ]
57
59
 
58
60
  [build-system]
@@ -1 +0,0 @@
1
- __version__ = "0.23.1"
File without changes
File without changes