diff --git a/jarvis/api/routers/investment.py b/jarvis/api/routers/investment.py index 20197410..0b0f0b9b 100644 --- a/jarvis/api/routers/investment.py +++ b/jarvis/api/routers/investment.py @@ -157,11 +157,12 @@ async def robinhood_path(request: Request, token: str = None): with open(models.fileio.robinhood) as static_file: html_content = static_file.read() content_type, _ = mimetypes.guess_type(html_content) + # serves as a static webpage return HTMLResponse( status_code=HTTPStatus.TEMPORARY_REDIRECT.real, content=html_content, media_type=content_type, - ) # serves as a static webpage + ) else: raise APIResponse( status_code=HTTPStatus.EXPECTATION_FAILED.real, diff --git a/jarvis/api/routers/stats.py b/jarvis/api/routers/stats.py index 444289f6..b8b83fee 100644 --- a/jarvis/api/routers/stats.py +++ b/jarvis/api/routers/stats.py @@ -102,7 +102,7 @@ def get_files() -> Generator[FilePath]: yield file_path -@cache.timed_cache(max_age=900) # Cache for 15 minutes +@cache.timed_cache(max_age=900) def total_lines_of_code() -> PositiveInt: """Cached function to calculate the total lines of code. @@ -114,7 +114,7 @@ def total_lines_of_code() -> PositiveInt: return sum(count_lines(file) for file in get_files()) -@cache.timed_cache(max_age=900) # Cache for 15 minutes +@cache.timed_cache(max_age=900) def total_files() -> PositiveInt: """Cached function to calculate the total number of files. diff --git a/jarvis/api/routers/stock_monitor.py b/jarvis/api/routers/stock_monitor.py index fb7204f1..065f9014 100644 --- a/jarvis/api/routers/stock_monitor.py +++ b/jarvis/api/routers/stock_monitor.py @@ -162,9 +162,8 @@ async def stock_monitor_api( else: # If apikey auth fails or unsupported sent_dict = settings.stock_monitor_helper.otp_sent recd_dict = settings.stock_monitor_helper.otp_recd - email_otp = email_otp or request.headers.get( - "email-otp" - ) # variable will be _ but headers should always be `-` + # variable will be _ but headers should always be `-` + email_otp = email_otp or request.headers.get("email-otp") if email_otp: recd_dict[input_data.email] = email_otp if secrets.compare_digest( @@ -336,7 +335,8 @@ async def stock_monitor_api( "Please choose a lower 'Min' value or try at a later time.", ) - stockmonitor_squire.insert_stock_userdata(params=new_entry) # Store it in database + # Store it in database + stockmonitor_squire.insert_stock_userdata(params=new_entry) response = ( f"Entry added to the database. Jarvis will notify you at {input_data.email!r} when a " diff --git a/jarvis/api/squire/discover.py b/jarvis/api/squire/discover.py index e2a7ce63..429a3c0e 100644 --- a/jarvis/api/squire/discover.py +++ b/jarvis/api/squire/discover.py @@ -75,9 +75,10 @@ def routes(routers: str) -> Generator[APIRouter]: APIRouter: API Router from scanned modules. """ + # sort by name of the route entrypoints: List[Entrypoint] = sorted( get_entrypoints(routers=routers), key=lambda ent: ent.stem - ) # sort by name of the route + ) for entrypoint in entrypoints: try: route = import_module(entrypoint.module) diff --git a/jarvis/api/squire/stockmonitor_squire.py b/jarvis/api/squire/stockmonitor_squire.py index b1aa166c..8b9eb77e 100644 --- a/jarvis/api/squire/stockmonitor_squire.py +++ b/jarvis/api/squire/stockmonitor_squire.py @@ -96,13 +96,15 @@ def put_daily_alerts( with stock_db.connection: cursor = stock_db.connection.cursor() params = [(key, *values) for param in params for key, values in param.items()] - cursor.execute("DELETE FROM stock_daily") # clear all existing data + # clear all existing data + cursor.execute("DELETE FROM stock_daily") query = ( f"INSERT OR REPLACE INTO stock_daily {settings.stock_monitor.alerts} VALUES " f"{settings.stock_monitor.alert_values};" ) for param in params: - cursor.execute(query, param) # write new data in db + # write new data in db + cursor.execute(query, param) stock_db.connection.commit() diff --git a/jarvis/api/squire/surveillance_squire.py b/jarvis/api/squire/surveillance_squire.py index 8b476965..c4659b0a 100644 --- a/jarvis/api/squire/surveillance_squire.py +++ b/jarvis/api/squire/surveillance_squire.py @@ -131,7 +131,8 @@ def gen_frames(manager: Queue, index: int, available_cameras: List[str]) -> None logger.info("Releasing camera: %s", available_cameras[index]) cam.release() break - frame = cv2.flip(src=frame, flipCode=1) # mirrors the frame + # mirrors the frame + frame = cv2.flip(src=frame, flipCode=1) ret, buffer = cv2.imencode(ext=".jpg", img=frame) frame = buffer.tobytes() manager.put(frame) diff --git a/jarvis/api/triggers/stock_monitor.py b/jarvis/api/triggers/stock_monitor.py index c575e658..140c3105 100644 --- a/jarvis/api/triggers/stock_monitor.py +++ b/jarvis/api/triggers/stock_monitor.py @@ -27,9 +27,10 @@ def generate_graph(logger: logging.Logger, ticker: str, bars: int = 300) -> str https://stackoverflow.com/a/49729752 """ logger.info("Generating price chart for '%s'", ticker) + # ~ 1 month dataframe = webull().get_bars( stock=ticker, interval="m60", count=bars, extendTrading=1 - ) # ~ 1 month + ) refined = dataframe[["close"]] if len(refined) == 0: refined = dataframe[["open"]] @@ -205,7 +206,10 @@ def skip_signal( if time.time() <= alert_time + hours * 60 * 60: return True else: - self.repeat_alerts.remove({alert_time: alert_entry}) + try: + self.repeat_alerts.remove({alert_time: alert_entry}) + except ValueError as err: + self.logger.error(err) return False # notification should be triggered if condition matches def send_notification(self) -> None: diff --git a/jarvis/executors/car.py b/jarvis/executors/car.py index 091d6821..f7a7f5e1 100644 --- a/jarvis/executors/car.py +++ b/jarvis/executors/car.py @@ -197,22 +197,18 @@ def enable_guard(self, phrase) -> str: or 1 ) if "hour" in phrase: - seconds = ( - requested_expiry * 3_600 - ) # Defaults to 1 hour if no numeric value in phrase + # Defaults to 1 hour if no numeric value in phrase + seconds = requested_expiry * 3_600 elif "day" in phrase: - seconds = ( - requested_expiry * 86_400 - ) # Defaults to 1 day if no numeric value in phrase + # Defaults to 1 day if no numeric value in phrase + seconds = requested_expiry * 86_400 elif "week" in phrase: - seconds = ( - requested_expiry * 604_800 - ) # Defaults to 1 week if no numeric value in phrase + # Defaults to 1 week if no numeric value in phrase + seconds = requested_expiry * 604_800 else: seconds = 3_600 # Defaults to 1 hour if no datetime conversion was received - expire = int( - (time.time() + seconds) * 1000 - ) # multiply by 1000 to including microseconds making it 13 digits + # multiply by 1000 to including microseconds making it 13 digits + expire = int((time.time() + seconds) * 1000) if response := self.object(operation="SECURE", end_time=expire): return response else: @@ -554,9 +550,8 @@ def vehicle( logger.error(lock_response) else: logger.info("Vehicle has been locked!") - time.sleep( - 3 - ) # Wait before locking the car, so that there is no overlap in refresh token + # Wait before locking the car, so that there is no overlap in refresh token + time.sleep(3) response = control.remote_engine_start( pin=models.env.car_pin, target_value=temp ) diff --git a/jarvis/executors/connection.py b/jarvis/executors/connection.py index 01ad87c2..d97f6a3e 100644 --- a/jarvis/executors/connection.py +++ b/jarvis/executors/connection.py @@ -21,9 +21,8 @@ def wifi(conn_object: classes.WiFiConnection) -> classes.WiFiConnection | None: socket_ = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: if models.settings.os == models.supported_platforms.windows: - connection = HTTPSConnection( - "8.8.8.8", timeout=3 - ) # Recreate a new connection everytime + # Recreate a new connection everytime + connection = HTTPSConnection("8.8.8.8", timeout=3) connection.request("HEAD", "/") else: socket_.connect(("8.8.8.8", 80)) @@ -37,7 +36,8 @@ def wifi(conn_object: classes.WiFiConnection) -> classes.WiFiConnection | None: except OSError as error: conn_object.os_errors += 1 logger.error("OSError [%d]: %s", error.errno, error.strerror) - pywifi.ControlPeripheral(logger=logger).enable() # Make sure Wi-Fi is enabled + # Make sure Wi-Fi is enabled + pywifi.ControlPeripheral(logger=logger).enable() connection_control = pywifi.ControlConnection( wifi_ssid=models.env.wifi_ssid, wifi_password=models.env.wifi_password, diff --git a/jarvis/executors/controls.py b/jarvis/executors/controls.py index 0a1daf06..aa1e2656 100644 --- a/jarvis/executors/controls.py +++ b/jarvis/executors/controls.py @@ -81,9 +81,8 @@ def exit_process() -> None: speaker.speak( text=f"You have {len(reminders)} pending reminders {models.env.title}!" ) - speaker.speak( - text=util.comma_separator(reminders) - ) # No need for string.capwords as speaker runs in a new loop + # No need for string.capwords as speaker runs in a new loop + speaker.speak(text=util.comma_separator(reminders)) if alarms := alarm.get_alarm_state(): if len(alarms) == 1: speaker.speak(text="You have a pending alarm at ") @@ -173,7 +172,8 @@ def restart_control(phrase: str = None, quiet: bool = False) -> None: speaker.speak(text="Restarting all background processes!") return if avail := list(files.get_processes().keys()): - avail.remove("jarvis") # cannot be restarted + # cannot be restarted + avail.remove("jarvis") else: speaker.speak( text="Unable to fetch background processes. Try specifying 'all'" @@ -317,9 +317,8 @@ def delete_logs() -> None: > models.env.log_retention ): logger.debug("Deleting log file: %s", os.path.join(__path, file_)) - os.remove( - os.path.join(__path, file_) - ) # removes the file if it is older than log retention time + # removes the file if it is older than log retention time + os.remove(os.path.join(__path, file_)) def delete_pycache() -> None: diff --git a/jarvis/executors/crontab.py b/jarvis/executors/crontab.py index 49dd875b..12287fa3 100644 --- a/jarvis/executors/crontab.py +++ b/jarvis/executors/crontab.py @@ -9,9 +9,8 @@ from jarvis.modules.logger import logger, multiprocessing_logger from jarvis.modules.models import models -LOG_FILE = os.path.join( - "logs", "cron_%d-%m-%Y.log" -) # Used by api functions that run on cron schedule +# Used by api functions that run on cron schedule +LOG_FILE = os.path.join("logs", "cron_%d-%m-%Y.log") def executor(statement: str, log_file: str = None, process_name: str = None) -> None: diff --git a/jarvis/executors/face.py b/jarvis/executors/face.py index 5e5a38fb..cfccf09d 100644 --- a/jarvis/executors/face.py +++ b/jarvis/executors/face.py @@ -37,10 +37,10 @@ def detected_face() -> None: if not os.path.exists(os.path.join(TRAINING_DIR, phrase)): os.makedirs(os.path.join(TRAINING_DIR, phrase)) img_name = f"{phrase}_{datetime.now().strftime('%B_%d_%Y_%I-%M_%p')}.jpg" # adds datetime to image name - os.rename(FACE_DETECTION_TEMP_FILE, img_name) # renames the files - shutil.move( - src=img_name, dst=os.path.join(TRAINING_DIR, phrase) - ) # move under TRAINING_DIR -> named directory + # renames the files + os.rename(FACE_DETECTION_TEMP_FILE, img_name) + # move under TRAINING_DIR -> named directory + shutil.move(src=img_name, dst=os.path.join(TRAINING_DIR, phrase)) speaker.speak( text=f"Image has been added to known database. I will be able to recognize {phrase} in future." ) diff --git a/jarvis/executors/files.py b/jarvis/executors/files.py index 8a40fd2a..0d383426 100644 --- a/jarvis/executors/files.py +++ b/jarvis/executors/files.py @@ -43,7 +43,7 @@ def put_frequent(data: Dict[str, int]) -> None: """ with open(models.fileio.frequent, "w") as file: yaml.dump(data=data, stream=file, sort_keys=False) - file.flush() # Write everything in buffer to file right away + file.flush() def get_location() -> DefaultDict[str, Dict | float | bool]: @@ -89,7 +89,7 @@ def delete_secure_send(key: str) -> None: logger.critical("data for key [%s] was removed unprecedentedly", key) with open(models.fileio.secure_send, "w") as file: yaml.dump(data=current_data, stream=file, Dumper=yaml.Dumper) - file.flush() # Write buffer to file immediately + file.flush() def put_secure_send(data: Dict[str, Dict[str, Any]]): @@ -101,7 +101,7 @@ def put_secure_send(data: Dict[str, Dict[str, Any]]): existing = get_secure_send() with open(models.fileio.secure_send, "w") as file: yaml.dump(data={**existing, **data}, stream=file, Dumper=yaml.Dumper) - file.flush() # Write buffer to file immediately + file.flush() logger.info( "Secure dict for [%s] will be cleared after 5 minutes", [*[*data.values()][0].keys()][0], @@ -146,7 +146,7 @@ def put_restrictions(restrictions: List[str]) -> None: """ with open(models.fileio.restrictions, "w") as file: yaml.dump(data=restrictions, stream=file, indent=2, sort_keys=False) - file.flush() # Write buffer to file immediately + file.flush() def get_gpt_data() -> List[Dict[str, str]]: @@ -171,7 +171,7 @@ def put_gpt_data(data: List[Dict[str, str]]) -> None: """ with open(models.fileio.gpt_data, "w") as file: yaml.dump(data=data, stream=file, indent=4, Dumper=yaml.Dumper) - file.flush() # Write buffer to file immediately + file.flush() def get_automation() -> Dict[str, List[Dict[str, str | bool]] | Dict[str, str | bool]]: @@ -210,7 +210,7 @@ def put_automation( with open(models.fileio.automation, "w") as file: yaml.dump(data=sorted_data, stream=file, indent=2) - file.flush() # Write buffer to file immediately + file.flush() def get_smart_devices() -> dict | bool | None: @@ -243,7 +243,7 @@ def put_smart_devices(data: dict) -> None: """ with open(models.fileio.smart_devices, "w") as file: yaml.dump(data=data, stream=file, indent=2, sort_keys=False) - file.flush() # Write buffer to file immediately + file.flush() def get_processes() -> Dict[str, List[int | List[str]]]: @@ -284,7 +284,7 @@ def put_reminders(data: List[Dict[str, str]]): """ with open(models.fileio.reminders, "w") as file: yaml.dump(data=data, stream=file, indent=2, sort_keys=False) - file.flush() # Write buffer to file immediately + file.flush() def get_alarms() -> List[Dict[str, str | bool]]: @@ -310,7 +310,7 @@ def put_alarms(data: List[Dict[str, str | bool]]): """ with open(models.fileio.alarms, "w") as file: yaml.dump(data=data, stream=file, indent=2, sort_keys=False) - file.flush() # Write buffer to file immediately + file.flush() def get_recognizer() -> classes.RecognizerSettings: diff --git a/jarvis/executors/ios_functions.py b/jarvis/executors/ios_functions.py index a75250b0..cd6d5b4d 100644 --- a/jarvis/executors/ios_functions.py +++ b/jarvis/executors/ios_functions.py @@ -146,9 +146,8 @@ def locate(phrase: str) -> None: return logger.info("Locating your %s", target_device) target_device.play_sound() - before_keyword, keyword, after_keyword = str(target_device).partition( - ":" - ) # partitions the hostname info + # partitions the hostname info + before_keyword, keyword, after_keyword = str(target_device).partition(":") if before_keyword == "Accessory": after_keyword = ( after_keyword.replace(f"{models.env.name}’s", "") diff --git a/jarvis/executors/lights.py b/jarvis/executors/lights.py index 3eb90f91..86dd8f5b 100644 --- a/jarvis/executors/lights.py +++ b/jarvis/executors/lights.py @@ -87,9 +87,8 @@ def avail_check(self, function_to_call: Callable) -> None: status = ThreadPool(processes=1).apply_async( func=self.thread_worker, args=(function_to_call,) ) - speaker.speak( - run=True - ) # Speak the initial response when the work is happening behind the scenes + # Speak the initial response when the work is happening behind the scenes + speaker.speak(run=True) try: failed = status.get(5) except ThreadTimeoutError as error: diff --git a/jarvis/executors/location.py b/jarvis/executors/location.py index d399bc6a..e19091ce 100644 --- a/jarvis/executors/location.py +++ b/jarvis/executors/location.py @@ -117,7 +117,7 @@ def distance(phrase) -> None: Args: phrase:Takes the phrase spoken as an argument. """ - check = phrase.split() # str to list + check = phrase.split() places = [] for word in check: # looks for words that start with uppercase @@ -195,9 +195,8 @@ def distance_controller(origin: str = None, destination: str = None) -> None: speaker.speak(text=f"I don't think {destination} exists {models.env.title}!") return if models.env.distance_unit == models.DistanceUnits.MILES: - dist = round( - geodesic(start, end).miles - ) # calculates miles from starting point to destination + # calculates miles from starting point to destination + dist = round(geodesic(start, end).miles) else: dist = round(geodesic(start, end).kilometers) if shared.called["directions"]: diff --git a/jarvis/executors/offline.py b/jarvis/executors/offline.py index 87a6e838..f58b568b 100644 --- a/jarvis/executors/offline.py +++ b/jarvis/executors/offline.py @@ -73,7 +73,8 @@ def background_task_runner() -> None: for i, task in enumerate(tasks): # Checks a particular tasks' elapsed time if task_dict[i] + task.seconds <= time.time() or dry_run: - task_dict[i] = time.time() # Updates that particular tasks' start time + # Updates that particular tasks' start time + task_dict[i] = time.time() if now.hour in task.ignore_hours: logger.debug("'%s' skipped honoring ignore hours", task) else: @@ -190,9 +191,8 @@ def background_task_runner() -> None: while not smart_listener.empty(): mutes = smart_listener.get(timeout=2) logger.debug(mutes) - meeting_muter.append( - mutes - ) # Write to a new list as queue will be empty after executing .get + # Write to a new list as queue will be empty after executing .get + meeting_muter.append(mutes) if meeting_muter := util.remove_duplicates(input_=meeting_muter): for each_muter in meeting_muter: for meeting_name, timing_info in each_muter.items(): @@ -204,9 +204,8 @@ def background_task_runner() -> None: meeting_name, support.time_converter(second=duration), ) - meeting_muter.remove( - each_muter - ) # Remove event from new list to avoid repetition + # Remove event from new list to avoid repetition + meeting_muter.remove(each_muter) listener_controls.put_listener_state(state=False) Timer( function=listener_controls.put_listener_state, @@ -258,9 +257,8 @@ def background_task_runner() -> None: logger.warning("Tasks list has been updated.") logger.info(DeepDiff(tasks, new_tasks, ignore_order=True)) tasks = new_tasks - task_dict = { - i: time.time() for i in range(len(tasks)) - } # Re-create start time for each task + # Re-create start time for each task + task_dict = {i: time.time() for i in range(len(tasks))} # Re-check for any newly added cron_jobs with logger disabled new_cron_jobs: List[crontab.expression.CronExpression] = list( @@ -270,9 +268,8 @@ def background_task_runner() -> None: # Don't log updated jobs since there will always be a difference when run on author mode cron_jobs = new_cron_jobs dry_run = False - time.sleep( - 0.5 - ) # Reduces CPU utilization as constant fileIO operations spike CPU % + # Reduces CPU utilization as constant fileIO operations spike CPU % + time.sleep(1) def ondemand_offline_automation(task: str) -> str | None: diff --git a/jarvis/executors/others.py b/jarvis/executors/others.py index b4620ce3..9b783cd6 100644 --- a/jarvis/executors/others.py +++ b/jarvis/executors/others.py @@ -223,12 +223,10 @@ def ip_scan(host_id: int) -> Tuple[str, str]: with ThreadPoolExecutor(max_workers=100) as executor: # scans host IDs 1 to 255 (eg: 192.168.1.1 to 192.168.1.255) for info in executor.map(ip_scan, range(1, 101)): - devices.append( - info - ) # this includes all the NoneType values returned by unassigned host IDs - devices = dict( - [i for i in devices if i] - ) # removes None values and converts list to dictionary of name and ip pair + # this includes all the NoneType values returned by unassigned host IDs + devices.append(info) + # removes None values and converts list to dictionary of name and ip pair + devices = dict([i for i in devices if i]) if not device or not file: support.flush_screen() @@ -250,9 +248,8 @@ def ip_scan(host_id: int) -> Tuple[str, str]: ) return for target in chosen: - file_url = serve_file( - file, "audio/mp3" - ) # serves the file on local host and generates the play url + # serves the file on local host and generates the play url + file_url = serve_file(file, "audio/mp3") support.flush_screen() with BlockPrint(): GoogleHome(host=target).play(file_url, "audio/mp3") diff --git a/jarvis/executors/port_handler.py b/jarvis/executors/port_handler.py index 957bfd54..12b37e54 100644 --- a/jarvis/executors/port_handler.py +++ b/jarvis/executors/port_handler.py @@ -23,6 +23,7 @@ def is_port_in_use(port: int) -> bool: return sock.connect_ex(("localhost", port)) == 0 +# noinspection PyUnresolvedReferences,PyProtectedMember def kill_port_pid(port: int, protocol: str = "tcp") -> bool | None: """Uses List all open files ``lsof`` to get the PID of the process that is listening on the given port and kills it. @@ -56,10 +57,8 @@ def kill_port_pid(port: int, protocol: str = "tcp") -> bool | None: ) pid = int(each_split[1]) if pid == models.settings.pid: - called_function = sys._getframe(1).f_code.co_name # noqa - called_file = sys._getframe(1).f_code.co_filename.replace( - f"{os.getcwd()}/", "" - ) # noqa + called_function = sys._getframe(1).f_code.co_name + called_file = sys._getframe(1).f_code.co_filename.replace(f"{os.getcwd()}/", "") # fmt: skip logger.warning( "%s from %s tried to kill the running process.", called_function, diff --git a/jarvis/executors/processor.py b/jarvis/executors/processor.py index c58155a1..7712209c 100644 --- a/jarvis/executors/processor.py +++ b/jarvis/executors/processor.py @@ -117,9 +117,8 @@ def stop_child_processes() -> None: children[child]: List[int] = util.matrix_to_flat_list( [list(filter(None, d)) for d in data if any(d)] ) - logger.info( - children - ) # Include empty lists so logs have more information but will get skipped when looping anyway + # Include empty lists so logs have more information but will get skipped when looping anyway + logger.info(children) for category, pids in children.items(): for pid in pids: try: diff --git a/jarvis/executors/robinhood.py b/jarvis/executors/robinhood.py index 382197f4..3341d3f1 100644 --- a/jarvis/executors/robinhood.py +++ b/jarvis/executors/robinhood.py @@ -52,9 +52,8 @@ def get_summary() -> str: shares_total.append(total) current = round(float(raw_details["last_trade_price"]), 2) current_total = round(shares_count * current, 2) - difference = round( - float(current_total - total), 2 - ) # calculates difference between current and purchased total + # calculates difference between current and purchased total + difference = round(float(current_total - total), 2) if difference < 0: loss_total.append(-difference) else: diff --git a/jarvis/executors/todo_list.py b/jarvis/executors/todo_list.py index a317a5a5..82e3cf9f 100644 --- a/jarvis/executors/todo_list.py +++ b/jarvis/executors/todo_list.py @@ -42,13 +42,11 @@ def get_todo() -> None: for category, item in downloaded: # condition below makes sure one category can have multiple items without repeating category for each item if category not in result: - result[ - category - ] = item # creates dict for category and item if category is not found in result + # creates dict for category and item if category is not found in result + result[category] = item else: - result[category] = ( - result[category] + ", " + item - ) # updates category if already found in result + # updates category if already found in result + result[category] = result[category] + ", " + item if result: if shared.called_by_offline: speaker.speak(text=json.dumps(result)) diff --git a/jarvis/executors/volume.py b/jarvis/executors/volume.py index 21fe2b68..9ed90818 100644 --- a/jarvis/executors/volume.py +++ b/jarvis/executors/volume.py @@ -16,12 +16,12 @@ def speaker_volume(level: int) -> None: Args: level: Takes the volume level as an argument. """ - logger.info( - "Jarvis' volume has been set to %d" % level + "%" - ) # % is mandatory because of string concatenation + # % is mandatory because of string concatenation + logger.info("Jarvis' volume has been set to %d" % level + "%") models.AUDIO_DRIVER.setProperty("volume", level / 100) +# noinspection PyUnresolvedReferences,PyProtectedMember def volume(phrase: str = None, level: int = None) -> None: """Controls volume from the numbers received. Defaults to 50%. @@ -47,7 +47,7 @@ def volume(phrase: str = None, level: int = None) -> None: if level is None: level = models.env.volume phrase = phrase or "" - caller = sys._getframe(1).f_code.co_name # noqa + caller = sys._getframe(1).f_code.co_name if "master" in phrase or "main" in phrase or caller in ("executor", "starter"): pyvolume.custom(level, logger) speaker_volume(level=level) diff --git a/jarvis/executors/word_match.py b/jarvis/executors/word_match.py index 0c931cc2..8fc722f8 100644 --- a/jarvis/executors/word_match.py +++ b/jarvis/executors/word_match.py @@ -10,9 +10,8 @@ def reverse_lookup(lookup: str, match_list: List | Tuple) -> str | None: """Returns the word in phrase that matches the one in given list.""" - reverse = sum( - [w.lower().split() for w in match_list], [] - ) # extract multi worded conditions in match list + # extract multi worded conditions in match list + reverse = sum([w.lower().split() for w in match_list], []) for word in lookup.split(): # loop through words in the phrase # check at least one word in phrase matches the multi worded condition if word in reverse: diff --git a/jarvis/modules/audio/speaker.py b/jarvis/modules/audio/speaker.py index 29cf802b..cde55412 100644 --- a/jarvis/modules/audio/speaker.py +++ b/jarvis/modules/audio/speaker.py @@ -47,9 +47,8 @@ def speech_synthesizer( logger.info("Converted %s -> %s", t_12, t_24) text = text.replace(t_12, t_24) if "IP" in text.split(): - ip_new = "-".join([i for i in text.split(" ")[-1]]).replace( - "-.-", ", " - ) # 192.168.1.1 -> 1-9-2, 1-6-8, 1, 1 + # 192.168.1.1 -> 1-9-2, 1-6-8, 1, 1 + ip_new = "-".join([i for i in text.split(" ")[-1]]).replace("-.-", ", ") text = text.replace(text.split(" ")[-1], ip_new).replace(" IP ", " I.P. ") # Raises UnicodeDecodeError within docker container text = text.replace("\N{DEGREE SIGN}F", " degrees fahrenheit") diff --git a/jarvis/modules/audio/speech_synthesis.py b/jarvis/modules/audio/speech_synthesis.py index a1ee0402..692bfa3b 100644 --- a/jarvis/modules/audio/speech_synthesis.py +++ b/jarvis/modules/audio/speech_synthesis.py @@ -221,10 +221,10 @@ def stream_logs(client: DockerClient, container_id: str) -> NoReturn: follow=None, until=None, ) - log_file = open( - file=models.fileio.speech_synthesis_log, mode="a", buffering=1 - ) # 1 line buffer on file - os.fsync(log_file.fileno()) # Tell os module to write the buffer of the file + # 1 line buffer on file + log_file = open(file=models.fileio.speech_synthesis_log, mode="a", buffering=1) + # Tell os module to write the buffer of the file + os.fsync(log_file.fileno()) __asterisks = "".join(["*" for _ in range(120)]) __spaces = "".join(["-" for _ in range(47)]) log_file.write(f"\n{__asterisks}\n") @@ -232,7 +232,7 @@ def stream_logs(client: DockerClient, container_id: str) -> NoReturn: log_file.write(f"{__asterisks}\n\n") for line in logs: log_file.write(line.decode(encoding="UTF-8")) - log_file.flush() # Write everything in buffer to file right away + log_file.flush() log_file.close() diff --git a/jarvis/modules/builtin_overrides.py b/jarvis/modules/builtin_overrides.py index 705c8cea..e1d90d07 100644 --- a/jarvis/modules/builtin_overrides.py +++ b/jarvis/modules/builtin_overrides.py @@ -28,7 +28,7 @@ def run_in_parallel(self) -> None: def ordered_load( stream, Loader=yaml.SafeLoader, object_pairs_hook=collections.OrderedDict # noqa -) -> collections.OrderedDict: # noqa +) -> collections.OrderedDict: """Custom loader for OrderedDict. Args: diff --git a/jarvis/modules/camera/camera.py b/jarvis/modules/camera/camera.py index e163af9f..f5ec0248 100644 --- a/jarvis/modules/camera/camera.py +++ b/jarvis/modules/camera/camera.py @@ -134,7 +134,8 @@ def _get_camera_info_windows(self) -> Generator[Dict[str, str]]: Dict[str, str]: Returns the information of all connected cameras as a list of dictionary. """ - output = list(filter(None, self.output)) # Filter null values in the list + # Filter null values in the list + output = list(filter(None, self.output)) if not output: return diff --git a/jarvis/modules/facenet/face.py b/jarvis/modules/facenet/face.py index 620695e3..84dec82d 100644 --- a/jarvis/modules/facenet/face.py +++ b/jarvis/modules/facenet/face.py @@ -94,10 +94,10 @@ def load_dataset(self, location: str) -> None: # generates face encoding matrix if encoded := face_recognition.face_encodings(img): encoded = encoded[0] - self.train_faces.append(encoded) # loads ended values to match - self.train_names.append( - char_dir - ) # loads the names of each named subdirectories + # loads ended values to match + self.train_faces.append(encoded) + # loads the names of each named subdirectories + self.train_names.append(char_dir) def face_recognition( self, location: str | FilePath, retry_count: int = 20 @@ -117,18 +117,17 @@ def face_recognition( logger.debug("Initiating face recognition.") self.load_dataset(location=location) for _ in range(retry_count): - ret, img = self.validation_video.read() # reads video from web cam + # reads video from web cam + ret, img = self.validation_video.read() if not ret: logger.warning( "Unable to read from camera index: %d", models.env.camera_index ) continue - identifier = face_recognition.face_locations( - img, model=self.MODEL - ) # gets image from the video read above - encoded_ = face_recognition.face_encodings( - img, identifier - ) # creates an encoding for the image + # gets image from the video read above + identifier = face_recognition.face_locations(img, model=self.MODEL) + # creates an encoding for the image + encoded_ = face_recognition.face_encodings(img, identifier) for face_encoding, face_location in zip(encoded_, identifier): # using learning_rate, the encoding is matched against the encoded matrix for images in named directory results = face_recognition.compare_faces( @@ -167,13 +166,13 @@ def face_detection( raise FileNotFoundError(cv2_cascades) cascade = cv2.CascadeClassifier(cv2_cascades) for _ in range(retry_count + 1): - ret, image = self.validation_video.read() # reads video from web cam + # reads video from web cam + ret, image = self.validation_video.read() if not ret: continue try: - img = cv2.cvtColor( - image, cv2.COLOR_BGR2GRAY - ) # convert the captured image to grayscale + # convert the captured image to grayscale + img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) except cv2.error as error: logger.error(error) img = image # proceed without performing grayscale diff --git a/jarvis/modules/logger.py b/jarvis/modules/logger.py index c020d7ee..b6bb8355 100644 --- a/jarvis/modules/logger.py +++ b/jarvis/modules/logger.py @@ -11,7 +11,7 @@ from jarvis.modules.models import models if not os.path.isdir("logs"): - os.mkdir("logs") # Creates only logs dir if limited mode is enabled + os.mkdir("logs") DEFAULT_LOG_FORM = "%(asctime)s - %(levelname)s - [%(processName)s:%(module)s:%(lineno)d] - %(funcName)s - %(message)s" DEFAULT_FORMATTER = logging.Formatter( diff --git a/jarvis/modules/meetings/events.py b/jarvis/modules/meetings/events.py index c81f27aa..b1a93d38 100644 --- a/jarvis/modules/meetings/events.py +++ b/jarvis/modules/meetings/events.py @@ -41,9 +41,8 @@ def events_writer() -> None: ) with db.connection: cursor = db.connection.cursor() - cursor.execute( - f"DELETE FROM {models.env.event_app}" - ) # Use f-string or %s as table names can't be parametrized + # Use f-string or %s as table names can't be parametrized + cursor.execute(f"DELETE FROM {models.env.event_app}") cursor.connection.commit() cursor.execute(query) cursor.connection.commit() @@ -116,7 +115,8 @@ def events_gatherer() -> str: event_app_launcher() if not failure: failure = f"[{models.env.event_app}:{error}] - {err_msg}" - failure = failure.replace('"', "") # An identifier can’t go after this “"” + # An identifier can’t go after this “"” + failure = failure.replace('"', "") logger.error(failure) pynotification.pynotifier(title="Jarvis", message=failure) return f"I was unable to read your {models.env.event_app} {models.env.title}! Please make sure it is in sync." @@ -191,9 +191,8 @@ def events(*args) -> None: text=f"Events table is empty {models.env.title}. Please try again in a minute or two." ) return - event = ThreadPool(processes=1).apply_async( - func=events_gatherer - ) # Runs parallely and awaits completion + # Runs parallely and awaits completion + event = ThreadPool(processes=1).apply_async(func=events_gatherer) speaker.speak( text=f"Please give me a moment {models.env.title}! Let me check your {models.env.event_app}.", run=True, diff --git a/jarvis/modules/meetings/ics_meetings.py b/jarvis/modules/meetings/ics_meetings.py index 30fc168b..ad0a1366 100644 --- a/jarvis/modules/meetings/ics_meetings.py +++ b/jarvis/modules/meetings/ics_meetings.py @@ -209,9 +209,8 @@ def meetings(phrase: str) -> None: text=f"Meetings table is empty {models.env.title}. Please try again in a minute or two." ) return - meeting = ThreadPool(processes=1).apply_async( - func=meetings_gatherer - ) # Runs parallely and awaits completion + # Runs parallely and awaits completion + meeting = ThreadPool(processes=1).apply_async(func=meetings_gatherer) speaker.speak( text=f"Please give me a moment {models.env.title}! I'm working on it.", run=True, diff --git a/jarvis/modules/microphone/graph_mic.py b/jarvis/modules/microphone/graph_mic.py index 276f84ac..e56639d5 100644 --- a/jarvis/modules/microphone/graph_mic.py +++ b/jarvis/modules/microphone/graph_mic.py @@ -201,13 +201,16 @@ def _kick_off() -> None: # Labels are not set, but if it is to be set, then set padding at least to 2 # ax.set_xlabel('Time') # ax.set_ylabel('Frequency') - fig.tight_layout(pad=0) # no padding + # no padding + fig.tight_layout(pad=0) plt.legend(["Microphone Amplitude"]) fig.canvas.manager.set_window_title("Realtime Spectrum Display") if settings.dark_mode: - ax.set_facecolor("xkcd:almost black") # https://xkcd.com/color/rgb/ + # https://xkcd.com/color/rgb/ + ax.set_facecolor("xkcd:almost black") # Takes RGB or RGBA values as arguments - # ax.set_facecolor((0.1, 0.1, 0.1)) # https://matplotlib.org/stable/api/colors_api.html + # https://matplotlib.org/stable/api/colors_api.html + # ax.set_facecolor((0.1, 0.1, 0.1)) stream = sounddevice.InputStream( device=settings.device, diff --git a/jarvis/modules/models/classes.py b/jarvis/modules/models/classes.py index 9058f8f5..ddc5bd8e 100644 --- a/jarvis/modules/models/classes.py +++ b/jarvis/modules/models/classes.py @@ -298,9 +298,8 @@ def check_hours_format(cls, v, values, **kwargs): # noqa refined = [] for multiple in v: if isinstance(multiple, str): - refined.extend( - handle_multiform(multiple.split("-")) - ) # comes back as a list of string + # comes back as a list of string + refined.extend(handle_multiform(multiple.split("-"))) else: refined.append(multiple) if refined: diff --git a/jarvis/modules/models/models.py b/jarvis/modules/models/models.py index a7729680..d5bafc5c 100644 --- a/jarvis/modules/models/models.py +++ b/jarvis/modules/models/models.py @@ -60,7 +60,8 @@ "robinhood": ("summary",), "listener": ("state",), } -KEEP_TABLES = ("vpn", "party", "listener") # TABLES to keep from `fileio.base_db` +# TABLES to keep from `fileio.base_db` +KEEP_TABLES = ("vpn", "party", "listener") startup = settings.pname in ("JARVIS", "telegram_api", "jarvis_api") # 'startup_gpt' is required since it has to be invoked only for certain child processes # this will avoid running GPT instance for pre-commit as well @@ -284,10 +285,11 @@ def _global_validations() -> None: # Validate voice for speech synthesis try: # noinspection HttpUrlsUsage + # Set connect and read timeout explicitly response = requests.get( url=f"http://{env.speech_synthesis_host}:{env.speech_synthesis_port}/api/voices", timeout=(3, 3), - ) # Set connect and read timeout explicitly + ) if response.ok: available_voices = [ value.get("id").replace("/", "_") diff --git a/jarvis/modules/speaker/speak.py b/jarvis/modules/speaker/speak.py index b40a7f90..8b919f04 100644 --- a/jarvis/modules/speaker/speak.py +++ b/jarvis/modules/speaker/speak.py @@ -39,9 +39,8 @@ class Speaker: def __init__(self): """Instantiates the speaker engine and loads the voices available in the hosting machine.""" self.engine = pyttsx3.init() - self.voices = self.engine.getProperty( - "voices" - ) # gets the list of voices available + # gets the list of voices available + self.voices = self.engine.getProperty("voices") # noinspection PyTypeChecker def get_all_voices(self) -> Generator[Dict[str, str | int]]: diff --git a/jarvis/modules/telegram/audio_handler.py b/jarvis/modules/telegram/audio_handler.py index 1db4d257..03172b13 100644 --- a/jarvis/modules/telegram/audio_handler.py +++ b/jarvis/modules/telegram/audio_handler.py @@ -25,7 +25,7 @@ def audio_converter_mac() -> Callable: Transcode function from ftransc. """ try: - from ftransc.core.transcoders import transcode # noqa + from ftransc.core.transcoders import transcode # noqa: F401 return transcode except (SystemExit, ModuleNotFoundError, ImportError) as error: diff --git a/jarvis/modules/timeout/timeout.py b/jarvis/modules/timeout/timeout.py index 90e32bad..a6d31d59 100644 --- a/jarvis/modules/timeout/timeout.py +++ b/jarvis/modules/timeout/timeout.py @@ -60,7 +60,8 @@ def timeout( process.join(timeout=1e-01) try: logger.info("Closing process: %d", process.pid) if logger else None - process.close() # Close immediately instead of waiting to be garbage collected + # Close immediately instead of waiting to be garbage collected + process.close() except ValueError as error: # Expected when join timeout is insufficient. The resources will be released eventually but not immediately. logger.error(error) if logger else None diff --git a/jarvis/modules/transformer/gpt.py b/jarvis/modules/transformer/gpt.py index ee38cd63..85ef0e19 100644 --- a/jarvis/modules/transformer/gpt.py +++ b/jarvis/modules/transformer/gpt.py @@ -300,5 +300,4 @@ def query(self, phrase: str) -> None: logger.error("Failed to load GPT instance for '%s'", models.settings.pname) instance = None else: - logger.warning("GPT has been disabled! To enable it, set the env var ollama=True") instance = None diff --git a/jarvis/modules/utils/support.py b/jarvis/modules/utils/support.py index 5357aebb..ca18ebee 100644 --- a/jarvis/modules/utils/support.py +++ b/jarvis/modules/utils/support.py @@ -219,13 +219,12 @@ def utc_to_local(utc_dt: datetime) -> datetime: datetime: Local datetime as an object. """ - utc_dt = utc_dt.replace( - tzinfo=timezone.utc - ) # Tell datetime object that the tz is UTC - local_tz = dateutil.tz.gettz( - datetime.now().astimezone().tzname() - ) # Get local timezone - return utc_dt.astimezone(local_tz) # Convert the UTC timestamp to local + # Tell datetime object that the tz is UTC + utc_dt = utc_dt.replace(tzinfo=timezone.utc) + # Get local timezone + local_tz = dateutil.tz.gettz(datetime.now().astimezone().tzname()) + # Convert the UTC timestamp to local + return utc_dt.astimezone(local_tz) def build_lookup() -> List[str]: @@ -604,9 +603,8 @@ def connected_to_network() -> bool: socket_ = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: if models.settings.os == models.supported_platforms.windows: - connection = HTTPSConnection( - "8.8.8.8", timeout=5 - ) # Recreate a new connection everytime + # Recreate a new connection everytime + connection = HTTPSConnection("8.8.8.8", timeout=5) connection.request("HEAD", "/") else: socket_.connect(("8.8.8.8", 80)) diff --git a/tests/main_test.py b/tests/main_test.py index 01cd7b5c..0748ca5a 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -30,8 +30,8 @@ def test_init_activator( """ mock_pvporcupine_create.return_value = MagicMock() mock_audio_open.return_value = MagicMock() - - self.activator.__init__() # Call the __init__() method explicitly + # Call the __init__() method explicitly + self.activator.__init__() # Assertions mock_pvporcupine_create.assert_called_once_with( @@ -51,18 +51,14 @@ def test_init_activator( self.assertEqual(self.activator.detector, mock_pvporcupine_create.return_value) self.assertEqual(self.activator.audio_stream, mock_audio_open.return_value) - @patch( - "jarvis.modules.audio.listener.listen" - ) # Patch the listener.listen from jarvis.modules.audio - @patch( - "jarvis.executors.commander.initiator" - ) # Patch the commander.initiator from jarvis.executors - @patch( - "jarvis.modules.audio.speaker.speak" - ) # Patch the speaker.speak from jarvis.modules.audio - @patch( - "jarvis.main.audio_engine.close" - ) # Patch the audio_engine.close from jarvis.main + # Patch the listener.listen from jarvis.modules.audio + @patch("jarvis.modules.audio.listener.listen") + # Patch the commander.initiator from jarvis.executors + @patch("jarvis.executors.commander.initiator") + # Patch the speaker.speak from jarvis.modules.audio + @patch("jarvis.modules.audio.speaker.speak") + # Patch the audio_engine.close from jarvis.main + @patch("jarvis.main.audio_engine.close") def test_executor( self, mock_audio_close: MagicMock, @@ -86,16 +82,14 @@ def test_executor( self.activator.executor() # Assertions - self.assertTrue(mock_audio_close.called) # audio_engine.close should be called - mock_listen.assert_called_once_with( - sound=False, no_conf=True - ) # listener.listen should be called - mock_initiator.assert_called_once_with( - phrase=SAMPLE_PHRASE - ) # commander.initiator should be called with the correct phrase - mock_speak.assert_called_once_with( - run=True - ) # speaker.speak should be called with run=True + # audio_engine.close should be called + self.assertTrue(mock_audio_close.called) + # listener.listen should be called + mock_listen.assert_called_once_with(sound=False, no_conf=True) + # commander.initiator should be called with the correct phrase + mock_initiator.assert_called_once_with(phrase=SAMPLE_PHRASE) + # speaker.speak should be called with run=True + mock_speak.assert_called_once_with(run=True) if __name__ == "__main__":