From 7f12c0c6f1596c7009a612750a5849ec72caff02 Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Sat, 14 Sep 2024 15:40:51 -0400 Subject: [PATCH 01/11] Add a timeout to requests on check_for_updates, default 10 but can be set optionally. added PEP 585 type hints to functions in module. --- comfy_cli/cmdline.py | 4 ++-- comfy_cli/command/launch.py | 2 +- comfy_cli/update.py | 37 ++++++++++++++++++++++++++++++------- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/comfy_cli/cmdline.py b/comfy_cli/cmdline.py index fa718f72..08d02e87 100644 --- a/comfy_cli/cmdline.py +++ b/comfy_cli/cmdline.py @@ -242,7 +242,7 @@ def install( ), ] = False, ): - check_for_updates() + check_for_updates(timeout=3) checker = EnvChecker() comfy_path, _ = workspace_manager.get_workspace_path() @@ -518,7 +518,7 @@ def which(): @app.command(help="Print out current environment variables.") @tracking.track_command() def env(): - check_for_updates() + check_for_updates(timeout=3) _env_checker = EnvChecker() table = _env_checker.fill_print_table() workspace_manager.fill_print_table(table) diff --git a/comfy_cli/command/launch.py b/comfy_cli/command/launch.py index 5d563c12..1005ef7b 100644 --- a/comfy_cli/command/launch.py +++ b/comfy_cli/command/launch.py @@ -108,7 +108,7 @@ def launch( background: bool = False, extra: list[str] | None = None, ): - check_for_updates() + check_for_updates(timeout=3) resolved_workspace = workspace_manager.workspace_path if not resolved_workspace: diff --git a/comfy_cli/update.py b/comfy_cli/update.py index 698ecd62..85ab82be 100644 --- a/comfy_cli/update.py +++ b/comfy_cli/update.py @@ -9,10 +9,19 @@ console = Console() -def check_for_newer_pypi_version(package_name, current_version): +def check_for_newer_pypi_version(package_name: str, current_version: str, timeout: float) -> tuple[bool, str]: + """ + Checks if a newer version of the specified package is available on PyPI. + + :param package_name: The name of the package to check. + :param current_version: The current version of the package. + :param timeout: Timeout in seconds for the request to PyPI. + :return: A tuple where the first value indicates if a newer version is available, + and the second value is the latest version (or the current version if no update is found). + """ url = f"https://pypi.org/pypi/{package_name}/json" try: - response = requests.get(url) + response = requests.get(url, timeout=timeout) response.raise_for_status() # Raises stored HTTPError, if one occurred latest_version = response.json()["info"]["version"] @@ -21,24 +30,38 @@ def check_for_newer_pypi_version(package_name, current_version): return False, current_version except requests.RequestException: - # print(f"Error checking latest version: {e}") + # Fail quietly on timeout or any request exception return False, current_version -def check_for_updates(): +def check_for_updates(timeout: float = 10) -> None: + """ + Checks for updates to the 'comfy-cli' package by comparing the current version + to the latest version on PyPI. If a newer version is available, a notification + is displayed. + + :param timeout: (default 10) Timeout in seconds for the request to check for updates. + If not provided, no timeout is enforced. + """ current_version = get_version_from_pyproject() - has_newer, newer_version = check_for_newer_pypi_version("comfy-cli", current_version) + has_newer, newer_version = check_for_newer_pypi_version("comfy-cli", current_version, timeout=timeout) if has_newer: notify_update(current_version, newer_version) -def get_version_from_pyproject(): +def get_version_from_pyproject() -> str: package_metadata = metadata("comfy-cli") return package_metadata["Version"] -def notify_update(current_version: str, newer_version: str): +def notify_update(current_version: str, newer_version: str) -> None: + """ + Notifies the user that a newer version of the 'comfy-cli' package is available. + + :param current_version: The current version of the package. + :param newer_version: The newer version available on PyPI. + """ message = ( f":sparkles: Newer version of [bold magenta]comfy-cli[/bold magenta] is available: [bold green]{newer_version}[/bold green].\n" f"Current version: [bold cyan]{current_version}[/bold cyan]\n" From 47d31f0f4c2c3e16818af5995d3811318cde68d6 Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Sat, 14 Sep 2024 16:25:25 -0400 Subject: [PATCH 02/11] Fix: correct documentation issue in function check_for_updates --- comfy_cli/update.py | 1 - 1 file changed, 1 deletion(-) diff --git a/comfy_cli/update.py b/comfy_cli/update.py index 85ab82be..b7d2a19c 100644 --- a/comfy_cli/update.py +++ b/comfy_cli/update.py @@ -41,7 +41,6 @@ def check_for_updates(timeout: float = 10) -> None: is displayed. :param timeout: (default 10) Timeout in seconds for the request to check for updates. - If not provided, no timeout is enforced. """ current_version = get_version_from_pyproject() has_newer, newer_version = check_for_newer_pypi_version("comfy-cli", current_version, timeout=timeout) From 6261b299a8c0a2ce935ce399eca163d77fab9545 Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 13:12:00 -0400 Subject: [PATCH 03/11] instead of fail gracefully, pass a warning through logging. by logging default settings this warning should be displayed --- comfy_cli/update.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/comfy_cli/update.py b/comfy_cli/update.py index b7d2a19c..4ca126c9 100644 --- a/comfy_cli/update.py +++ b/comfy_cli/update.py @@ -1,4 +1,5 @@ import sys +import logging from importlib.metadata import metadata import requests @@ -6,6 +7,9 @@ from rich.console import Console from rich.panel import Panel +# Set up a logger for the module +logger = logging.getLogger(__name__) + console = Console() @@ -29,8 +33,9 @@ def check_for_newer_pypi_version(package_name: str, current_version: str, timeou return True, latest_version return False, current_version - except requests.RequestException: - # Fail quietly on timeout or any request exception + except requests.RequestException as e: + logger.warning(f"Unable to fetch {package_name} version metadata from PyPI. Retaining current version {current_version}. " + f"Exception: {type(e).__name__} - {e}") return False, current_version From 22c07d2129d0000cfffe04b51e07aa20f876a0b9 Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 16:21:14 -0400 Subject: [PATCH 04/11] try to add a timeout with warning onto the tracking call to support the fully offline mode since its getting hit dispite disabiling tracking --- comfy_cli/tracking.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index da0e993e..7d3316b2 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -1,6 +1,7 @@ import functools import logging as logginglib import uuid +import asyncio import typer from mixpanel import Mixpanel @@ -41,7 +42,7 @@ def disable(): typer.echo(f"Tracking is now {'disabled'}.") -def track_event(event_name: str, properties: any = None): +def track_event(event_name: str, properties: any = None, timeout: int = 10): if properties is None: properties = {} logging.debug(f"tracking event called with event_name: {event_name} and properties: {properties}") @@ -52,7 +53,17 @@ def track_event(event_name: str, properties: any = None): try: properties["cli_version"] = cli_version properties["tracing_id"] = tracing_id - mp.track(distinct_id=user_id, event_name=event_name, properties=properties) + + # Define a function to wrap the mp.track call to enable timeout + async def track_with_timeout(): + await asyncio.to_thread(mp.track, distinct_id=user_id, event_name=event_name, properties=properties) + + # Run the mp.track with a timeout + try: + asyncio.run(asyncio.wait_for(track_with_timeout(), timeout=timeout)) + except asyncio.TimeoutError: + logging.warning(f"Tracking event '{event_name}' timed out after {timeout} seconds") + except Exception as e: logging.warning(f"Failed to track event: {e}") # Log the error but do not raise From a10057bc24cdf0104ddb95230b75d2c7e60ba478 Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 16:31:43 -0400 Subject: [PATCH 05/11] add success into the debug logs for tracking --- comfy_cli/tracking.py | 1 + 1 file changed, 1 insertion(+) diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index 7d3316b2..0188a367 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -61,6 +61,7 @@ async def track_with_timeout(): # Run the mp.track with a timeout try: asyncio.run(asyncio.wait_for(track_with_timeout(), timeout=timeout)) + logging.debug(f"Successfully tracked event '{event_name}'") except asyncio.TimeoutError: logging.warning(f"Tracking event '{event_name}' timed out after {timeout} seconds") From 1baf6e6fe12da24c4dbe18587b8c282949f6eebf Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 16:56:15 -0400 Subject: [PATCH 06/11] switch to a threading timeout --- comfy_cli/tracking.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index 0188a367..8b8bc65b 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -1,7 +1,7 @@ import functools import logging as logginglib import uuid -import asyncio +import threading import typer from mixpanel import Mixpanel @@ -42,7 +42,7 @@ def disable(): typer.echo(f"Tracking is now {'disabled'}.") -def track_event(event_name: str, properties: any = None, timeout: int = 10): +def track_event(event_name: str, properties: any = None, timeout: float = 10): if properties is None: properties = {} logging.debug(f"tracking event called with event_name: {event_name} and properties: {properties}") @@ -55,15 +55,23 @@ def track_event(event_name: str, properties: any = None, timeout: int = 10): properties["tracing_id"] = tracing_id # Define a function to wrap the mp.track call to enable timeout - async def track_with_timeout(): - await asyncio.to_thread(mp.track, distinct_id=user_id, event_name=event_name, properties=properties) + def track_with_timeout(): + mp.track(distinct_id=user_id, event_name=event_name, properties=properties) - # Run the mp.track with a timeout - try: - asyncio.run(asyncio.wait_for(track_with_timeout(), timeout=timeout)) - logging.debug(f"Successfully tracked event '{event_name}'") - except asyncio.TimeoutError: + # Create a thread to run the track_call function + track_thread = threading.Thread(target=track_call) + + # Start the thread + track_thread.start() + + # Wait for the thread to finish within the timeout period + track_thread.join(timeout) + + # Check if the thread is still active after the timeout + if track_thread.is_alive(): logging.warning(f"Tracking event '{event_name}' timed out after {timeout} seconds") + else: + logging.debug(f"Successfully tracked event '{event_name}'") except Exception as e: logging.warning(f"Failed to track event: {e}") # Log the error but do not raise From f88a773ffdc79a4505017042edd0513e96974993 Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 17:36:15 -0400 Subject: [PATCH 07/11] fix the call name being incorrect that we are sending to threading --- comfy_cli/tracking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index 8b8bc65b..1399a6b7 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -55,7 +55,7 @@ def track_event(event_name: str, properties: any = None, timeout: float = 10): properties["tracing_id"] = tracing_id # Define a function to wrap the mp.track call to enable timeout - def track_with_timeout(): + def track_call(): mp.track(distinct_id=user_id, event_name=event_name, properties=properties) # Create a thread to run the track_call function From 9866329373d881f1dad8c126f02607c8d08632ad Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 19:02:27 -0400 Subject: [PATCH 08/11] put tracking back to the way it was but fix its bug where it sets constants to a string which is assumed to a bool causing it to always evaluate to true --- comfy_cli/tracking.py | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index 1399a6b7..3666fdd6 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -1,7 +1,6 @@ import functools import logging as logginglib import uuid -import threading import typer from mixpanel import Mixpanel @@ -42,7 +41,7 @@ def disable(): typer.echo(f"Tracking is now {'disabled'}.") -def track_event(event_name: str, properties: any = None, timeout: float = 10): +def track_event(event_name: str, properties: any = None): if properties is None: properties = {} logging.debug(f"tracking event called with event_name: {event_name} and properties: {properties}") @@ -53,30 +52,10 @@ def track_event(event_name: str, properties: any = None, timeout: float = 10): try: properties["cli_version"] = cli_version properties["tracing_id"] = tracing_id - - # Define a function to wrap the mp.track call to enable timeout - def track_call(): - mp.track(distinct_id=user_id, event_name=event_name, properties=properties) - - # Create a thread to run the track_call function - track_thread = threading.Thread(target=track_call) - - # Start the thread - track_thread.start() - - # Wait for the thread to finish within the timeout period - track_thread.join(timeout) - - # Check if the thread is still active after the timeout - if track_thread.is_alive(): - logging.warning(f"Tracking event '{event_name}' timed out after {timeout} seconds") - else: - logging.debug(f"Successfully tracked event '{event_name}'") - + mp.track(distinct_id=user_id, event_name=event_name, properties=properties) except Exception as e: logging.warning(f"Failed to track event: {e}") # Log the error but do not raise - def track_command(sub_command: str = None): """ A decorator factory that logs the command function name and selected arguments when it's called. @@ -139,5 +118,5 @@ def init_tracking(enable_tracking: bool): def set_tracking_enabled(enabled: bool): - config_manager.set(constants.CONFIG_KEY_ENABLE_TRACKING, str(enabled)) + config_manager.set(constants.CONFIG_KEY_ENABLE_TRACKING, enabled) return enabled From 1a3778d101e35157c3f8c0dfe74e4e45ef61323b Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 19:27:43 -0400 Subject: [PATCH 09/11] specify we are pulling a boolean --- comfy_cli/config_manager.py | 12 +++++++++--- comfy_cli/tracking.py | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/comfy_cli/config_manager.py b/comfy_cli/config_manager.py index 616bf090..2330df6d 100644 --- a/comfy_cli/config_manager.py +++ b/comfy_cli/config_manager.py @@ -34,14 +34,20 @@ def set(self, key, value): """ Set a key-value pair in the config file. """ - self.config["DEFAULT"][key] = value + self.config["DEFAULT"][key] = str(value) self.write_config() # Write changes to file immediately - def get(self, key): + def get(self, key, type_cast=str): """ Get a value from the config file. Returns None if the key does not exist. """ - return self.config["DEFAULT"].get(key, None) # Returns None if the key does not exist + value = self.config["DEFAULT"].get(key, None) # Returns None if the key does not exist + + # Handle boolean conversion + if type_cast == bool: + return value.lower() in ("true", "yes", "1") + + return type_cast(value) def load(self): config_file_path = self.get_config_file_path() diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index 3666fdd6..aa0304ee 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -45,7 +45,7 @@ def track_event(event_name: str, properties: any = None): if properties is None: properties = {} logging.debug(f"tracking event called with event_name: {event_name} and properties: {properties}") - enable_tracking = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING) + enable_tracking = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING, type_cast=bool) if not enable_tracking: return From c8477693144ae962d3b90a1fe79b64439674b96b Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Tue, 17 Sep 2024 19:52:57 -0400 Subject: [PATCH 10/11] add consistency to tracking varible use --- comfy_cli/tracking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index aa0304ee..b8830d80 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -81,7 +81,7 @@ def wrapper(*args, **kwargs): def prompt_tracking_consent(skip_prompt: bool = False, default_value: bool = False): - tracking_enabled = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING) + tracking_enabled = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING, type_cast=bool) if tracking_enabled is not None: return @@ -97,7 +97,7 @@ def init_tracking(enable_tracking: bool): Initialize the tracking system by setting the user identifier and tracking enabled status. """ logging.debug(f"Initializing tracking with enable_tracking: {enable_tracking}") - config_manager.set(constants.CONFIG_KEY_ENABLE_TRACKING, str(enable_tracking)) + config_manager.set(constants.CONFIG_KEY_ENABLE_TRACKING, enable_tracking) if not enable_tracking: return From 3b2fb3dcce37325f9f007cc7e208f34b0dc4e940 Mon Sep 17 00:00:00 2001 From: Jason L Weirather Date: Wed, 18 Sep 2024 01:16:35 -0400 Subject: [PATCH 11/11] notice that code expects None could be a valid setting for the configuration, so don't cast to a bool when checking that --- comfy_cli/config_manager.py | 2 ++ comfy_cli/tracking.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/comfy_cli/config_manager.py b/comfy_cli/config_manager.py index 2330df6d..3e0608db 100644 --- a/comfy_cli/config_manager.py +++ b/comfy_cli/config_manager.py @@ -42,6 +42,8 @@ def get(self, key, type_cast=str): Get a value from the config file. Returns None if the key does not exist. """ value = self.config["DEFAULT"].get(key, None) # Returns None if the key does not exist + if value is None: + return None # Handle boolean conversion if type_cast == bool: diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index b8830d80..5ca7d347 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -81,7 +81,7 @@ def wrapper(*args, **kwargs): def prompt_tracking_consent(skip_prompt: bool = False, default_value: bool = False): - tracking_enabled = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING, type_cast=bool) + tracking_enabled = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING) if tracking_enabled is not None: return