Skip to content

Commit 72b1c54

Browse files
committed
split choose and launch code to dedicated modules
1 parent 98cd345 commit 72b1c54

File tree

4 files changed

+123
-115
lines changed

4 files changed

+123
-115
lines changed

src/qbpm/choose.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import shutil
2+
import subprocess
3+
from collections.abc import Iterable
4+
from pathlib import Path
5+
from sys import platform
6+
7+
from . import Profile
8+
from .launch import launch_qutebrowser
9+
from .utils import env_menus, error, installed_menus, or_phrase
10+
11+
12+
def choose_profile(
13+
profile_dir: Path, menu: str | None, foreground: bool, qb_args: tuple[str, ...]
14+
) -> bool:
15+
menu = menu or next(installed_menus(), None)
16+
if not menu:
17+
possible_menus = or_phrase([menu for menu in env_menus() if menu != "fzf-tmux"])
18+
error(
19+
"no menu program found, use --menu to provide a dmenu-compatible menu or install one of "
20+
+ possible_menus
21+
)
22+
return False
23+
if menu == "applescript" and platform != "darwin":
24+
error(f"applescript cannot be used on a {platform} host")
25+
return False
26+
real_profiles = {profile.name for profile in profile_dir.iterdir()}
27+
if len(real_profiles) == 0:
28+
error("no profiles")
29+
return False
30+
profiles = [*real_profiles, "qutebrowser"]
31+
32+
command = menu_command(menu, profiles, qb_args)
33+
if not command:
34+
return False
35+
36+
selection_cmd = subprocess.Popen(
37+
command,
38+
shell=True,
39+
stdout=subprocess.PIPE,
40+
stderr=None,
41+
)
42+
out = selection_cmd.stdout
43+
selection = out and out.read().decode(errors="ignore").rstrip("\n")
44+
45+
if selection == "qutebrowser" and "qutebrowser" not in real_profiles:
46+
return launch_qutebrowser(None, foreground, qb_args)
47+
elif selection:
48+
profile = Profile(selection, profile_dir)
49+
return launch_qutebrowser(profile, foreground, qb_args)
50+
else:
51+
error("no profile selected")
52+
return False
53+
54+
55+
def menu_command(
56+
menu: str, profiles: Iterable[str], qb_args: tuple[str, ...]
57+
) -> str | None:
58+
profiles = sorted(profiles)
59+
arg_string = " ".join(qb_args)
60+
if menu == "applescript":
61+
profile_list = '", "'.join(profiles)
62+
return f"""osascript -e \'set profiles to {{"{profile_list}"}}
63+
set profile to choose from list profiles with prompt "qutebrowser: {arg_string}" default items {{item 1 of profiles}}
64+
item 1 of profile\'"""
65+
66+
prompt = "-p qutebrowser"
67+
command = menu
68+
if len(menu.split(" ")) == 1:
69+
program = Path(menu).name
70+
if program == "rofi":
71+
command = f"{menu} -dmenu -no-custom {prompt} -mesg '{arg_string}'"
72+
elif program == "wofi":
73+
command = f"{menu} --dmenu {prompt}"
74+
elif program.startswith("dmenu"):
75+
command = f"{menu} {prompt}"
76+
elif program.startswith("fzf"):
77+
command = f"{menu} --prompt 'qutebrowser '"
78+
elif program == "fuzzel":
79+
command = f"{menu} -d"
80+
exe = command.split(" ")[0]
81+
if not shutil.which(exe):
82+
error(f"command '{exe}' not found")
83+
return None
84+
profile_list = "\n".join(profiles)
85+
return f'echo "{profile_list}" | {command}'

src/qbpm/launch.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import shutil
2+
import subprocess
3+
4+
from . import Profile
5+
from .paths import qutebrowser_exe
6+
from .utils import error
7+
8+
9+
def launch_qutebrowser(
10+
profile: Profile | None, foreground: bool, qb_args: tuple[str, ...]
11+
) -> bool:
12+
qb = profile.cmdline() if profile else [qutebrowser_exe()]
13+
return launch(foreground, [*qb, *qb_args])
14+
15+
16+
def launch(foreground: bool, args: list[str]) -> bool:
17+
if not shutil.which(args[0]):
18+
error("qutebrowser is not installed")
19+
return False
20+
21+
if foreground:
22+
return subprocess.run(args, check=False).returncode == 0
23+
else:
24+
p = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
25+
try:
26+
# give qb a chance to validate input before returning to shell
27+
stdout, stderr = p.communicate(timeout=0.1)
28+
print(stderr.decode(errors="ignore"), end="")
29+
except subprocess.TimeoutExpired:
30+
pass
31+
32+
return True

src/qbpm/main.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import click
99

1010
from . import Profile, operations, profiles
11+
from .choose import choose_profile
12+
from .launch import launch_qutebrowser
1113
from .paths import default_profile_dir, qutebrowser_data_dir
1214
from .utils import SUPPORTED_MENUS, error, or_phrase
1315

