Coverage for /home/runner/work/coxbuild/coxbuild/src/coxbuild/extensions/python/package.py: 0%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import shutil
2from pathlib import Path
3from typing import Tuple
5from coxbuild.schema import depend, group, precond, run, task
7from .. import ProjectSettings, withProject
8from . import Settings, grouped, withSettings
10subgrouped = group("package")
13def loadFromRequirements(lines: list[str]) -> dict[str, str]:
14 results: dict[str, str] = {}
15 for line in lines:
16 sl = line.split("==")
17 if len(sl) != 2:
18 continue
19 k, v = sl
20 results[k] = v
21 return results
24def installedPackages() -> dict[str, str]:
25 output = run(["python", "-m", "pip", "freeze"], pipe=True).stdout
26 return loadFromRequirements(output.splitlines())
29def hasPackages(packages: dict[str, str]):
30 installed = installedPackages()
31 for p, v in packages.items():
32 if p not in installed or (v != "*" and installed[p] != v):
33 return False
34 return True
37def upgradePackages(*packages: str):
38 run(["python", "-m", "pip", "install", "--upgrade", *packages], retry=3)
41@grouped
42@subgrouped
43@withSettings
44@task
45def restore(requirements: Path | None = None, *, settings: Settings):
46 """Restore Python packages from requirements.txt."""
47 run(["python", "-m", "pip", "install", "-r",
48 str(requirements or settings.requirements)], retry=3)
51@restore.precond
52def needRestore(*, settings: Settings):
53 req: Path = settings.requirements
54 if not req.exists():
55 return False
56 reqs = loadFromRequirements(req.read_text("utf-8").splitlines())
57 return not hasPackages(reqs)
60@grouped
61@subgrouped
62@precond(lambda: not hasPackages({"build": "*", "twine": "*"}))
63@task
64def prebuild():
65 """Restore Python package build tools."""
66 upgradePackages("build", "twine")
69@grouped
70@subgrouped
71@withProject
72@depend(prebuild)
73@task
74def build(src: Path | None = None, dist: Path | None = None, *, project: ProjectSettings):
75 """Build Python package."""
76 src = src or project.src
77 run(["python", "-m", "build", "-o", str(dist or project.package)],
78 cwd=src)
79 for item in src.glob("*.egg-info"):
80 if not item.is_dir():
81 continue
82 shutil.rmtree(item)
85@grouped
86@subgrouped
87@withProject
88@task
89def installBuilt(dist: Path | None = None, *, project: ProjectSettings):
90 """Install the built package."""
91 run(["python", "-m", "pip", "install",
92 str(list((dist or project.package).glob("*.whl"))[0])])
95@grouped
96@subgrouped
97@withProject
98@task
99def uninstallBuilt(dist: Path | None = None, *, project: ProjectSettings):
100 """Uninstall the built package."""
101 run(["python", "-m", "pip", "uninstall",
102 str(list((dist or project.package).glob("*.whl"))[0]), "-y"])
105@grouped
106@subgrouped
107@withProject
108@depend(build)
109@task
110def deploy(dist: Path | None = None, *, project: ProjectSettings):
111 """Upload the package to PYPI."""
112 run(["python", "-m", "twine", "upload",
113 "--skip-existing", "--repository", "pypi", str(dist or project.package) + "/*"])