Advertisement
4ndr0666

mpm.py

May 1st, 2023 (edited)
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.08 KB | Source Code | 0 0
  1. import os
  2. import subprocess
  3.  
  4. import click
  5. import tomli
  6. import toml
  7. from rich.console import Console
  8. from rich.table import Table
  9.  
  10. console = Console()
  11.  
  12. COLORS = {
  13.     "red": "\033[31m",
  14.     "green": "\033[32m",
  15.     "yellow": "\033[33m",
  16.     "blue": "\033[34m",
  17.     "reset": "\033[0m",
  18. }
  19.  
  20. SORTABLE_FIELDS = ["name", "description", "latest_version"]
  21.  
  22.  
  23. class ManagerPool:
  24.     ALLOWED_EXTRA_OPTION = [
  25.         "--verbose",
  26.         "--all",
  27.     ]
  28.  
  29.  
  30. class PackageManager:
  31.     def __init__(self, manager_pool):
  32.         self._manager_pool = manager_pool
  33.  
  34.     def get_pool(self):
  35.         raise NotImplementedError()
  36.  
  37.  
  38. class PythonPackageManager(PackageManager):
  39.     def __init__(self, manager_pool):
  40.         super().__init__(manager_pool)
  41.  
  42.     def get_pool(self):
  43.         command = ["pip", "search", "-v"]
  44.         result = self._manager_pool.run_command(command)
  45.         package_data = result.stdout.strip().split("\n")
  46.         packages = []
  47.  
  48.         for package_str in package_data:
  49.             package_str = package_str.strip()
  50.             package_data = package_str.split(" ")
  51.             name, description, version = (
  52.                 package_data[0],
  53.                 " ".join(package_data[1:-1]),
  54.                 package_data[-1],
  55.             )
  56.             packages.append({"name": name, "description": description, "latest_version": version})
  57.  
  58.         return packages
  59.  
  60.  
  61. class ManagerPool:
  62.     ALLOWED_EXTRA_OPTION = [
  63.         "--verbose",
  64.         "--all",
  65.     ]
  66.  
  67.     def run_command(self, command, capture_output=True):
  68.         try:
  69.             process = subprocess.run(
  70.                 command, check=True, capture_output=capture_output, text=True
  71.             )
  72.             return process
  73.         except subprocess.CalledProcessError as e:
  74.             console.print(f"Error: {e}\n{e.stderr}", style="red")
  75.             raise click.Abort()
  76.  
  77.  
  78. def print_table(results, fields=None):
  79.     if not fields:
  80.         fields = results[0].keys()
  81.  
  82.     table = Table(show_header=True, header_style="bold")
  83.     for field in fields:
  84.         table.add_column(field)
  85.  
  86.     for result in results:
  87.         row = []
  88.         for field in fields:
  89.             row.append(str(result.get(field, "")))
  90.         table.add_row(*row)
  91.  
  92.     console.print(table)
  93.  
  94.  
  95. manager_pool = ManagerPool()
  96.  
  97.  
  98. @click.group()
  99. def cli():
  100.     pass
  101.  
  102.  
  103. @cli.command()
  104. @click.argument("name")
  105. @click.option(
  106.     "--version",
  107.     help="Version to search for.",
  108.     required=True,
  109. )
  110. def precise_search_cli(name, version, verbose=False):
  111.     console.print(f"Searching for package {name} version {version}...")
  112.  
  113.     command = ["mpm", "search", name, "--version", version]
  114.     if verbose:
  115.         console.print(f"Running command {' '.join(command)}")
  116.  
  117.     result = manager_pool.run_command(command)
  118.  
  119.     if result.returncode == 0:
  120.         print_table([{"name": name, "description": result.stdout}], fields=["name", "description"])
  121.     else:
  122.         console.print(f"Could not find package {name} version {version}", style="red")
  123.  
  124.  
  125. @cli.command()
  126. @click.option("-v", "--verbose", count=True, help="Increase verbosity of output")
  127. @click.argument("package")
  128. def install(verbose, package):
  129.     """
  130.    Install a package
  131.  
  132.    Example usage: `mpm install <package>`
  133.    """
  134.     console.print(f"Installing {package}...")
  135.  
  136.     command = ["mpm", "install", package]
  137.  
  138.     if verbose:
  139.         console.print("Running the following command:\n", style="cyan")
  140.         console.print(f"  {' '.join(command)}", style="cyan")
  141.     console.print("\n")
  142.  
  143. @cli.command()
  144. @click.argument("name")
  145. @click.option(
  146.     "--version",
  147.     help="Version to search for.",
  148.     required=True,
  149. )
  150. def precise_search_cli(name, version, verbose=False):
  151.     console.print(f"Searching for package {name} version {version}...")
  152.  
  153.     command = ["mpm", "search", name, "--version", version]
  154.  
  155.     if verbose:
  156.         console.print("Running the following command:\n", style="cyan")
  157.         console.print(f"  {' '.join(command)}", style="cyan")
  158.         console.print("\n")
  159.  
  160.     run_command(command)
  161.  
  162.  
  163. @cli.command()
  164. @click.option(
  165.     "--overwrite",
  166.     is_flag=True,
  167.     help="Overwrite the existing TOML file.",
  168. )
  169. @click.option(
  170.     "--merge",
  171.     is_flag=True,
  172.     help="Merge the snapshot with the existing TOML file.",
  173. )
  174. @click.option(
  175.     "--update-version",
  176.     is_flag=True,
  177.     help="Update the version numbers in the TOML file.",
  178. )
  179. @click.option(
  180.     "-v",
  181.     "--verbose",
  182.     is_flag=True,
  183.     help="Increase verbosity of output.",
  184. )
  185. @click.argument("toml_path", default="")
  186. def snapshot(toml_path, overwrite, merge, update_version, verbose=False):
  187.     if not toml_path:
  188.         toml_path = os.path.expanduser("~/.config/mpm/packages.toml")
  189.  
  190.     # If the TOML file does not exist or is empty, prompt the user to create a new snapshot or correct the TOML file manually.
  191.     if not os.path.exists(toml_path) or os.path.getsize(toml_path) == 0:
  192.         console.print(f"Error: {toml_path} does not exist or is empty. Create a new snapshot or correct the TOML file manually.", style="red")
  193.         return
  194.  
  195.     try:
  196.         with open(toml_path, "rb") as toml_file:
  197.             tomli.load(toml_file)
  198.     except tomli.TOMLDecodeError as e:
  199.         console.print(f"Error: Invalid TOML data in {toml_path}. {str(e)}", style="red")
  200.         return
  201.  
  202.     command = ["mpm", "snapshot"]
  203.  
  204.     if overwrite:
  205.         command.append("--overwrite")
  206.  
  207.     if merge:
  208.         command.append("--merge")
  209.  
  210.     if update_version:
  211.         command.append("--update-version")
  212.  
  213.     command.append(toml_path)
  214.  
  215.     run_command(command, verbose=verbose)
  216.  
  217.  
  218. @cli.command()
  219. def remove_duplicates():
  220.     command = ["mpm", "duplicates"]
  221.     run_command(command)
  222.  
  223.  
  224. @cli.command()
  225. @click.argument("name")
  226. def search(name):
  227.     command = ["mpm", "search", name]
  228.     run_command(command)
  229.  
  230.  
  231. @cli.command()
  232. @click.argument("toml_path", default="")
  233. @click.option(
  234.     "--overwrite",
  235.     is_flag=True,
  236.     help="Overwrite existing snapshot TOML file.",
  237. )
  238. @click.option(
  239.     "--merge",
  240.     is_flag=True,
  241.     help="Merge existing snapshot TOML file with new snapshot.",
  242. )
  243. @click.option(
  244.     "--update-version",
  245.     is_flag=True,
  246.     help="Update the package version of an existing package in the snapshot.",
  247. )
  248. @click.option(
  249.     "--verbose",
  250.     is_flag=True,
  251.     help="Increase verbosity of output.",
  252. )
  253. def snapshot(toml_path, overwrite, merge, update_version, verbose):
  254.     """
  255.    Create or update a TOML file containing installed packages using mpm.
  256.  
  257.    Example usage: `mpm snapshot`
  258.    """
  259.     if not toml_path:
  260.         toml_path = os.path.expanduser
  261. @cli.command()
  262. @click.option(
  263.     "--overwrite",
  264.     "-o",
  265.     is_flag=True,
  266.     default=False,
  267.     help="Overwrite existing snapshot TOML file.",
  268. )
  269. @click.option(
  270.     "--merge",
  271.     "-m",
  272.     is_flag=True,
  273.     default=False,
  274.     help="Merge existing snapshot TOML file with new snapshot.",
  275. )
  276. @click.option(
  277.     "--update-version",
  278.     "-u",
  279.     is_flag=True,
  280.     default=False,
  281.     help="Update the package version of an existing package in the snapshot.",
  282. )
  283. @click.argument("toml_path", default="")
  284. @click.option(
  285.     "--verbose",
  286.     "-v",
  287.     is_flag=True,
  288.     default=False,
  289.     help="Increase verbosity of output.",
  290. )
  291. def snapshot(toml_path, overwrite, merge, update_version, verbose):
  292.     """
  293.    Create or update a TOML file containing installed packages using mpm.
  294.  
  295.    Example usage: `mpm snapshot`
  296.    """
  297.     if not toml_path:
  298.         toml_path = os.path.expanduser("~/.config/mpm/packages.toml")
  299.  
  300.     # If the TOML file does not exist or is empty, prompt the user to create a new snapshot or correct the TOML file manually.
  301.     if not os.path.exists(toml_path) or os.path.getsize(toml_path) == 0:
  302.         console.print(
  303.             f"{COLORS['red']}Error: {toml_path} does not exist or is empty. Create a new snapshot or correct the TOML file manually.{COLORS['reset']}"
  304.         )
  305.         return
  306.  
  307.     try:
  308.         with open(toml_path, "rb") as toml_file:
  309.             tomli.load(toml_file)
  310.     except tomli.TOMLDecodeError as e:
  311.         console.print(
  312.             f"{COLORS['red']}Error: Invalid TOML data in {toml_path}. {str(e)}{COLORS['reset']}"
  313.         )
  314.         return
  315.  
  316.     command = ["mpm", "snapshot"]
  317.  
  318.     if overwrite:
  319.         command.append("--overwrite")
  320.  
  321.     if merge:
  322.         command.append("--merge")
  323.  
  324.     if update_version:
  325.         command.append("--update-version")
  326.  
  327.     command.append(toml_path)
  328.  
  329.     if verbose:
  330.         console.print(click.style("Running the following command:", fg="cyan"))
  331.         console.print(click.style(f"  {' '.join(command)}", fg="cyan"))
  332.         console.print("\n")
  333.  
  334.     run_command(command)
  335.  
  336.  
  337. @cli.command()
  338. def extra_options():
  339.     """
  340.    Run a command with extra options using mpm.
  341.  
  342.    Example usage: `mpm extra-options`
  343.    """
  344.     allowed_extra_options = ManagerPool.ALLOWED_EXTRA_OPTION
  345.     console.print("Allowed extra options:")
  346.     for option in allowed_extra_options:
  347.         console.print(f"- {option}")
  348.  
  349.     selected_options = input("Enter the extra options you want to run (separated by spaces): ")
  350.     extra_options = selected_options.split()
  351.  
  352.     for option in extra_options:
  353.         if option not in allowed_extra_options:
  354.             console.print(f"Invalid extra option: {option}")
  355.             return
  356.     command = ["mpm", "list", "--output-format", "table"]
  357.     command.extend(extra_options)
  358.     run_command(command)
  359.  
  360.  
  361. @cli.command()
  362. def console_print():
  363.     """
  364.    List all installed packages using mpm.
  365.  
  366.    Example usage: `mpm console-print`
  367.    """
  368.     command = ["mpm", "installed"]
  369.     run_command(command)
  370.  
  371.  
  372. @cli.command()
  373. def precise_search():
  374.     """
  375.    Search for a specific package using mpm.
  376.  
  377.    Example usage: `mpm precise-search`
  378.    """
  379.     manager_pool = ManagerPool()
  380.     package_manager = PackageManager(manager_pool)
  381.     pool = package_manager.get_pool()
  382.     package_name = input("Enter package name to search for: ")
  383. results = pool.search(package_name)
  384. print_table(results, fields=SORTABLE_FIELDS)
  385.  
  386. if name == "main":
  387. cli()
  388.  
  389.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement