fast-dev-cli 0.19.1__tar.gz → 0.19.2__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.19.1 → fast_dev_cli-0.19.2}/PKG-INFO +1 -1
- fast_dev_cli-0.19.2/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/fast_dev_cli/cli.py +100 -82
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/pyproject.toml +2 -2
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_sync.py +26 -8
- fast_dev_cli-0.19.1/fast_dev_cli/__init__.py +0 -1
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/LICENSE +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/README.md +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/pdm_build.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/scripts/check.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/scripts/deps.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/scripts/format.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/scripts/test.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/__init__.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/assets/uv-tx.lock +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/assets/uv.lock +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/conftest.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_bump.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_deps.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_exec.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_fast_test.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_functions.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_help.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_lint.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_poetry_version_plugin.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_pypi.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_upgrade.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_upload.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/test_version.py +0 -0
- {fast_dev_cli-0.19.1 → fast_dev_cli-0.19.2}/tests/utils.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.19.2"
|
|
@@ -12,16 +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
|
|
16
|
-
TYPE_CHECKING,
|
|
17
|
-
Any,
|
|
18
|
-
Literal,
|
|
19
|
-
# Optional is required by Option generated by typer
|
|
20
|
-
Optional,
|
|
21
|
-
cast,
|
|
22
|
-
get_args,
|
|
23
|
-
overload,
|
|
24
|
-
)
|
|
15
|
+
from typing import TYPE_CHECKING, Any, Literal, cast, get_args, overload
|
|
25
16
|
|
|
26
17
|
import typer
|
|
27
18
|
from click import UsageError
|
|
@@ -391,7 +382,36 @@ class BumpUp(DryRun):
|
|
|
391
382
|
return is_emoji(first_char)
|
|
392
383
|
|
|
393
384
|
@staticmethod
|
|
385
|
+
def parse_dynamic_version(
|
|
386
|
+
toml_text: str,
|
|
387
|
+
context: dict,
|
|
388
|
+
work_dir: Path | None = None,
|
|
389
|
+
) -> str | None:
|
|
390
|
+
if work_dir is None:
|
|
391
|
+
work_dir = Project.get_work_dir()
|
|
392
|
+
for tool in ("pdm", "hatch"):
|
|
393
|
+
with contextlib.suppress(KeyError):
|
|
394
|
+
version_path = cast(str, context["tool"][tool]["version"]["path"])
|
|
395
|
+
if (
|
|
396
|
+
Path(version_path).exists()
|
|
397
|
+
or work_dir.joinpath(version_path).exists()
|
|
398
|
+
):
|
|
399
|
+
return version_path
|
|
400
|
+
# version = { source = "file", path = "fast_dev_cli/__init__.py" }
|
|
401
|
+
v_key = "version = "
|
|
402
|
+
p_key = 'path = "'
|
|
403
|
+
for line in toml_text.splitlines():
|
|
404
|
+
if not line.startswith(v_key):
|
|
405
|
+
continue
|
|
406
|
+
if p_key in (value := line.split(v_key, 1)[-1].split("#")[0]):
|
|
407
|
+
filename = value.split(p_key, 1)[-1].split('"')[0]
|
|
408
|
+
if work_dir.joinpath(filename).exists():
|
|
409
|
+
return filename
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
@classmethod
|
|
394
413
|
def parse_filename(
|
|
414
|
+
cls,
|
|
395
415
|
toml_text: str | None = None,
|
|
396
416
|
work_dir: Path | None = None,
|
|
397
417
|
package_name: str | None = None,
|
|
@@ -414,74 +434,55 @@ class BumpUp(DryRun):
|
|
|
414
434
|
try:
|
|
415
435
|
version_value = context["tool"]["poetry"]["version"]
|
|
416
436
|
except KeyError:
|
|
417
|
-
if not Project.manage_by_poetry()
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
with contextlib.suppress(KeyError):
|
|
422
|
-
version_path = cast(
|
|
423
|
-
str, context["tool"][tool]["version"]["path"]
|
|
424
|
-
)
|
|
425
|
-
if (
|
|
426
|
-
Path(version_path).exists()
|
|
427
|
-
or work_dir.joinpath(version_path).exists()
|
|
428
|
-
):
|
|
429
|
-
return version_path
|
|
430
|
-
# version = { source = "file", path = "fast_dev_cli/__init__.py" }
|
|
431
|
-
v_key = "version = "
|
|
432
|
-
p_key = 'path = "'
|
|
433
|
-
for line in toml_text.splitlines():
|
|
434
|
-
if not line.startswith(v_key):
|
|
435
|
-
continue
|
|
436
|
-
if p_key in (value := line.split(v_key, 1)[-1].split("#")[0]):
|
|
437
|
-
filename = value.split(p_key, 1)[-1].split('"')[0]
|
|
438
|
-
if work_dir.joinpath(filename).exists():
|
|
439
|
-
return filename
|
|
437
|
+
if not Project.manage_by_poetry() and (
|
|
438
|
+
filename := cls.parse_dynamic_version(toml_text, context, work_dir)
|
|
439
|
+
):
|
|
440
|
+
return filename
|
|
440
441
|
else:
|
|
441
442
|
by_version_plugin = version_value in ("0", "0.0.0", "init")
|
|
442
443
|
if by_version_plugin:
|
|
444
|
+
return cls.parse_plugin_version(context, package_name)
|
|
445
|
+
|
|
446
|
+
return TOML_FILE
|
|
447
|
+
|
|
448
|
+
@staticmethod
|
|
449
|
+
def parse_plugin_version(context: dict, package_name: str | None) -> str:
|
|
450
|
+
try:
|
|
451
|
+
package_item = context["tool"]["poetry"]["packages"]
|
|
452
|
+
except KeyError:
|
|
443
453
|
try:
|
|
444
|
-
|
|
454
|
+
project_name = context["project"]["name"]
|
|
445
455
|
except KeyError:
|
|
446
|
-
|
|
447
|
-
project_name = context["project"]["name"]
|
|
448
|
-
except KeyError:
|
|
449
|
-
packages = []
|
|
450
|
-
else:
|
|
451
|
-
packages = [(poetry_module_name(project_name), "")]
|
|
452
|
-
else:
|
|
453
|
-
packages = [
|
|
454
|
-
(j, i.get("from", ""))
|
|
455
|
-
for i in package_item
|
|
456
|
-
if (j := i.get("include"))
|
|
457
|
-
]
|
|
458
|
-
# In case of managed by `poetry-plugin-version`
|
|
459
|
-
cwd = Path.cwd()
|
|
460
|
-
pattern = re.compile(r"__version__\s*=\s*['\"]")
|
|
461
|
-
ds: list[Path] = []
|
|
462
|
-
if package_name is not None:
|
|
463
|
-
packages.insert(0, (package_name, ""))
|
|
464
|
-
for package_name, source_dir in packages:
|
|
465
|
-
ds.append(cwd / package_name)
|
|
466
|
-
ds.append(cwd / "src" / package_name)
|
|
467
|
-
if source_dir and source_dir != "src":
|
|
468
|
-
ds.append(cwd / source_dir / package_name)
|
|
469
|
-
module_name = poetry_module_name(cwd.name)
|
|
470
|
-
ds.extend([cwd / module_name, cwd / "src" / module_name, cwd])
|
|
471
|
-
for d in ds:
|
|
472
|
-
init_file = d / "__init__.py"
|
|
473
|
-
if (
|
|
474
|
-
init_file.exists() and pattern.search(init_file.read_text("utf8"))
|
|
475
|
-
) or (
|
|
476
|
-
(init_file := init_file.with_name("__version__.py")).exists()
|
|
477
|
-
and pattern.search(init_file.read_text("utf8"))
|
|
478
|
-
):
|
|
479
|
-
break
|
|
456
|
+
packages = []
|
|
480
457
|
else:
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
458
|
+
packages = [(poetry_module_name(project_name), "")]
|
|
459
|
+
else:
|
|
460
|
+
packages = [
|
|
461
|
+
(j, i.get("from", "")) for i in package_item if (j := i.get("include"))
|
|
462
|
+
]
|
|
463
|
+
# In case of managed by `poetry-plugin-version`
|
|
464
|
+
cwd = Path.cwd()
|
|
465
|
+
pattern = re.compile(r"__version__\s*=\s*['\"]")
|
|
466
|
+
ds: list[Path] = []
|
|
467
|
+
if package_name is not None:
|
|
468
|
+
packages.insert(0, (package_name, ""))
|
|
469
|
+
for package_name, source_dir in packages:
|
|
470
|
+
ds.append(cwd / package_name)
|
|
471
|
+
ds.append(cwd / "src" / package_name)
|
|
472
|
+
if source_dir and source_dir != "src":
|
|
473
|
+
ds.append(cwd / source_dir / package_name)
|
|
474
|
+
module_name = poetry_module_name(cwd.name)
|
|
475
|
+
ds.extend([cwd / module_name, cwd / "src" / module_name, cwd])
|
|
476
|
+
for d in ds:
|
|
477
|
+
init_file = d / "__init__.py"
|
|
478
|
+
if (init_file.exists() and pattern.search(init_file.read_text("utf8"))) or (
|
|
479
|
+
(init_file := init_file.with_name("__version__.py")).exists()
|
|
480
|
+
and pattern.search(init_file.read_text("utf8"))
|
|
481
|
+
):
|
|
482
|
+
break
|
|
483
|
+
else:
|
|
484
|
+
raise ParseError("Version file not found! Where are you now?")
|
|
485
|
+
return os.path.relpath(init_file, cwd)
|
|
485
486
|
|
|
486
487
|
def get_part(self, s: str) -> str:
|
|
487
488
|
choices: dict[str, str] = {}
|
|
@@ -517,7 +518,11 @@ class BumpUp(DryRun):
|
|
|
517
518
|
cmd += " && git push && git push --tags && git log -1"
|
|
518
519
|
else:
|
|
519
520
|
cmd += " --allow-dirty"
|
|
520
|
-
if
|
|
521
|
+
if (
|
|
522
|
+
should_sync
|
|
523
|
+
and not self._no_sync
|
|
524
|
+
and (sync := Project.get_sync_command(only_me=True))
|
|
525
|
+
):
|
|
521
526
|
cmd = f"{sync} && " + cmd
|
|
522
527
|
return cmd
|
|
523
528
|
|
|
@@ -561,7 +566,7 @@ def bump_version(
|
|
|
561
566
|
commit: bool = Option(
|
|
562
567
|
False, "--commit", "-c", help="Whether run `git commit` after version changed"
|
|
563
568
|
),
|
|
564
|
-
emoji:
|
|
569
|
+
emoji: bool | None = Option(
|
|
565
570
|
None, "--emoji", help="Whether add emoji prefix to commit message"
|
|
566
571
|
),
|
|
567
572
|
no_sync: bool = Option(
|
|
@@ -744,9 +749,12 @@ class Project:
|
|
|
744
749
|
return True
|
|
745
750
|
|
|
746
751
|
@classmethod
|
|
747
|
-
def get_sync_command(
|
|
752
|
+
def get_sync_command(
|
|
753
|
+
cls, prod: bool = True, doc: dict | None = None, only_me: bool = False
|
|
754
|
+
) -> str:
|
|
755
|
+
pdm_i = "pdm install --frozen" + " --prod" * prod
|
|
748
756
|
if cls.is_pdm_project():
|
|
749
|
-
return
|
|
757
|
+
return pdm_i
|
|
750
758
|
elif cls.manage_by_poetry(cache=True):
|
|
751
759
|
cmd = "poetry install"
|
|
752
760
|
if prod:
|
|
@@ -761,7 +769,17 @@ class Project:
|
|
|
761
769
|
cmd += " --only=main"
|
|
762
770
|
return cmd
|
|
763
771
|
elif cls.get_manage_tool(cache=True) == "uv":
|
|
764
|
-
|
|
772
|
+
install_me = "uv pip install -e ."
|
|
773
|
+
if doc is None:
|
|
774
|
+
doc = tomllib.loads(cls.load_toml_text())
|
|
775
|
+
is_distribution = (
|
|
776
|
+
doc.get("tool", {}).get("pdm", {}).get("distribution") is not False
|
|
777
|
+
)
|
|
778
|
+
if only_me:
|
|
779
|
+
return install_me if is_distribution else pdm_i
|
|
780
|
+
cmd = "uv sync --inexact" + " --no-dev" * prod
|
|
781
|
+
if is_distribution:
|
|
782
|
+
cmd += f" && {install_me}"
|
|
765
783
|
return ""
|
|
766
784
|
|
|
767
785
|
@classmethod
|
|
@@ -1251,7 +1269,7 @@ def check(
|
|
|
1251
1269
|
|
|
1252
1270
|
@cli.command(name="lint")
|
|
1253
1271
|
def make_style(
|
|
1254
|
-
files:
|
|
1272
|
+
files: list[str] | None = typer.Argument(default=None), # noqa:B008
|
|
1255
1273
|
check_only: bool = Option(False, "--check-only", "-c"),
|
|
1256
1274
|
bandit: bool = Option(False, "--bandit", help="Run `bandit -r <package_dir>`"),
|
|
1257
1275
|
prefix: bool = Option(
|
|
@@ -1434,9 +1452,9 @@ def dev(
|
|
|
1434
1452
|
|
|
1435
1453
|
@cli.command(name="dev")
|
|
1436
1454
|
def runserver(
|
|
1437
|
-
file_or_port:
|
|
1438
|
-
port:
|
|
1439
|
-
host:
|
|
1455
|
+
file_or_port: str | None = typer.Argument(default=None),
|
|
1456
|
+
port: int | None = Option(None, "-p", "--port"),
|
|
1457
|
+
host: str | None = Option(None, "-h", "--host"),
|
|
1440
1458
|
dry: bool = DryOption,
|
|
1441
1459
|
) -> None:
|
|
1442
1460
|
"""Start a fastapi server(only for fastapi>=0.111.0)"""
|
|
@@ -1624,7 +1642,7 @@ class UvPypi(DryRun):
|
|
|
1624
1642
|
|
|
1625
1643
|
@cli.command()
|
|
1626
1644
|
def pypi(
|
|
1627
|
-
file:
|
|
1645
|
+
file: str | None = typer.Argument(default=None),
|
|
1628
1646
|
dry: bool = DryOption,
|
|
1629
1647
|
verbose: bool = False,
|
|
1630
1648
|
quiet: bool = False,
|
|
@@ -41,7 +41,7 @@ dependencies = [
|
|
|
41
41
|
"bumpversion2 >=1.4.3",
|
|
42
42
|
"pytest >=8.2.0",
|
|
43
43
|
]
|
|
44
|
-
version = "0.19.
|
|
44
|
+
version = "0.19.2"
|
|
45
45
|
|
|
46
46
|
[project.urls]
|
|
47
47
|
Homepage = "https://github.com/waketzheng/fast-dev-cli"
|
|
@@ -57,7 +57,7 @@ fast = "fast_dev_cli:cli.main"
|
|
|
57
57
|
[dependency-groups]
|
|
58
58
|
dev = [
|
|
59
59
|
"twine>=6.1.0",
|
|
60
|
-
"asynctor>=0.
|
|
60
|
+
"asynctor>=0.10.0",
|
|
61
61
|
]
|
|
62
62
|
|
|
63
63
|
[tool.pdm]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import functools
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
@@ -30,6 +31,16 @@ build-backend = "poetry.core.masonry.api"
|
|
|
30
31
|
"""
|
|
31
32
|
|
|
32
33
|
|
|
34
|
+
@functools.cache
|
|
35
|
+
def is_pip_installed() -> bool:
|
|
36
|
+
try:
|
|
37
|
+
import pip # NOQA
|
|
38
|
+
except ImportError:
|
|
39
|
+
return False
|
|
40
|
+
else:
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
|
|
33
44
|
def test_sync_not_in_venv(mocker, capsys):
|
|
34
45
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
|
|
35
46
|
test_dir = Path(__file__).parent
|
|
@@ -44,10 +55,15 @@ def test_sync_not_in_venv(mocker, capsys):
|
|
|
44
55
|
mocker.patch(
|
|
45
56
|
"fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
|
|
46
57
|
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
pdm_export = "pdm export --without-hashes --with=dev -o req.txt"
|
|
59
|
+
installing = "pdm run python -m pip install -r req.txt"
|
|
60
|
+
if not is_pip_installed():
|
|
61
|
+
installing = (
|
|
62
|
+
"pdm run python -m ensurepip && pdm run python -m pip install -U pip && "
|
|
63
|
+
+ installing
|
|
64
|
+
)
|
|
65
|
+
expected = f"{pdm_export} && {installing}"
|
|
66
|
+
assert Sync("req.txt", "", True, dry=True).gen() == expected
|
|
51
67
|
|
|
52
68
|
|
|
53
69
|
def test_sync(mocker):
|
|
@@ -62,10 +78,12 @@ def test_sync(mocker):
|
|
|
62
78
|
mocker.patch(
|
|
63
79
|
"fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
|
|
64
80
|
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
pdm_export = "pdm export --without-hashes --with=dev -o req.txt"
|
|
82
|
+
if is_pip_installed():
|
|
83
|
+
expected = f"{pdm_export} && python -m pip install -r req.txt"
|
|
84
|
+
else:
|
|
85
|
+
expected = f"{pdm_export} && python -m ensurepip && python -m pip install -U pip && python -m pip install -r req.txt"
|
|
86
|
+
assert Sync("req.txt", "", True, dry=True).gen() == expected
|
|
69
87
|
|
|
70
88
|
|
|
71
89
|
UV_TOML_EXAMPLE = """
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.19.1"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|