Commit fb7c23cb authored by Adrian DC's avatar Adrian DC
Browse files

style: resolve #170: implement simple standard Python typings

parent 65683a5c
Loading
Loading
Loading
Loading
Loading
+23 −17
Changes for gitlabci_local/cli/main.py: 23 added lines, 17 removed lines.
Original line number Diff line number Diff line
#!/usr/bin/env python3

# Standard libraries
from argparse import ArgumentParser, RawTextHelpFormatter, SUPPRESS
from argparse import (_ArgumentGroup, _MutuallyExclusiveGroup, ArgumentParser, Namespace,
                      RawTextHelpFormatter, SUPPRESS)
from os import environ
from pathlib import Path
from subprocess import check_output, DEVNULL, Popen
from sys import argv, exit as sys_exit
from typing import List, Optional

# Components
from ..engines.engine import supported as engine_supported
@@ -23,24 +25,26 @@ from ..prints.colors import Colors
from ..system.platform import Platform

# Main, pylint: disable=too-many-branches,too-many-locals,too-many-statements
def main():
def main() -> None:

    # Variables
    hint = ''
    interactive = Platform.IS_TTY_STDIN and Platform.IS_TTY_STDOUT
    result = False
    group: _ArgumentGroup
    hint: str = ''
    interactive: bool = Platform.IS_TTY_STDIN and Platform.IS_TTY_STDOUT
    result: bool = False
    subgroup: _MutuallyExclusiveGroup

    # Arguments creation
    parser = ArgumentParser(
    parser: ArgumentParser = ArgumentParser(
        prog=Bundle.NAME, description=
        f'{Bundle.NAME}: Launch {Bundle.CONFIGURATION} jobs locally (aliases: {Bundle.ALIAS})',
        add_help=False, formatter_class=RawTextHelpFormatter)

    # Arguments default definitions
    tags_default = ['deploy', 'local', 'publish']
    tags_default: List[str] = ['deploy', 'local', 'publish']

    # Arguments enumerations definitions
    networks_enum = ['bridge', 'host', 'none']
    networks_enum: List[str] = ['bridge', 'host', 'none']

    # Arguments internal definitions
    group = parser.add_argument_group('internal arguments')
@@ -160,7 +164,7 @@ def main():
        'Regex names is supported unless --no-regex is used')

    # Arguments parser
    options = parser.parse_args()
    options: Namespace = parser.parse_args()

    # Help informations
    if options.help:
@@ -171,7 +175,7 @@ def main():
        sys_exit(0)

    # Instantiate settings
    settings = Settings(Bundle.NAME)
    settings: Settings = Settings(name=Bundle.NAME)

    # Settings informations
    if options.settings:
@@ -179,7 +183,7 @@ def main():
        sys_exit(0)

    # Instantiate updates
    updates = Updates(Bundle.NAME, settings)
    updates: Updates = Updates(name=Bundle.NAME, settings=settings)

    # Version informations
    if options.version:
@@ -207,11 +211,11 @@ def main():
        environ[Bundle.ENV_ENGINE] = options.engine
        options.engine_default = False
    else:
        options.engine = settings.get('engines', 'engine')
        options.engine = settings.get(group='engines', key='engine')
        options.engine_default = True
        if not options.engine:
            options.engine = ','.join(engine_supported())
            settings.set('engines', 'engine', options.engine)
            settings.set(group='engines', key='engine', value=options.engine)

    # Prepare no_git_safeties
    if not options.no_git_safeties and Bundle.ENV_NO_GIT_SAFETIES in environ:
@@ -258,7 +262,7 @@ def main():

    # Pull jobs images
    elif options.pull:
        result = ImagesFeature(jobs=jobs, options=options).pull(force=options.force)
        result = ImagesFeature(jobs=jobs, options=options).pull()

    # Remove jobs images
    elif options.rmi:
@@ -301,14 +305,16 @@ def main():

            # Prepare WinPTY variables
            hint = ' (on Windows, winpty is required)'
            winpty = None
            winpty: Optional[str] = None
            if Bundle.ENV_WINPTY_PATH in environ:
                winpty = environ[Bundle.ENV_WINPTY_PATH]

            # Acquire WinPTY path
            try:
                if not winpty:
                    winpty = check_output(['where', 'winpty.exe'], stderr=DEVNULL).strip()
                    winpty = str(
                        check_output(args=['where', 'winpty.exe'],
                                     stderr=DEVNULL).strip())
            except FileNotFoundError: # pragma: no cover
                pass
            else:
@@ -317,7 +323,7 @@ def main():
                _environ = dict(environ)
                _environ[Bundle.ENV_WINPTY] = 'true'
                try:
                    with Popen([winpty] + argv if winpty else argv,
                    with Popen(args=[winpty] + argv if winpty else argv,
                               env=_environ) as process:
                        process.wait()
                        sys_exit(process.returncode)
+18 −18
Changes for gitlabci_local/engines/docker.py: 18 added lines, 18 removed lines.
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
from os import environ

# Modules libraries
from docker import from_env
from docker import DockerClient, from_env
from docker.errors import APIError, DockerException, ImageNotFound

# Components
@@ -16,16 +16,16 @@ from .interface import Interface
class Docker(Interface):

    # Constants
    ENV_DOCKER_CERT_PATH = 'DOCKER_CERT_PATH'
    ENV_DOCKER_HOST = 'DOCKER_HOST'
    ENV_DOCKER_TLS_VERIFY = 'DOCKER_TLS_VERIFY'
    ENV_DOCKER_CERT_PATH: str = 'DOCKER_CERT_PATH'
    ENV_DOCKER_HOST: str = 'DOCKER_HOST'
    ENV_DOCKER_TLS_VERIFY: str = 'DOCKER_TLS_VERIFY'

    # Members
    __client = None
    __client: DockerClient = None
    __container = None

    # Constructor
    def __init__(self):
    def __init__(self) -> None:

        # Prepare container
        self.__container = None
@@ -38,7 +38,7 @@ class Docker(Interface):
            raise ModuleNotFoundError() from None

    # Sockets, pylint: disable=no-self-use
    def __sockets(self, variables, volumes):
    def __sockets(self, variables, volumes) -> None:

        # Variables
        docker_host = ''
@@ -84,7 +84,7 @@ class Docker(Interface):
            )

    # Command exec, pylint: disable=no-self-use
    def cmd_exec(self):
    def cmd_exec(self) -> str:

        # Result
        return 'docker exec -it'
@@ -97,13 +97,13 @@ class Docker(Interface):
        return self.__container.name

    # Exec
    def exec(self, command):
    def exec(self, command: str):

        # Execute command in container
        return self.__container.exec_run(command)

    # Get
    def get(self, image):
    def get(self, image: str) -> None:

        # Validate image exists
        try:
@@ -120,7 +120,7 @@ class Docker(Interface):
        return self.__container.logs(stream=True)

    # Pull
    def pull(self, image, force=False):
    def pull(self, image: str, force: bool = False) -> None:

        # Force image removal
        if force:
@@ -157,7 +157,7 @@ class Docker(Interface):
        Platform.flush()

    # Remove
    def remove(self):
    def remove(self) -> None:

        # Remove container
        if self.__container:
@@ -165,7 +165,7 @@ class Docker(Interface):
            self.__container = None

    # Remove image
    def rmi(self, image):
    def rmi(self, image: str) -> None:

        # Remove image
        try:
@@ -175,7 +175,7 @@ class Docker(Interface):

    # Run, pylint: disable=too-many-arguments,unused-argument
    def run(self, image, command, entrypoint, variables, network, option_sockets,
            services, volumes, directory, temp_folder):
            services, volumes, directory, temp_folder) -> None:

        # Append sockets mounts
        if option_sockets:
@@ -199,16 +199,16 @@ class Docker(Interface):
        )

    # Stop
    def stop(self, timeout):
    def stop(self, timeout: int) -> None:

        # Stop container
        self.__container.stop(timeout=timeout)

    # Supports
    def supports(self, binary):
    def supports(self, binary: str) -> bool:

        # Variables
        exit_code = 1
        exit_code: int = 1

        # Validate binary support
        try:
@@ -220,7 +220,7 @@ class Docker(Interface):
        return exit_code == 0

    # Wait
    def wait(self):
    def wait(self) -> bool:

        # Wait container
        result = self.__container.wait()
