fast-dev-cli 0.17.2__tar.gz → 0.18.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.
Files changed (34) hide show
  1. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/PKG-INFO +7 -7
  2. fast_dev_cli-0.18.0/fast_dev_cli/__init__.py +1 -0
  3. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/fast_dev_cli/cli.py +160 -1
  4. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/pyproject.toml +7 -9
  5. fast_dev_cli-0.18.0/tests/assets/uv-tx.lock +1476 -0
  6. fast_dev_cli-0.18.0/tests/assets/uv.lock +1476 -0
  7. fast_dev_cli-0.18.0/tests/test_deps.py +33 -0
  8. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_lint.py +2 -2
  9. fast_dev_cli-0.18.0/tests/test_pypi.py +43 -0
  10. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_version.py +1 -1
  11. fast_dev_cli-0.17.2/fast_dev_cli/__init__.py +0 -1
  12. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/LICENSE +0 -0
  13. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/README.md +0 -0
  14. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/fast_dev_cli/__main__.py +0 -0
  15. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/fast_dev_cli/py.typed +0 -0
  16. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/pdm_build.py +0 -0
  17. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/scripts/check.py +0 -0
  18. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/scripts/deps.py +0 -0
  19. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/scripts/format.py +0 -0
  20. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/scripts/test.py +0 -0
  21. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/__init__.py +0 -0
  22. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/conftest.py +0 -0
  23. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_bump.py +0 -0
  24. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_exec.py +0 -0
  25. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_fast_test.py +0 -0
  26. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_functions.py +0 -0
  27. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_help.py +0 -0
  28. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_poetry_version_plugin.py +0 -0
  29. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_runserver.py +0 -0
  30. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_sync.py +0 -0
  31. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_tag.py +0 -0
  32. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_upgrade.py +0 -0
  33. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/tests/test_upload.py +0 -0
  34. {fast_dev_cli-0.17.2 → fast_dev_cli-0.18.0}/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.18.0
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -22,14 +22,14 @@ Classifier: Typing :: Typed
22
22
  Classifier: License :: OSI Approved :: MIT License
23
23
  Project-URL: Homepage, https://github.com/waketzheng/fast-dev-cli
24
24
  Requires-Python: >=3.9
25
- Requires-Dist: typer<1,>=0.12.3
25
+ Requires-Dist: typer-slim<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.18.0"
@@ -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))
@@ -33,16 +33,16 @@ classifiers = [
33
33
  "License :: OSI Approved :: MIT License",
34
34
  ]
35
35
  dependencies = [
36
- "typer >=0.12.3,<1",
36
+ "typer-slim >=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.18.0"
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