-
-
Notifications
You must be signed in to change notification settings - Fork 168
/
subproject.py
82 lines (66 loc) · 2.42 KB
/
subproject.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
"""Objects to interact with subprojects.
A *subproject* is a project that gets rendered and/or updated with Copier.
"""
import sys
from pathlib import Path
from typing import Optional
import yaml
from plumbum.cmd import git
from plumbum.machines import local
from pydantic.dataclasses import dataclass
from .template import Template
from .types import AbsolutePath, AnyByStrDict, VCSTypes
from .vcs import is_in_git_repo
# HACK https://github.com/python/mypy/issues/8520#issuecomment-772081075
if sys.version_info >= (3, 8):
from functools import cached_property
else:
from backports.cached_property import cached_property
@dataclass
class Subproject:
"""Object that represents the subproject and its current state.
Attributes:
local_abspath:
Absolute path on local disk pointing to the subproject root folder.
answers_relpath:
Relative path to [the answers file][the-copier-answersyml-file].
"""
local_abspath: AbsolutePath
answers_relpath: Path = Path(".copier-answers.yml")
def is_dirty(self) -> bool:
"""Indicates if the local template root is dirty.
Only applicable for VCS-tracked templates.
"""
if self.vcs == "git":
with local.cwd(self.local_abspath):
return bool(git("status", "--porcelain").strip())
return False
@property
def _raw_answers(self) -> AnyByStrDict:
"""The last answers, loaded raw as yaml."""
try:
return yaml.safe_load(
(self.local_abspath / self.answers_relpath).read_text()
)
except OSError:
return {}
@cached_property
def last_answers(self) -> AnyByStrDict:
"""Last answers, excluding private ones (except _src_path and _commit)."""
return {
key: value
for key, value in self._raw_answers.items()
if key in {"_src_path", "_commit"} or not key.startswith("_")
}
@cached_property
def template(self) -> Optional[Template]:
"""Template, as it was used the last time."""
last_url = self.last_answers.get("_src_path")
last_ref = self.last_answers.get("_commit")
if last_url:
return Template(url=last_url, ref=last_ref)
@cached_property
def vcs(self) -> Optional[VCSTypes]:
"""VCS type of the subproject."""
if is_in_git_repo(self.local_abspath):
return "git"