Module cli_tool_audit.models
This module contains dataclasses for the tool audit.
Expand source code
"""
This module contains dataclasses for the tool audit.
"""
import datetime
import enum
import hashlib
from dataclasses import asdict, dataclass
from typing import Optional
class SchemaType(enum.Enum):
SNAPSHOT = "snapshot"
"""Version is entire output of --version switch. Compatibility is by exact match."""
SEMVER = "semver"
"""Major, minor, patch, pre-release, and build metadata. Compatibility is by specification."""
PEP440 = "pep440"
"""Superficially looks like a superset of semver. As governed by PEP440."""
EXISTENCE = "existence"
"""Only check if the tool exists. No version check. If it exists, it is compatible."""
def __str__(self):
return self.value
@dataclass
class CliToolConfig:
"""
Represents what tool and what version the user wants to audit on their system.
"""
name: str
"""Tool name without path"""
version: Optional[str] = None
"""Desired version"""
version_switch: Optional[str] = None
"""Command line switch to get version, e.g. -V, --version, etc."""
schema: Optional[SchemaType] = None
"""Snapshot, semver, pep440, existence"""
if_os: Optional[str] = None
"""Which OS this tool is needed for. Single value. Comparison evaluated by prefix. Same values as sys.platform"""
tags: Optional[list[str]] = None
install_command: Optional[str] = None
"""Having failed to find the right version what command can be run. Let the user run it."""
install_docs: Optional[str] = None
"""For failed tool checks, where can the user find out how to install."""
def cache_hash(self) -> str:
"""
Generate a hash for a CliToolConfig instance.
Returns:
str: The hash of the tool configuration.
"""
config_str = ""
for key, value in asdict(self).items():
config_str += f"{key}={value};" # Concatenate key-value pairs
# Use hashlib to compute an MD5 hash of the concatenated string
return hashlib.md5(config_str.encode()).hexdigest() # nosec
@dataclass
class ToolCheckResult:
"""
Represents the result of a tool check and version check.
"""
tool: str
desired_version: str
is_needed_for_os: bool
is_available: bool
is_snapshot: bool
"""Snapshot schema"""
found_version: Optional[str]
"""Same as snapshot_version, the exact text produced by --version switch."""
parsed_version: Optional[str]
"""Clean stringification of the version object for current schema."""
is_compatible: str
is_broken: bool
last_modified: Optional[datetime.datetime]
tool_config: CliToolConfig
def status(self) -> str:
"""Compress many status fields into one for display
Returns:
str: The status of the tool.
"""
# Status compression
# Wrong OS. If wrong OS "Wrong OS"
# Found/Not. If not found "Not Found"
# If schema = existence, "Found"
# Runnable/Not. If not, "Not Runnable"
# Compatible/Not. If yes, "Compatible"
# If no "Incompatible"
if not self.is_needed_for_os:
status = "Wrong OS"
elif not self.is_available:
status = "Not available"
# need schema!
elif self.tool_config.schema == "existence":
status = "Available"
elif self.is_broken:
status = "Can't run"
else:
status = self.is_compatible
return status
def is_problem(self) -> bool:
"""Is this tool's state a problem?
If it is needed for this OS, if it is incompatible or unavailable, it is a problem.
Returns:
bool: True if this tool's state is a problem, False otherwise.
"""
missing_or_incompatible = self.is_compatible != "Compatible" or not self.is_available
return self.is_needed_for_os and missing_or_incompatible
@dataclass
class ToolAvailabilityResult:
"""
Represents only if the tool is available or not.
"""
is_available: bool
is_broken: bool
version: Optional[str]
"""Desired version"""
last_modified: Optional[datetime.datetime]
if __name__ == "__main__":
# Example usage
config = CliToolConfig(name="example_tool", version="1.0.0")
print(config.cache_hash())
Classes
class CliToolConfig (name: str, version: Optional[str] = None, version_switch: Optional[str] = None, schema: Optional[SchemaType] = None, if_os: Optional[str] = None, tags: Optional[list[str]] = None, install_command: Optional[str] = None, install_docs: Optional[str] = None)-
Represents what tool and what version the user wants to audit on their system.
Expand source code
@dataclass class CliToolConfig: """ Represents what tool and what version the user wants to audit on their system. """ name: str """Tool name without path""" version: Optional[str] = None """Desired version""" version_switch: Optional[str] = None """Command line switch to get version, e.g. -V, --version, etc.""" schema: Optional[SchemaType] = None """Snapshot, semver, pep440, existence""" if_os: Optional[str] = None """Which OS this tool is needed for. Single value. Comparison evaluated by prefix. Same values as sys.platform""" tags: Optional[list[str]] = None install_command: Optional[str] = None """Having failed to find the right version what command can be run. Let the user run it.""" install_docs: Optional[str] = None """For failed tool checks, where can the user find out how to install.""" def cache_hash(self) -> str: """ Generate a hash for a CliToolConfig instance. Returns: str: The hash of the tool configuration. """ config_str = "" for key, value in asdict(self).items(): config_str += f"{key}={value};" # Concatenate key-value pairs # Use hashlib to compute an MD5 hash of the concatenated string return hashlib.md5(config_str.encode()).hexdigest() # nosecClass variables
var if_os : Optional[str]-
Which OS this tool is needed for. Single value. Comparison evaluated by prefix. Same values as sys.platform
var install_command : Optional[str]-
Having failed to find the right version what command can be run. Let the user run it.
var install_docs : Optional[str]-
For failed tool checks, where can the user find out how to install.
var name : str-
Tool name without path
var schema : Optional[SchemaType]-
Snapshot, semver, pep440, existence
var version : Optional[str]-
Desired version
var version_switch : Optional[str]-
Command line switch to get version, e.g. -V, –version, etc.
Methods
def cache_hash(self) ‑> str-
Generate a hash for a CliToolConfig instance.
Returns
str- The hash of the tool configuration.
Expand source code
def cache_hash(self) -> str: """ Generate a hash for a CliToolConfig instance. Returns: str: The hash of the tool configuration. """ config_str = "" for key, value in asdict(self).items(): config_str += f"{key}={value};" # Concatenate key-value pairs # Use hashlib to compute an MD5 hash of the concatenated string return hashlib.md5(config_str.encode()).hexdigest() # nosec
class SchemaType (*args, **kwds)-
Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access::
>>> Color.RED <Color.RED: 1>- value lookup:
>>> Color(1) <Color.RED: 1>- name lookup:
>>> Color['RED'] <Color.RED: 1>Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Expand source code
class SchemaType(enum.Enum): SNAPSHOT = "snapshot" """Version is entire output of --version switch. Compatibility is by exact match.""" SEMVER = "semver" """Major, minor, patch, pre-release, and build metadata. Compatibility is by specification.""" PEP440 = "pep440" """Superficially looks like a superset of semver. As governed by PEP440.""" EXISTENCE = "existence" """Only check if the tool exists. No version check. If it exists, it is compatible.""" def __str__(self): return self.valueAncestors
- enum.Enum
Class variables
var EXISTENCE-
Only check if the tool exists. No version check. If it exists, it is compatible.
var PEP440-
Superficially looks like a superset of semver. As governed by PEP440.
var SEMVER-
Major, minor, patch, pre-release, and build metadata. Compatibility is by specification.
var SNAPSHOT-
Version is entire output of –version switch. Compatibility is by exact match.
class ToolAvailabilityResult (is_available: bool, is_broken: bool, version: Optional[str], last_modified: Optional[datetime.datetime])-
Represents only if the tool is available or not.
Expand source code
@dataclass class ToolAvailabilityResult: """ Represents only if the tool is available or not. """ is_available: bool is_broken: bool version: Optional[str] """Desired version""" last_modified: Optional[datetime.datetime]Class variables
var is_available : boolvar is_broken : boolvar last_modified : Optional[datetime.datetime]var version : Optional[str]-
Desired version
class ToolCheckResult (tool: str, desired_version: str, is_needed_for_os: bool, is_available: bool, is_snapshot: bool, found_version: Optional[str], parsed_version: Optional[str], is_compatible: str, is_broken: bool, last_modified: Optional[datetime.datetime], tool_config: CliToolConfig)-
Represents the result of a tool check and version check.
Expand source code
@dataclass class ToolCheckResult: """ Represents the result of a tool check and version check. """ tool: str desired_version: str is_needed_for_os: bool is_available: bool is_snapshot: bool """Snapshot schema""" found_version: Optional[str] """Same as snapshot_version, the exact text produced by --version switch.""" parsed_version: Optional[str] """Clean stringification of the version object for current schema.""" is_compatible: str is_broken: bool last_modified: Optional[datetime.datetime] tool_config: CliToolConfig def status(self) -> str: """Compress many status fields into one for display Returns: str: The status of the tool. """ # Status compression # Wrong OS. If wrong OS "Wrong OS" # Found/Not. If not found "Not Found" # If schema = existence, "Found" # Runnable/Not. If not, "Not Runnable" # Compatible/Not. If yes, "Compatible" # If no "Incompatible" if not self.is_needed_for_os: status = "Wrong OS" elif not self.is_available: status = "Not available" # need schema! elif self.tool_config.schema == "existence": status = "Available" elif self.is_broken: status = "Can't run" else: status = self.is_compatible return status def is_problem(self) -> bool: """Is this tool's state a problem? If it is needed for this OS, if it is incompatible or unavailable, it is a problem. Returns: bool: True if this tool's state is a problem, False otherwise. """ missing_or_incompatible = self.is_compatible != "Compatible" or not self.is_available return self.is_needed_for_os and missing_or_incompatibleClass variables
var desired_version : strvar found_version : Optional[str]-
Same as snapshot_version, the exact text produced by –version switch.
var is_available : boolvar is_broken : boolvar is_compatible : strvar is_needed_for_os : boolvar is_snapshot : bool-
Snapshot schema
var last_modified : Optional[datetime.datetime]var parsed_version : Optional[str]-
Clean stringification of the version object for current schema.
var tool : strvar tool_config : CliToolConfig
Methods
def is_problem(self) ‑> bool-
Is this tool's state a problem? If it is needed for this OS, if it is incompatible or unavailable, it is a problem.
Returns
bool- True if this tool's state is a problem, False otherwise.
Expand source code
def is_problem(self) -> bool: """Is this tool's state a problem? If it is needed for this OS, if it is incompatible or unavailable, it is a problem. Returns: bool: True if this tool's state is a problem, False otherwise. """ missing_or_incompatible = self.is_compatible != "Compatible" or not self.is_available return self.is_needed_for_os and missing_or_incompatible def status(self) ‑> str-
Compress many status fields into one for display
Returns
str- The status of the tool.
Expand source code
def status(self) -> str: """Compress many status fields into one for display Returns: str: The status of the tool. """ # Status compression # Wrong OS. If wrong OS "Wrong OS" # Found/Not. If not found "Not Found" # If schema = existence, "Found" # Runnable/Not. If not, "Not Runnable" # Compatible/Not. If yes, "Compatible" # If no "Incompatible" if not self.is_needed_for_os: status = "Wrong OS" elif not self.is_available: status = "Not available" # need schema! elif self.tool_config.schema == "existence": status = "Available" elif self.is_broken: status = "Can't run" else: status = self.is_compatible return status