API Reference

Core

resqui.core

Context dataclass

A basic context to hold

Source code in src/resqui/core.py
@dataclass(frozen=True)
class Context:
    """A basic context to hold"""

    github_token: Optional[str] = None
    dashverse_token: Optional[str] = None

CheckResult dataclass

Datatype for indicator check results.

Source code in src/resqui/core.py
@dataclass
class CheckResult:
    """
    Datatype for indicator check results.
    """

    process: str = "Undefined process"
    status_id: str = "missing"
    output: str = "missing"
    evidence: str = "missing"
    success: bool = False

    def __bool__(self):
        return self.success

Summary

Summary of the software quality assessment.

Source code in src/resqui/core.py
class Summary:
    """
    Summary of the software quality assessment.
    """

    def __init__(
        self,
        author,
        email,
        project_name,
        repo_url,
        software_version,
        branch_hash_or_tag,
    ):
        self.author = author
        self.email = email
        self.project_name = project_name
        self.repo_url = repo_url
        self.software_version = software_version
        self.branch_hash_or_tag = branch_hash_or_tag
        self.checks = []

    def add_indicator_result(self, indicator, checking_software, result):
        self.checks.append(
            {
                "@type": "CheckResult",
                "assessesIndicator": {"@id": indicator["@id"]},
                "checkingSoftware": {
                    "name": checking_software.name,
                    "version": checking_software.version,
                },
                "process": result.process,
                "status": {"@id": result.status_id},
                "output": result.output,
                "evidence": result.evidence,
            }
        )

    def to_json(self):
        return json.dumps(
            {
                "@context": "https://w3id.org/everse/rsqa/0.0.1/",
                "@type": "SoftwareQualityAssessment",
                "dateCreated": str(datetime.now()),
                "license": "CC0-1.0",
                "author": {"@type": "Person", "name": "Quality Pipeline"},
                "assessedSoftware": {
                    "@type": "SoftwareApplication",
                    "name": self.project_name,
                    "softwareVersion": self.software_version,
                    "url": self.repo_url,
                },
                "checks": self.checks,
            },
            sort_keys=True,
            indent=4,
        )

    def write(self, filename):
        with open(filename, "w") as f:
            f.write(self.to_json())

    def upload(self, dashverse_token=None):
        api = APIClient(dashverse_token)
        api.post(self.to_json())

Configuration

resqui.config

Configuration

A basic wrapper for the configuration.

Source code in src/resqui/config.py
class Configuration:
    """
    A basic wrapper for the configuration.
    """

    def __init__(self, filepath=None):
        if filepath is None:
            print("Loading default configuration.")
            self._cfg = DEFAULT_CONFIG
        else:
            print(f"Loading configuration from '{filepath}'.")
            with open(filepath) as f:
                self._cfg = json.load(f)

Plugins

resqui.plugins.base

IndicatorPlugin

Skeleton for an Indicator Plugin

Source code in src/resqui/plugins/base.py
class IndicatorPlugin:
    """Skeleton for an Indicator Plugin"""

    name = None
    version = None
    id = None
    indicators = []

PluginInitError

Bases: Exception

Thrown if the initialisation of a plugin fails (e.g. missing GITHUB token)

Source code in src/resqui/plugins/base.py
class PluginInitError(Exception):
    """Thrown if the initialisation of a plugin fails (e.g. missing GITHUB token)"""

    pass

Executors

resqui.executors.python

PythonExecutor

A Python executor which uses a temporary virtual environment.

The packages should be a list of package names with optional requirement specifiers as accepted by pip.

More information: https://packaging.python.org/en/latest/glossary/#term-Requirement-Specifier

Source code in src/resqui/executors/python.py
class PythonExecutor:
    """A Python executor which uses a temporary virtual environment.

    The `packages` should be a list of package names with optional
    requirement specifiers as accepted by `pip`.

    More information:
    https://packaging.python.org/en/latest/glossary/#term-Requirement-Specifier

    """

    def __init__(self, packages=None, environment=None):
        """Instantiates a virtual environment in a temporary folder."""
        self.temp_dir = tempfile.mkdtemp()
        self.environment = environment if environment is not None else {}
        try:
            venv.create(self.temp_dir, with_pip=True)
            if packages is None:
                return
            for package in packages:
                self.install(package)
        except (FileNotFoundError, subprocess.CalledProcessError) as e:
            raise ExecutorInitError(f"failed to initialise Python executor: {e}")

    def install(self, package):
        try:
            subprocess.run(
                [f"{self.temp_dir}/bin/pip", "install", package],
                check=True,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
            )
        except (FileNotFoundError, subprocess.CalledProcessError) as e:
            raise ExecutorInitError(f"failed to initialise Python executor: {e}")

    def is_installed(self, package_name, version=None):
        out = self.execute(
            normalized(
                """
            from importlib.metadata import distributions
            for dist in distributions():
                name = dist.metadata.get('Name', '<unknown>')
                version = getattr(dist, 'version', '<unknown>')
                print(f"{name}=={version}")
        """
            )
        )
        package = package_name + "" if version is None else f"=={version}"
        return package in out.stdout

    def execute(self, script):
        """Run the script in the virtual environment."""
        env = os.environ.copy()
        env.update(self.environment)
        return subprocess.run(
            [f"{self.temp_dir}/bin/python", "-c", script],
            capture_output=True,
            text=True,
            env=env,
        )

    def __del__(self):
        """Cleanup the temporary virtual environment on destruction."""
        try:
            if os.path.exists(self.temp_dir):
                shutil.rmtree(self.temp_dir)
        except Exception as e:
            print(f"Failed to remove virtualenv at {self.temp_dir}: {e}")

__del__()

Cleanup the temporary virtual environment on destruction.

Source code in src/resqui/executors/python.py
def __del__(self):
    """Cleanup the temporary virtual environment on destruction."""
    try:
        if os.path.exists(self.temp_dir):
            shutil.rmtree(self.temp_dir)
    except Exception as e:
        print(f"Failed to remove virtualenv at {self.temp_dir}: {e}")

__init__(packages=None, environment=None)

Instantiates a virtual environment in a temporary folder.

Source code in src/resqui/executors/python.py
def __init__(self, packages=None, environment=None):
    """Instantiates a virtual environment in a temporary folder."""
    self.temp_dir = tempfile.mkdtemp()
    self.environment = environment if environment is not None else {}
    try:
        venv.create(self.temp_dir, with_pip=True)
        if packages is None:
            return
        for package in packages:
            self.install(package)
    except (FileNotFoundError, subprocess.CalledProcessError) as e:
        raise ExecutorInitError(f"failed to initialise Python executor: {e}")

execute(script)

Run the script in the virtual environment.

Source code in src/resqui/executors/python.py
def execute(self, script):
    """Run the script in the virtual environment."""
    env = os.environ.copy()
    env.update(self.environment)
    return subprocess.run(
        [f"{self.temp_dir}/bin/python", "-c", script],
        capture_output=True,
        text=True,
        env=env,
    )

resqui.executors.docker

DockerExecutor

A Docker executor.

Source code in src/resqui/executors/docker.py
class DockerExecutor:
    """A Docker executor."""

    def __init__(self, image_url, pull_args=None):
        self.url = image_url
        if pull_args is None:
            pull_args = []
        command = ["docker", "pull"] + pull_args + [self.url]
        try:
            subprocess.run(
                command,
                check=True,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
            )
        except subprocess.CalledProcessError:
            raise ExecutorInitError(
                f"failed to initialise Docker executor: '{' '.join(command)}'"
            )

    def run(self, command, run_args=None):
        """
        Run command (popenargs) inside a Docker container and return a
        CompletedProcess instance from the subprocess Python module.

        Extra arguments to the command can be passed as a list of
        strings via `run_args`.
        """
        if run_args is None:
            run_args = []
        cmd = ["docker", "run"] + run_args + [self.url] + command
        return subprocess.run(cmd, capture_output=True, text=True)

run(command, run_args=None)

Run command (popenargs) inside a Docker container and return a CompletedProcess instance from the subprocess Python module.

Extra arguments to the command can be passed as a list of strings via run_args.

Source code in src/resqui/executors/docker.py
def run(self, command, run_args=None):
    """
    Run command (popenargs) inside a Docker container and return a
    CompletedProcess instance from the subprocess Python module.

    Extra arguments to the command can be passed as a list of
    strings via `run_args`.
    """
    if run_args is None:
        run_args = []
    cmd = ["docker", "run"] + run_args + [self.url] + command
    return subprocess.run(cmd, capture_output=True, text=True)

resqui.executors.base

ExecutorInitError

Bases: Exception

Thrown if the initialisation of an execution fails (e.g. Docker not installed)

Source code in src/resqui/executors/base.py
class ExecutorInitError(Exception):
    """Thrown if the initialisation of an execution fails (e.g. Docker not installed)"""

    pass