def desktop(profile: Profile) -> bool: exists = profile.exists() if exists: profiles.create_desktop_file(profile) else: error(f"profile {profile.name} not found at {profile.root}") return exists
def from_session( session: str, profile_name: Optional[str] = None, profile_dir: Optional[Path] = None, desktop_file: bool = True, overwrite: bool = False, ) -> Optional[Profile]: if session.endswith(".yml"): session_file = Path(session).expanduser() session_name = session_file.stem else: session_name = session session_file = user_data_dir() / "sessions" / (session_name + ".yml") if not session_file.is_file(): error(f"{session_file} is not a file") return None profile = Profile(profile_name or session_name, profile_dir) if not profiles.new_profile(profile, None, desktop_file, overwrite): return None session_dir = profile.root / "data" / "sessions" session_dir.mkdir(parents=True, exist_ok=overwrite) shutil.copy(session_file, session_dir / "_autosave.yml") return profile
def edit(profile: Profile) -> bool: if not profile.exists(): error(f"profile {profile.name} not found at {profile.root}") return False editor = os.environ.get("VISUAL") or os.environ.get("EDITOR") or "vim" os.execlp(editor, editor, str(profile.root / "config" / "config.py")) return True
def menu_command(menu: str, profiles: List[str], args: argparse.Namespace) -> Optional[str]: arg_string = " ".join(args.qb_args) if menu == "applescript": profile_list = '", "'.join(profiles) return f"""osascript -e \'set profiles to {{"{profile_list}"}} set profile to choose from list profiles with prompt "qutebrowser: {arg_string}" default items {{item 1 of profiles}} item 1 of profile\'""" prompt = "-p qutebrowser" command = menu if len(menu.split(" ")) == 1: program = Path(menu).name if program == "rofi": command = f"{menu} -dmenu -no-custom {prompt} -mesg {arg_string}" elif program == "wofi": command = f"{menu} --dmenu {prompt}" elif program in ["dmenu", "dmenu-wl"]: command = f"{menu} {prompt}" elif program == "fzf": command = f"{menu} --prompt 'qutebrowser '" exe = command.split(" ")[0] if not shutil.which(exe): error(f"command '{exe}' not found") return None profile_list = "\n".join(profiles) return f'echo "{profile_list}" | {command}'
def ensure_profile_exists(profile: Profile, create: bool = True) -> bool: if profile.root.exists() and not profile.root.is_dir(): error(f"{profile.root} is not a directory") return False if not profile.root.exists() and create: return new_profile(profile) if not profile.root.exists(): error(f"{profile.root} does not exist") return False return True
def create_profile(profile: Profile, overwrite: bool = False) -> bool: if not profile.check(): return False if not overwrite and profile.root.exists(): error(f"{profile.root} already exists") return False config_dir = profile.root / "config" config_dir.mkdir(parents=True, exist_ok=overwrite) print(profile.root) return True
def choose(args: argparse.Namespace) -> bool: menu = args.menu or get_default_menu() if not menu: error(f"No menu program found, please install one of: {AUTO_MENUS}") return False if menu == "applescript" and platform != "darwin": error(f"Menu applescript cannot be used on a {platform} host") return False profiles = [profile.name for profile in sorted(args.profile_dir.iterdir())] if len(profiles) == 0: error("No profiles") return False command = menu_command(menu, profiles, args) if not command: return False selection_cmd = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=None, ) out = selection_cmd.stdout selection = out and out.read().decode(errors="ignore").rstrip("\n") if selection: profile = Profile(selection, args.profile_dir) launch(profile, True, args.foreground, args.qb_args) else: error("No profile selected") return False return True
def launch(profile: Profile, strict: bool, foreground: bool, qb_args: List[str]) -> bool: if not profiles.ensure_profile_exists(profile, not strict): return False args = profile.cmdline() + qb_args if not shutil.which(args[0]): error("qutebrowser is not installed") return False if foreground: return subprocess.run(args).returncode == 0 else: p = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) try: # give qb a chance to validate input before returning to shell stdout, stderr = p.communicate(timeout=0.1) print(stderr.decode(errors="ignore"), end="") except subprocess.TimeoutExpired: pass return True
def main(mock_args: Optional[list[str]] = None) -> None: parser = argparse.ArgumentParser(description="qutebrowser profile manager") parser.set_defaults(operation=lambda args: parser.print_help(), passthrough=False) parser.add_argument( "-P", "--profile-dir", metavar="directory", type=Path, help="directory in which profiles are stored", ) parser.add_argument( "--version", action="version", version=__version__, ) subparsers = parser.add_subparsers() new = subparsers.add_parser("new", help="create a new profile") new.add_argument("profile_name", metavar="profile", help="name of the new profile") new.add_argument("home_page", metavar="url", nargs="?", help="profile's home page") new.set_defaults(operation=build_op(profiles.new_profile)) creator_args(new) session = subparsers.add_parser( "from-session", help="create a new profile from a qutebrowser session") session.add_argument( "session", help="path to session file or name of session. " "e.g. ~/.local/share/qutebrowser/sessions/example.yml or example", ) session.add_argument( "profile_name", metavar="profile", nargs="?", help="name of the new profile. if unset the session name will be used", ) session.set_defaults(operation=build_op(operations.from_session)) creator_args(session) desktop = subparsers.add_parser( "desktop", help="create a desktop file for an existing profile") desktop.add_argument("profile_name", metavar="profile", help="profile to create a desktop file for") desktop.set_defaults(operation=build_op(operations.desktop)) launch = subparsers.add_parser( "launch", help="launch qutebrowser with the given profile") launch.add_argument( "profile_name", metavar="profile", help= "profile to launch. it will be created if it does not exist, unless -s is set", ) launch.add_argument( "-n", "--new", action="store_false", dest="strict", help="create the profile if it doesn't exist", ) launch.add_argument( "-f", "--foreground", action="store_true", help= "launch qutebrowser in the foreground and print its stdout and stderr to the console", ) launch.set_defaults(operation=build_op(operations.launch), passthrough=True) list_ = subparsers.add_parser("list", help="list existing profiles") list_.set_defaults(operation=operations.list_) choose = subparsers.add_parser( "choose", help="interactively choose a profile to launch", ) menus = sorted(SUPPORTED_MENUS) choose.add_argument( "-m", "--menu", help= f'menu application to use. this may be any dmenu-compatible command (e.g. "dmenu -i -p qbpm" or "/path/to/rofi -d") or one of the following menus with built-in support: {menus}', ) choose.add_argument( "-f", "--foreground", action="store_true", help= "launch qutebrowser in the foreground and print its stdout and stderr to the console", ) choose.set_defaults(operation=operations.choose, passthrough=True) edit = subparsers.add_parser("edit", help="edit a profile's config.py") edit.add_argument("profile_name", metavar="profile", help="profile to edit") edit.set_defaults(operation=build_op(operations.edit)) raw_args = parser.parse_known_args(mock_args) args = raw_args[0] if args.passthrough: args.qb_args = raw_args[1] elif len(raw_args[1]) > 0: error(f"unrecognized arguments: {' '.join(raw_args[1])}") exit(1) if not args.profile_dir: args.profile_dir = Path( environ.get("QBPM_PROFILE_DIR") or DEFAULT_PROFILE_DIR) if not args.operation(args): exit(1)
def check(self) -> Optional["Profile"]: if "/" in self.name: error("profile name cannot contain slashes") return None return self