@@ -128,7 +130,7 @@ def from_session(
128130
def launch(context: Context, profile_name: str, **kwargs: Any) -> None:
129131
"""Launch qutebrowser with a specific profile. All QB_ARGS are passed on to qutebrowser."""
130132
profile = Profile(profile_name, **vars(context))
131-
exit_with(operations.launch(profile, **kwargs))
133+
exit_with(launch_qutebrowser(profile, **kwargs))
132134

133135

134136
@main.command(context_settings={"ignore_unknown_options": True})
@@ -148,7 +150,7 @@ def choose(context: Context, **kwargs: Any) -> None:
148150
Support is built in for many X and Wayland launchers, as well as applescript dialogs.
149151
All QB_ARGS are passed on to qutebrowser.
150152
"""
151-
exit_with(operations.choose(profile_dir=context.profile_dir, **kwargs))
153+
exit_with(choose_profile(profile_dir=context.profile_dir, **kwargs))
152154

153155

154156
@main.command()
@@ -193,7 +195,7 @@ def then_launch(
193195
) -> None:
194196
exit_with(
195197
operation(profile, **kwargs)
196-
and ((not launch) or operations.launch(profile, foreground, qb_args))
198+
and ((not launch) or launch_qutebrowser(profile, foreground, qb_args))
197199
)
198200

199201

src/qbpm/operations.py

Lines changed: 1 addition & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import shutil
2-
import subprocess
3-
from collections.abc import Iterable
42
from pathlib import Path
5-
from sys import platform
63

74
from . import Profile, profiles
85
from .desktop import create_desktop_file
9-
from .paths import qutebrowser_exe
10-
from .utils import env_menus, error, installed_menus, or_phrase
6+
from .utils import error
117

128

139
def from_session(
@@ -27,117 +23,10 @@ def from_session(
2723
return True
2824

2925

30-
def launch(profile: Profile, foreground: bool, qb_args: tuple[str, ...]) -> bool:
31-
if not profiles.exists(profile):
32-
return False
33-
34-
args = profile.cmdline() + list(qb_args)
35-
return launch_internal(foreground, args)
36-
37-
38-
def launch_qutebrowser(foreground: bool, qb_args: tuple[str, ...]) -> bool:
39-
return launch_internal(foreground, [qutebrowser_exe(), *qb_args])
40-
41-
42-
def launch_internal(foreground: bool, args: list[str]) -> bool:
43-
if not shutil.which(args[0]):
44-
error("qutebrowser is not installed")
45-
return False
46-
47-
if foreground:
48-
return subprocess.run(args, check=False).returncode == 0
49-
else:
50-
p = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
51-
try:
52-
# give qb a chance to validate input before returning to shell
53-
stdout, stderr = p.communicate(timeout=0.1)
54-
print(stderr.decode(errors="ignore"), end="")
55-
except subprocess.TimeoutExpired:
56-
pass
57-
58-
return True
59-
60-
6126
def desktop(profile: Profile) -> bool:
6227
exists = profile.exists()
6328
if exists:
6429
create_desktop_file(profile)
6530
else:
6631
error(f"profile {profile.name} not found at {profile.root}")
6732
return exists
68-
69-
70-
def choose(
71-
profile_dir: Path, menu: str | None, foreground: bool, qb_args: tuple[str, ...]
72-
) -> bool:
73-
menu = menu or next(installed_menus(), None)
74-
if not menu:
75-
possible_menus = or_phrase([menu for menu in env_menus() if menu != "fzf-tmux"])
76-
error(
77-
"no menu program found, use --menu to provide a dmenu-compatible menu or install one of "
78-
+ possible_menus
79-
)
80-
return False
81-
if menu == "applescript" and platform != "darwin":
82-
error(f"applescript cannot be used on a {platform} host")
83-
return False
84-
real_profiles = {profile.name for profile in profile_dir.iterdir()}
85-
if len(real_profiles) == 0:
86-
error("no profiles")
87-
return False
88-
profiles = [*real_profiles, "qutebrowser"]
89-
90-
command = menu_command(menu, profiles, qb_args)
91-
if not command:
92-
return False
93-
94-
selection_cmd = subprocess.Popen(
95-
command,
96-
shell=True,
97-
stdout=subprocess.PIPE,
98-
stderr=None,
99-
)
100-
out = selection_cmd.stdout
101-
selection = out and out.read().decode(errors="ignore").rstrip("\n")
102-
103-
if selection == "qutebrowser" and "qutebrowser" not in real_profiles:
104-
return launch_qutebrowser(foreground, qb_args)
105-
elif selection:
106-
profile = Profile(selection, profile_dir)
107-
return launch(profile, foreground, qb_args)
108-
else:
109-
error("no profile selected")
110-
return False
111-
112-
113-
def menu_command(
114-
menu: str, profiles: Iterable[str], qb_args: tuple[str, ...]
115-
) -> str | None:
116-
profiles = sorted(profiles)
117-
arg_string = " ".join(qb_args)
118-
if menu == "applescript":
119-
profile_list = '", "'.join(profiles)
120-
return f"""osascript -e \'set profiles to {{"{profile_list}"}}
121-
set profile to choose from list profiles with prompt "qutebrowser: {arg_string}" default items {{item 1 of profiles}}
122-
item 1 of profile\'"""
123-
124-
prompt = "-p qutebrowser"
125-
command = menu
126-
if len(menu.split(" ")) == 1:
127-
program = Path(menu).name
128-
if program == "rofi":
129-
command = f"{menu} -dmenu -no-custom {prompt} -mesg '{arg_string}'"
130-
elif program == "wofi":
131-
command = f"{menu} --dmenu {prompt}"
132-
elif program.startswith("dmenu"):
133-
command = f"{menu} {prompt}"
134-
elif program.startswith("fzf"):
135-
command = f"{menu} --prompt 'qutebrowser '"
136-
elif program == "fuzzel":
137-
command = f"{menu} -d"
138-
exe = command.split(" ")[0]
139-
if not shutil.which(exe):
140-
error(f"command '{exe}' not found")
141-
return None
142-
profile_list = "\n".join(profiles)
143-
return f'echo "{profile_list}" | {command}'

0 commit comments

Comments
 (0)