From b6563400c4e21a6657bd0133702136d206ae30f0 Mon Sep 17 00:00:00 2001 From: jack-mil <62065280+jack-mil@users.noreply.github.com> Date: Sat, 20 Jul 2024 17:57:00 -0400 Subject: [PATCH] feat: validate that the browser path exists on a dryrun --- bing_rewards/__init__.py | 52 ++++++++++++++++++++++------------------ bing_rewards/options.py | 6 +---- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/bing_rewards/__init__.py b/bing_rewards/__init__.py index 3e42c8a..9f6c508 100644 --- a/bing_rewards/__init__.py +++ b/bing_rewards/__init__.py @@ -18,6 +18,7 @@ import os import random +import shutil import subprocess import sys import threading @@ -25,22 +26,22 @@ import webbrowser from importlib import resources from io import SEEK_END, SEEK_SET +from pathlib import Path +from typing import TYPE_CHECKING from urllib.parse import quote_plus -import bing_rewards.options as opt +if TYPE_CHECKING: + from argparse import Namespace + from collections.abc import Generator if os.name == 'posix': import signal -from typing import TYPE_CHECKING from pynput import keyboard from pynput.keyboard import Key -if TYPE_CHECKING: - from argparse import Namespace - from collections.abc import Generator - from pathlib import Path +import bing_rewards.options as app_options def word_generator() -> Generator[str]: @@ -65,8 +66,21 @@ def word_generator() -> Generator[str]: def browser_cmd(exe: Path, agent: str, profile: str = '') -> list[str]: - """Generate command to open Google Chrome with user-agent `agent`.""" - cmd = [str(exe), '--new-window', f'--user-agent="{agent}"'] + """Validate command to open Google Chrome with user-agent `agent`.""" + exe = Path(exe) + if exe.is_file() and exe.exists(): + cmd = [str(exe.resolve())] + elif pth := shutil.which(exe): + cmd = [str(pth)] + else: + print( + f'Command "{exe}" could not be found.\n' + 'Make sure it is available on PATH, ' + 'or use the --exe flag to give an absolute path.' + ) + sys.exit(1) + + cmd.extend(['--new-window', f'--user-agent="{agent}"']) # Switch to non default profile if supplied with valid string # NO CHECKING IS DONE if the profile exists if profile: @@ -74,32 +88,23 @@ def browser_cmd(exe: Path, agent: str, profile: str = '') -> list[str]: return cmd -def open_browser(options: Namespace, agent: str) -> subprocess.Popen: +def open_browser(cmd: list[str]) -> subprocess.Popen: """Try to open a browser, and exit if the command cannot be found. Returns the subprocess.Popen object to handle the browser process. """ - - cmd = browser_cmd(options.browser_path, agent, options.profile) try: # Open browser as a subprocess # Only if a new window should be opened if os.name == 'posix': chrome = subprocess.Popen( - cmd, - stderr=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - preexec_fn=os.setsid, + cmd, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL, start_new_session=True ) else: chrome = subprocess.Popen(cmd) - except FileNotFoundError as e: + except OSError as e: print('Unexpected error:', e) - print( - f'Command "{cmd[0]}" could not be found.\n' - 'Make sure it is available on PATH, ' - 'or use the --exe flag to give an absolute path.' - ) + print(f"Running command: '{' '.join(cmd)}'") sys.exit(1) print(f'Opening browser with pid {chrome.pid}') @@ -124,9 +129,10 @@ def search(count: int, words_gen: Generator, agent: str, options: Namespace): Open a chromium browser window with specified `agent` string, complete `count` searches from list `words`, finally terminate browser process on completion. """ + cmd = browser_cmd(options.browser_path, agent, options.profile) chrome = None if not options.no_window and not options.dryrun: - chrome = open_browser(options, agent) + chrome = open_browser(cmd) # Wait for Chrome to load time.sleep(options.load_delay) @@ -183,7 +189,7 @@ def main(): and executes search function in separate thread. Setup listener callback for ESC key. """ - options = opt.get_options() + options = app_options.get_options() words_gen = word_generator() diff --git a/bing_rewards/options.py b/bing_rewards/options.py index 56cee97..1c8fec0 100644 --- a/bing_rewards/options.py +++ b/bing_rewards/options.py @@ -1,7 +1,5 @@ """Defaults and helper functions related to getting user config and command line arguments.""" -from __future__ import annotations - import dataclasses import json import os @@ -207,7 +205,5 @@ def get_options() -> Namespace: """Combine the defaults, config file options, and command line arguments into one Namespace.""" file_config = read_config() args = parse_args() - args.__dict__ = vars(file_config) | { - k: v for k, v in vars(args).items() if v is not None - } + args.__dict__ = vars(file_config) | {k: v for k, v in vars(args).items() if v is not None} return args