fast-dev-cli 0.17.2__tar.gz → 0.17.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.
Files changed (31) hide show
  1. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/PKG-INFO +6 -6
  2. fast_dev_cli-0.17.3/fast_dev_cli/__init__.py +1 -0
  3. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/fast_dev_cli/cli.py +160 -1
  4. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/pyproject.toml +6 -8
  5. fast_dev_cli-0.17.3/tests/test_deps.py +33 -0
  6. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_lint.py +2 -2
  7. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_version.py +1 -1
  8. fast_dev_cli-0.17.2/fast_dev_cli/__init__.py +0 -1
  9. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/LICENSE +0 -0
  10. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/README.md +0 -0
  11. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/fast_dev_cli/__main__.py +0 -0
  12. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/fast_dev_cli/py.typed +0 -0
  13. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/pdm_build.py +0 -0
  14. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/check.py +0 -0
  15. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/deps.py +0 -0
  16. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/format.py +0 -0
  17. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/test.py +0 -0
  18. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/__init__.py +0 -0
  19. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/conftest.py +0 -0
  20. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_bump.py +0 -0
  21. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_exec.py +0 -0
  22. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_fast_test.py +0 -0
  23. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_functions.py +0 -0
  24. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_help.py +0 -0
  25. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_poetry_version_plugin.py +0 -0
  26. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_runserver.py +0 -0
  27. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_sync.py +0 -0
  28. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_tag.py +0 -0
  29. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_upgrade.py +0 -0
  30. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_upload.py +0 -0
  31. {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fast-dev-cli
3
- Version: 0.17.2
3
+ Version: 0.17.3
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -24,12 +24,12 @@ Project-URL: Homepage, https://github.com/waketzheng/fast-dev-cli
24
24
  Requires-Python: >=3.9
25
25
  Requires-Dist: typer<1,>=0.12.3
26
26
  Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
27
- Requires-Dist: emoji<3,>=2.12.1
27
+ Requires-Dist: emoji>=2.12.1
28
28
  Requires-Dist: packaging>=20.5
29
- Requires-Dist: coverage<8,>=7.5.1
30
- Requires-Dist: mypy<2,>=1.15.0
31
- Requires-Dist: bumpversion2<2,>=1.4.3
32
- Requires-Dist: pytest<9,>=8.2.0
29
+ Requires-Dist: coverage>=7.5.1
30
+ Requires-Dist: mypy>=1.15.0
31
+ Requires-Dist: bumpversion2>=1.4.3
32
+ Requires-Dist: pytest>=8.2.0
33
33
  Provides-Extra: include-optional-dependencies
34
34
  Requires-Dist: all; extra == "include-optional-dependencies"
35
35
  Description-Content-Type: text/markdown
@@ -0,0 +1 @@
1
+ __version__ = "0.17.3"
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import contextlib
4
+ import functools
4
5
  import importlib.metadata as importlib_metadata
5
6
  import os
6
7
  import re
@@ -21,6 +22,7 @@ from typing import (
21
22
  )
22
23
 
23
24
  import typer
25
+ from click import UsageError
24
26
  from typer import Exit, Option, echo, secho
25
27
  from typer.models import ArgumentInfo, OptionInfo
26
28
 
@@ -285,7 +287,7 @@ def exit_if_run_failed(
285
287
 
286
288
  class DryRun:
287
289
  def __init__(self, _exit: bool = False, dry: bool = False) -> None:
288
- self.dry = dry
290
+ self.dry = _ensure_bool(dry)
289
291
  self._exit = _exit
290
292
 
291
293
  def gen(self) -> str:
@@ -1317,6 +1319,163 @@ def run_by_subprocess(cmd: str, dry: bool = DryOption) -> None:
1317
1319
  raise Exit(rc)
1318
1320
 
1319
1321
 
1322
+ class MakeDeps(DryRun):
1323
+ def __init__(self, tool: str, prod: bool = False, dry: bool = False) -> None:
1324
+ self._tool = tool
1325
+ self._prod = prod
1326
+ super().__init__(dry=dry)
1327
+
1328
+ def should_ensure_pip(self) -> bool:
1329
+ return True
1330
+
1331
+ def should_upgrade_pip(self) -> bool:
1332
+ return True
1333
+
1334
+ def get_groups(self) -> list[str]:
1335
+ if self._prod:
1336
+ return []
1337
+ return ["dev"]
1338
+
1339
+ def gen(self) -> str:
1340
+ if self._tool == "pdm":
1341
+ return "pdm sync " + ("--prod" if self._prod else "-G :all")
1342
+ elif self._tool == "uv":
1343
+ return "uv sync --inexact --active" + (
1344
+ "" if self._prod else " --all-extras --all-groups"
1345
+ )
1346
+ elif self._tool == "poetry":
1347
+ return "poetry install " + (
1348
+ "--only=main" if self._prod else "--all-extras --all-groups"
1349
+ )
1350
+ else:
1351
+ cmd = "python -m pip install -e ."
1352
+ if gs := self.get_groups():
1353
+ cmd += " " + " ".join(f"--group {g}" for g in gs)
1354
+ upgrade = "python -m pip install --upgrade pip"
1355
+ if self.should_ensure_pip():
1356
+ cmd = f"python -m ensurepip && {upgrade} && {cmd}"
1357
+ elif self.should_upgrade_pip():
1358
+ cmd = "{upgrade} && {cmd}"
1359
+ return cmd
1360
+
1361
+
1362
+ @cli.command(name="deps")
1363
+ def make_deps(
1364
+ prod: bool = Option(
1365
+ False,
1366
+ "--prod",
1367
+ help="Only instead production dependencies.",
1368
+ ),
1369
+ tool: str = ToolOption,
1370
+ use_uv: bool = Option(False, "--uv", help="Use `uv` to install deps"),
1371
+ use_pdm: bool = Option(False, "--pdm", help="Use `pdm` to install deps"),
1372
+ use_pip: bool = Option(False, "--pip", help="Use `pip` to install deps"),
1373
+ use_poetry: bool = Option(False, "--poetry", help="Use `poetry` to install deps"),
1374
+ dry: bool = DryOption,
1375
+ ) -> None:
1376
+ """Run: ruff check/format to reformat code and then mypy to check"""
1377
+ if use_uv + use_pdm + use_pip + use_poetry > 1:
1378
+ raise UsageError("`--uv/--pdm/--pip/--poetry` can only choose one!")
1379
+ if use_uv:
1380
+ tool = "uv"
1381
+ elif use_pdm:
1382
+ tool = "pdm"
1383
+ elif use_pip:
1384
+ tool = "pip"
1385
+ elif use_poetry:
1386
+ tool = "poetry"
1387
+ elif tool == ToolOption.default:
1388
+ tool = Project.get_manage_tool(cache=True) or "pip"
1389
+ MakeDeps(tool, prod, dry=dry).run()
1390
+
1391
+
1392
+ class UvPypi(DryRun):
1393
+ PYPI = "https://pypi.org/simple"
1394
+ HOST = "https://files.pythonhosted.org"
1395
+
1396
+ def __init__(self, lock_file: Path, dry: bool, verbose: bool, quiet: bool) -> None:
1397
+ super().__init__(dry=dry)
1398
+ self.lock_file = lock_file
1399
+ self._verbose = _ensure_bool(verbose)
1400
+ self._quiet = _ensure_bool(quiet)
1401
+
1402
+ def run(self) -> None:
1403
+ try:
1404
+ rc = self.update_lock(self.lock_file, self._verbose, self._quiet)
1405
+ except ValueError as e:
1406
+ secho(str(e), fg=typer.colors.RED)
1407
+ raise Exit(1) from e
1408
+ else:
1409
+ if rc != 0:
1410
+ raise Exit(rc)
1411
+
1412
+ @classmethod
1413
+ def update_lock(cls, p: Path, verbose: bool, quiet: bool) -> int:
1414
+ text = p.read_text("utf-8")
1415
+ registry_pattern = r'(registry = ")(.*?)"'
1416
+ replace_registry = functools.partial(
1417
+ re.sub, registry_pattern, rf'\1{cls.PYPI}"'
1418
+ )
1419
+ registry_urls = {i[1] for i in re.findall(registry_pattern, text)}
1420
+ download_pattern = r'(url = ")(https?://.*?)(/packages/.*?\.)(gz|whl)"'
1421
+ replace_host = functools.partial(
1422
+ re.sub, download_pattern, rf'\1{cls.HOST}\3\4"'
1423
+ )
1424
+ download_hosts = {i[1] for i in re.findall(download_pattern, text)}
1425
+ if not registry_urls:
1426
+ raise ValueError(f"Failed to find pattern {registry_pattern!r} in {p}")
1427
+ if len(registry_urls) == 1:
1428
+ current_registry = registry_urls.pop()
1429
+ if current_registry == cls.PYPI:
1430
+ if download_hosts == {cls.HOST}:
1431
+ if verbose:
1432
+ echo(f"Registry of {p} is {cls.PYPI}, no need to change.")
1433
+ return 0
1434
+ else:
1435
+ text = replace_registry(text)
1436
+ if verbose:
1437
+ echo(f"{current_registry} --> {cls.PYPI}")
1438
+ else:
1439
+ # TODO: ask each one to confirm replace
1440
+ text = replace_registry(text)
1441
+ if verbose:
1442
+ for current_registry in sorted(registry_urls):
1443
+ echo(f"{current_registry} --> {cls.PYPI}")
1444
+ if len(download_hosts) == 1:
1445
+ current_host = download_hosts.pop()
1446
+ if current_host != cls.HOST:
1447
+ text = replace_host(text)
1448
+ if verbose:
1449
+ print(current_host, "-->", cls.HOST)
1450
+ elif download_hosts:
1451
+ # TODO: ask each one to confirm replace
1452
+ text = replace_host(text)
1453
+ if verbose:
1454
+ for current_host in sorted(download_hosts):
1455
+ echo(f"{current_host} --> {cls.HOST}")
1456
+ size = p.write_text(text, encoding="utf-8")
1457
+ if verbose:
1458
+ echo(f"Updated {p} with {size} bytes.")
1459
+ if quiet:
1460
+ return 0
1461
+ return 1
1462
+
1463
+
1464
+ @cli.command()
1465
+ def pypi(
1466
+ dry: bool = DryOption,
1467
+ verbose: bool = False,
1468
+ quiet: bool = False,
1469
+ ) -> None:
1470
+ """Change registry of uv.lock to be pypi.org"""
1471
+ if not (p := Path("uv.lock")).exists() and not (
1472
+ (p := Project.get_work_dir() / p.name).exists()
1473
+ ):
1474
+ yellow_warn(f"{p.name!r} not found!")
1475
+ return
1476
+ UvPypi(p, dry, verbose, quiet).run()
1477
+
1478
+
1320
1479
  def version_callback(value: bool) -> None:
1321
1480
  if value:
1322
1481
  echo("Fast Dev Cli Version: " + typer.style(__version__, bold=True))
@@ -35,14 +35,14 @@ classifiers = [
35
35
  dependencies = [
36
36
  "typer >=0.12.3,<1",
37
37
  "tomli >=2.0.1,<3; python_version < '3.11'",
38
- "emoji >=2.12.1,<3",
38
+ "emoji >=2.12.1",
39
39
  "packaging >=20.5",
40
- "coverage >=7.5.1,<8",
41
- "mypy >=1.15.0,<2",
42
- "bumpversion2 >=1.4.3,<2",
43
- "pytest >=8.2.0,<9",
40
+ "coverage >=7.5.1",
41
+ "mypy >=1.15.0",
42
+ "bumpversion2 >=1.4.3",
43
+ "pytest >=8.2.0",
44
44
  ]
45
- version = "0.17.2"
45
+ version = "0.17.3"
46
46
 
47
47
  [project.urls]
48
48
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -58,8 +58,6 @@ fast = "fast_dev_cli:cli.main"
58
58
  [dependency-groups]
59
59
  dev = [
60
60
  "twine>=6.1.0",
61
- ]
62
- test = [
63
61
  "asynctor>=0.8.6",
64
62
  ]
65
63
 
@@ -0,0 +1,33 @@
1
+ from fast_dev_cli.cli import MakeDeps, capture_cmd_output
2
+
3
+
4
+ def test_make_deps_class():
5
+ assert (
6
+ MakeDeps("uv", prod=False).gen()
7
+ == "uv sync --inexact --active --all-extras --all-groups"
8
+ )
9
+ assert MakeDeps("uv", prod=True).gen() == "uv sync --inexact --active"
10
+ assert MakeDeps("pdm", prod=False).gen() == "pdm sync -G :all"
11
+ assert MakeDeps("pdm", prod=True).gen() == "pdm sync --prod"
12
+ assert (
13
+ MakeDeps("poetry", prod=False).gen()
14
+ == "poetry install --all-extras --all-groups"
15
+ )
16
+ assert MakeDeps("poetry", prod=True).gen() == "poetry install --only=main"
17
+ assert (
18
+ MakeDeps("pip", prod=False).gen()
19
+ == "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install -e . --group dev"
20
+ )
21
+ assert (
22
+ MakeDeps("pip", prod=True).gen()
23
+ == "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install -e ."
24
+ )
25
+
26
+
27
+ def test_fast_deps():
28
+ out = capture_cmd_output("fast deps --dry")
29
+ assert out == "--> pdm sync -G :all"
30
+ out = capture_cmd_output("fast deps --uv --prod --dry")
31
+ assert out == "--> uv sync --inexact --active"
32
+ out = capture_cmd_output("fast deps --uv --dry")
33
+ assert out == "--> uv sync --inexact --active --all-extras --all-groups"
@@ -99,7 +99,7 @@ def test_check_bandit(tmp_path):
99
99
  assert "bandit -r ." in command
100
100
 
101
101
 
102
- def test_check_skip_mypy(mock_skip_mypy_0, mocker, capsys):
102
+ def test_check_skip_mypy(mock_skip_mypy_0, mocker):
103
103
  cmd = "fast check --skip-mypy --dry"
104
104
  cmd2 = "fast lint --check-only --skip-mypy --dry"
105
105
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
@@ -271,7 +271,7 @@ def test_skip_mypy_option(mock_skip_mypy_0, mocker):
271
271
  )
272
272
 
273
273
 
274
- def test_skip_mypy_fast_lint(mock_skip_mypy_0, mocker, capsys):
274
+ def test_skip_mypy_fast_lint(mock_skip_mypy_0, mocker):
275
275
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
276
276
  command = capture_cmd_output("fast lint --skip-mypy --dry")
277
277
  cmds = LINT_CMD.split(SEP)
@@ -72,7 +72,7 @@ def test_display_self_version(capsys):
72
72
  assert out_v == out
73
73
 
74
74
 
75
- def test_fast_version(capsys):
75
+ def test_fast_version():
76
76
  out = Shell("fast version").capture_output().strip()
77
77
  assert f"Fast Dev Cli Version: {__version__}" in out
78
78
  assert f"fast_dev_cli/__init__.py: {__version__}" in out
@@ -1 +0,0 @@
1
- __version__ = "0.17.2"
File without changes
File without changes