diff --git a/src/ypkgupgr/__init__.py b/src/ypkgupgr/__init__.py index c396f3b..e0de755 100644 --- a/src/ypkgupgr/__init__.py +++ b/src/ypkgupgr/__init__.py @@ -1,19 +1,19 @@ +import asyncio import subprocess import sys -import os -import asyncio -import logging -import platformdirs + import click + +from .appdata import create_appdata_dirs, log_dir from .colors import Colors from .consts import APP_NAME, AUTHOR_NAME -from .logs import * -from .appdata import create_appdata_dirs, log_dir +from .graphics import progress_update, progress_ring, clear_screen from .ignored import ignored, get_ignored_packages, ignore_packages, unignore_packages from .ignored import unignore_all as actually_unignore_all -from .graphics import progress_update, progress_ring, clear_screen +from .logs import * from .misc import * + async def update(name: str, line: int): """ Updates the package using its name. @@ -25,8 +25,8 @@ async def update(name: str, line: int): global ypkgupgr_outdated get_ignored_packages() - - if (name in ignored): # Package in ignored + + if (name in ignored): # Package in ignored logger.info(f"Package {name} ignored.") progress_update(line, f"{name}: {Colors.YELLOW}Ignored") finished_count += 1 @@ -60,14 +60,14 @@ async def update(name: str, line: int): process = await asyncio.create_subprocess_shell('"' + sys.executable + '"' + " -m pip install --upgrade " + name, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) - + logger.debug("Subprocess shell created.") - + # Gets this process' return code. return_code = await process.wait() logger.debug(f"Subprocess ended with code {return_code}") - + # Adds a finished and updates the progress ring. finished_count += 1 progress = int((finished_count / outdated_count) * 100) @@ -87,10 +87,10 @@ async def update(name: str, line: int): logger.error(errline) logger.info("End of pip output.") - + progress_update(line, f"{name}: {Colors.RED}Error") # print(f"{name} failed to update. ({progress}% - {finished_count}/{outdated_count} complete or failed)") - + if failed == "": failed = name else: @@ -98,13 +98,14 @@ async def update(name: str, line: int): logger.debug(f"Added {name} to failed.") + async def start_updates(lines: list[str]): """Updates each package.""" global line_count tasks = [] - line_no = 3 # Tasks start at line 3, because there was 1 line already printed + one to separate. + line_no = 3 # Tasks start at line 3, because there was 1 line already printed + one to separate. for line in lines: package_info = line.split() package_name = package_info[0] @@ -115,12 +116,13 @@ async def start_updates(lines: list[str]): tasks.append(asyncio.create_task(update(package_name, line_no))) line_no += 1 - + line_count = line_no - + for task in tasks: await task + def update_packages(): """ If calling from a python file, please use a subprocess instead. @@ -130,28 +132,29 @@ def update_packages(): global ypkgupgr_outdated logger.info(f"Starting update. Platform: {sys.platform}") - + # Clears the screen. clear_screen() - progress_ring(progress = 0, intermediate = True) + progress_ring(progress=0, intermediate=True) print("Getting outdated pip packages...") logger.info("Getting outdated packages.") # Runs the pip list --outdated command to get the outdated packages. - outdated_packages = subprocess.check_output([sys.executable, '-m', 'pip', 'list', '--outdated', '--disable-pip-version-check']).decode('utf-8') + outdated_packages = subprocess.check_output( + [sys.executable, '-m', 'pip', 'list', '--outdated', '--disable-pip-version-check']).decode('utf-8') # Splits the output into lines and ignore the header. lines = outdated_packages.strip().split('\n')[2:] # Checks if there are any outdated packages. if len(lines) <= 0: - progress_ring(progress = 100, complete = True) + progress_ring(progress=100, complete=True) print("No outdated packages found.") logger.info("No outdated packages.") return - + outdated_count = len(lines) logger.info(f"Outdated packages: {lines}") @@ -179,10 +182,12 @@ def update_packages(): # Warns the user if ypkgupgr hasn't been updated. if ypkgupgr_outdated: - print(f'The ypkgupgr package is outdated, and you are using the script. Please use "{sys.executable} -m ypkgupgr" to update it.\n') + print( + f'The ypkgupgr package is outdated, and you are using the script. Please use "{sys.executable} -m ypkgupgr" to update it.\n') logger.info("ypkgupgr is outdated and the script is used on Windows.") - - progress_ring(progress = 100,complete = True) + + progress_ring(progress=100, complete=True) + @click.group(context_settings=dict(help_option_names=["-?", "--help"]), invoke_without_command=True) @click.option('--clear-log', is_flag=True, help='Clear the log file before writing to it.') @@ -195,7 +200,7 @@ def update_command(ctx, clear_log, log_debug_var, ignore, unignore, unignore_all global outdated_count global ypkgupgr_outdated global ran_from_script - + create_appdata_dirs() # Log commands are handled in init_logging. @@ -203,72 +208,74 @@ def update_command(ctx, clear_log, log_debug_var, ignore, unignore, unignore_all if ran_from_script: log_info("Running from script.") - + if (ctx.invoked_subcommand is not None): log_info(f"Starting command {ctx.invoked_subcommand}.") return log_info(f"Starting command. Options: {clear_log}, {log_debug_var}, {ignore}, {unignore}, {unignore_all_var}") - + if (len(ignore) > 0): ignore_packages(list(ignore)) # ^ ignores everything after --ignore. - + if (len(unignore) > 0): unignore_packages(list(unignore)) # ^ unignores everything after --unignore. - + if (unignore_all_var): actually_unignore_all() # Return after both ignoring and unignoring packages. if (len(ignore) > 0 or len(unignore) > 0 or unignore_all_var): return - + update_packages() + @update_command.command(help="Add all packages in the given arguments to the ignored file and exit.") @click.argument("to_ignore", nargs=-1) @click.option('--clear-log', is_flag=True, help='Clear the log file before writing to it.') @click.option('--log-debug', 'log_debug_var', is_flag=True, help='Log debug information.') def ignore(to_ignore, clear_log, log_debug_var): - init_logging(clear_log, log_debug_var) if (len(to_ignore) > 0): ignore_packages(to_ignore) + @update_command.command(help="Remove all packages in the given arguments from the ignored file and exit.") @click.option('--clear-log', is_flag=True, help='Clear the log file before writing to it.') @click.option('--log-debug', 'log_debug_var', is_flag=True, help='Log debug information.') @click.argument("to_unignore", nargs=-1) def unignore(to_unignore, clear_log, log_debug_var): - init_logging(clear_log, log_debug_var) if (len(to_unignore) > 0): unignore_packages(to_unignore) + @update_command.command(help="Clear the ignored file and exit.") @click.option('--clear-log', is_flag=True, help='Clear the log file before writing to it.') @click.option('--log-debug', 'log_debug_var', is_flag=True, help='Log debug information.') def unignore_all(clear_log, log_debug_var): - init_logging(clear_log, log_debug_var) actually_unignore_all() + @update_command.command(help="Open the logs directory with your default file explorer and exit.") def open_logs(): click.launch(log_dir) -def run_from_script(): + +def run_from_script(): """ Runs update_packages() and sets ran_from_script to True. That contributes to fixing issue #11 of the original repo. (https://github.com/yesseruser/yesserpackageupdater/issues/11) """ global ran_from_script - + ran_from_script = True - update_command() \ No newline at end of file + update_command() diff --git a/src/ypkgupgr/graphics.py b/src/ypkgupgr/graphics.py index 1ccde58..f64a0ed 100644 --- a/src/ypkgupgr/graphics.py +++ b/src/ypkgupgr/graphics.py @@ -1,12 +1,14 @@ -import sys +import os from .logs import log_debug from .colors import Colors from .misc import failed, line_length + def clear_screen(): - print("\033c", end='') + os.system('cls' if os.name == 'nt' else 'clear') + -def progress_ring(progress, complete = False, intermediate = False): +def progress_ring(progress, complete=False, intermediate=False): """ Updates Windows Terminal's progress ring. """ @@ -14,11 +16,11 @@ def progress_ring(progress, complete = False, intermediate = False): global outdated_count global finished_count - # Checks if the user is on Windows. Otherwise the progress string might be printed and would confuse the user on Linux and Mac. - if sys.platform != "win32": - log_debug("Progress ring not shown because platform is not Windows.") + # Checks if the WT_SESSION variable is set to prevent printing on consoles where this isn't supported. + if "WT_SESSION" not in os.environ: + log_debug("Progress ring not shown because WT_SESSION key not present.") return - + # Gets the correct progress ring state. state = 0 if complete: @@ -29,14 +31,15 @@ def progress_ring(progress, complete = False, intermediate = False): state = 1 else: state = 2 - + # Prints the progress string according to https://github.com/MicrosoftDocs/terminal/blob/main/TerminalDocs/tutorials/progress-bar-sequences.md print(f"{chr(27)}]9;4;{state};{progress}{chr(7)}", end="") log_debug(f"Progress ring updated with the following data: State: {state}; Progress: {progress}") + def progress_update(line: int, text: str): for i in range(line_length[line] + 1): text += " " print(f"\033[{line};1H" + Colors.RESET + text) - line_length[line] = len(text) \ No newline at end of file + line_length[line] = len(text)