Skip to content

Commit e1d9317

Browse files
committed
autoconfig.yml support
1 parent ddc0d49 commit e1d9317

File tree

6 files changed

+125
-13
lines changed

6 files changed

+125
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ qbpm now reads configuration options from a `$XDG_CONFIG_HOME/qbpm/config.toml`!
66
- run `qbpm config default > "$(qbpm config path)"`
77
- supported configuration options:
88
- `config_py_template`: control the contents of `config.py` in new profiles
9+
- `symlink_autoconfig`: symlink qutebrowser's `autoconfig.yml` in new profiles
910
- `profile_directory` and `qutebrowser_config_directory`
1011
- equivalent `--profile-dir` to `--qutebrowser-config-dir`
1112
- `generate_desktop_file` and `desktop_file_directory`
@@ -15,6 +16,7 @@ qbpm now reads configuration options from a `$XDG_CONFIG_HOME/qbpm/config.toml`!
1516
- see default config file for more detailed documentation
1617

1718
## other
19+
- support for symlinking `autoconfig.yml` in addition to or instead of sourcing `config.py`
1820
- `contrib/qbpm.desktop`: add `MimeType` and `Keywords`, fix incorrect formatting of `Categories`
1921
- allow help text to be slightly wider to avoid awkward line breaks
2022
- macOS: fix detection of qutebrowser binary in /Applications

src/qbpm/config.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
@dataclass(kw_only=True)
1717
class Config:
1818
config_py_template: str | None = None
19+
symlink_autoconfig: bool = False
1920
qutebrowser_config_directory: Path | None = None
2021
profile_directory: Path = field(default_factory=paths.default_profile_dir)
2122
generate_desktop_file: bool = platform.system() == "Linux"
@@ -57,14 +58,21 @@ def find_config(config_path: Path | None) -> Config:
5758
return Config.load(config_path)
5859

5960

60-
def find_qutebrowser_config_dir(qb_config_dir: Path | None) -> Path | None:
61+
def find_qutebrowser_config_dir(
62+
qb_config_dir: Path | None, autoconfig: bool = False
63+
) -> Path | None:
6164
dirs = (
6265
[qb_config_dir, qb_config_dir / "config"]
6366
if qb_config_dir
6467
else list(paths.qutebrowser_config_dirs())
6568
)
6669
for config_dir in dirs:
67-
if (config_dir / "config.py").exists():
70+
if (config_dir / "config.py").exists() or (
71+
autoconfig and (config_dir / "autoconfig.yml").exists()
72+
):
6873
return config_dir.absolute()
69-
error(f"couldn't find config.py in {or_phrase(dirs)}")
74+
if autoconfig:
75+
error(f"couldn't find config.py or autoconfig.yml in {or_phrase(dirs)}")
76+
else:
77+
error(f"couldn't find config.py in {or_phrase(dirs)}")
7078
return None