+27 −24
Changes for gitlabci_local/engines/engine.py: 27 added lines, 24 removed lines.
Original line number Diff line number Diff line
#!/usr/bin/env python3

# Standard libraries
from argparse import Namespace
from enum import Enum
from typing import List

# Components
from .docker import Docker
@@ -17,28 +19,28 @@ class Backend(Enum):
class Names:

    # Constants
    AUTO = 'auto'
    DOCKER = 'docker'
    PODMAN = 'podman'
    AUTO: str = 'auto'
    DOCKER: str = 'docker'
    PODMAN: str = 'podman'

    # Defaults
    DEFAULTS = [
    DEFAULTS: List[str] = [
        PODMAN,
        DOCKER,
    ]

    # Getter
    @staticmethod
    def get(override):
    def get(override: str) -> List[str]:

        # Adapt override
        override = override.lower() if override else None
        override = override.lower() if override else ''

        # Handle engine overrides
        if override:
            auto = False
            names = []
            overrides = override.split(',')
            auto: bool = False
            names: List[str] = []
            overrides: List[str] = override.split(',')
            for item in overrides:
                if item:
                    if Names.AUTO == item:
@@ -57,7 +59,7 @@ class Names:
        return names

# Supported engines
def supported():
def supported() -> List[str]:
    return [Names.AUTO] + Names.DEFAULTS

# Engine class
@@ -65,13 +67,13 @@ class Engine:

    # Members
    __engine = None
    __name = None
    __name: str = ''

    # Constructor
    def __init__(self, options):
    def __init__(self, options: Namespace) -> None:

        # Acquire engine names
        names = Names.get(options.engine)
        names: List[str] = Names.get(options.engine)

        # Iterate through names
        for name in names:
@@ -99,7 +101,7 @@ class Engine:
            raise NotImplementedError('Unknown or unsupported container engine...')

    # Command exec
    def cmd_exec(self):
    def cmd_exec(self) -> str:
        return self.__engine.cmd_exec()

    # Container
@@ -112,7 +114,7 @@ class Engine:
        return self.__engine.exec(command)

    # Get
    def get(self, image):
    def get(self, image: str) -> None:
        self.__engine.get(image)

    # Logs
@@ -121,24 +123,25 @@ class Engine:

    # Name
    @property
    def name(self):
    def name(self) -> str:
        return self.__name

    # Pull
    def pull(self, image, force=False):
    def pull(self, image: str, force: bool = False) -> None:
        self.__engine.pull(image, force=force)

    # Remove
    def remove(self):
    def remove(self) -> None:
        self.__engine.remove()

    # Remove image
    def rmi(self, image):
    def rmi(self, image: str) -> None:
        self.__engine.rmi(image)

    # Run, pylint: disable=too-many-arguments
    def run(self, image, command, entrypoint, variables, network, option_sockets,
            services, volumes, directory, temp_folder):
    def run(self, image: str, command, entrypoint: str, variables, network: str,
            option_sockets: bool, services: bool, volumes, directory: str,
            temp_folder: str):
        return self.__engine.run(
            image=image,
            command=command,
@@ -153,13 +156,13 @@ class Engine:
        )

    # Stop
    def stop(self, timeout):
    def stop(self, timeout: int) -> None:
        self.__engine.stop(timeout)

    # Supports
    def supports(self, binary):
    def supports(self, binary: str) -> bool:
        return self.__engine.supports(binary)

    # Wait
    def wait(self):
    def wait(self) -> bool:
        return self.__engine.wait()
+24 −19
Changes for gitlabci_local/engines/podman.py: 24 added lines, 19 removed lines.
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
# Standard libraries
from os import environ
from subprocess import DEVNULL, PIPE, Popen, run
from typing import List

# Components
from ..package.bundle import Bundle
@@ -14,11 +15,11 @@ from .interface import Interface
class Podman(Interface):

    # Members
    __binary = 'podman'
    __binary: str = 'podman'
    __container = None

    # Constructor
    def __init__(self):
    def __init__(self) -> None:

        # Prepare container
        self.__container = None
@@ -36,7 +37,7 @@ class Podman(Interface):
            raise ModuleNotFoundError() from None

    # Internal execution
    def __exec(self, arguments, quiet=False):
    def __exec(self, arguments: List[str], quiet: bool = False):

        # Execute quiet command
        if quiet:
@@ -47,13 +48,16 @@ class Podman(Interface):
        return run([self.__binary] + arguments, check=False, stdout=PIPE, stderr=PIPE)

    # Internal watcher
    def __watch(self, arguments):
    def __watch(self, arguments: List[str]):

        # Watch command outputs, pylint: disable=consider-using-with
        return iter(Popen([self.__binary] + arguments, stdout=PIPE).stdout.readline, b'')
        stdout = Popen([self.__binary] + arguments, stdout=PIPE).stdout
        if not stdout:
            return None
        return iter(stdout.readline, b'')

    # Command exec
    def cmd_exec(self):
    def cmd_exec(self) -> str:

        # Result
        if Platform.IS_USER_SUDO:
@@ -80,7 +84,7 @@ class Podman(Interface):
        return self.__exec(['exec', self.__container] + command)

    # Get
    def get(self, image):
    def get(self, image: str) -> None:

        # Validate image exists
        result = self.__exec([
@@ -103,7 +107,7 @@ class Podman(Interface):
        return self.__watch(['logs', '--follow', self.__container])

    # Pull
    def pull(self, image, force=False):
    def pull(self, image: str, force: bool = False) -> None:

        # Header
        print(f'Pulling from {image}')
@@ -138,7 +142,7 @@ class Podman(Interface):
        Platform.flush()

    # Remove
    def remove(self):
    def remove(self) -> None:

        # Remove container
        if self.__container:
@@ -146,7 +150,7 @@ class Podman(Interface):
            self.__container = None

    # Remove image
    def rmi(self, image):
    def rmi(self, image: str) -> None:

        # Remove image
        result = self.__exec([
@@ -164,15 +168,16 @@ class Podman(Interface):
            ])

    # Run, pylint: disable=too-many-arguments,too-many-locals,unused-argument
    def run(self, image, command, entrypoint, variables, network, option_sockets,
            services, volumes, directory, temp_folder):
    def run(self, image: str, command, entrypoint: str, variables, network: str,
            option_sockets: bool, services: bool, volumes, directory: str,
            temp_folder: str) -> None:

        # Variables
        args_command = []
        args_entrypoint = []
        args_env = []
        args_run = []
        args_volumes = []
        args_entrypoint: List[str] = []
        args_env: List[str] = []
        args_run: List[str] = []
        args_volumes: List[str] = []

        # Adapt command
        if isinstance(command, list): # pragma: no cover
@@ -229,13 +234,13 @@ class Podman(Interface):
            raise RuntimeError(result.stderr.decode('utf-8').replace('\\n', '\n'))

    # Stop
    def stop(self, timeout):
    def stop(self, timeout: int) -> None:

        # Stop container
        self.__exec(['stop', '--time', str(timeout), self.__container])

    # Supports
    def supports(self, binary):
    def supports(self, binary: str) -> bool:

        # Validate binary support
        result = self.exec(['whereis', binary])
@@ -244,7 +249,7 @@ class Podman(Interface):
        return result.returncode == 0

    # Wait
    def wait(self):
    def wait(self) -> bool:

        # Wait container
        result = self.__exec(['wait', self.__container])
+4 −3
Changes for gitlabci_local/features/configurations.py: 4 added lines, 3 removed lines.
Original line number Diff line number Diff line
#!/usr/bin/env python3

# Standard libraries
from argparse import Namespace
from copy import deepcopy
from sys import maxsize

@@ -18,7 +19,7 @@ class ConfigurationsFeature:
    __configuration = None

    # Constructor
    def __init__(self, jobs, options):
    def __init__(self, jobs, options: Namespace) -> None:

        # Prepare configuration
        self.__configuration = {}
@@ -34,7 +35,7 @@ class ConfigurationsFeature:
                self.__cleanup(job)

    # Cleanup
    def __cleanup(self, job):
    def __cleanup(self, job: str) -> None:

        # Cleanup job configurations
        if job in self.__configuration:
@@ -53,7 +54,7 @@ class ConfigurationsFeature:
            del self.__configuration[job]['options']

    # Dump
    def dump(self):
    def dump(self) -> bool:

        # Dump configuration results
        print(yaml_dump(self.__configuration, indent=2, width=maxsize))
Loading