Linter bug fixes. Mostly undeclared, unused, or missing imports. One or two outright typos. There's still issues with undeclared types that need to be fixed after api connection refactor is fully implemented.

This commit is contained in:
SimplifiedPrivacy 2026-08-05 12:03:11 -04:00
parent a5f57f4f5e
commit 8da4978498
34 changed files with 228 additions and 150 deletions

View file

@ -59,86 +59,86 @@ class ClientController:
return not ClientVersionController.is_latest(version)
@staticmethod
def legacy_sync(client_observer: ClientObserver = None, connection_observer: ConnectionObserver = None):
if client_observer is not None:
client_observer.notify('synchronizing', "Fetching list of new data ..")
# @staticmethod
# def legacy_sync(client_observer: ClientObserver = None, connection_observer: ConnectionObserver = None):
# if client_observer is not None:
# client_observer.notify('synchronizing', "Fetching list of new data ..")
result = coordinate_cache_sync(client_observer, connection_observer)
# result = coordinate_cache_sync(client_observer, connection_observer)
# Outright Error:
if not result["success"]:
error_msg = result["error"]
if client_observer is not None:
client_observer.notify('synchronizing', f'Error! {error_msg}')
return
# # Outright Error:
# if not result["success"]:
# error_msg = result["error"]
# if client_observer is not None:
# client_observer.notify('synchronizing', f'Error! {error_msg}')
# return
# Same:
changed_tables = result["changed_tables"]
if not changed_tables:
if client_observer is not None:
client_observer.notify('synchronized')
return
# # Same:
# changed_tables = result["changed_tables"]
# if not changed_tables:
# if client_observer is not None:
# client_observer.notify('synchronized')
# return
# We only make it past this point if there's New Data
# # We only make it past this point if there's New Data
# flag for after the save,
data_was_saved = False
# # flag for after the save,
# data_was_saved = False
# Fetch and update the real data (no longer metadata)...
# # Fetch and update the real data (no longer metadata)...
# =================== ORM BASED MODELS ==================
"""
Note: for the new ORM based models,
it does the Tor/system check in the API call itself.
"""
# # =================== ORM BASED MODELS ==================
# """
# Note: for the new ORM based models,
# it does the Tor/system check in the API call itself.
# """
if "locations" in changed_tables:
logger.info("Sync of Locations")
if client_observer is not None:
client_observer.notify('synchronizing', 'Fetching Locations List..')
# if "locations" in changed_tables:
# logger.info("Sync of Locations")
# if client_observer is not None:
# client_observer.notify('synchronizing', 'Fetching Locations List..')
final_result = sync_one_orm_model(Location, "locations")
evaluate_errors(final_result)
# final_result = sync_one_orm_model(Location, "locations")
# evaluate_errors(final_result)
if "operators" in changed_tables:
logger.info("Sync of Operators")
if client_observer is not None:
client_observer.notify('synchronizing', 'Fetching Operators List..')
# if "operators" in changed_tables:
# logger.info("Sync of Operators")
# if client_observer is not None:
# client_observer.notify('synchronizing', 'Fetching Operators List..')
final_result_two = sync_one_orm_model(Operator, "operators")
evaluate_errors(final_result_two)
# final_result_two = sync_one_orm_model(Operator, "operators")
# evaluate_errors(final_result_two)
# =================== MANUAL-SQL BASED MODELS ==================
try:
from core.controllers.ConnectionController import ConnectionController
ConnectionController.with_preferred_connection(task=ClientController.__sync, changed_tables=changed_tables, client_observer=client_observer, connection_observer=connection_observer)
# # =================== MANUAL-SQL BASED MODELS ==================
# try:
# from core.controllers.ConnectionController import ConnectionController
# ConnectionController.with_preferred_connection(task=ClientController.__sync, changed_tables=changed_tables, client_observer=client_observer, connection_observer=connection_observer)
# We set the flag to true,
# the reason we use a flag, and don't just save it right here,
# is because we want to isolate the success (or failure) of the real data,
# from the potential failure of the ORM session metadata.
data_was_saved = True
# # We set the flag to true,
# # the reason we use a flag, and don't just save it right here,
# # is because we want to isolate the success (or failure) of the real data,
# # from the potential failure of the ORM session metadata.
# data_was_saved = True
except:
# sync failed here,
if client_observer is not None:
client_observer.notify('synchronizing', 'Fetch Failed, but you can use old data.')
finally:
if data_was_saved:
filtered_metadata = result["filtered_metadata"] # from the top of the function
save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service
# except:
# # sync failed here,
# if client_observer is not None:
# client_observer.notify('synchronizing', 'Fetch Failed, but you can use old data.')
# finally:
# if data_was_saved:
# filtered_metadata = result["filtered_metadata"] # from the top of the function
# save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service
if client_observer is None:
logger.error("Error: No client_observer to update the UI, the final part of the sync function skipped")
return # can't update their UI
# if client_observer is None:
# logger.error("Error: No client_observer to update the UI, the final part of the sync function skipped")
# return # can't update their UI
if save_successful:
logger.info("Metadata Saved Successfully")
client_observer.notify('synchronized', "Fetch & Save Complete!")
else:
client_observer.notify('synchronizing', "Saving List of Metadata Failed.")
# if save_successful:
# logger.info("Metadata Saved Successfully")
# client_observer.notify('synchronized', "Fetch & Save Complete!")
# else:
# client_observer.notify('synchronizing', "Saving List of Metadata Failed.")
@staticmethod

