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.
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/PKG-INFO +6 -6
- fast_dev_cli-0.17.3/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/fast_dev_cli/cli.py +160 -1
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/pyproject.toml +6 -8
- fast_dev_cli-0.17.3/tests/test_deps.py +33 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_lint.py +2 -2
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_version.py +1 -1
- fast_dev_cli-0.17.2/fast_dev_cli/__init__.py +0 -1
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/LICENSE +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/README.md +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/pdm_build.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/check.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/deps.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/format.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/scripts/test.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/__init__.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/conftest.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_bump.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_exec.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_fast_test.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_functions.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_help.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_poetry_version_plugin.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_sync.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_upgrade.py +0 -0
- {fast_dev_cli-0.17.2 → fast_dev_cli-0.17.3}/tests/test_upload.py +0 -0
- {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.
|
|
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
|
|
27
|
+
Requires-Dist: emoji>=2.12.1
|
|
28
28
|
Requires-Dist: packaging>=20.5
|
|
29
|
-
Requires-Dist: coverage
|
|
30
|
-
Requires-Dist: mypy
|
|
31
|
-
Requires-Dist: bumpversion2
|
|
32
|
-
Requires-Dist: pytest
|
|
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
|
|
38
|
+
"emoji >=2.12.1",
|
|
39
39
|
"packaging >=20.5",
|
|
40
|
-
"coverage >=7.5.1
|
|
41
|
-
"mypy >=1.15.0
|
|
42
|
-
"bumpversion2 >=1.4.3
|
|
43
|
-
"pytest >=8.2.0
|
|
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.
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|