src/qbpm/config.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ c.window.title_format += ' ({profile_name})'
88
config.load_autoconfig()
99
"""
1010

11+
# symlink autoconfig.yml in new profiles if the os supports it
12+
# symlink_autoconfig = false
13+
1114
# location to store qutebrowser profiles
1215
# profile_directory = "~/.local/share/qutebrowser-profiles"
1316

src/qbpm/profiles.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from . import Profile
55
from .config import Config, find_qutebrowser_config_dir
66
from .desktop import create_desktop_file
7-
from .log import error
7+
from .log import error, info
88

99
MIME_TYPES = [
1010
"text/html",
@@ -31,7 +31,6 @@ def create_profile(profile: Profile, overwrite: bool = False) -> bool:
3131

3232
config_dir = profile.root / "config"
3333
config_dir.mkdir(parents=True, exist_ok=overwrite)
34-
print(profile.root)
3534
return True
3635

3736

@@ -42,20 +41,41 @@ def create_config(
4241
home_page: str | None = None,
4342
overwrite: bool = False,
4443
) -> None:
44+
source = qb_config_dir / "config.py"
45+
if not source.is_file():
46+
return
4547
user_config = profile.root / "config" / "config.py"
4648
with user_config.open(mode="w" if overwrite else "x") as dest_config:
4749
out = partial(print, file=dest_config)
4850
out(
4951
config_py_template.format(
5052
profile_name=profile.name,
51-
source_config_py=qb_config_dir / "config.py",
53+
source_config_py=source,
5254
)
5355
)
5456
# TODO move to template?
5557
if home_page:
5658
out(f"c.url.start_pages = ['{home_page}']")
5759

5860

61+
def link_autoconfig(
62+
profile: Profile,
63+
qb_config_dir: Path,
64+
overwrite: bool = False,
65+
) -> None:
66+
if not hasattr(Path, "symlink_to"):
67+
return
68+
source = qb_config_dir / "autoconfig.yml"
69+
dest = profile.root / "config" / "autoconfig.yml"
70+
if not source.is_file() or dest.resolve() == source.resolve():
71+
return
72+
if overwrite and dest.exists():
73+
backup = Path(str(dest) + ".bak")
74+
info(f"backing up existing autoconfig to {backup}")
75+
dest.replace(backup)
76+
dest.symlink_to(source)
77+
78+
5979
def check(profile: Profile) -> bool:
6080
if not profile.check_name():
6181
return False
@@ -82,7 +102,9 @@ def new_profile(
82102
if qb_config_dir and not qb_config_dir.is_dir():
83103
error(f"{qb_config_dir} is not a directory")
84104
return False
85-
qb_config_dir = find_qutebrowser_config_dir(qb_config_dir)
105+
qb_config_dir = find_qutebrowser_config_dir(
106+
qb_config_dir, config.symlink_autoconfig
107+
)
86108
if not qb_config_dir:
87109
return False
88110
if not config.config_py_template:
@@ -92,7 +114,10 @@ def new_profile(
92114
create_config(
93115
profile, qb_config_dir, config.config_py_template, home_page, overwrite
94116
)
117+
if config.symlink_autoconfig:
118+
link_autoconfig(profile, qb_config_dir, overwrite)
95119
if config.generate_desktop_file:
96120
create_desktop_file(profile, config.desktop_file_directory)
121+
print(profile.root)
97122
return True
98123
return False

tests/test_config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def test_full_config(tmp_path: Path):
3838
config_py_template = \"""
3939
config.load_autoconfig()
4040
\"""
41+
symlink_autoconfig = true
4142
qutebrowser_config_directory = "~/.config/qutebrowser"
4243
profile_directory = "profile"
4344
generate_desktop_file = false
@@ -47,6 +48,7 @@ def test_full_config(tmp_path: Path):
4748
""")
4849
assert find_config(file) == Config(
4950
config_py_template="config.load_autoconfig()\n",
51+
symlink_autoconfig=True,
5052
qutebrowser_config_directory=Path("~/.config/qutebrowser").expanduser(),
5153
profile_directory=Path("profile"),
5254
desktop_file_directory=Path("desktop"),
@@ -65,6 +67,14 @@ def test_find_qb_config(tmp_path: Path):
6567
assert find_qutebrowser_config_dir(qb_dir / "config") == qb_conf_dir
6668

6769

70+
def test_find_autoconfig(tmp_path: Path):
71+
qb_dir = tmp_path / "qb"
72+
qb_conf_dir = qb_dir / "config"
73+
qb_conf_dir.mkdir(parents=True)
74+
(qb_conf_dir / "autoconfig.yml").touch()
75+
assert find_qutebrowser_config_dir(qb_dir, autoconfig=True) == qb_conf_dir
76+
77+
6878
def test_find_qb_config_default(tmp_path: Path):
6979
(tmp_path / "config.py").touch()
7080
assert find_qutebrowser_config_dir(None) == tmp_path

tests/test_profiles.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,18 @@ def test_create_profile_nested_conflict(tmp_path: Path):
5454

5555

5656
def test_create_config(tmp_path: Path):
57+
(tmp_path / "config.py").touch()
5758
profile = Profile("test", tmp_path)
5859
config_dir = profile.root / "config"
5960
config_dir.mkdir(parents=True)
60-
profiles.create_config(profile, tmp_path, "")
61-
assert list(config_dir.iterdir()) == [config_dir / "config.py"]
61+
profiles.create_config(profile, tmp_path, "{source_config_py}")
62+
config = config_dir / "config.py"
63+
assert list(config_dir.iterdir()) == [config]
64+
assert str(tmp_path / "config.py") in config.read_text()
6265

6366

6467
def test_overwrite_config(tmp_path: Path):
68+
(tmp_path / "config.py").touch()
6569
profile = Profile("test", tmp_path)
6670
url = "http://example.com"
6771
config_dir = profile.root / "config"
@@ -76,6 +80,41 @@ def test_overwrite_config(tmp_path: Path):
7680
raise AssertionError()
7781

7882

83+
def test_link_autoconfig(tmp_path: Path):
84+
profile = Profile("test", tmp_path)
85+
config_dir = profile.root / "config"
86+
config_dir.mkdir(parents=True)
87+
(tmp_path / "autoconfig.yml").touch()
88+
profiles.link_autoconfig(profile, tmp_path, False)
89+
config = config_dir / "autoconfig.yml"
90+
assert list(config_dir.iterdir()) == [config]
91+
assert config.resolve().parent == tmp_path
92+
93+
94+
def test_autoconfig_present(tmp_path: Path):
95+
profile = Profile("test", tmp_path)
96+
config_dir = profile.root / "config"
97+
config_dir.mkdir(parents=True)
98+
(tmp_path / "autoconfig.yml").touch()
99+
profiles.link_autoconfig(profile, tmp_path, False)
100+
profiles.link_autoconfig(profile, tmp_path, False)
101+
config = config_dir / "autoconfig.yml"
102+
assert list(config_dir.iterdir()) == [config]
103+
assert config.resolve().parent == tmp_path
104+
105+
106+
def test_overwrite_autoconfig(tmp_path: Path):
107+
profile = Profile("test", tmp_path)
108+
config_dir = profile.root / "config"
109+
config_dir.mkdir(parents=True)
110+
(config_dir / "autoconfig.yml").touch()
111+
(tmp_path / "autoconfig.yml").touch()
112+
profiles.link_autoconfig(profile, tmp_path, True)
113+
config = config_dir / "autoconfig.yml"
114+
assert set(config_dir.iterdir()) == {config, config_dir / "autoconfig.yml.bak"}
115+
assert config.resolve().parent == tmp_path
116+
117+
79118
def test_new_profile(tmp_path: Path):
80119
(tmp_path / "config.py").touch()
81120
profile = Profile("test", tmp_path / "test")
@@ -86,16 +125,41 @@ def test_new_profile(tmp_path: Path):
86125
check_new_profile(profile)
87126

88127

128+
def test_new_profile_autoconfig(tmp_path: Path):
129+
(tmp_path / "autoconfig.yml").touch()
130+
profile = Profile("test", tmp_path / "test")
131+
config = Config.load(None)
132+
config.qutebrowser_config_directory = tmp_path
133+
config.generate_desktop_file = False
134+
config.symlink_autoconfig = True
135+
profiles.new_profile(profile, config)
136+
config_dir = profile.root / "config"
137+
assert set(config_dir.iterdir()) == {config_dir / "autoconfig.yml"}
138+
139+
140+
def test_new_profile_both(tmp_path: Path):
141+
(tmp_path / "config.py").touch()
142+
(tmp_path / "autoconfig.yml").touch()
143+
profile = Profile("test", tmp_path / "test")
144+
config = Config.load(None)
145+
config.qutebrowser_config_directory = tmp_path
146+
config.generate_desktop_file = False
147+
config.symlink_autoconfig = True
148+
profiles.new_profile(profile, config)
149+
assert len(set((profile.root / "config").iterdir())) == 2 # noqa: PLR2004
150+
151+
89152
def test_config_template(tmp_path: Path):
153+
(tmp_path / "config.py").touch()
90154
profile = Profile("test", tmp_path)
155+
config_dir = profile.root / "config"
156+
config_dir.mkdir(parents=True)
91157
template = "# Profile: {profile_name}\nconfig.source('{source_config_py}')"
92-
93158
profiles.create_profile(profile)
94-
profiles.create_config(profile, tmp_path / "config", template)
95-
159+
profiles.create_config(profile, tmp_path, template)
96160
config_content = (profile.root / "config" / "config.py").read_text()
97161
assert "# Profile: test" in config_content
98-
assert f"config.source('{tmp_path / 'config' / 'config.py'}')" in config_content
162+
assert f"config.source('{tmp_path / 'config.py'}')" in config_content
99163

100164

101165
def test_missing_qb_config(tmp_path: Path):

0 commit comments

Comments
 (0)