From 003423bb01f9852a50d079cf6ae27d050649a3d0 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Tue, 4 May 2021 14:15:23 -0700 Subject: [PATCH] Introduce sphinx processing for html API web site * Add docs target to Makefile * Started updating some docstrings * Added support for connections from containers.conf * Ported more uses of XDG_* envvar to xdg package * Downgrade modules in requirements.txt as needed by RHEL/Fedora packaging. Signed-off-by: Jhon Honce --- README.md | 6 ++++-- docs/source/conf.py | 23 ++++++++++++++++++--- docs/source/index.rst | 16 +++++++++++---- podman/api/__init__.py | 6 ++++++ podman/api/ssh.py | 8 ++++---- podman/api/uds.py | 10 +++++----- podman/client.py | 18 +++++++++-------- podman/domain/containers_create.py | 16 +++++++-------- podman/domain/images_manager.py | 9 ++++----- podman/domain/manifests.py | 3 +++ podman/domain/networks_manager.py | 23 ++++++++++++--------- podman/domain/pods_manager.py | 9 +++++---- podman/domain/volumes.py | 7 +------ podman/errors/__init__.py | 32 +++++++++++++++++++++--------- podman/errors/exceptions.py | 16 ++++++--------- podman/tlsconfig.py | 7 +++---- requirements.txt | 5 +++-- 17 files changed, 131 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 273eb513..7edb339f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# podman-py [![Build Status](https://api.cirrus-ci.com/github/containers/podman-py.svg)](https://cirrus-ci.com/github/containers/podman-py/master) +# podman-py +[![Build Status](https://api.cirrus-ci.com/github/containers/podman-py.svg)](https://cirrus-ci.com/github/containers/podman-py/master) +[![Bors enabled](https://bors.tech/images/badge_small.svg)](https://app.bors.tech/repositories/23171) -This python package is a library of bindings to use the RESTful API for [Podman](https://github.com/containers/podman). +This python package is a library of bindings to use the RESTful API of [Podman](https://github.com/containers/podman). It is currently under development and contributors are welcome! diff --git a/docs/source/conf.py b/docs/source/conf.py index 875216be..7365a401 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,6 +13,8 @@ import os import sys +from sphinx.domains.python import PythonDomain + sys.path.insert(0, os.path.abspath('../../..')) @@ -48,7 +50,8 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +# html_theme = 'alabaster' +html_theme = 'default' html_theme_options = { 'description': 'A Python library for the Podman service API', 'fixed_sidebar': True, @@ -68,7 +71,7 @@ napoleon_include_init_with_doc = False napoleon_include_private_with_doc = False napoleon_include_special_with_doc = False -napoleon_use_admonition_for_examples = True +napoleon_use_admonition_for_examples = False napoleon_use_admonition_for_notes = True napoleon_use_admonition_for_references = False napoleon_use_ivar = False @@ -79,11 +82,25 @@ napoleon_attr_annotations = True +class PatchedPythonDomain(PythonDomain): + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): + if 'refspecific' in node: + del node['refspecific'] + return super(PatchedPythonDomain, self).resolve_xref( + env, fromdocname, builder, typ, target, node, contnode + ) + + def skip(app, what, name, obj, would_skip, options): - if name in ("ApiConnection", "DockerClient"): + # isort: unique-list + cls = ['ApiConnection', 'DockerClient', 'DockerException'] + + if name in cls: return True + return None def setup(app): app.connect("autodoc-skip-member", skip) + app.add_domain(PatchedPythonDomain, override=True) diff --git a/docs/source/index.rst b/docs/source/index.rst index f270833d..33b8c663 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,16 +1,24 @@ -Welcome to PodmanPy's documentation! +.. image:: https://github.com/containers/podman/blob/master/logo/podman-logo.png?raw=true + +What is PodmanPy? ==================================== +PodmanPy is a Python3 module that allows you to write Python scripts that access resources +maintained by a Podman service. + +PodmanPy leverages the Podman service RESTful API. Connections via http+ssh://, http+unix:// or +tcp:// are supported. + .. automodule:: podman :members: :inherited-members: .. toctree:: - :maxdepth: 2 - :caption: Contents: - + :maxdepth: 2 + :caption: Contents: + modules Indices and tables ================== diff --git a/podman/api/__init__.py b/podman/api/__init__.py index 71999414..978a5c7a 100644 --- a/podman/api/__init__.py +++ b/podman/api/__init__.py @@ -33,11 +33,17 @@ def _api_version(release: str, significant: int = 3) -> str: VERSION: str = _api_version(version.__version__) COMPATIBLE_VERSION: str = _api_version(version.__compatible_version__, 2) +try: + from typing_extensions import Literal +except ModuleNotFoundError: + from typing import Literal + # isort: unique-list __all__ = [ 'APIClient', 'COMPATIBLE_VERSION', 'DEFAULT_CHUNK_SIZE', + 'Literal', 'VERSION', 'cached_property', 'create_tar', diff --git a/podman/api/ssh.py b/podman/api/ssh.py index 70b40b90..624937d4 100644 --- a/podman/api/ssh.py +++ b/podman/api/ssh.py @@ -219,13 +219,13 @@ class SSHPoolManager(urllib3.PoolManager): ) # Map supported schemes to Pool Classes - pool_classes_by_scheme = { + _pool_classes_by_scheme = { "http": SSHConnectionPool, "http+ssh": SSHConnectionPool, } # Map supported schemes to Pool Key index generator - key_fn_by_scheme = { + _key_fn_by_scheme = { "http": functools.partial(_key_normalizer, _PoolKey), "http+ssh": functools.partial(_key_normalizer, _PoolKey), } @@ -238,8 +238,8 @@ def __init__(self, num_pools=10, headers=None, **kwargs): headers: Additional headers to add to operations. """ super().__init__(num_pools, headers, **kwargs) - self.pool_classes_by_scheme = SSHPoolManager.pool_classes_by_scheme - self.key_fn_by_scheme = SSHPoolManager.key_fn_by_scheme + self.pool_classes_by_scheme = SSHPoolManager._pool_classes_by_scheme + self.key_fn_by_scheme = SSHPoolManager._key_fn_by_scheme class SSHAdapter(HTTPAdapter): diff --git a/podman/api/uds.py b/podman/api/uds.py index acd46798..2d36077c 100644 --- a/podman/api/uds.py +++ b/podman/api/uds.py @@ -105,13 +105,13 @@ class UDSPoolManager(urllib3.PoolManager): ) # Map supported schemes to Pool Classes - pool_classes_by_scheme = { + _pool_classes_by_scheme = { "http": UDSConnectionPool, "http+ssh": UDSConnectionPool, } # Map supported schemes to Pool Key index generator - key_fn_by_scheme = { + _key_fn_by_scheme = { "http": functools.partial(_key_normalizer, _PoolKey), "http+ssh": functools.partial(_key_normalizer, _PoolKey), } @@ -124,8 +124,8 @@ def __init__(self, num_pools=10, headers=None, **kwargs): headers: Additional headers to add to operations. """ super().__init__(num_pools, headers, **kwargs) - self.pool_classes_by_scheme = UDSPoolManager.pool_classes_by_scheme - self.key_fn_by_scheme = UDSPoolManager.key_fn_by_scheme + self.pool_classes_by_scheme = UDSPoolManager._pool_classes_by_scheme + self.key_fn_by_scheme = UDSPoolManager._key_fn_by_scheme class UDSAdapter(HTTPAdapter): @@ -151,7 +151,7 @@ def __init__( pool_maxsize: The maximum number of connections to save in the pool. Keyword Args: - timeout (float): + timeout (float): Time in seconds to wait for response Examples: requests.Session.mount( diff --git a/podman/client.py b/podman/client.py index 377fcd13..58e06464 100644 --- a/podman/client.py +++ b/podman/client.py @@ -24,16 +24,17 @@ class PodmanClient(AbstractContextManager): - """Create client to Podman service. + """Client to connect to a Podman service. Examples: + with PodmanClient("ssh://root@api.example:22/run/podman/podman.sock?secure=True", identity="~alice/.ssh/api_ed25519" ) """ def __init__(self, *args, **kwargs) -> None: - """Instantiate PodmanClient object + """Initialize PodmanClient. Keyword Args: base_url (str): Full URL to Podman service. See examples. @@ -95,22 +96,23 @@ def from_env( """Returns connection to service using environment variables and parameters. Environment variables: - DOCKER_HOST, CONTAINER_HOST: URL to Podman service - DOCKER_TLS_VERIFY, CONTAINER_TLS_VERIFY: Verify host against CA certificate - DOCKER_CERT_PATH, CONTAINER_CERT_PATH: Path to TLS certificates for host connection + + - DOCKER_HOST, CONTAINER_HOST: URL to Podman service + - DOCKER_TLS_VERIFY, CONTAINER_TLS_VERIFY: Verify host against CA certificate + - DOCKER_CERT_PATH, CONTAINER_CERT_PATH: Path to TLS certificates for host connection Args: version: API version to use. Default: auto, use version from server timeout: Timeout for API calls, in seconds. max_pool_size: Number of connections to save in pool. - ssl_version: Ignored. SSH configuration delegated to SSH client configuration. + ssl_version: SSH configuration delegated to SSH client configuration. Ignored. assert_hostname: Ignored. environment: Dict containing input environment. Default: os.environ credstore_env: Dict containing environment for credential store use_ssh_client: Use system ssh client rather than ssh module. Always, True. Returns: - PodmanClient: used to communicate with Podman service + Client used to communicate with a Podman service. """ environment = environment or os.environ credstore_env = credstore_env or dict() @@ -220,4 +222,4 @@ def swarm(self): # Aliases to minimize effort to port to PodmanPy DockerClient = PodmanClient -from_env = PodmanClient.from_env #: :meta hide-value: +from_env = PodmanClient.from_env diff --git a/podman/domain/containers_create.py b/podman/domain/containers_create.py index 3f82b3af..b4143ba6 100644 --- a/podman/domain/containers_create.py +++ b/podman/domain/containers_create.py @@ -58,6 +58,7 @@ def create( For example, /dev/sda:/dev/xvda:rwm allows the container to have read-write access to the host's /dev/sda via a node named /dev/xvda inside the container. + dns (List[str]): Set custom DNS servers. dns_opt (List[str]): Additional options to be added to the container's resolv.conf file. dns_search (List[str]): DNS search domains. @@ -123,7 +124,7 @@ def create( pids_limit (int): Tune a container's pids limit. Set -1 for unlimited. platform (str): Platform in the format os[/arch[/variant]]. Only used if the method needs to pull the requested image. - ports (Dict[str, Union[int, Tuple[str, int], List[int]): Ports to bind inside + ports (Dict[str, Union[int, Tuple[str, int], List[int]]]): Ports to bind inside the container. The keys of the dictionary are the ports to bind inside the container, either as an @@ -135,13 +136,12 @@ def create( - The port number, as an integer. For example, {'2222/tcp': 3333} will expose port 2222 inside the container as port 3333 on the - host. - - None, to assign a random host port. For example, - {'2222/tcp': None}. + host. + - None, to assign a random host port. For example, {'2222/tcp': None}. - A tuple of (address, port) if you want to specify the host interface. For example, {'1111/tcp': ('127.0.0.1', 1111)}. - A list of integers, if you want to bind multiple host ports to a single container - port. For example, {'1111/tcp': [1234, 4567]}. + port. For example, {'1111/tcp': [1234, 4567]}. privileged (bool): Give extended privileges to this container. publish_all_ports (bool): Publish all ports to the host. @@ -153,7 +153,7 @@ def create( - Name: One of on-failure, or always. - MaximumRetryCount: Number of times to restart the container on failure. - For example: + For example, {"Name": "on-failure", "MaximumRetryCount": 5} runtime (str): Runtime to use with this container. @@ -172,7 +172,7 @@ def create( tmpfs (Dict[str, str]): Temporary filesystems to mount, as a dictionary mapping a path inside the container to options for that path. - For example: + For example, { '/mnt/vol2': '', @@ -194,7 +194,7 @@ def create( volume_driver (str): The name of a volume driver/plugin. volumes (Dict[str, Dict[str, str]]): A dictionary to configure volumes mounted inside the container. The key is either the host path or a volume name, and the value is - a dictionary with the keys: + a dictionary with the keys: - bind The path to mount the volume inside the container - mode Either rw to mount the volume read/write, or ro to mount it read-only. diff --git a/podman/domain/images_manager.py b/podman/domain/images_manager.py index e1ad4fa2..768086ff 100644 --- a/podman/domain/images_manager.py +++ b/podman/domain/images_manager.py @@ -318,7 +318,7 @@ def remove( image: Union[Image, str], force: Optional[bool] = None, noprune: bool = False, # pylint: disable=unused-argument - ) -> List[Dict[str, Union[str, int]]]: + ) -> List[Dict[api.Literal["Deleted", "Untagged", "Errors", "ExitCode"], Union[str, int]]]: """Delete image from Podman service. Args: @@ -327,14 +327,11 @@ def remove( noprune: Ignored. Returns: - List[Dict[Literal["Deleted", "Untagged", "Errors", "ExitCode"], Union[str, int]]] + Dictionaries of the images deleted and untagged. Raises: ImageNotFound: when image does not exist APIError: when service returns an error - - Notes: - The dictionaries with keys Errors and ExitCode are added. """ if isinstance(image, Image): image = image.id @@ -359,9 +356,11 @@ def search(self, term: str, **kwargs) -> List[Dict[str, Any]]: Keyword Args: filters (Mapping[str, List[str]): Refine results of search. Available filters: + - is-automated (bool): Image build is automated. - is-official (bool): Image build is owned by product provider. - stars (int): Image has at least this number of stars. + noTrunc (bool): Do not truncate any result string. Default: True. limit (int): Maximum number of results. diff --git a/podman/domain/manifests.py b/podman/domain/manifests.py index 8b15a93c..5fcb5588 100644 --- a/podman/domain/manifests.py +++ b/podman/domain/manifests.py @@ -180,6 +180,9 @@ def create( body = response.json() manifest = self.get(body["Id"]) manifest.attrs["names"] = names + + if manifest.attrs["manifests"] is None: + manifest.attrs["manifests"] = list() return manifest def exists(self, key: str) -> bool: diff --git a/podman/domain/networks_manager.py b/podman/domain/networks_manager.py index 0698e084..fd5ac4f3 100644 --- a/podman/domain/networks_manager.py +++ b/podman/domain/networks_manager.py @@ -135,25 +135,29 @@ def list(self, **kwargs) -> List[Network]: Keyword Args: names (List[str]): List of names to filter by. - ids (List[str]): List of ids to filter by. - filters (Mapping[str,str]): Criteria for listing networks. - Available filters: + ids (List[str]): List of identifiers to filter by. + filters (Mapping[str,str]): Criteria for listing networks. Available filters: + - driver="bridge": Matches a network's driver. Only "bridge" is supported. - label=(Union[str, List[str]]): format either "key", "key=value" - or a list of such. + or a list of such. - type=(str): Filters networks by type, legal values are: + - "custom" - "builtin" - - plugin=(List[str]]): Matches CNI plugins included in a network, - legal values are (Podman only): + + - plugin=(List[str]]): Matches CNI plugins included in a network, legal + values are (Podman only): + - bridge - portmap - firewall - tuning - dnsname - macvlan + greedy (bool): Fetch more details for each network individually. - You might want this to get the containers attached to them. (ignored) + You might want this to get the containers attached to them. Ignored. Raises: APIError: when error returned by service @@ -173,13 +177,14 @@ def list(self, **kwargs) -> List[Network]: return [self.prepare_model(i) for i in response.json()] - def prune(self, filters: Optional[Dict[str, Any]] = None, **kwargs) -> Dict[str, Any]: + def prune( + self, filters: Optional[Dict[str, Any]] = None, **kwargs + ) -> Dict[api.Literal["NetworksDeleted", "SpaceReclaimed"], Any]: """Delete unused Networks. Args: filters: Criteria for selecting volumes to delete. Ignored. - Keyword Args: compatible (bool): Should compatible API be used. Default: True diff --git a/podman/domain/pods_manager.py b/podman/domain/pods_manager.py index 58fa554b..1ef1f479 100644 --- a/podman/domain/pods_manager.py +++ b/podman/domain/pods_manager.py @@ -64,16 +64,17 @@ def list(self, **kwargs) -> List[Pod]: Keyword Args: filters (Mapping[str, str]): Criteria for listing pods. Available filters: + - ctr-ids (List[str]): List of container ids to filter by. - ctr-names (List[str]): List of container names to filter by. - ctr-number (List[int]): list pods with given number of containers. - ctr-status (List[str]): List pods with containers in given state. - Legal values are: "created", "running", "paused", - "stopped", "exited", "unknown" + Legal values are: "created", "running", "paused", "stopped", + "exited", or "unknown" - id (str) - List pod with this id. - name (str) - List pod with this name. - - status (List[str]): List pods in given state. Legal values are: "created", - "running", "paused", "stopped", "exited", "unknown" + - status (List[str]): List pods in given state. Legal values are: + "created", "running", "paused", "stopped", "exited", or "unknown" - label (List[str]): List pods with given labels. - network (List[str]): List pods associated with given Network Ids (not Names). diff --git a/podman/domain/volumes.py b/podman/domain/volumes.py index d79feaca..df0b966e 100644 --- a/podman/domain/volumes.py +++ b/podman/domain/volumes.py @@ -8,11 +8,6 @@ from podman.domain.manager import Manager, PodmanResource from podman.errors import APIError -try: - from typing_extensions import Literal -except ModuleNotFoundError: - from typing import Literal - logger = logging.getLogger("podman.volumes") @@ -115,7 +110,7 @@ def list(self, *_, **kwargs) -> List[Volume]: def prune( self, filters: Optional[Dict[str, str]] = None # pylint: disable=unused-argument - ) -> Dict[Literal["VolumesDeleted", "SpaceReclaimed"], Any]: + ) -> Dict[api.Literal["VolumesDeleted", "SpaceReclaimed"], Any]: """Delete unused volumes. Args: diff --git a/podman/errors/__init__.py b/podman/errors/__init__.py index a0b2385d..2f7d506a 100644 --- a/podman/errors/__init__.py +++ b/podman/errors/__init__.py @@ -4,6 +4,8 @@ 'importlib' exceptions are used to differentiate between APIConnection and PodmanClient Errors. Therefore, installing both APIConnection and PodmanClient is not supported. PodmanClient related errors take precedence over APIConnection ones. + + ApiConnection and associated classes have been deprecated. """ import warnings from http.client import HTTPException @@ -38,8 +40,7 @@ class NotFoundError(HTTPException): """HTTP request returned a http.HTTPStatus.NOT_FOUND. - Notes: - TODO Delete when APIConnection EOL. + Deprecated. """ def __init__(self, message, response=None): @@ -55,34 +56,44 @@ def __init__(self, message, response=None): class ImageNotFound(NotFoundError): """HTTP request returned a http.HTTPStatus.NOT_FOUND. - Specialized for Image not found. + + Specialized for Image not found. Deprecated. """ class NetworkNotFound(NotFoundError): - """Network request returned a http.HTTPStatus.NOT_FOUND.""" + """Network request returned a http.HTTPStatus.NOT_FOUND. + + Deprecated. + """ class ContainerNotFound(NotFoundError): """HTTP request returned a http.HTTPStatus.NOT_FOUND. - Specialized for Container not found. + + Specialized for Container not found. Deprecated. """ class PodNotFound(NotFoundError): """HTTP request returned a http.HTTPStatus.NOT_FOUND. - Specialized for Pod not found. + + Specialized for Pod not found. Deprecated. """ class ManifestNotFound(NotFoundError): """HTTP request returned a http.HTTPStatus.NOT_FOUND. - Specialized for Manifest not found. + + Specialized for Manifest not found. Deprecated. """ class RequestError(HTTPException): - """Podman service reported issue with the request""" + """Podman service reported issue with the request. + + Deprecated. + """ def __init__(self, message, response=None): super().__init__(message) @@ -91,7 +102,10 @@ def __init__(self, message, response=None): class InternalServerError(HTTPException): - """Podman service reported an internal error.""" + """Podman service reported an internal error. + + Deprecated. + """ def __init__(self, message, response=None): super().__init__(message) diff --git a/podman/errors/exceptions.py b/podman/errors/exceptions.py index 024e10df..22f413e8 100644 --- a/podman/errors/exceptions.py +++ b/podman/errors/exceptions.py @@ -20,7 +20,7 @@ def __init__( response: Union[Response, "APIResponse", None] = None, explanation: Optional[str] = None, ): - """Create an APIError. + """Initialize APIError. Args: message: Message from service. Default: response.text, may be enhanced or wrapped by @@ -56,15 +56,15 @@ def status_code(self) -> Optional[int]: return None def is_error(self) -> bool: - """Returns True if an HTTP occurred.""" + """Returns True when HTTP operation resulted in an error.""" return self.is_client_error() or self.is_server_error() def is_client_error(self) -> bool: - """Returns True if error occurred in request.""" + """Returns True when request is incorrect.""" return 400 <= (self.status_code or 0) < 500 def is_server_error(self) -> bool: - """Returns True if error occurred in service.""" + """Returns True when error occurred in service.""" return 500 <= (self.status_code or 0) < 600 @@ -77,11 +77,7 @@ class NotFound(APIError): class ImageNotFound(APIError): - """Image not found on Podman service. - - Notes: - Compatible name, missing Error suffix. - """ + """Image not found on Podman service.""" class DockerException(Exception): @@ -100,7 +96,7 @@ class BuildError(PodmanError): """Error occurred during build operation.""" def __init__(self, reason: str, build_log: Iterable[str]) -> None: - """Create BuildError. + """Initialize BuildError. Args: reason: describes the error diff --git a/podman/tlsconfig.py b/podman/tlsconfig.py index 005d6eb7..64cd2fc1 100644 --- a/podman/tlsconfig.py +++ b/podman/tlsconfig.py @@ -12,16 +12,15 @@ class TLSConfig: def __init__(self, *args, **kwargs): """Initialize TLSConfig. - Args: + Keyword Args: client_cert (tuple of str): Path to client cert, path to client key. ca_cert (str): Path to CA cert file. - verify (bool or str): This can be False, or a path to a CA cert - file. + verify (bool or str): This can be False, or a path to a CA cert file. ssl_version (int): Ignored. assert_hostname (bool): Verify the hostname of the server. Notes: - Ignored keywords may be delegated to the SSH client configuration. + Keywords may be delegated to the SSH client configuration. """ @staticmethod diff --git a/requirements.txt b/requirements.txt index e16047e8..2a97765d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ requests>=2.24 toml>=0.10.2 -urllib3~=1.26.4 -pyxdg~=0.27 +urllib3~=1.24.2 +pyxdg~=0.26 +sphinx wheel setuptools