Skip to content

Commit bd8b741

Browse files
committed
feat(config): Config object
1 parent 2654f9f commit bd8b741

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

src/vcspull/config.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
A lot of these items are todo.
66
77
"""
8+
import dataclasses
89
import fnmatch
910
import logging
1011
import os
1112
import pathlib
1213
import typing as t
1314

15+
import yaml
16+
17+
from libvcs._internal.query_list import QueryList
18+
from libvcs._internal.shortcuts import create_project
1419
from libvcs.sync.git import GitRemote
1520
from vcspull.validator import is_valid_config
1621

@@ -24,6 +29,85 @@
2429
if t.TYPE_CHECKING:
2530
from typing_extensions import TypeGuard
2631

32+
from libvcs.sync.git import GitSync
33+
from libvcs.sync.hg import HgSync
34+
from libvcs.sync.svn import SvnSync
35+
36+
Repo = t.Union["GitSync", "HgSync", "SvnSync"]
37+
38+
39+
@dataclasses.dataclass
40+
class Config:
41+
repo_dict_map: list["Repo"]
42+
repos: list["Repo"] = dataclasses.field(init=False, default_factory=list)
43+
44+
def __post_init__(self) -> None:
45+
for repo in self.repo_dict_map:
46+
if isinstance(repo, dict):
47+
self.repos.append(create_project(**repo))
48+
49+
def filter_repos(self, **kwargs: object) -> list["Repo"]:
50+
return QueryList(self.repos).filter(**kwargs)
51+
52+
@classmethod
53+
def from_yaml_file(
54+
cls, file_path: pathlib.Path, cwd: pathlib.Path = pathlib.Path.cwd()
55+
) -> "Config":
56+
# load yaml
57+
raw_config = yaml.load(open(file_path).read(), Loader=yaml.Loader)
58+
repos: list[ConfigDict] = []
59+
for directory, repo_map in raw_config.items():
60+
assert isinstance(repo_map, dict)
61+
for repo, repo_data in repo_map.items():
62+
conf: dict[str, t.Any] = {}
63+
64+
if isinstance(repo_data, str):
65+
conf["url"] = repo_data
66+
else:
67+
conf = update_dict(conf, repo_data)
68+
69+
if "repo" in conf:
70+
if "url" not in conf:
71+
conf["url"] = conf.pop("repo")
72+
else:
73+
conf.pop("repo", None)
74+
75+
if "name" not in conf:
76+
conf["name"] = repo
77+
78+
if "dir" not in conf:
79+
conf["dir"] = expand_dir(
80+
pathlib.Path(expand_dir(pathlib.Path(directory), cwd=cwd))
81+
/ conf["name"],
82+
cwd,
83+
)
84+
85+
if "remotes" in conf:
86+
assert isinstance(conf["remotes"], dict)
87+
for remote_name, url in conf["remotes"].items():
88+
if isinstance(url, GitRemote):
89+
continue
90+
if isinstance(url, str):
91+
conf["remotes"][remote_name] = GitRemote(
92+
name=remote_name, fetch_url=url, push_url=url
93+
)
94+
elif isinstance(url, dict):
95+
assert "push_url" in url
96+
assert "fetch_url" in url
97+
conf["remotes"][remote_name] = GitRemote(
98+
name=remote_name, **url
99+
)
100+
101+
def is_valid_config_dict(val: t.Any) -> "TypeGuard[ConfigDict]":
102+
assert isinstance(val, dict)
103+
return True
104+
105+
assert is_valid_config_dict(conf)
106+
repos.append(conf)
107+
108+
# assert validate_config(config) # mypy happy
109+
return cls(repo_dict_map=repos) # type:ignore
110+
27111

28112
def expand_dir(
29113
_dir: pathlib.Path, cwd: pathlib.Path = pathlib.Path.cwd()

tests/test_config_object.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import pathlib
2+
import typing as t
3+
4+
import pytest
5+
6+
from libvcs._internal.types import StrPath
7+
from vcspull import config as config_tools
8+
9+
10+
class LoadYaml(t.Protocol):
11+
def __call__(
12+
self, content: str, dir: StrPath = ..., filename: str = ...
13+
) -> pathlib.Path:
14+
...
15+
16+
17+
@pytest.fixture
18+
def load_yaml(tmp_path: pathlib.Path) -> LoadYaml:
19+
def fn(
20+
content: str, dir: StrPath = "randomdir", filename: str = "randomfilename.yaml"
21+
) -> pathlib.Path:
22+
_dir = tmp_path / dir
23+
_dir.mkdir()
24+
_config = _dir / filename
25+
_config.write_text(content, encoding="utf-8")
26+
27+
return _config
28+
29+
return fn
30+
31+
32+
def test_simple_format(load_yaml: LoadYaml) -> None:
33+
config_file = load_yaml(
34+
"""
35+
vcspull:
36+
libvcs: git+https://github.com/vcs-python/libvcs
37+
"""
38+
)
39+
40+
config = config_tools.Config.from_yaml_file(config_file)
41+
42+
assert len(config.repos) == 1
43+
repo = config.repos[0]
44+
dir = repo.dir.parent.parent
45+
46+
assert dir / "vcspull" == repo.dir.parent
47+
assert dir / "vcspull" / "libvcs" == repo.dir
48+
49+
assert hasattr(config, "filter_repos")
50+
assert callable(config.filter_repos)
51+
assert config.filter_repos(dir=dir / "vcspull" / "libvcs") == [repo]

0 commit comments

Comments
 (0)