Skip to content

Commit

Permalink
Restructure project and support all arch info in CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
dormant-user committed Jan 1, 2025
1 parent ac33c71 commit c455ec2
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 19 deletions.
53 changes: 42 additions & 11 deletions pyarchitecture/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
import time

from pyarchitecture import disks
from pyarchitecture import cpu, disks, gpu

version = "0.0.0-a0"

Expand All @@ -13,16 +13,24 @@ def commandline() -> None:
**Flags**
- ``--version | -V``: Prints the version.
- ``--help | -H``: Prints the help section.
- ``print``: Prints the disk information in terminal.
- ``save``: Saves the disk information into a JSON file.
- ``--filename``: Filename to store the disk information.
- ``disk``: Prints the disk information in the terminal.
- ``cpu``: Prints the CPU name in the terminal.
- ``gpu``: Prints the GPU information in the terminal.
- ``save``: Saves the chosen information into a JSON file.
- ``--filename``: Filename to store the information.
"""
assert sys.argv[0].lower().endswith("pyarchitecture"), "Invalid commandline trigger!!"
assert (
sys.argv[0].lower().endswith("pyarchitecture")
), "Invalid commandline trigger!!"

print_ver = "--version" in sys.argv or "-V" in sys.argv
get_help = "--help" in sys.argv or "-H" in sys.argv
disk_info = "disk" in sys.argv
cpu_info = "cpu" in sys.argv
gpu_info = "gpu" in sys.argv
all_info = "all" in sys.argv
save_info = "save" in sys.argv

filename = None
custom_filename = "--filename" in sys.argv
if custom_filename:
Expand All @@ -41,8 +49,10 @@ def commandline() -> None:
"--version | -V": "Prints the version.",
"--help | -H": "Prints the help section.",
"disk": "Prints the disk information in the terminal.",
"save": "Saves the disk information into a JSON file.",
"--filename": "Filename to store the disk information.",
"cpu": "Prints the CPU name in the terminal.",
"gpu": "Prints the GPU information in the terminal.",
"save": "Saves the chosen information into a JSON file.",
"--filename": "Filename to store the information.",
}
# weird way to increase spacing to keep all values monotonic
_longest_key = len(max(options.keys()))
Expand All @@ -56,15 +66,36 @@ def commandline() -> None:
print(f"PyArchitecture {version}")
sys.exit(0)

if disk_info:
if disk_info and not save_info:
for disk in disks.get_all_disks():
print(disk)
sys.exit(0)
elif save_info:
if cpu_info and not save_info:
print(cpu.get_cpu_name())
sys.exit(0)
if gpu_info and not save_info:
print(gpu.get_gpu_names())
sys.exit(0)

if save_info:
filename = filename or f"PyArchitecture_{int(time.time())}.json"
if all_info:
data = {
"Disks": disks.get_all_disks(),
"CPU": cpu.get_cpu_name(),
"GPU": gpu.get_gpu_names(),
}
else:
data = {}
if cpu_info:
data["CPU"] = cpu.get_cpu_name()
if gpu_info:
data["GPU"] = gpu.get_gpu_names()
if disk_info:
data["Disks"] = disks.get_all_disks()
with open(filename, "w") as file:
json.dump(disks.get_all_disks(), file, indent=2)
print(f"Physical disks' information has been stored in {filename!r}")
json.dump(data, file, indent=2)
print(f"Architecture information has been stored in {filename!r}")
sys.exit(0)
else:
get_help = True
Expand Down
36 changes: 36 additions & 0 deletions pyarchitecture/cpu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import logging
import os

from pyarchitecture import models
from pyarchitecture.cpu import main

LOGGER = logging.getLogger(__name__)


def _get_cpu_lib(user_input: str | os.PathLike) -> str:
"""Get the CPU library for the appropriate OS.
Args:
user_input: CPU library input by user.
"""
cpu_lib = (
user_input
or os.environ.get("cpu_lib")
or os.environ.get("CPU_LIB")
or models.default_cpu_lib()[models.OPERATING_SYSTEM]
)
assert os.path.isfile(cpu_lib), f"CPU library {cpu_lib!r} doesn't exist"
return cpu_lib


def get_cpu_name(cpu_lib: str | os.PathLike = None) -> str:
"""OS-agnostic function to get all CPUs connected to the host system.
Args:
cpu_lib: Custom CPU library path.
Returns:
List[Dict[str, str]]:
Returns CPU name.
"""
return main.get_name(_get_cpu_lib(cpu_lib))
46 changes: 46 additions & 0 deletions pyarchitecture/cpu/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import logging
import os
import subprocess

from pyarchitecture import models

LOGGER = logging.getLogger(__name__)


def _darwin(cpu_lib: str | os.PathLike) -> str:
"""Get processor information for macOS."""
command = [cpu_lib, "-n", "machdep.cpu.brand_string"]
return subprocess.check_output(command).decode().strip()


def _linux(cpu_lib: str | os.PathLike) -> str:
"""Get processor information for Linux."""
with open(cpu_lib) as file:
for line in file:
if "model name" in line:
return line.split(":")[1].strip()


def _windows(cpu_lib: str | os.PathLike) -> str:
"""Get processor information for Windows."""
command = f"{cpu_lib} cpu get name"
output = subprocess.check_output(command, shell=True).decode()
return output.strip().split("\n")[1]


def get_name(cpu_lib: str | os.PathLike) -> str | None:
"""Get processor information for the host operating system.
Returns:
str:
Returns the processor information as a string.
"""
os_map = {
models.OperatingSystem.darwin: _darwin,
models.OperatingSystem.linux: _linux,
models.OperatingSystem.windows: _windows,
}
try:
return os_map[models.OPERATING_SYSTEM](cpu_lib)
except Exception as error:
LOGGER.error(error)
16 changes: 8 additions & 8 deletions pyarchitecture/disks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@
import os
from typing import Dict, List

from . import linux, macOS, models, windows
from pyarchitecture import models
from pyarchitecture.disks import linux, macOS, windows

LOGGER = logging.getLogger(__name__)


def get_disk_lib(user_input: str | os.PathLike) -> str:
def _get_disk_lib(user_input: str | os.PathLike) -> str:
"""Get the disk library for the appropriate OS.
Args:
user_input: Disk library input by user.
"""
disk_lib = (
user_input
or os.environ.get("disk_lib")
or os.environ.get("DISK_LIB")
or models.default_disk_lib()[models.OPERATING_SYSTEM]
user_input
or os.environ.get("disk_lib")
or os.environ.get("DISK_LIB")
or models.default_disk_lib()[models.OPERATING_SYSTEM]
)
assert os.path.isfile(disk_lib), f"Disk library {disk_lib!r} doesn't exist"
return disk_lib
Expand All @@ -39,8 +40,7 @@ def get_all_disks(disk_lib: str | os.PathLike = None) -> List[Dict[str, str]]:
models.OperatingSystem.windows: windows.drive_info,
}
try:
disk_lib = get_disk_lib(disk_lib)
disk_lib = _get_disk_lib(disk_lib)
return os_map[models.OperatingSystem(models.OPERATING_SYSTEM)](disk_lib)
except Exception as error:
LOGGER.error(error)
return []
37 changes: 37 additions & 0 deletions pyarchitecture/gpu/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import logging
import os
from typing import Dict, List

from pyarchitecture import models
from pyarchitecture.gpu import main

LOGGER = logging.getLogger(__name__)


def _get_gpu_lib(user_input: str | os.PathLike) -> str:
"""Get the GPU library for the appropriate OS.
Args:
user_input: GPU library input by user.
"""
gpu_lib = (
user_input
or os.environ.get("gpu_lib")
or os.environ.get("GPU_LIB")
or models.default_gpu_lib()[models.OPERATING_SYSTEM]
)
assert os.path.isfile(gpu_lib), f"GPU library {gpu_lib!r} doesn't exist"
return gpu_lib


def get_gpu_names(gpu_lib: str | os.PathLike = None) -> List[Dict[str, str]]:
"""OS-agnostic function to get all GPUs connected to the host system.
Args:
gpu_lib: Custom GPU library path.
Returns:
List[Dict[str, str]]:
Returns the GPU model and vendor information as a list of key-value pairs.
"""
return main.get_names(_get_gpu_lib(gpu_lib))
124 changes: 124 additions & 0 deletions pyarchitecture/gpu/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import json
import logging
import os
import subprocess
from typing import Dict, List, Optional

from pyarchitecture import models

LOGGER = logging.getLogger(__name__)


def _darwin(gpu_lib: str | os.PathLike) -> Optional[List[Dict[str, str]]]:
"""Get GPU model and vendor information for Linux operating system.
Returns:
List[Dict[str, str]]:
Returns a list of GPU model and vendor information.
"""
result = subprocess.run(
[gpu_lib, "SPDisplaysDataType", "-json"],
capture_output=True,
text=True,
)
if result.stderr:
LOGGER.debug(result.stderr)
return
displays = json.loads(result.stdout).get("SPDisplaysDataType", [])
gpu_info = []
for display in displays:
if "sppci_model" in display.keys():
gpu_info.append(
dict(
model=display.get("sppci_model"),
cores=display.get("sppci_cores", "N/A"),
memory=display.get(
"sppci_vram", display.get("spdisplays_vram", "N/A")
),
vendor=display.get("sppci_vendor", "N/A"),
)
)
return gpu_info


def _linux(gpu_lib: str | os.PathLike) -> Optional[List[Dict[str, str]]]:
"""Get GPU model and vendor information for Linux operating system.
Returns:
List[Dict[str, str]]:
Returns a list of GPU model and vendor information.
"""
result = subprocess.run(
[gpu_lib],
capture_output=True,
text=True,
)
if result.stderr:
LOGGER.debug(result.stderr)
return
gpus = result.stdout.splitlines()
gpu_info = []
for line in gpus:
if "VGA" in line:
gpu = line.split(":")[-1].strip()
else:
continue
gpu_info.append(
dict(
model=gpu.split(":")[-1].strip(),
)
)
return gpu_info


def _windows(gpu_lib: str | os.PathLike) -> Optional[List[Dict[str, str]]]:
"""Get GPU model and vendor information for Windows operating system.
Returns:
List[Dict[str, str]]:
Returns a list of GPU model and vendor information.
"""
result = subprocess.run(
[
gpu_lib,
"path",
"win32_videocontroller",
"get",
"Name,AdapterCompatibility",
"/format:csv",
],
stdout=subprocess.PIPE,
text=True,
)
if result.stderr:
LOGGER.debug(result.stderr)
return
gpus_raw = [line for line in result.stdout.splitlines() if line.strip()]
try:
keys = (
gpus_raw[0]
.replace("Node", "node")
.replace("AdapterCompatibility", "vendor")
.replace("Name", "model")
.split(",")
)
values = "".join(gpus_raw[1:]).split(",")
except ValueError as error:
LOGGER.debug(error)
return
if len(values) >= len(keys):
result = []
for i in range(0, len(values), len(keys)):
result.append(dict(zip(keys, values[i : i + len(keys)]))) # noqa: E203
return result
else:
LOGGER.debug("ValueError: Not enough values for the keys")


def get_names(gpu_lib: str | os.PathLike) -> List[Dict[str, str]]:
"""Get list of GPU model and vendor information based on the operating system."""
fn_map = dict(linux=_linux, darwin=_darwin, windows=_windows)
try:
return fn_map[models.OPERATING_SYSTEM](gpu_lib)
except (subprocess.SubprocessError, FileNotFoundError) as error:
LOGGER.debug(error)
Loading

0 comments on commit c455ec2

Please sign in to comment.