fast-dev-cli 0.21.1__tar.gz → 0.22.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.
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/PKG-INFO +15 -10
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/README.md +5 -5
- fast_dev_cli-0.22.0/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/fast_dev_cli/cli.py +84 -21
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/pyproject.toml +24 -46
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_deps.py +1 -1
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_fast_test.py +1 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_lint.py +72 -33
- fast_dev_cli-0.22.0/tests/test_runserver.py +136 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_sync.py +14 -8
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_upload.py +1 -1
- fast_dev_cli-0.21.1/fast_dev_cli/__init__.py +0 -1
- fast_dev_cli-0.21.1/pdm_build.py +0 -39
- fast_dev_cli-0.21.1/scripts/check.py +0 -30
- fast_dev_cli-0.21.1/scripts/deps.py +0 -156
- fast_dev_cli-0.21.1/scripts/format.py +0 -13
- fast_dev_cli-0.21.1/scripts/test.py +0 -48
- fast_dev_cli-0.21.1/tests/test_runserver.py +0 -97
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/LICENSE +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/__init__.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/assets/uv-tx.lock +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/assets/uv-upload-time.lock +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/assets/uv.lock +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/conftest.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_bump.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_exec.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_functions.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_help.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_poetry_version_plugin.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_pypi.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_upgrade.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.0}/tests/test_version.py +0 -0
- {fast_dev_cli-0.21.1 → fast_dev_cli-0.22.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.
|
|
3
|
+
Version: 0.22.0
|
|
4
4
|
Summary: Python project development tool.
|
|
5
5
|
Author-Email: Waket Zheng <waketzheng@gmail.com>>
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -23,10 +23,15 @@ Project-URL: Homepage, https://github.com/waketzheng/fast-dev-cli
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Requires-Dist: typer<1,>=0.24.0
|
|
25
25
|
Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
|
|
26
|
-
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
Provides-Extra: standard
|
|
27
|
+
Requires-Dist: coverage>=7.5.1; extra == "standard"
|
|
28
|
+
Requires-Dist: pytest>=8.2.0; extra == "standard"
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: emoji>=2.12.1; extra == "all"
|
|
31
|
+
Requires-Dist: packaging>=20.5; extra == "all"
|
|
32
|
+
Requires-Dist: bandit>=1.7.9; extra == "all"
|
|
33
|
+
Requires-Dist: pytest-mock>=3.14.0; extra == "all"
|
|
34
|
+
Requires-Dist: fast-dev-cli[standard]; extra == "all"
|
|
30
35
|
Description-Content-Type: text/markdown
|
|
31
36
|
|
|
32
37
|
<p align="center">
|
|
@@ -90,8 +95,8 @@ Python 3.10+
|
|
|
90
95
|
<div class="termy">
|
|
91
96
|
|
|
92
97
|
```bash
|
|
93
|
-
uv tool install
|
|
94
|
-
# Or: pipx install
|
|
98
|
+
uv tool install fast-dev-cli
|
|
99
|
+
# Or: pipx install fast-dev-cli
|
|
95
100
|
```
|
|
96
101
|
*Will create a `fast` command in ~/.local/bin
|
|
97
102
|
|
|
@@ -102,7 +107,7 @@ uv tool install --python 3.14 fastdevcli-slim
|
|
|
102
107
|
<div class="termy">
|
|
103
108
|
|
|
104
109
|
```bash
|
|
105
|
-
pip install fast-dev-cli
|
|
110
|
+
pip install "fast-dev-cli[standard]"
|
|
106
111
|
```
|
|
107
112
|
*Will install: fast-dev-cli emoji typer packaging pytest coverage*
|
|
108
113
|
|
|
@@ -118,7 +123,7 @@ fast lint [/path/to/file-or-directory]
|
|
|
118
123
|
```bash
|
|
119
124
|
fast check
|
|
120
125
|
```
|
|
121
|
-
- Bump up version in pyproject.toml
|
|
126
|
+
- Bump up version in pyproject.toml(or package.json)
|
|
122
127
|
```bash
|
|
123
128
|
fast bump patch # 0.1.0 -> 0.1.1
|
|
124
129
|
fast bump minor # 0.1.0 -> 0.2.0
|
|
@@ -146,7 +151,7 @@ fast pypi
|
|
|
146
151
|
## Use it without installed
|
|
147
152
|
|
|
148
153
|
```bash
|
|
149
|
-
uvx --from
|
|
154
|
+
uvx --from fast-dev-cli fast
|
|
150
155
|
```
|
|
151
156
|
|
|
152
157
|
## Lint/Check by ty instead of mypy
|
|
@@ -59,8 +59,8 @@ Python 3.10+
|
|
|
59
59
|
<div class="termy">
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
-
uv tool install
|
|
63
|
-
# Or: pipx install
|
|
62
|
+
uv tool install fast-dev-cli
|
|
63
|
+
# Or: pipx install fast-dev-cli
|
|
64
64
|
```
|
|
65
65
|
*Will create a `fast` command in ~/.local/bin
|
|
66
66
|
|
|
@@ -71,7 +71,7 @@ uv tool install --python 3.14 fastdevcli-slim
|
|
|
71
71
|
<div class="termy">
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
-
pip install fast-dev-cli
|
|
74
|
+
pip install "fast-dev-cli[standard]"
|
|
75
75
|
```
|
|
76
76
|
*Will install: fast-dev-cli emoji typer packaging pytest coverage*
|
|
77
77
|
|
|
@@ -87,7 +87,7 @@ fast lint [/path/to/file-or-directory]
|
|
|
87
87
|
```bash
|
|
88
88
|
fast check
|
|
89
89
|
```
|
|
90
|
-
- Bump up version in pyproject.toml
|
|
90
|
+
- Bump up version in pyproject.toml(or package.json)
|
|
91
91
|
```bash
|
|
92
92
|
fast bump patch # 0.1.0 -> 0.1.1
|
|
93
93
|
fast bump minor # 0.1.0 -> 0.2.0
|
|
@@ -115,7 +115,7 @@ fast pypi
|
|
|
115
115
|
## Use it without installed
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
uvx --from
|
|
118
|
+
uvx --from fast-dev-cli fast
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
## Lint/Check by ty instead of mypy
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.22.0"
|
|
@@ -1453,7 +1453,9 @@ class Sync(DryRun):
|
|
|
1453
1453
|
if should_remove or not is_venv():
|
|
1454
1454
|
raise EnvError("There project is not managed by uv/pdm/poetry!")
|
|
1455
1455
|
return f"python -m pip install -r {self.filename}"
|
|
1456
|
-
prefix = ""
|
|
1456
|
+
prefix = ""
|
|
1457
|
+
if not is_venv():
|
|
1458
|
+
prefix = f"{tool} run " + "--no-sync " * (tool == "uv")
|
|
1457
1459
|
ensure_pip = " {1}python -m ensurepip && {1}python -m pip install -U pip &&"
|
|
1458
1460
|
export_cmd = "uv export --no-hashes --all-extras --all-groups --frozen"
|
|
1459
1461
|
if tool in ("poetry", "pdm"):
|
|
@@ -1549,30 +1551,86 @@ def upload(
|
|
|
1549
1551
|
exit_if_run_failed(cmd, dry=dry)
|
|
1550
1552
|
|
|
1551
1553
|
|
|
1554
|
+
def should_use_just() -> bool:
|
|
1555
|
+
if shutil.which("just") is None:
|
|
1556
|
+
return False
|
|
1557
|
+
d = Path.cwd()
|
|
1558
|
+
for _ in range(5):
|
|
1559
|
+
f = d / "justfile"
|
|
1560
|
+
if f.exists():
|
|
1561
|
+
text = f.read_text(encoding="utf-8")
|
|
1562
|
+
return any(i.startswith("dev *args:") for i in text.splitlines())
|
|
1563
|
+
if d.joinpath("pyproject.toml").exists():
|
|
1564
|
+
break
|
|
1565
|
+
return False
|
|
1566
|
+
|
|
1567
|
+
|
|
1552
1568
|
def dev(
|
|
1553
1569
|
port: int | None | OptionInfo,
|
|
1554
1570
|
host: str | None | OptionInfo,
|
|
1571
|
+
fastapi: bool | None = None,
|
|
1572
|
+
uvicorn: bool | None = None,
|
|
1573
|
+
prod: bool | None = None,
|
|
1574
|
+
reload: bool | None = None,
|
|
1555
1575
|
file: str | None | ArgumentInfo = None,
|
|
1556
1576
|
dry: bool = False,
|
|
1557
1577
|
) -> None:
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1578
|
+
if should_use_just():
|
|
1579
|
+
args = [i for i in sys.argv[2:] if i != "--dry"]
|
|
1580
|
+
cmd = "just dev"
|
|
1581
|
+
else:
|
|
1582
|
+
cmd = "uvicorn" if uvicorn else "fastapi dev"
|
|
1583
|
+
args = []
|
|
1584
|
+
if (host := getattr(host, "default", host)) and host not in (
|
|
1585
|
+
"localhost",
|
|
1586
|
+
"127.0.0.1",
|
|
1587
|
+
):
|
|
1588
|
+
args.append(f"--host={host}")
|
|
1589
|
+
no_port_yet = True
|
|
1590
|
+
if file is not None:
|
|
1591
|
+
try:
|
|
1592
|
+
port = int(str(file))
|
|
1593
|
+
except ValueError:
|
|
1594
|
+
if m := re.search(r"(.*):(\d+)$", str(file)):
|
|
1595
|
+
h, p = m.group(1), m.group(2)
|
|
1596
|
+
if h and "--host" not in str(args):
|
|
1597
|
+
if h == "0":
|
|
1598
|
+
args.append("--host=0.0.0.0")
|
|
1599
|
+
else:
|
|
1600
|
+
args.append(f"--host={h}")
|
|
1601
|
+
args.append(f"--port={p}")
|
|
1602
|
+
if uvicorn:
|
|
1603
|
+
p = Path("main.py")
|
|
1604
|
+
if p.exists():
|
|
1605
|
+
cmd += " main:app"
|
|
1606
|
+
elif Path("app", p.name).exists():
|
|
1607
|
+
cmd += " app.main:app"
|
|
1608
|
+
elif Path("app.py").exists():
|
|
1609
|
+
cmd += " app:app"
|
|
1610
|
+
else:
|
|
1611
|
+
if uvicorn and (
|
|
1612
|
+
(filepath := Path(str(file))).is_file()
|
|
1613
|
+
or filepath.suffix == ".py"
|
|
1614
|
+
):
|
|
1615
|
+
file = filepath.stem + ":app"
|
|
1616
|
+
parent_names = [j for i in filepath.parents if (j := i.name)]
|
|
1617
|
+
if parent_names:
|
|
1618
|
+
file = ".".join([*parent_names[::-1], file])
|
|
1619
|
+
cmd += f" {file}"
|
|
1620
|
+
else:
|
|
1621
|
+
if port != 8000:
|
|
1622
|
+
args.append(f"--port={port}")
|
|
1623
|
+
no_port_yet = False
|
|
1624
|
+
if (
|
|
1625
|
+
no_port_yet
|
|
1626
|
+
and (port := getattr(port, "default", port))
|
|
1627
|
+
and str(port) != "8000"
|
|
1628
|
+
):
|
|
1629
|
+
args.append(f"--port={port}")
|
|
1630
|
+
if shutil.which("pdm") is not None:
|
|
1631
|
+
cmd = "pdm run " + cmd
|
|
1632
|
+
if args:
|
|
1633
|
+
cmd += " " + " ".join(args)
|
|
1576
1634
|
exit_if_run_failed(cmd, dry=dry)
|
|
1577
1635
|
|
|
1578
1636
|
|
|
@@ -1581,13 +1639,18 @@ def runserver(
|
|
|
1581
1639
|
file_or_port: str | None = typer.Argument(default=None),
|
|
1582
1640
|
port: int | None = Option(None, "-p", "--port"),
|
|
1583
1641
|
host: str | None = Option(None, "-h", "--host"),
|
|
1642
|
+
fastapi: bool | None = None,
|
|
1643
|
+
uvicorn: bool | None = None,
|
|
1644
|
+
prod: bool | None = None,
|
|
1645
|
+
reload: bool | None = None,
|
|
1584
1646
|
dry: bool = DryOption,
|
|
1585
1647
|
) -> None:
|
|
1586
1648
|
"""Start a fastapi server(only for fastapi>=0.111.0)"""
|
|
1649
|
+
f = functools.partial(dev, port, host, fastapi, uvicorn, prod, reload, dry=dry)
|
|
1587
1650
|
if getattr(file_or_port, "default", file_or_port):
|
|
1588
|
-
|
|
1651
|
+
f(file=file_or_port)
|
|
1589
1652
|
else:
|
|
1590
|
-
|
|
1653
|
+
f()
|
|
1591
1654
|
|
|
1592
1655
|
|
|
1593
1656
|
@cli.command(name="exec")
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = [
|
|
3
|
-
"pdm-backend",
|
|
4
|
-
]
|
|
5
|
-
build-backend = "pdm.backend"
|
|
6
|
-
|
|
7
1
|
[project]
|
|
8
2
|
name = "fast-dev-cli"
|
|
9
3
|
dynamic = []
|
|
@@ -34,28 +28,41 @@ classifiers = [
|
|
|
34
28
|
dependencies = [
|
|
35
29
|
"typer>=0.24.0,<1",
|
|
36
30
|
"tomli >=2.0.1,<3; python_version < '3.11'",
|
|
37
|
-
"coverage >=7.5.1",
|
|
38
|
-
"pytest >=8.2.0",
|
|
39
31
|
]
|
|
40
|
-
version = "0.
|
|
32
|
+
version = "0.22.0"
|
|
41
33
|
|
|
42
34
|
[project.urls]
|
|
43
35
|
Homepage = "https://github.com/waketzheng/fast-dev-cli"
|
|
44
36
|
|
|
45
37
|
[project.optional-dependencies]
|
|
46
|
-
|
|
47
|
-
"
|
|
38
|
+
standard = [
|
|
39
|
+
"coverage >=7.5.1",
|
|
40
|
+
"pytest >=8.2.0",
|
|
41
|
+
]
|
|
42
|
+
all = [
|
|
43
|
+
"emoji >=2.12.1",
|
|
44
|
+
"packaging >=20.5",
|
|
45
|
+
"bandit>=1.7.9",
|
|
46
|
+
"pytest-mock >=3.14.0",
|
|
47
|
+
"fast-dev-cli[standard]",
|
|
48
48
|
]
|
|
49
49
|
|
|
50
50
|
[project.scripts]
|
|
51
|
-
fast = "fast_dev_cli:
|
|
51
|
+
fast = "fast_dev_cli.cli:main"
|
|
52
52
|
|
|
53
53
|
[dependency-groups]
|
|
54
54
|
dev = [
|
|
55
55
|
"twine>=6.1.0",
|
|
56
56
|
"asynctor>=0.10.0",
|
|
57
|
+
"mypy>=1.19.1",
|
|
57
58
|
]
|
|
58
59
|
|
|
60
|
+
[build-system]
|
|
61
|
+
requires = [
|
|
62
|
+
"pdm-backend",
|
|
63
|
+
]
|
|
64
|
+
build-backend = "pdm.backend"
|
|
65
|
+
|
|
59
66
|
[tool.pdm]
|
|
60
67
|
distribution = true
|
|
61
68
|
|
|
@@ -63,24 +70,14 @@ distribution = true
|
|
|
63
70
|
source = "file"
|
|
64
71
|
path = "fast_dev_cli/__init__.py"
|
|
65
72
|
|
|
66
|
-
[tool.pdm.build]
|
|
67
|
-
source-includes = [
|
|
68
|
-
"scripts/",
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
[tool.pdm.dev-dependencies]
|
|
72
|
-
dev = [
|
|
73
|
-
"-e file:///${PROJECT_ROOT}/#egg=fast-dev-cli[all]",
|
|
74
|
-
]
|
|
75
|
-
|
|
76
73
|
[tool.pdm.scripts]
|
|
77
|
-
up = "
|
|
74
|
+
up = "just up {args}"
|
|
78
75
|
tree = "pdm list --tree {args}"
|
|
79
|
-
deps = "
|
|
76
|
+
deps = "just deps {args}"
|
|
80
77
|
prod = "pdm install --prod --frozen {args}"
|
|
81
78
|
test = "pdm run fast test {args}"
|
|
82
|
-
lint = "
|
|
83
|
-
check = "
|
|
79
|
+
lint = "just lint {args}"
|
|
80
|
+
check = "just check {args}"
|
|
84
81
|
tag = "pdm run fast tag {args}"
|
|
85
82
|
bump = "pdm run fast bump patch --commit {args}"
|
|
86
83
|
|
|
@@ -99,7 +96,7 @@ composite = [
|
|
|
99
96
|
|
|
100
97
|
[tool.pdm.scripts.start]
|
|
101
98
|
composite = [
|
|
102
|
-
"
|
|
99
|
+
"prek install",
|
|
103
100
|
"deps",
|
|
104
101
|
]
|
|
105
102
|
|
|
@@ -109,25 +106,6 @@ composite = [
|
|
|
109
106
|
"ruff check --fix --unsafe-fixes {args}",
|
|
110
107
|
]
|
|
111
108
|
|
|
112
|
-
[tool.waketzheng._internal-slim-build.packages.fastdevcli-slim.project]
|
|
113
|
-
name = "fastdevcli-slim"
|
|
114
|
-
|
|
115
|
-
[tool.waketzheng._internal-slim-build.packages.fastdevcli-slim.project.scripts]
|
|
116
|
-
fast = "fast_dev_cli:cli.main"
|
|
117
|
-
|
|
118
|
-
[tool.waketzheng._internal-slim-build.packages.fast-dev-cli]
|
|
119
|
-
include-optional-dependencies = [
|
|
120
|
-
"standard",
|
|
121
|
-
]
|
|
122
|
-
|
|
123
|
-
[tool.waketzheng._internal-slim-build.packages.fast-dev-cli.project.optional-dependencies]
|
|
124
|
-
include-optional-dependencies = [
|
|
125
|
-
"all",
|
|
126
|
-
]
|
|
127
|
-
|
|
128
|
-
[tool.waketzheng._internal-slim-build.packages.fast-dev-cli.project.scripts]
|
|
129
|
-
fast = "fast_dev_cli:cli.main"
|
|
130
|
-
|
|
131
109
|
[tool.pytest.ini_options]
|
|
132
110
|
testpaths = [
|
|
133
111
|
"tests",
|
|
@@ -30,7 +30,7 @@ def test_make_deps_class():
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def test_fast_deps():
|
|
33
|
-
out = capture_cmd_output("fast deps --dry")
|
|
33
|
+
out = capture_cmd_output("fast deps --pdm --dry")
|
|
34
34
|
assert out == "--> pdm install --frozen -G :all"
|
|
35
35
|
out = capture_cmd_output("fast deps --uv --prod --dry")
|
|
36
36
|
assert out == "--> uv sync --inexact --active"
|
|
@@ -42,6 +42,7 @@ def test_cli_test(mocker, capsys):
|
|
|
42
42
|
def test_test_with_pdm_run(mocker: MockerFixture, capsys):
|
|
43
43
|
mocker.patch("fast_dev_cli.cli.check_call", return_value=False)
|
|
44
44
|
mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=None)
|
|
45
|
+
mocker.patch("fast_dev_cli.cli.Project.get_manage_tool", return_value="pdm")
|
|
45
46
|
unitcase(dry=True)
|
|
46
47
|
assert (
|
|
47
48
|
'--> pdm run coverage run -m pytest -s && pdm run coverage report --omit="tests/*" -m'
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import platform
|
|
4
|
+
import re
|
|
3
5
|
import shutil
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
|
|
@@ -54,6 +56,11 @@ def mock_ignore_missing_imports_0(monkeypatch):
|
|
|
54
56
|
monkeypatch.setenv("IGNORE_MISSING_IMPORTS", "0")
|
|
55
57
|
|
|
56
58
|
|
|
59
|
+
@pytest.fixture
|
|
60
|
+
def mock_ty_0(monkeypatch):
|
|
61
|
+
monkeypatch.setenv("FASTDEVCLI_TY", "0")
|
|
62
|
+
|
|
63
|
+
|
|
57
64
|
SEP = " && "
|
|
58
65
|
_CMD = "ruff format{} . && ruff check --extend-select=I,B,SIM{} . && mypy ."
|
|
59
66
|
LINT_CMD = _CMD.format("", " --fix")
|
|
@@ -178,16 +185,12 @@ def test_lint_cmd(mock_no_dmypy, monkeypatch):
|
|
|
178
185
|
command = capture_cmd_output(f"{lint_cmd} . --dry")
|
|
179
186
|
for cmd in LINT_CMD.split(SEP):
|
|
180
187
|
assert cmd in command
|
|
181
|
-
assert (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
)
|
|
186
|
-
assert
|
|
187
|
-
capture_cmd_output(f"{lint_cmd} .")
|
|
188
|
-
== capture_cmd_output(f"{lint_cmd}")
|
|
189
|
-
== capture_cmd_output(f"{run}fast lint")
|
|
190
|
-
)
|
|
188
|
+
assert capture_cmd_output(f"{lint_cmd} --dry") == command
|
|
189
|
+
out = capture_cmd_output(f"{run}fast lint --dry")
|
|
190
|
+
assert out == "--> " + LINT_CMD
|
|
191
|
+
assert capture_cmd_output(f"{lint_cmd} .") == capture_cmd_output(f"{lint_cmd}")
|
|
192
|
+
out = capture_cmd_output(f"{run}fast lint")
|
|
193
|
+
assert LINT_CMD in out
|
|
191
194
|
assert "mypy --strict" in capture_cmd_output("fast lint --strict --dry")
|
|
192
195
|
monkeypatch.setenv("FASTDEVCLI_STRICT", "1")
|
|
193
196
|
assert "mypy --strict" in capture_cmd_output("fast lint --dry")
|
|
@@ -241,7 +244,8 @@ def test_lint_with_prefix(mocker):
|
|
|
241
244
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
|
|
242
245
|
with capture_stdout() as stream:
|
|
243
246
|
make_style(["."], check_only=False, dry=True, prefix=True)
|
|
244
|
-
|
|
247
|
+
prefix = "uv run" if platform.system() == "Windows" else ".venv/bin/mypy"
|
|
248
|
+
assert prefix in stream.getvalue()
|
|
245
249
|
|
|
246
250
|
|
|
247
251
|
def test_lint_auto_suffix(tmp_work_dir):
|
|
@@ -262,44 +266,62 @@ def test_fast_lint_with_uv():
|
|
|
262
266
|
|
|
263
267
|
def test_make_style(mock_skip_mypy_0, mocker, mock_no_dmypy):
|
|
264
268
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
269
|
+
expected = LINT_CMD
|
|
270
|
+
check_expected = CHECK_CMD
|
|
271
|
+
if shutil.which("mypy") is None:
|
|
272
|
+
prefix = "uv run " if platform.system() == "Windows" else ".venv/bin/"
|
|
273
|
+
expected = expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
274
|
+
check_expected = check_expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
265
275
|
with capture_stdout() as stream:
|
|
266
276
|
make_style(check_only=False, dry=True)
|
|
267
|
-
assert
|
|
277
|
+
assert expected in stream.getvalue()
|
|
268
278
|
with capture_stdout() as stream:
|
|
269
279
|
make_style(["."], check_only=False, dry=True)
|
|
270
|
-
assert
|
|
280
|
+
assert expected in stream.getvalue()
|
|
271
281
|
with capture_stdout() as stream:
|
|
272
282
|
make_style(".", check_only=False, dry=True) # type:ignore[arg-type]
|
|
273
|
-
assert
|
|
283
|
+
assert expected in stream.getvalue()
|
|
274
284
|
with capture_stdout() as stream:
|
|
275
285
|
make_style(["."], check_only=True, dry=True)
|
|
276
|
-
assert
|
|
286
|
+
assert check_expected in stream.getvalue()
|
|
277
287
|
with capture_stdout() as stream:
|
|
278
288
|
only_check(dry=True)
|
|
279
|
-
assert
|
|
289
|
+
assert check_expected in stream.getvalue()
|
|
280
290
|
|
|
281
291
|
|
|
282
292
|
def test_lint_class(mocker, mock_no_dmypy):
|
|
283
293
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
284
|
-
|
|
294
|
+
expected = LINT_CMD
|
|
295
|
+
check_expected = CHECK_CMD
|
|
296
|
+
if shutil.which("mypy") is None:
|
|
297
|
+
prefix = "uv run " if platform.system() == "Windows" else ".venv/bin/"
|
|
298
|
+
expected = expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
299
|
+
check_expected = check_expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
300
|
+
assert LintCode(".").gen() == expected
|
|
285
301
|
check = LintCode(".", check_only=True)
|
|
286
|
-
assert check.gen() ==
|
|
302
|
+
assert check.gen() == check_expected
|
|
287
303
|
mocker.patch("fast_dev_cli.cli.Project.work_dir", return_value=None)
|
|
288
304
|
assert LintCode(".").gen() == LINT_CMD
|
|
289
305
|
|
|
290
306
|
|
|
291
|
-
def test_lint_func(mocker, mock_no_dmypy):
|
|
307
|
+
def test_lint_func(mocker, mock_no_dmypy, mock_ty_0):
|
|
292
308
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
309
|
+
expected = LINT_CMD
|
|
310
|
+
if shutil.which("mypy") is None:
|
|
311
|
+
prefix = "uv run " if platform.system() == "Windows" else ".venv/bin/"
|
|
312
|
+
expected = expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
293
313
|
with capture_stdout() as stream:
|
|
294
314
|
lint(".", dry=True)
|
|
295
|
-
assert
|
|
315
|
+
assert expected in stream.getvalue()
|
|
296
316
|
with mock_sys_argv(["tests"]), capture_stdout() as stream:
|
|
297
317
|
lint(dry=True)
|
|
298
|
-
|
|
318
|
+
with_dir = re.sub(r" \.$", " tests", expected.replace(" . ", " tests "))
|
|
319
|
+
out = stream.getvalue()
|
|
320
|
+
assert with_dir in out
|
|
299
321
|
with capture_stdout() as stream:
|
|
300
322
|
lint(["lint"], dry=True)
|
|
301
|
-
assert
|
|
302
|
-
assert
|
|
323
|
+
assert expected in stream.getvalue()
|
|
324
|
+
assert expected in capture_cmd_output("pdm run python -m fast_dev_cli lint --dry")
|
|
303
325
|
|
|
304
326
|
|
|
305
327
|
def test_lint_without_ruff_installed(mocker, mock_no_dmypy):
|
|
@@ -332,7 +354,11 @@ def test_lint_without_mypy_installed(mocker, mock_no_dmypy):
|
|
|
332
354
|
|
|
333
355
|
def test_no_fix(mock_no_fix, mocker, mock_no_dmypy):
|
|
334
356
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
335
|
-
|
|
357
|
+
expected = LINT_CMD.replace(" --fix", "")
|
|
358
|
+
if shutil.which("mypy") is None:
|
|
359
|
+
prefix = "uv run " if platform.system() == "Windows" else ".venv/bin/"
|
|
360
|
+
expected = expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
361
|
+
assert LintCode(".").gen() == expected
|
|
336
362
|
|
|
337
363
|
|
|
338
364
|
def test_skip_mypy(mock_skip_mypy, mocker):
|
|
@@ -360,34 +386,47 @@ def test_skip_mypy_fast_lint(mock_skip_mypy_0, mocker):
|
|
|
360
386
|
|
|
361
387
|
def test_skip_mypy_0(mock_skip_mypy_0, mocker, mock_no_dmypy):
|
|
362
388
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
363
|
-
|
|
389
|
+
expected = LINT_CMD
|
|
390
|
+
if shutil.which("mypy") is None:
|
|
391
|
+
prefix = "uv run " if platform.system() == "Windows" else ".venv/bin/"
|
|
392
|
+
expected = expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
393
|
+
assert LintCode(".").gen() == expected
|
|
364
394
|
|
|
365
395
|
|
|
366
396
|
def test_ignore_missing_imports(mock_ignore_missing_imports, mocker, mock_no_dmypy):
|
|
367
397
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
398
|
+
expected = LINT_CMD.replace("mypy ", "mypy --ignore-missing-imports ")
|
|
399
|
+
if shutil.which("mypy") is None:
|
|
400
|
+
prefix = "uv run " if platform.system() == "Windows" else ".venv/bin/"
|
|
401
|
+
expected = expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
402
|
+
assert LintCode(".").gen() == expected
|
|
371
403
|
|
|
372
404
|
|
|
373
405
|
def test_ignore_missing_imports_0(mock_ignore_missing_imports_0, mocker, mock_no_dmypy):
|
|
374
406
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
375
|
-
|
|
407
|
+
expected = LINT_CMD
|
|
408
|
+
if shutil.which("mypy") is None:
|
|
409
|
+
prefix = "uv run " if platform.system() == "Windows" else ".venv/bin/"
|
|
410
|
+
expected = expected.replace("&& mypy", f"&& {prefix}mypy")
|
|
411
|
+
assert LintCode(".").gen() == expected
|
|
376
412
|
|
|
377
413
|
|
|
378
|
-
def test_not_in_root(mocker, mock_no_dmypy):
|
|
414
|
+
def test_not_in_root(mocker, mock_no_dmypy, mock_ty_0):
|
|
379
415
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
380
416
|
root = Path(__file__).parent.parent
|
|
417
|
+
expected = LINT_CMD
|
|
418
|
+
if shutil.which("mypy") is None:
|
|
419
|
+
expected = expected.replace("&& mypy", "&& uv run mypy")
|
|
381
420
|
with chdir(root / "fast_dev_cli"):
|
|
382
|
-
assert LintCode(".").gen() ==
|
|
421
|
+
assert LintCode(".").gen() == expected
|
|
383
422
|
with chdir(root / "tests"):
|
|
384
|
-
assert LintCode(".").gen() ==
|
|
423
|
+
assert LintCode(".").gen() == expected
|
|
385
424
|
sub = Path("temp_dir")
|
|
386
425
|
sub.mkdir()
|
|
387
426
|
with chdir(sub):
|
|
388
427
|
cmd = LintCode(".").gen()
|
|
389
428
|
sub.rmdir()
|
|
390
|
-
assert cmd ==
|
|
429
|
+
assert cmd == expected
|
|
391
430
|
|
|
392
431
|
|
|
393
432
|
def test_get_manage_tool(tmp_path):
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
|
|
5
|
+
import fast_dev_cli
|
|
6
|
+
from fast_dev_cli.cli import dev, main, run_and_echo, runserver
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_runserver(capsys):
|
|
10
|
+
runserver(dry=True)
|
|
11
|
+
out = capsys.readouterr().out.strip()
|
|
12
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev"
|
|
13
|
+
runserver(port=8000, dry=True)
|
|
14
|
+
out = capsys.readouterr().out.strip()
|
|
15
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev"
|
|
16
|
+
runserver(port=9000, dry=True)
|
|
17
|
+
out = capsys.readouterr().out.strip()
|
|
18
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --port=9000"
|
|
19
|
+
runserver(host="0.0.0.0", dry=True)
|
|
20
|
+
out = capsys.readouterr().out.strip()
|
|
21
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --host=0.0.0.0"
|
|
22
|
+
runserver(port=9000, host="0.0.0.0", dry=True)
|
|
23
|
+
out = capsys.readouterr().out.strip()
|
|
24
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --host=0.0.0.0 --port=9000"
|
|
25
|
+
runserver("9000", host="0.0.0.0", dry=True)
|
|
26
|
+
out = capsys.readouterr().out.strip()
|
|
27
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --host=0.0.0.0 --port=9000"
|
|
28
|
+
runserver("app.py", host="0.0.0.0", dry=True)
|
|
29
|
+
out = capsys.readouterr().out.strip()
|
|
30
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev app.py --host=0.0.0.0"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_runserver_no_pdm(capsys, mocker):
|
|
34
|
+
mocker.patch("shutil.which", return_value=None)
|
|
35
|
+
runserver(dry=True)
|
|
36
|
+
out = capsys.readouterr().out.strip()
|
|
37
|
+
assert out.replace("--> ", "") == "fastapi dev"
|
|
38
|
+
runserver(file_or_port=":8000", dry=True)
|
|
39
|
+
out = capsys.readouterr().out.strip()
|
|
40
|
+
assert out.replace("--> ", "") == "fastapi dev --port=8000"
|
|
41
|
+
runserver(file_or_port="0:9000", dry=True)
|
|
42
|
+
out = capsys.readouterr().out.strip()
|
|
43
|
+
assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0 --port=9000"
|
|
44
|
+
runserver(file_or_port="0.0.0.0:8000", dry=True)
|
|
45
|
+
out = capsys.readouterr().out.strip()
|
|
46
|
+
assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0 --port=8000"
|
|
47
|
+
runserver(port=9000, host="0.0.0.0", dry=True)
|
|
48
|
+
out = capsys.readouterr().out.strip()
|
|
49
|
+
assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0 --port=9000"
|
|
50
|
+
runserver("9000", host="0.0.0.0", dry=True)
|
|
51
|
+
out = capsys.readouterr().out.strip()
|
|
52
|
+
assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0 --port=9000"
|
|
53
|
+
runserver("app.py", uvicorn=True, host="0.0.0.0", dry=True)
|
|
54
|
+
out = capsys.readouterr().out.strip()
|
|
55
|
+
assert out.replace("--> ", "") == "uvicorn app:app --host=0.0.0.0"
|
|
56
|
+
runserver(
|
|
57
|
+
"tests/test_runserver.py", uvicorn=True, port=9999, host="0.0.0.0", dry=True
|
|
58
|
+
)
|
|
59
|
+
out = capsys.readouterr().out.strip()
|
|
60
|
+
assert (
|
|
61
|
+
out.replace("--> ", "")
|
|
62
|
+
== "uvicorn tests.test_runserver:app --host=0.0.0.0 --port=9999"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_dev(capsys):
|
|
67
|
+
dev(None, None, dry=True)
|
|
68
|
+
out = capsys.readouterr().out.strip()
|
|
69
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev"
|
|
70
|
+
dev(port=8000, host="", dry=True)
|
|
71
|
+
out = capsys.readouterr().out.strip()
|
|
72
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev"
|
|
73
|
+
dev(port=9000, host=None, dry=True)
|
|
74
|
+
out = capsys.readouterr().out.strip()
|
|
75
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --port=9000"
|
|
76
|
+
dev(8000, host="0.0.0.0", dry=True)
|
|
77
|
+
out = capsys.readouterr().out.strip()
|
|
78
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --host=0.0.0.0"
|
|
79
|
+
dev(port=9000, host="0.0.0.0", dry=True)
|
|
80
|
+
out = capsys.readouterr().out.strip()
|
|
81
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --host=0.0.0.0 --port=9000"
|
|
82
|
+
dev(8001, host="0.0.0.0", dry=True)
|
|
83
|
+
out = capsys.readouterr().out.strip()
|
|
84
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --host=0.0.0.0 --port=8001"
|
|
85
|
+
dev(None, file="8001", host="0.0.0.0", dry=True)
|
|
86
|
+
out = capsys.readouterr().out.strip()
|
|
87
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev --host=0.0.0.0 --port=8001"
|
|
88
|
+
dev(None, file="main.py", host="0.0.0.0", dry=True)
|
|
89
|
+
out = capsys.readouterr().out.strip()
|
|
90
|
+
assert out.replace("--> ", "") == "pdm run fastapi dev main.py --host=0.0.0.0"
|
|
91
|
+
dev(8001, file="main.py", host="0.0.0.0", dry=True)
|
|
92
|
+
out = capsys.readouterr().out.strip()
|
|
93
|
+
assert (
|
|
94
|
+
out.replace("--> ", "")
|
|
95
|
+
== "pdm run fastapi dev main.py --host=0.0.0.0 --port=8001"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_fast_dev(tmp_path):
|
|
100
|
+
file = tmp_path / "a.txt"
|
|
101
|
+
read_text = functools.partial(file.read_text, encoding="utf-8")
|
|
102
|
+
run_and_echo(f"fast dev 9000 --dry > {file}", verbose=False)
|
|
103
|
+
assert "fastapi dev --port=9000" in read_text()
|
|
104
|
+
run_and_echo(f"fast dev main.py --dry > {file}", verbose=False)
|
|
105
|
+
assert "fastapi dev main.py" in read_text()
|
|
106
|
+
run_and_echo(f"fast dev main.py --port=9000 --dry > {file}", verbose=False)
|
|
107
|
+
assert "fastapi dev main.py --port=9000" in read_text()
|
|
108
|
+
run_and_echo(
|
|
109
|
+
f"fast dev main.py --host=0.0.0.0 --port=9000 --dry > {file}", verbose=False
|
|
110
|
+
)
|
|
111
|
+
assert "fastapi dev main.py --host=0.0.0.0 --port=9000" in read_text()
|
|
112
|
+
run_and_echo(f"fast dev 9000 --host=0.0.0.0 --dry > {file}", verbose=False)
|
|
113
|
+
assert "fastapi dev --host=0.0.0.0 --port=9000" in read_text()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_run_by_module(tmp_path):
|
|
117
|
+
out = tmp_path / "b.txt"
|
|
118
|
+
fast = "python -m fast_dev_cli"
|
|
119
|
+
run_and_echo(f"{fast} dev 9000 --dry > {out}", verbose=False)
|
|
120
|
+
assert "fastapi dev --port=9000" in out.read_text()
|
|
121
|
+
run_and_echo(f"{fast} dev main.py --dry > {out}", verbose=False)
|
|
122
|
+
assert "fastapi dev main.py" in out.read_text()
|
|
123
|
+
run_and_echo(f"{fast} dev main.py --port=9000 --dry > {out}", verbose=False)
|
|
124
|
+
assert "fastapi dev main.py --port=9000" in out.read_text()
|
|
125
|
+
run_and_echo(
|
|
126
|
+
f"{fast} dev main.py --host=0.0.0.0 --port=9000 --dry > {out}", verbose=False
|
|
127
|
+
)
|
|
128
|
+
assert "fastapi dev main.py --host=0.0.0.0 --port=9000" in out.read_text()
|
|
129
|
+
run_and_echo(f"{fast} dev 9000 --host=0.0.0.0 --dry > {out}", verbose=False)
|
|
130
|
+
assert "fastapi dev --host=0.0.0.0 --port=9000" in out.read_text()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_main(mocker):
|
|
134
|
+
mocker.patch("fast_dev_cli.cli.cli")
|
|
135
|
+
main()
|
|
136
|
+
fast_dev_cli.cli.cli.assert_called_once() # type:ignore[attr-defined]
|
|
@@ -56,13 +56,17 @@ def test_sync_not_in_venv(mocker, capsys):
|
|
|
56
56
|
"fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
|
|
57
57
|
)
|
|
58
58
|
pdm_export = "pdm export --without-hashes --with=dev -o req.txt"
|
|
59
|
-
|
|
59
|
+
uv_export = "uv export --no-hashes --all-extras --all-groups --frozen -o req.txt"
|
|
60
|
+
using_pdm = Path("pdm.lock").exists()
|
|
61
|
+
export_cmd = pdm_export if using_pdm else uv_export
|
|
62
|
+
tool_run = "pdm run" if using_pdm else "uv run --no-sync"
|
|
63
|
+
installing = f"{tool_run} python -m pip install -r req.txt"
|
|
60
64
|
if not is_pip_installed():
|
|
61
65
|
installing = (
|
|
62
|
-
"
|
|
66
|
+
f"{tool_run} python -m ensurepip && {tool_run} python -m pip install -U pip && "
|
|
63
67
|
+ installing
|
|
64
68
|
)
|
|
65
|
-
expected = f"{
|
|
69
|
+
expected = f"{export_cmd} && {installing}"
|
|
66
70
|
assert Sync("req.txt", "", True, dry=True).gen() == expected
|
|
67
71
|
|
|
68
72
|
|
|
@@ -79,10 +83,12 @@ def test_sync(mocker):
|
|
|
79
83
|
"fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
|
|
80
84
|
)
|
|
81
85
|
pdm_export = "pdm export --without-hashes --with=dev -o req.txt"
|
|
86
|
+
uv_export = "uv export --no-hashes --all-extras --all-groups --frozen -o req.txt"
|
|
87
|
+
export_cmd = pdm_export if Path("pdm.lock").exists() else uv_export
|
|
82
88
|
if is_pip_installed():
|
|
83
|
-
expected = f"{
|
|
89
|
+
expected = f"{export_cmd} && python -m pip install -r req.txt"
|
|
84
90
|
else:
|
|
85
|
-
expected = f"{
|
|
91
|
+
expected = f"{export_cmd} && python -m ensurepip && python -m pip install -U pip && python -m pip install -r req.txt"
|
|
86
92
|
assert Sync("req.txt", "", True, dry=True).gen() == expected
|
|
87
93
|
|
|
88
94
|
|
|
@@ -210,12 +216,12 @@ def test_sync_uv(mocker, tmp_path):
|
|
|
210
216
|
toml.with_name("uv.lock").write_text(UV_LOCK_EXAMPLE)
|
|
211
217
|
assert (
|
|
212
218
|
Sync("req.txt", "", True, dry=True).gen()
|
|
213
|
-
== "uv export --no-hashes --all-extras --all-groups --frozen -o req.txt && uv run python -m ensurepip && uv run python -m pip install -U pip && uv run python -m pip install -r req.txt"
|
|
219
|
+
== "uv export --no-hashes --all-extras --all-groups --frozen -o req.txt && uv run --no-sync python -m ensurepip && uv run --no-sync python -m pip install -U pip && uv run --no-sync python -m pip install -r req.txt"
|
|
214
220
|
)
|
|
215
|
-
run_and_echo("uv run python -m ensurepip")
|
|
221
|
+
run_and_echo("uv run --no-sync python -m ensurepip")
|
|
216
222
|
assert (
|
|
217
223
|
Sync("req.txt", "", True, dry=True).gen()
|
|
218
|
-
== "uv export --no-hashes --all-extras --all-groups --frozen -o req.txt && uv run python -m pip install -r req.txt"
|
|
224
|
+
== "uv export --no-hashes --all-extras --all-groups --frozen -o req.txt && uv run --no-sync python -m pip install -r req.txt"
|
|
219
225
|
)
|
|
220
226
|
|
|
221
227
|
|
|
@@ -8,7 +8,7 @@ from .utils import chdir
|
|
|
8
8
|
def test_upload(capsys):
|
|
9
9
|
upload(dry=True)
|
|
10
10
|
out = capsys.readouterr().out.strip()
|
|
11
|
-
assert out.replace("--> ", "") == "
|
|
11
|
+
assert out.replace("--> ", "") == "uv build && uv publish"
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def test_upload_poetry(tmp_path, capsys):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.21.1"
|
fast_dev_cli-0.21.1/pdm_build.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
from pdm.backend.hooks import Context
|
|
5
|
-
|
|
6
|
-
BUILD_PACKAGE = os.getenv("BUILD_PACKAGE", "fast-dev-cli")
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def pdm_build_initialize(context: Context) -> None:
|
|
10
|
-
metadata = context.config.metadata
|
|
11
|
-
# Get custom config for the current package, from the env var
|
|
12
|
-
config: dict[str, Any] = context.config.data["tool"]["waketzheng"][
|
|
13
|
-
"_internal-slim-build"
|
|
14
|
-
]["packages"][BUILD_PACKAGE]
|
|
15
|
-
project_config: dict[str, Any] = config["project"]
|
|
16
|
-
# Get main optional dependencies, extras
|
|
17
|
-
optional_dependencies: dict[str, list[str]] = metadata.get(
|
|
18
|
-
"optional-dependencies", {}
|
|
19
|
-
)
|
|
20
|
-
# Get custom optional dependencies name to always include in this (non-slim) package
|
|
21
|
-
include_optional_dependencies: list[str] = config.get(
|
|
22
|
-
"include-optional-dependencies", []
|
|
23
|
-
)
|
|
24
|
-
# Override main [project] configs with custom configs for this package
|
|
25
|
-
for key, value in project_config.items():
|
|
26
|
-
metadata[key] = value
|
|
27
|
-
# Get custom build config for the current package
|
|
28
|
-
build_config: dict[str, Any] = (
|
|
29
|
-
config.get("tool", {}).get("pdm", {}).get("build", {})
|
|
30
|
-
)
|
|
31
|
-
# Override PDM build config with custom build config for this package
|
|
32
|
-
for key, value in build_config.items():
|
|
33
|
-
context.config.build_config[key] = value
|
|
34
|
-
# Get main dependencies
|
|
35
|
-
dependencies: list[str] = metadata.get("dependencies", [])
|
|
36
|
-
# Add optional dependencies to the default dependencies for this (non-slim) package
|
|
37
|
-
for include_optional in include_optional_dependencies:
|
|
38
|
-
optional_dependencies_group = optional_dependencies.get(include_optional, [])
|
|
39
|
-
dependencies.extend(optional_dependencies_group)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding:utf-8 -*-
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
|
|
6
|
-
TOOL = ("pdm", "poetry", "uv")[0]
|
|
7
|
-
parent = os.path.abspath(os.path.dirname(__file__))
|
|
8
|
-
work_dir = os.path.dirname(parent)
|
|
9
|
-
if os.getcwd() != work_dir:
|
|
10
|
-
os.chdir(work_dir)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def run_and_echo(cmd, tool=TOOL, verbose=True):
|
|
14
|
-
# type: (str, str, bool) -> int
|
|
15
|
-
if tool:
|
|
16
|
-
cmd = tool + " run " + cmd
|
|
17
|
-
if verbose:
|
|
18
|
-
print("--> " + cmd)
|
|
19
|
-
return os.system(cmd)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if run_and_echo("fast check --skip-mypy", verbose=False) != 0:
|
|
23
|
-
print("\033[1m Please run './scripts/format.py' to auto-fix style issues \033[0m")
|
|
24
|
-
sys.exit(1)
|
|
25
|
-
package_name = os.path.basename(work_dir).replace("-", "_").replace(" ", "_")
|
|
26
|
-
if run_and_echo("ty check {} {}".format(package_name, "tests")) != 0:
|
|
27
|
-
sys.exit(1)
|
|
28
|
-
if run_and_echo("bandit -r {}".format(package_name)) != 0:
|
|
29
|
-
sys.exit(1)
|
|
30
|
-
print("Done. ✨ 🍰 ✨")
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
"""
|
|
3
|
-
Install deps by `pdm install -G :all --frozen`
|
|
4
|
-
if value of `pdm config use_uv` is True,
|
|
5
|
-
otherwise by the following shells:
|
|
6
|
-
|
|
7
|
-
pdm run python -m ensurepip
|
|
8
|
-
pdm run python -m pip install --upgrade pip
|
|
9
|
-
pdm run python -m pip install --group dev -e .
|
|
10
|
-
|
|
11
|
-
Usage::
|
|
12
|
-
pdm run python scripts/deps.py
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import os
|
|
18
|
-
import platform
|
|
19
|
-
import shlex
|
|
20
|
-
import subprocess
|
|
21
|
-
import sys
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
|
|
24
|
-
__version__ = "0.1.3"
|
|
25
|
-
__updated_at__ = "2025.07.21"
|
|
26
|
-
SHELL = """
|
|
27
|
-
pdm run python -m ensurepip
|
|
28
|
-
pdm run python -m pip install --upgrade pip
|
|
29
|
-
pdm run python -m pip install --group dev -e .
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def run_and_echo(
|
|
34
|
-
cmd: str,
|
|
35
|
-
env: dict[str, str] | None = None,
|
|
36
|
-
dry: bool = False,
|
|
37
|
-
verbose: bool = True,
|
|
38
|
-
**kw,
|
|
39
|
-
) -> int:
|
|
40
|
-
if verbose:
|
|
41
|
-
print("-->", cmd)
|
|
42
|
-
if dry:
|
|
43
|
-
return 0
|
|
44
|
-
if env is not None:
|
|
45
|
-
env = dict(os.environ, **env)
|
|
46
|
-
return subprocess.run(shlex.split(cmd), env=env, **kw).returncode
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def capture_output(cmd: str) -> str:
|
|
50
|
-
r = subprocess.run(
|
|
51
|
-
shlex.split(cmd), capture_output=True, text=True, encoding="utf-8"
|
|
52
|
-
)
|
|
53
|
-
return r.stdout.strip()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def is_using_uv() -> bool:
|
|
57
|
-
return capture_output("pdm config use_uv").lower() == "true"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def pop_if_contains(args: list[str], flag: str) -> bool:
|
|
61
|
-
try:
|
|
62
|
-
index = args.index(flag)
|
|
63
|
-
except ValueError:
|
|
64
|
-
return False
|
|
65
|
-
else:
|
|
66
|
-
args.pop(index)
|
|
67
|
-
return True
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def prefer_pdm(args: list[str]) -> bool:
|
|
71
|
-
if pop_if_contains(args, "--pip"):
|
|
72
|
-
return False
|
|
73
|
-
return platform.system() != "Windows" or is_using_uv()
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def not_distribution() -> bool:
|
|
77
|
-
toml = Path(__file__).parent / "pyproject.toml"
|
|
78
|
-
if not toml.exists():
|
|
79
|
-
toml = toml.parent.parent / toml.name
|
|
80
|
-
if not toml.exists():
|
|
81
|
-
return False
|
|
82
|
-
if sys.version_info >= (3, 11):
|
|
83
|
-
import tomllib
|
|
84
|
-
else:
|
|
85
|
-
try:
|
|
86
|
-
import tomli as tomllib
|
|
87
|
-
except ImportError:
|
|
88
|
-
return False
|
|
89
|
-
doc = tomllib.loads(toml.read_text("utf8"))
|
|
90
|
-
try:
|
|
91
|
-
return not doc["tool"]["pdm"]["distribution"]
|
|
92
|
-
except KeyError:
|
|
93
|
-
...
|
|
94
|
-
return False
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def run_shell(command: str, args: list[str], dry: bool) -> int | None:
|
|
98
|
-
if args:
|
|
99
|
-
command += " " + " ".join(i if i.startswith("-") else repr(i) for i in args)
|
|
100
|
-
if run_and_echo(command, dry=dry) == 0:
|
|
101
|
-
return None
|
|
102
|
-
return 1
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def pip_available() -> bool:
|
|
106
|
-
rc = run_and_echo(
|
|
107
|
-
"pdm run python -m pip --version", capture_output=True, verbose=False
|
|
108
|
-
)
|
|
109
|
-
return rc == 0
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def main() -> int | None:
|
|
113
|
-
args = sys.argv[1:]
|
|
114
|
-
if len(args) == 1:
|
|
115
|
-
a1 = args[0]
|
|
116
|
-
if a1 == "-h" or "--help" in args:
|
|
117
|
-
print(__doc__)
|
|
118
|
-
return None
|
|
119
|
-
elif a1 in ("-v", "--version"):
|
|
120
|
-
print(__version__)
|
|
121
|
-
return None
|
|
122
|
-
dry = pop_if_contains(args, "--dry") or pop_if_contains(args, "--dry-run")
|
|
123
|
-
if pop_if_contains(args, "--uv"):
|
|
124
|
-
if os.path.exists("uv.lock"):
|
|
125
|
-
return run_shell("uv sync --all-extras --all-groups", args, dry)
|
|
126
|
-
command = (
|
|
127
|
-
"uv install --all-extras -r pyproject.toml --group dev --no-verify-hashes"
|
|
128
|
-
)
|
|
129
|
-
return run_shell(command, args, dry)
|
|
130
|
-
if prefer_pdm(args):
|
|
131
|
-
command = "pdm install --frozen -G :all"
|
|
132
|
-
return run_shell(command, args, dry)
|
|
133
|
-
shell = SHELL.strip()
|
|
134
|
-
if pop_if_contains(args, "--no-dev"):
|
|
135
|
-
shell = shell.replace(" --group dev", "")
|
|
136
|
-
if args:
|
|
137
|
-
extras = " ".join(i if i.startswith("-") else repr(i) for i in args)
|
|
138
|
-
if extras.startswith("-"):
|
|
139
|
-
shell += " " + extras
|
|
140
|
-
else:
|
|
141
|
-
shell += extras # e.g.: '[xls,fastapi]'
|
|
142
|
-
elif not_distribution():
|
|
143
|
-
shell = shell.replace(" -e .", "")
|
|
144
|
-
cmds = shell.splitlines()
|
|
145
|
-
if pip_available(): # If pip exists, skip installing it by ensurepip
|
|
146
|
-
cmds = cmds[1:]
|
|
147
|
-
for cmd in cmds:
|
|
148
|
-
if run_and_echo(cmd, dry=dry) != 0:
|
|
149
|
-
return 1
|
|
150
|
-
if not dry:
|
|
151
|
-
print("Done.")
|
|
152
|
-
return 0
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if __name__ == "__main__":
|
|
156
|
-
sys.exit(main())
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding:utf-8 -*-
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
|
|
6
|
-
parent = os.path.abspath(os.path.dirname(__file__))
|
|
7
|
-
work_dir = os.path.dirname(parent)
|
|
8
|
-
if os.getcwd() != work_dir:
|
|
9
|
-
os.chdir(work_dir)
|
|
10
|
-
|
|
11
|
-
cmd = "pdm run fast lint --skip-mypy"
|
|
12
|
-
if os.system(cmd) != 0:
|
|
13
|
-
sys.exit(1)
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding:utf-8 -*-
|
|
3
|
-
import os
|
|
4
|
-
import shlex
|
|
5
|
-
import subprocess
|
|
6
|
-
import sys
|
|
7
|
-
import time
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
|
|
10
|
-
work_dir = Path(__file__).parent.resolve().parent
|
|
11
|
-
if Path.cwd() != work_dir:
|
|
12
|
-
os.chdir(str(work_dir))
|
|
13
|
-
|
|
14
|
-
CMD = "pdm run coverage run -m pytest"
|
|
15
|
-
COMBINE = "pdm run coverage combine .coverage*"
|
|
16
|
-
REPORT = "pdm run coverage report -m"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def remove_outdate_files(start_time: float) -> None:
|
|
20
|
-
for file in work_dir.glob(".coverage*"):
|
|
21
|
-
if file.stat().st_mtime < start_time:
|
|
22
|
-
file.unlink()
|
|
23
|
-
print(f"Removed outdate file: {file}")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def run_command(cmd: str, shell=False) -> None:
|
|
27
|
-
print("-->", cmd, flush=True)
|
|
28
|
-
r = subprocess.run(cmd if shell else shlex.split(cmd), shell=shell)
|
|
29
|
-
r.returncode and sys.exit(1)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def combine_result_files(shell=COMBINE) -> None:
|
|
33
|
-
to_be_combine = [i.name for i in work_dir.glob(".coverage.*")]
|
|
34
|
-
if to_be_combine:
|
|
35
|
-
if sys.platform == "win32":
|
|
36
|
-
if work_dir.joinpath(".coverage").exists():
|
|
37
|
-
shell = shell.replace("*", " ")
|
|
38
|
-
else:
|
|
39
|
-
shell = shell.replace(".coverage*", "")
|
|
40
|
-
shell += " ".join(to_be_combine)
|
|
41
|
-
run_command(shell, True)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
started_at = time.time()
|
|
45
|
-
run_command(CMD)
|
|
46
|
-
remove_outdate_files(started_at)
|
|
47
|
-
combine_result_files()
|
|
48
|
-
run_command(REPORT)
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import fast_dev_cli
|
|
4
|
-
from fast_dev_cli.cli import dev, main, run_and_echo, runserver
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def test_runserver(capsys):
|
|
8
|
-
runserver(dry=True)
|
|
9
|
-
out = capsys.readouterr().out.strip()
|
|
10
|
-
assert out.replace("--> ", "") == "fastapi dev"
|
|
11
|
-
runserver(port=8000, dry=True)
|
|
12
|
-
out = capsys.readouterr().out.strip()
|
|
13
|
-
assert out.replace("--> ", "") == "fastapi dev"
|
|
14
|
-
runserver(port=9000, dry=True)
|
|
15
|
-
out = capsys.readouterr().out.strip()
|
|
16
|
-
assert out.replace("--> ", "") == "fastapi dev --port=9000"
|
|
17
|
-
runserver(host="0.0.0.0", dry=True)
|
|
18
|
-
out = capsys.readouterr().out.strip()
|
|
19
|
-
assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0"
|
|
20
|
-
runserver(port=9000, host="0.0.0.0", dry=True)
|
|
21
|
-
out = capsys.readouterr().out.strip()
|
|
22
|
-
assert out.replace("--> ", "") == "fastapi dev --port=9000 --host=0.0.0.0"
|
|
23
|
-
runserver("9000", host="0.0.0.0", dry=True)
|
|
24
|
-
out = capsys.readouterr().out.strip()
|
|
25
|
-
assert out.replace("--> ", "") == "fastapi dev --port=9000 --host=0.0.0.0"
|
|
26
|
-
runserver("app.py", host="0.0.0.0", dry=True)
|
|
27
|
-
out = capsys.readouterr().out.strip()
|
|
28
|
-
assert out.replace("--> ", "") == "fastapi dev app.py --host=0.0.0.0"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def test_dev(capsys):
|
|
32
|
-
dev(None, None, dry=True)
|
|
33
|
-
out = capsys.readouterr().out.strip()
|
|
34
|
-
assert out.replace("--> ", "") == "fastapi dev"
|
|
35
|
-
dev(port=8000, host="", dry=True)
|
|
36
|
-
out = capsys.readouterr().out.strip()
|
|
37
|
-
assert out.replace("--> ", "") == "fastapi dev"
|
|
38
|
-
dev(port=9000, host=None, dry=True)
|
|
39
|
-
out = capsys.readouterr().out.strip()
|
|
40
|
-
assert out.replace("--> ", "") == "fastapi dev --port=9000"
|
|
41
|
-
dev(8000, host="0.0.0.0", dry=True)
|
|
42
|
-
out = capsys.readouterr().out.strip()
|
|
43
|
-
assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0"
|
|
44
|
-
dev(port=9000, host="0.0.0.0", dry=True)
|
|
45
|
-
out = capsys.readouterr().out.strip()
|
|
46
|
-
assert out.replace("--> ", "") == "fastapi dev --port=9000 --host=0.0.0.0"
|
|
47
|
-
dev(8001, host="0.0.0.0", dry=True)
|
|
48
|
-
out = capsys.readouterr().out.strip()
|
|
49
|
-
assert out.replace("--> ", "") == "fastapi dev --port=8001 --host=0.0.0.0"
|
|
50
|
-
dev(None, file="8001", host="0.0.0.0", dry=True)
|
|
51
|
-
out = capsys.readouterr().out.strip()
|
|
52
|
-
assert out.replace("--> ", "") == "fastapi dev --port=8001 --host=0.0.0.0"
|
|
53
|
-
dev(None, file="main.py", host="0.0.0.0", dry=True)
|
|
54
|
-
out = capsys.readouterr().out.strip()
|
|
55
|
-
assert out.replace("--> ", "") == "fastapi dev main.py --host=0.0.0.0"
|
|
56
|
-
dev(8001, file="main.py", host="0.0.0.0", dry=True)
|
|
57
|
-
out = capsys.readouterr().out.strip()
|
|
58
|
-
assert out.replace("--> ", "") == "fastapi dev main.py --port=8001 --host=0.0.0.0"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_fast_dev(tmp_path):
|
|
62
|
-
out = tmp_path / "a.txt"
|
|
63
|
-
run_and_echo(f"fast dev 9000 --dry > {out}", verbose=False)
|
|
64
|
-
assert "fastapi dev --port=9000" in out.read_text()
|
|
65
|
-
run_and_echo(f"fast dev main.py --dry > {out}", verbose=False)
|
|
66
|
-
assert "fastapi dev main.py" in out.read_text()
|
|
67
|
-
run_and_echo(f"fast dev main.py --port=9000 --dry > {out}", verbose=False)
|
|
68
|
-
assert "fastapi dev main.py --port=9000" in out.read_text()
|
|
69
|
-
run_and_echo(
|
|
70
|
-
f"fast dev main.py --port=9000 --host=0.0.0.0 --dry > {out}", verbose=False
|
|
71
|
-
)
|
|
72
|
-
assert "fastapi dev main.py --port=9000 --host=0.0.0.0" in out.read_text()
|
|
73
|
-
run_and_echo(f"fast dev 9000 --host=0.0.0.0 --dry > {out}", verbose=False)
|
|
74
|
-
assert "fastapi dev --port=9000 --host=0.0.0.0" in out.read_text()
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def test_run_by_module(tmp_path):
|
|
78
|
-
out = tmp_path / "b.txt"
|
|
79
|
-
fast = "python -m fast_dev_cli"
|
|
80
|
-
run_and_echo(f"{fast} dev 9000 --dry > {out}", verbose=False)
|
|
81
|
-
assert "fastapi dev --port=9000" in out.read_text()
|
|
82
|
-
run_and_echo(f"{fast} dev main.py --dry > {out}", verbose=False)
|
|
83
|
-
assert "fastapi dev main.py" in out.read_text()
|
|
84
|
-
run_and_echo(f"{fast} dev main.py --port=9000 --dry > {out}", verbose=False)
|
|
85
|
-
assert "fastapi dev main.py --port=9000" in out.read_text()
|
|
86
|
-
run_and_echo(
|
|
87
|
-
f"{fast} dev main.py --port=9000 --host=0.0.0.0 --dry > {out}", verbose=False
|
|
88
|
-
)
|
|
89
|
-
assert "fastapi dev main.py --port=9000 --host=0.0.0.0" in out.read_text()
|
|
90
|
-
run_and_echo(f"{fast} dev 9000 --host=0.0.0.0 --dry > {out}", verbose=False)
|
|
91
|
-
assert "fastapi dev --port=9000 --host=0.0.0.0" in out.read_text()
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def test_main(mocker):
|
|
95
|
-
mocker.patch("fast_dev_cli.cli.cli")
|
|
96
|
-
main()
|
|
97
|
-
fast_dev_cli.cli.cli.assert_called_once() # type:ignore[attr-defined]
|
|
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
|