View file

@ -136,12 +136,10 @@ class ConnectionController:
try:
tor_module = TorModule(Constants.HV_TOR_STATE_HOME)
tor_module.create_session(port_number, connection_observer)
except TorServiceInitializationError as e:
logger.error(f"TorServiceInitializationError. Tor Can't Start: {e}")
if connection_observer is not None:
connection_observer.notify('custom_message', "Tor Can't Initialize")
except Exception as e:
logger.error(f"Tor Can't Start: {e}")
if connection_observer is not None:
connection_observer.notify('custom_message', "Tor Can't Initialize")
@staticmethod
def terminate_tor_session_connection(port_number: int):

View file

@ -165,12 +165,19 @@ def new_sync(client_observer: ClientObserver, connection_observer: ConnectionObs
if total_skipped == 0:
client_observer.notify('synchronized', "Fetch & Save Complete!")
save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service
return Result(valid=True, message="Finshed sync.")
save_successful = save_metadata(filtered_metadata)
if save_successful:
return Result(valid=True, message="Finshed sync.")
else:
error_msg = "Finshed sync, but had issues with the saving of metadata for next time."
logger.error(error_msg)
return Result(valid=True, message=error_msg)
elif total_skipped < quantity_of_entries:
error_msg = f"Partial Success. {total_skipped} skipped."
client_observer.notify('synchronized', error_msg)
return Result(valid=True, data=skipped, message=error_msg)
else:
error_msg = f"Sync Failed. All {total_skipped} entries were skipped!"
client_observer.notify('synchronized', error_msg)

View file

@ -11,7 +11,8 @@ from core.observers.BaseObserver import BaseObserver
from core.services.payment_phase.save_and_send_intitial_billing import save_and_send_intitial_billing
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
from core.services.networking.api_requests.step5_solve_api_problems import solve_api_problems
from core.errors.logger import logger
from core.errors.exceptions import ServerSideError, NetworkingError
from core.services.prepare_tickets.ticket_tracker import does_ticket_tracker_exist
@ -132,7 +133,7 @@ def initiate_payment(
return invoice_data_object
except InvalidData as e:
except ValueError as e:
error_msg = "Invalid Data."
ticket_observer.notify("failed_input", subject=error_msg)
invoice_data_object.add_error_code("invalid_data")

View file

@ -19,6 +19,7 @@ from core.services.helpers.get_value_from_config import get_value_from_config
from core.utils.basic_operations.does_file_exist import does_file_exist
from core.utils.basic_operations.write_or_read_from_json import update_json
from core.services.prepare_tickets.ticket_tracker import get_all_unused_tickets
from core.errors.logger import logger
import random
@ -162,8 +163,10 @@ def use_ticket(
error_msg = f"Ticket is already tied to {location} with the subscription {subscription}"
ticket_observer.notify("failed_input", subject=error_msg)
return {"valid": False, "message": error_msg}
except:
except ValueError as e:
error_msg = f"Your local ticket tracker has no value for ticket {which_ticket}"
logger.error(error_msg)
logger.error(str(e))
return {"valid": False, "message": error_msg}
# the actual work here, everything else is just handling:

View file

@ -37,7 +37,7 @@ class Result():
def user_message(self) -> str:
"""Human-readable error for the UI."""
messages = {
DBErrorType.SUCCESS: "Operation completed successfully.",
DBErrorType.UNKNOWN: f"Error: {self.message}",
ResultError.SUCCESS: "Operation completed successfully.",
ResultError.UNKNOWN: f"Error: {self.message}",
}
return messages.get(self.error_type, "Unknown error")

View file

@ -1,7 +1,7 @@
from core.Constants import Constants
from core.models.ClientVersion import ClientVersion
# from core.models.Location import Location
# from core.models.Operator import Operator
from core.models.orm_models.Location import Location
from core.models.orm_models.Operator import Operator
from core.models.Subscription import Subscription
from core.models.SubscriptionPlan import SubscriptionPlan
from core.models.invoice.Invoice import Invoice

View file

@ -13,6 +13,8 @@ from core.services.failed_verification.test_if_new_key_works import test_if_new_
from core.services.networking.make_url import make_url
from core.services.networking.api_requests.step1_get_or_post import get_data_from_api
from core.services.networking.httpx import connect
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
# utils
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
@ -72,12 +74,21 @@ def get_new_pubkey_from_api(connection_observer: ConnectionObserver) -> dict | N
url = make_url(which_key_plan)
# the result of this is a python dictionary with single '
api_results = get_data_from_server(url, None, connection_observer)
# api_results = get_data_from_server(url, None, connection_observer)
api_results = connect.single_endpoint(
method="get",
url=url,
observer=connection_observer,
payload=None
)
if "data" in api_results:
new_public_key = api_results["data"]
if api_results.valid:
new_public_key = api_results.data
return new_public_key
else:
logger.error(f"API Results returned were invalid for that key.")
return None
return new_public_key
def are_keys_different(old_public_key, new_public_key) -> bool:

View file

@ -24,7 +24,7 @@ from core.errors.logger import logger
# generic
import json, os
from typing import Any
from typing import Any, Optional
import time

View file

@ -174,6 +174,7 @@ def quad9_proxy_dns_lookup(
domain: str,
custom_proxy: str,
timeout: int = 10,
client_observer: ClientObserver = None
) -> str:
logger.debug("Doing a Proxy Quad9 DNS lookup")

View file

@ -11,10 +11,11 @@ from core.services.subscriptions.subscriptions import activate_subscription
from core.errors.logger import logger
from core.errors.exceptions import *
from core.errors.exceptions import FirewallError
from typing import Union, Optional, Callable
from core.models.session.SessionProfile import SessionProfile
from core.models.system.SystemProfile import SystemProfile
from core.Errors import ConnectionTerminationError, InvalidSubscriptionError
from core.Errors import ConnectionTerminationError, InvalidSubscriptionError, MissingSubscriptionError
from core.services.WebServiceApiService import WebServiceApiService
from core.controllers.ConnectionController import ConnectionController
from core.models.BaseProfile import ProfileType

View file

@ -182,4 +182,4 @@ async def switch_get_and_post(method: str, url: str, client: httpx.Client, paylo
else:
# nevermind,
logger.info("Our strategy of switching GET/POST did NOT work.")
return initial_result
return second_result

View file

@ -35,8 +35,6 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload
Try a request to a single endpoint using either an existing client, or creating a new one, then error handling.
"""
global _port_used
connection_type = ConfigurationController.get_connection_enum()
client = httpx_client.get_http_session()
@ -62,9 +60,6 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload
########################################################
# FROM THIS POINT ON, WE HAVE A CLIENT
########################################################
# #### SPOOF
# _port_used = 9050
# initial_result = ApiResponse(valid=False, error_type=ErrorType.DNS_TEMPORARY)
initial_result = make_request(
method=method,
@ -367,3 +362,7 @@ def custom_dns_resolver_for_SINGLE_ENDPOINT(method: str, url: str, observer: Con
# """Merge two dictionaries. dict1 values take precedence on key collision."""
# return {**dict2, **dict1}
def replace_http_with_https(url):
if url.startswith("http://"):
return url.replace("http://", "https://", 1)
return url

View file

@ -176,4 +176,4 @@ def switch_get_and_post(method: str, url: str, client: httpx.Client, payload: di
else:
# nevermind,
logger.info("Our strategy of switching GET/POST did NOT work.")
return initial_result
return second_result

View file

@ -16,6 +16,7 @@ from core.services.networking.regular_get_request import regular_get_request
from core.errors.exceptions import *
from core.errors.logger import logger
import traceback
from typing import Optional
# Generic GET request to an endpoint, filtered by the user's preference of connection type (Tor or Not)

View file

@ -84,13 +84,13 @@ def orchestrate_dns_check(target_interface: str) -> Result:
else:
logger.info(f"[DNS Orchestrator] Some of the DNS checks did NOT work. But this might be because the tunnel is down.")
targets_not_found = 0
target_not_found = 0
for each_object in list_of_results_as_objects:
if each_object.error_type == SearchError.TARGET_MISSING:
target_not_found = target_not_found + 1
return Result(valid=False, data=targets_not_found)
return Result(valid=False, data=target_not_found)

View file

@ -1,6 +1,6 @@
from core.services.networking.systemwide.systemwide_errors import systemwide_hell_raiser
from core.utils.basic_operations.wrap_with import wrap_with
from core.utils.run_commands import run_generic_command import run_generic_command
from core.utils.run_commands import run_generic_command
from core.models.Result import Result, ResultError
from core.errors.logger import logger

View file

@ -10,6 +10,8 @@ from core.services.networking.systemwide.systemwide_utils import get_firewall_se
from core.services.networking.systemwide import dns
from core.services.networking.systemwide.general_connection_tools.general_firewall_dns_tools import generic_enable_firewall_w_retry
from core.errors.exceptions import FirewallError, DNSError
from essentials.observers.ConnectionObserver import ConnectionObserver
# generic
import subprocess
@ -106,7 +108,7 @@ def start_singbox(
if not activation_result.valid:
error_msg = f"Singbox failed to start after {QUANTITY_OF_ATTEMPTS} attempts"
logger.error(f"[{function_name}] {error_msg}")
return Result(valid=False, error_type=ResultError=PROCESS_WONT_START, message=error_msg)
return Result(valid=False, error_type=ResultError.PROCESS_WONT_START, message=error_msg)
process_id = activation_result.data
time.sleep(2)
@ -129,7 +131,7 @@ def start_singbox(
)
# ============= FIREWALL =============
logger.info(f"[{function_name}] Attempting to enable the Firewall for {SINGBOX_TUN_IF} and {Constants.SINGBOX_INTERNAL_SUBNET}...")
logger.info(f"[{function_name}] Attempting to enable the Firewall for {Constants.SINGBOX_TUN_IF} and {Constants.SINGBOX_INTERNAL_SUBNET}...")
# this is labeled "generic" for being protocol neutral
firewall_result = generic_enable_firewall_w_retry(
interface_name=Constants.SINGBOX_TUN_IF,

View file

@ -230,6 +230,7 @@ def _establish_connection_with_retry(
except ConnectionError as e:
# Unrecoverable error, fail immediately
logger.error(str(e))
raise
except CalledProcessError as e:

View file

@ -1,5 +1,7 @@
from core.utils.run_commands import run_generic_command
from core.models.Result import Result, ResultError
from core.errors.logger import logger
from subprocess import CalledProcessError
import os
import re

View file

@ -1,4 +1,5 @@
from core.services.networking.tor_tools.monitor_bootstrap import monitor_bootstrap_progress
from core.services.networking.tor_tools.install_tor import install_tor
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
from core.services.networking.tor_tools import ports
from core.Constants import Constants
@ -89,7 +90,7 @@ def bootstrap(
current_port = ports.get_random_available_port()
use_new_folder = True
elif strategy == BootstrapStrategy.REINSTALL_AND_RETRY:
if not install_tor().valid:
if not install_tor(observer).valid:
return ApiResponse(valid=False, error_type=ErrorType.INSTALL_FAILED)
use_new_folder = True

View file

@ -27,7 +27,7 @@ def get_distro_package_manager() -> Optional[str]:
return None
async def install_tor() -> ApiResponse:
async def install_tor(observer: ConnectionObserver) -> ApiResponse:
"""
Prompt user to install Tor with pkexec, detecting distro for correct package manager.
"""

View file

@ -7,13 +7,13 @@ from core.Constants import Constants
from typing import Optional
import subprocess
import asyncio
import subprocess
import socket
import httpx
from httpx_socks import AsyncProxyTransport
import json
import re
import os
import signal
def is_port_in_use(port: int) -> bool:
"""

View file

@ -71,7 +71,7 @@ def get_bootstrap_port(port_tried: int, observer: ConnectionObserver):
def diagnose_tor_port(port_tried: int, observer: ConnectionObserver) -> ApiResponse:
# Step 1) Is Tor Installed?
if not is_installed('tor'):
installed = install_tor()
installed = install_tor(observer)
if installed:
return ApiResponse(valid=True, error_type=ErrorType.TOR_NOT_INSTALLED, port=Constants.DEFAULT_TOR_PORT)
else:

View file

@ -35,7 +35,7 @@ def establish_tor_connection(observer: ConnectionObserver) -> ApiResponse:
# Step 2) Install Tor if needed.
if result.error_type == ErrorType.TOR_NOT_INSTALLED:
installed = install_tor()
installed = install_tor(observer)
if not installed:
return ApiResponse(valid=False, error_type=ErrorType.REFUSAL_TO_INSTALL_TOR)

View file

@ -11,6 +11,7 @@ from core.services.networking.api_requests.step1_get_or_post import send_data_to
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
from core.services.networking.api_requests.step5_solve_api_problems import solve_api_problems
from core.services.networking.httpx import connect
from core.services.networking.make_url import make_url
from core.services.payment_phase.extract_payment_details import extract_payment_details
@ -42,24 +43,35 @@ def save_and_send_intitial_billing(
"""
# Save choices:
save_billing_choices(payload)
saved_choices = save_billing_choices(payload)
if not saved_choices:
error_msg = "Unable to save billing choices locally. Check file permissions and storage space."
logger.error(error_msg)
return TicketInvoice(is_valid=False, final_error_msg=error_msg)
# send them:
which_endpoint = "start_payment"
url = make_url(which_endpoint)
api_reply_object = send_data_to_server(payload, url, connection_observer)
# api_reply_object = send_data_to_server(payload, url, connection_observer)
api_reply_object = connect.single_endpoint(
method="post",
url=url,
observer=connection_observer,
payload=payload
)
# if not api_reply_object.valid:
# api_reply_object = solve_api_problems(
# api_reply_object=api_reply_object,
# get_or_post="post",
# url=url,
# payload=payload,
# connection_observer=connection_observer,
# client_observer=None
# )
if not api_reply_object.valid:
api_reply_object = solve_api_problems(
api_reply_object=api_reply_object,
get_or_post="post",
url=url,
payload=payload,
connection_observer=connection_observer,
client_observer=None
)
if not api_reply_object.valid:
return api_reply_object
return api_reply_object
reply_dict_data = api_reply_object.data

View file

@ -8,4 +8,4 @@ def save_billing_choices(payload: dict) -> None:
billing_folder = Constants.HV_TICKETING_CONFIG_HOME
filepath = f"{billing_folder}/billing_choices.json"
write_json_to_file(payload, filepath)
return write_json_to_file(payload, filepath)

View file

@ -7,6 +7,8 @@ from typing import Any
# services
from core.services.networking.make_url import make_url
from core.services.networking.api_requests.step1_get_or_post import get_data_from_api
from core.services.networking.httpx import connect
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
# utils
from core.utils.basic_operations.does_file_exist import does_file_exist
@ -30,18 +32,20 @@ def get_from_server_and_save(
url = make_url(string_form_of_key_name)
# the result of this is a python dictionary with single '
public_key_results = get_data_from_server(url, connection_observer)
# public_key_results = get_data_from_server(url, connection_observer)
public_key_results = connect.single_endpoint(
method="get",
url=url,
observer=connection_observer,
payload=None
)
if isinstance(public_key_results, dict) and "valid" in public_key_results:
status = public_key_results.get("valid", False)
if public_key_results.valid:
# extract:
public_key = public_key_results.data
if status == True:
# extract:
public_key = public_key_results.get("data", False)
# save it:
did_it_save = write_string_to_text_file(public_key, file_path)
# save it:
did_it_save = write_string_to_text_file(public_key, file_path)
return public_key_results

View file

@ -71,7 +71,7 @@ def get_public_key_by_config(connection_observer: ConnectionObserver) -> dict:
if reply in list_of_failures:
return complete_failure_msg
if not instance(reply, dict):
if not isinstance(reply, dict):
return {
"status": False,
"message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}",
@ -141,7 +141,7 @@ def get_public_key_from_LOCAL_files_only(
if reply in list_of_failures:
return complete_failure_msg
if not instance(reply, dict):
if not isinstance(reply, dict):
return {
"status": False,
"message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}",

View file

@ -21,4 +21,4 @@ def setup_ticket_tracker(how_many_profiles: int) -> None:
}
counter += 1
write_json_to_file(ticket_data, ticket_tracker_path)
return write_json_to_file(ticket_data, ticket_tracker_path)

View file

@ -94,9 +94,9 @@ def ticket_prep_orchestrator(
)
# make a json to keep track of which tickets are used:
setup_ticket_tracker(how_many_profiles)
setup_tracker = setup_ticket_tracker(how_many_profiles)
if did_prep_work:
if did_prep_work and setup_tracker:
# this means it unblinded, & setup the tracker without erroring out,
# but it does NOT mean that verification of the blind sigs worked.
return {"valid": True, "message": "worked"}

View file

@ -1,7 +1,4 @@
from core.utils.basic_operations.write_or_read_from_json import (
write_json_to_file,
read_entire_json,
)
from core.utils.basic_operations.write_or_read_from_json import read_entire_json
from core.errors.exceptions import *
from core.errors.logger import logger
from core.Constants import Constants
@ -62,7 +59,7 @@ def get_data_for_a_single_ticket(which_ticket_as_int: int) -> tuple:
error_msg = f"Entire file of tickets does not exist. Check the location {ticket_tracker_path}"
logger.error(error_msg, exc_info=True)
print(error_msg)
raise InvalidData(error_msg)
raise ValueError(error_msg)
# the data is a string for lookups:
which_ticket = str(which_ticket_as_int)
@ -74,6 +71,6 @@ def get_data_for_a_single_ticket(which_ticket_as_int: int) -> tuple:
subscription = data.get(which_ticket, {}).get("subscription")
return status, location, subscription
else:
raise InvalidData(
raise ValueError(
f"Key '{which_ticket}' does not exist in JSON file {ticket_tracker_path}"
)

View file

@ -1,4 +1,3 @@
from core.errors.exceptions import *
from core.errors.logger import logger
import json
@ -6,7 +5,7 @@ import os
from pathlib import Path
def write_json_to_file(data: dict, filepath: str) -> None:
def write_json_to_file(data: dict, filepath: str) -> bool:
try:
# Create directory if it doesn't exist
directory = os.path.dirname(filepath)
@ -16,10 +15,20 @@ def write_json_to_file(data: dict, filepath: str) -> None:
with open(filepath, "w") as f:
json.dump(data, f, indent=4)
return True
except TypeError as e:
raise TypeError(f"Data is not JSON serializable: {e}")
logger.error(f"Data is not JSON serializable: {e}")
return False
except FileNotFoundError:
logger.error(f"File not found: {filepath}")
return False
except IsADirectoryError:
logger.error(f"Path is a directory, not a file: {filepath}")
return False
except IOError as e:
raise IOError(f"Error writing to file {filepath}: {e}")
logger.error(f"Error reading file {filepath}: {e}")
return False
def read_entire_json(filepath: str) -> dict:
@ -43,13 +52,13 @@ def read_entire_json(filepath: str) -> dict:
f"Invalid JSON in file {filepath}: {e.msg}", e.doc, e.pos
)
except FileNotFoundError:
raise FileNotFoundError(f"File not found: {filepath}")
logger.error(f"File not found: {filepath}")
return False
except IsADirectoryError:
raise IsADirectoryError(f"Path is a directory, not a file: {filepath}")
logger.error(f"Path is a directory, not a file: {filepath}")
return False
except IOError as e:
raise IOError(f"Error reading file {filepath}: {e}")
logger.error(f"Error reading file {filepath}: {e}")
return False
@ -103,8 +112,14 @@ def update_value_in_json_with_two_values(
return True
except:
raise InvalidData(f"Error reading file {filepath}")
except FileNotFoundError:
logger.error(f"File not found: {filepath}")
return False
except IsADirectoryError:
logger.error(f"Path is a directory, not a file: {filepath}")
return False
except IOError as e:
logger.error(f"Error reading file {filepath}: {e}")
return False
@ -122,6 +137,12 @@ def update_json(filepath, key_to_add, value_to_update):
write_json_to_file(data, filepath)
return True
except:
raise InvalidData(f"Error reading file {filepath}")
except FileNotFoundError:
logger.error(f"File not found: {filepath}")
return False
except IsADirectoryError:
logger.error(f"Path is a directory, not a file: {filepath}")
return False
except IOError as e:
logger.error(f"Error reading file {filepath}: {e}")
return False

View file

@ -131,14 +131,14 @@ def run_generic_command(
try:
returncode, stdout = _run_command_via_terminal(command, timeout)
output_data = stdout
# output_data = stdout
if returncode == 0:
logger.info(f"{human_readable_goal} was successful")
return Result(valid=True, data=output_data)
return Result(valid=True, data=stdout)
else:
# Try stderr first, fallback to stdout for error parsing
error_output = (stderr or stdout).strip()
error_output = stdout.strip()
error_enum = parse_errors(error_output)
logger.error(f"{human_readable_goal} Failed, error type is {error_enum}.")
return Result(valid=False, error_type=error_enum, goal=human_readable_goal, message=error_output)
@ -147,3 +147,19 @@ def run_generic_command(
return Result(valid=False, error_type=ResultError.TIMEOUT, goal=human_readable_goal, data=output_data, message=f"Command timed out {e}")
except Exception as e:
return Result(valid=False, error_type=ResultError.UNKNOWN, goal=human_readable_goal, data=output_data, message=str(e))
def parse_errors(error_output: str) -> ResultError:
# Interface missing
if "No such device" in error_output:
error_enum = ResultError.INTERFACE
# Permission error
elif "No permissions" in error_output:
error_enum = ResultError.PERMISSION
elif "sudo: a password is required" or "password is required" in error_output:
error_enum = ResultError.PERMISSION
elif "command not found" in error_output:
error_enum = ResultError.MISSING_DEPENDENCY
else:
error_enum = ResultError.UNKNOWN
return error_enum