mirror of https://github.com/docker/docker-py.git
Add mypy CI, fix errors
Signed-off-by: Viicos <65306057+Viicos@users.noreply.github.com>
This commit is contained in:
parent
85ce456a2b
commit
0eb9d94e9c
|
|
@ -13,9 +13,11 @@ jobs:
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
- run: pip install -U ruff==0.0.284
|
- run: pip install -U ruff==0.0.284 mypy==1.5.1
|
||||||
- name: Run ruff
|
- name: Run ruff
|
||||||
run: ruff docker tests
|
run: ruff docker tests
|
||||||
|
- name: Run mypy
|
||||||
|
run: mypy docker
|
||||||
|
|
||||||
unit-tests:
|
unit-tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,163 @@
|
||||||
build
|
# Byte-compiled / optimized / DLL files
|
||||||
dist
|
__pycache__/
|
||||||
*.egg-info
|
*.py[cod]
|
||||||
*.egg/
|
*$py.class
|
||||||
*.pyc
|
|
||||||
*.swp
|
|
||||||
|
|
||||||
.tox
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
.coverage
|
.coverage
|
||||||
html/*
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
# Compiled Documentation
|
# Translations
|
||||||
_build/
|
*.mo
|
||||||
README.rst
|
*.pot
|
||||||
|
|
||||||
# setuptools_scm
|
# Django stuff:
|
||||||
_version.py
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
.idea/
|
ENV/
|
||||||
*.iml
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Ruff linter
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
|
||||||
6
Makefile
6
Makefile
|
|
@ -46,7 +46,7 @@ build-dind-certs:
|
||||||
docker build -t dpy-dind-certs -f tests/Dockerfile-dind-certs .
|
docker build -t dpy-dind-certs -f tests/Dockerfile-dind-certs .
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: ruff unit-test-py3 integration-dind integration-dind-ssl
|
test: ruff mypy unit-test-py3 integration-dind integration-dind-ssl
|
||||||
|
|
||||||
.PHONY: unit-test-py3
|
.PHONY: unit-test-py3
|
||||||
unit-test-py3: build-py3
|
unit-test-py3: build-py3
|
||||||
|
|
@ -167,6 +167,10 @@ integration-dind-ssl: build-dind-certs build-py3 setup-network
|
||||||
ruff: build-py3
|
ruff: build-py3
|
||||||
docker run -t --rm docker-sdk-python3 ruff docker tests
|
docker run -t --rm docker-sdk-python3 ruff docker tests
|
||||||
|
|
||||||
|
.PHONY: mypy
|
||||||
|
mypy: build-py3
|
||||||
|
docker run -t --rm docker-sdk-python3 mypy docker
|
||||||
|
|
||||||
.PHONY: docs
|
.PHONY: docs
|
||||||
docs: build-docs
|
docs: build-docs
|
||||||
docker run --rm -t -v `pwd`:/src docker-sdk-python-docs sphinx-build docs docs/_build
|
docker run --rm -t -v `pwd`:/src docker-sdk-python-docs sphinx-build docs docs/_build
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import urllib
|
||||||
import ssl
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any, AnyStr, Optional, Union, Dict, overload, NoReturn, Iterator
|
from typing import Any, AnyStr, Optional, Union, Dict, cast, overload, NoReturn, Iterator
|
||||||
|
|
||||||
if sys.version_info >= (3, 8):
|
if sys.version_info >= (3, 8):
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
@ -25,11 +25,12 @@ from ..errors import (DockerException, InvalidVersion, TLSParameterError,
|
||||||
create_api_error_from_http_exception)
|
create_api_error_from_http_exception)
|
||||||
from ..tls import TLSConfig
|
from ..tls import TLSConfig
|
||||||
from ..transport import SSLHTTPAdapter, UnixHTTPAdapter
|
from ..transport import SSLHTTPAdapter, UnixHTTPAdapter
|
||||||
|
from ..transport.basehttpadapter import BaseHTTPAdapter
|
||||||
from ..utils import check_resource, config, update_headers, utils
|
from ..utils import check_resource, config, update_headers, utils
|
||||||
from ..utils.json_stream import json_stream
|
from ..utils.json_stream import json_stream
|
||||||
from ..utils.proxy import ProxyConfig
|
from ..utils.proxy import ProxyConfig
|
||||||
from ..utils.socket import consume_socket_output, demux_adaptor, frames_iter
|
from ..utils.socket import consume_socket_output, demux_adaptor, frames_iter
|
||||||
from ..utils.typing import BytesOrDict
|
from ..utils.typing import StrOrDict
|
||||||
from .build import BuildApiMixin
|
from .build import BuildApiMixin
|
||||||
from .config import ConfigApiMixin
|
from .config import ConfigApiMixin
|
||||||
from .container import ContainerApiMixin
|
from .container import ContainerApiMixin
|
||||||
|
|
@ -112,6 +113,8 @@ class APIClient(
|
||||||
'base_url',
|
'base_url',
|
||||||
'timeout']
|
'timeout']
|
||||||
|
|
||||||
|
_custom_adapter: BaseHTTPAdapter
|
||||||
|
|
||||||
def __init__(self, base_url: Optional[str] = None, version: Optional[str] = None,
|
def __init__(self, base_url: Optional[str] = None, version: Optional[str] = None,
|
||||||
timeout: int = DEFAULT_TIMEOUT_SECONDS, tls: Optional[Union[bool, TLSConfig]] = False,
|
timeout: int = DEFAULT_TIMEOUT_SECONDS, tls: Optional[Union[bool, TLSConfig]] = False,
|
||||||
user_agent: str = DEFAULT_USER_AGENT, num_pools: Optional[int] = None,
|
user_agent: str = DEFAULT_USER_AGENT, num_pools: Optional[int] = None,
|
||||||
|
|
@ -124,7 +127,6 @@ class APIClient(
|
||||||
'If using TLS, the base_url argument must be provided.'
|
'If using TLS, the base_url argument must be provided.'
|
||||||
)
|
)
|
||||||
|
|
||||||
self.base_url = base_url
|
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.headers['User-Agent'] = user_agent
|
self.headers['User-Agent'] = user_agent
|
||||||
|
|
||||||
|
|
@ -143,9 +145,7 @@ class APIClient(
|
||||||
)
|
)
|
||||||
self.credstore_env = credstore_env
|
self.credstore_env = credstore_env
|
||||||
|
|
||||||
base_url = utils.parse_host(
|
base_url = cast(str, utils.parse_host(base_url, IS_WINDOWS_PLATFORM, tls=bool(tls)))
|
||||||
base_url, IS_WINDOWS_PLATFORM, tls=bool(tls)
|
|
||||||
)
|
|
||||||
# SSH has a different default for num_pools to all other adapters
|
# SSH has a different default for num_pools to all other adapters
|
||||||
num_pools = num_pools or DEFAULT_NUM_POOLS_SSH if \
|
num_pools = num_pools or DEFAULT_NUM_POOLS_SSH if \
|
||||||
base_url.startswith('ssh://') else DEFAULT_NUM_POOLS
|
base_url.startswith('ssh://') else DEFAULT_NUM_POOLS
|
||||||
|
|
@ -261,9 +261,9 @@ class APIClient(
|
||||||
)
|
)
|
||||||
|
|
||||||
quote_f = partial(urllib.parse.quote, safe="/:")
|
quote_f = partial(urllib.parse.quote, safe="/:")
|
||||||
args = map(quote_f, args)
|
args_quoted = map(quote_f, args)
|
||||||
|
|
||||||
formatted_path = pathfmt.format(*args)
|
formatted_path = pathfmt.format(*args_quoted)
|
||||||
if kwargs.get('versioned_api', True):
|
if kwargs.get('versioned_api', True):
|
||||||
return f'{self.base_url}/v{self._version}{formatted_path}'
|
return f'{self.base_url}/v{self._version}{formatted_path}'
|
||||||
else:
|
else:
|
||||||
|
|
@ -281,7 +281,7 @@ class APIClient(
|
||||||
...
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def _result(self, response: requests.Response, json: Literal[False] = ..., binary: Literal[False] = ...) -> str:
|
def _result(self, response: requests.Response, json: Literal[False] = ..., binary: Literal[False] = ...) -> str: # type: ignore[misc]
|
||||||
...
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
|
@ -329,7 +329,7 @@ class APIClient(
|
||||||
def _attach_websocket(self, container: str, params: Optional[Dict[str, Any]] = None) -> websocket.WebSocket:
|
def _attach_websocket(self, container: str, params: Optional[Dict[str, Any]] = None) -> websocket.WebSocket:
|
||||||
url = self._url("/containers/{0}/attach/ws", container)
|
url = self._url("/containers/{0}/attach/ws", container)
|
||||||
req = requests.Request("POST", url, params=self._attach_params(params))
|
req = requests.Request("POST", url, params=self._attach_params(params))
|
||||||
full_url = req.prepare().url
|
full_url = cast(str, req.prepare().url)
|
||||||
full_url = full_url.replace("http://", "ws://", 1)
|
full_url = full_url.replace("http://", "ws://", 1)
|
||||||
full_url = full_url.replace("https://", "wss://", 1)
|
full_url = full_url.replace("https://", "wss://", 1)
|
||||||
return self._create_websocket_connection(full_url)
|
return self._create_websocket_connection(full_url)
|
||||||
|
|
@ -364,10 +364,10 @@ class APIClient(
|
||||||
...
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def _stream_helper(self, response: requests.Response, decode: Literal[False] = ...) -> Iterator[bytes]:
|
def _stream_helper(self, response: requests.Response, decode: Literal[False] = ...) -> Iterator[str]:
|
||||||
...
|
...
|
||||||
|
|
||||||
def _stream_helper(self, response: requests.Response, decode: bool = False) -> Iterator[BytesOrDict]:
|
def _stream_helper(self, response: requests.Response, decode: bool = False) -> Iterator[StrOrDict]:
|
||||||
"""Generator for data coming from a chunked-encoded HTTP response."""
|
"""Generator for data coming from a chunked-encoded HTTP response."""
|
||||||
|
|
||||||
if response.raw._fp.chunked:
|
if response.raw._fp.chunked:
|
||||||
|
|
@ -386,7 +386,7 @@ class APIClient(
|
||||||
else:
|
else:
|
||||||
# Response isn't chunked, meaning we probably
|
# Response isn't chunked, meaning we probably
|
||||||
# encountered an error immediately
|
# encountered an error immediately
|
||||||
yield self._result(response, json=decode)
|
yield self._result(response, json=decode) # type: ignore[misc]
|
||||||
|
|
||||||
def _multiplexed_buffer_helper(self, response: requests.Response) -> Iterator[bytes]:
|
def _multiplexed_buffer_helper(self, response: requests.Response) -> Iterator[bytes]:
|
||||||
"""A generator of multiplexed data blocks read from a buffered
|
"""A generator of multiplexed data blocks read from a buffered
|
||||||
|
|
@ -426,14 +426,18 @@ class APIClient(
|
||||||
yield data
|
yield data
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def _stream_raw_result(self, response: requests.Response, chunk_size: int = ..., decode: Literal[False] = ...) -> Iterator[bytes]:
|
def _stream_raw_result(self, response: requests.Response, chunk_size: int, decode: Literal[False]) -> Iterator[bytes]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def _stream_raw_result(self, response: requests.Response, decode: Literal[False]) -> Iterator[bytes]:
|
||||||
...
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def _stream_raw_result(self, response: requests.Response, chunk_size: int = ..., decode: Literal[True] = ...) -> Iterator[str]:
|
def _stream_raw_result(self, response: requests.Response, chunk_size: int = ..., decode: Literal[True] = ...) -> Iterator[str]:
|
||||||
...
|
...
|
||||||
|
|
||||||
def _stream_raw_result(self, response: requests.Response, chunk_size: int = 1, decode: bool = True) -> Iterator[AnyStr]:
|
def _stream_raw_result(self, response: requests.Response, chunk_size: int = 1, decode: bool = True) -> Iterator[AnyStr]: # type: ignore[misc]
|
||||||
''' Stream result for TTY-enabled container and raw binary data'''
|
''' Stream result for TTY-enabled container and raw binary data'''
|
||||||
self._raise_for_status(response)
|
self._raise_for_status(response)
|
||||||
|
|
||||||
|
|
@ -489,13 +493,13 @@ class APIClient(
|
||||||
timeout = -1
|
timeout = -1
|
||||||
|
|
||||||
if hasattr(s, 'gettimeout'):
|
if hasattr(s, 'gettimeout'):
|
||||||
timeout = s.gettimeout()
|
timeout = cast(int, s.gettimeout()) # type: ignore[union-attr]
|
||||||
|
|
||||||
# Don't change the timeout if it is already disabled.
|
# Don't change the timeout if it is already disabled.
|
||||||
if timeout is None or timeout == 0.0:
|
if timeout is None or timeout == 0.0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
s.settimeout(None)
|
s.settimeout(None) # type: ignore[union-attr]
|
||||||
|
|
||||||
@check_resource('container')
|
@check_resource('container')
|
||||||
def _check_is_tty(self, container: str) -> bool:
|
def _check_is_tty(self, container: str) -> bool:
|
||||||
|
|
@ -510,7 +514,7 @@ class APIClient(
|
||||||
def _get_result(self, container: str, stream: Literal[False], res: requests.Response) -> bytes:
|
def _get_result(self, container: str, stream: Literal[False], res: requests.Response) -> bytes:
|
||||||
...
|
...
|
||||||
|
|
||||||
def _get_result(self, container: str, stream: bool, res: requests.Response):
|
def _get_result(self, container, stream, res):
|
||||||
return self._get_result_tty(stream, res, self._check_is_tty(container))
|
return self._get_result_tty(stream, res, self._check_is_tty(container))
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, AnyStr, Tuple, Dict, Optional, Union
|
from typing import Any, AnyStr, Tuple, Dict, Optional, Union, cast
|
||||||
|
|
||||||
from . import credentials
|
from . import credentials
|
||||||
from . import errors
|
from . import errors
|
||||||
|
|
@ -67,7 +67,7 @@ def split_repo_name(repo_name: str) -> Tuple[str, str]:
|
||||||
):
|
):
|
||||||
# This is a docker index repo (ex: username/foobar or ubuntu)
|
# This is a docker index repo (ex: username/foobar or ubuntu)
|
||||||
return INDEX_NAME, repo_name
|
return INDEX_NAME, repo_name
|
||||||
return tuple(parts)
|
return tuple(parts) # type: ignore[return-value]
|
||||||
|
|
||||||
|
|
||||||
def get_credential_store(authconfig, registry):
|
def get_credential_store(authconfig, registry):
|
||||||
|
|
@ -82,7 +82,7 @@ class AuthConfig(dict):
|
||||||
dct['auths'] = {}
|
dct['auths'] = {}
|
||||||
self.update(dct)
|
self.update(dct)
|
||||||
self._credstore_env = credstore_env
|
self._credstore_env = credstore_env
|
||||||
self._stores = {}
|
self._stores: Dict[str, credentials.Store] = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse_auth(cls, entries: Dict[str, Any], raise_on_error: bool = False) -> Dict[str, Any]:
|
def parse_auth(cls, entries: Dict[str, Any], raise_on_error: bool = False) -> Dict[str, Any]:
|
||||||
|
|
@ -145,7 +145,7 @@ class AuthConfig(dict):
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_config(cls, config_path: str, config_dict: Dict[str, Any], credstore_env: Optional[Dict[str, Any]] = None) -> AuthConfig:
|
def load_config(cls, config_path: Optional[str], config_dict: Optional[Dict[str, Any]], credstore_env: Optional[Dict[str, Any]] = None) -> AuthConfig:
|
||||||
"""
|
"""
|
||||||
Loads authentication data from a Docker configuration file in the given
|
Loads authentication data from a Docker configuration file in the given
|
||||||
root directory or if config_path is passed use given path.
|
root directory or if config_path is passed use given path.
|
||||||
|
|
@ -161,7 +161,7 @@ class AuthConfig(dict):
|
||||||
return cls({}, credstore_env)
|
return cls({}, credstore_env)
|
||||||
try:
|
try:
|
||||||
with open(config_file) as f:
|
with open(config_file) as f:
|
||||||
config_dict = json.load(f)
|
config_dict = cast(Dict[str, Any], json.load(f))
|
||||||
except (OSError, KeyError, ValueError) as e:
|
except (OSError, KeyError, ValueError) as e:
|
||||||
# Likely missing new Docker config file or it's in an
|
# Likely missing new Docker config file or it's in an
|
||||||
# unknown format, continue to attempt to read old location
|
# unknown format, continue to attempt to read old location
|
||||||
|
|
@ -245,7 +245,7 @@ class AuthConfig(dict):
|
||||||
log.debug("No entry found")
|
log.debug("No entry found")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _resolve_authconfig_credstore(self, registry: str, credstore_name: str) -> Optional[Dict[str, Any]]:
|
def _resolve_authconfig_credstore(self, registry: Optional[str], credstore_name: str) -> Optional[Dict[str, Any]]:
|
||||||
if not registry or registry == INDEX_NAME:
|
if not registry or registry == INDEX_NAME:
|
||||||
# The ecosystem is a little schizophrenic with index.docker.io VS
|
# The ecosystem is a little schizophrenic with index.docker.io VS
|
||||||
# docker.io - in that case, it seems the full URL is necessary.
|
# docker.io - in that case, it seems the full URL is necessary.
|
||||||
|
|
@ -273,14 +273,14 @@ class AuthConfig(dict):
|
||||||
f'Credentials store error: {repr(e)}'
|
f'Credentials store error: {repr(e)}'
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
def _get_store_instance(self, name: str) -> Dict[str, Any]:
|
def _get_store_instance(self, name: str) -> credentials.Store:
|
||||||
if name not in self._stores:
|
if name not in self._stores:
|
||||||
self._stores[name] = credentials.Store(
|
self._stores[name] = credentials.Store(
|
||||||
name, environment=self._credstore_env
|
name, environment=self._credstore_env
|
||||||
)
|
)
|
||||||
return self._stores[name]
|
return self._stores[name]
|
||||||
|
|
||||||
def get_credential_store(self, registry: str) -> str:
|
def get_credential_store(self, registry: Optional[str]) -> Optional[str]:
|
||||||
if not registry or registry == INDEX_NAME:
|
if not registry or registry == INDEX_NAME:
|
||||||
registry = INDEX_URL
|
registry = INDEX_URL
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class APIError(requests.exceptions.HTTPError, DockerException):
|
||||||
"""
|
"""
|
||||||
An HTTP error from the API.
|
An HTTP error from the API.
|
||||||
"""
|
"""
|
||||||
def __init__(self, message: str, response: Optional[requests.Response] = None, explanation: Optional[str] = None) -> None:
|
def __init__(self, message: Any, response: Optional[requests.Response] = None, explanation: Optional[str] = None) -> None:
|
||||||
# requests 1.2 supports response as a keyword argument, but
|
# requests 1.2 supports response as a keyword argument, but
|
||||||
# requests 1.1 doesn't
|
# requests 1.1 doesn't
|
||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
|
|
@ -78,6 +78,7 @@ class APIError(requests.exceptions.HTTPError, DockerException):
|
||||||
def status_code(self) -> Optional[int]:
|
def status_code(self) -> Optional[int]:
|
||||||
if self.response is not None:
|
if self.response is not None:
|
||||||
return self.response.status_code
|
return self.response.status_code
|
||||||
|
return None
|
||||||
|
|
||||||
def is_error(self) -> bool:
|
def is_error(self) -> bool:
|
||||||
return self.is_client_error() or self.is_server_error()
|
return self.is_client_error() or self.is_server_error()
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class TLSConfig:
|
||||||
"""
|
"""
|
||||||
Configure a client with these TLS options.
|
Configure a client with these TLS options.
|
||||||
"""
|
"""
|
||||||
client.ssl_version = self.ssl_version
|
client.ssl_version = self.ssl_version # type: ignore[attr-defined]
|
||||||
|
|
||||||
if self.verify and self.ca_cert:
|
if self.verify and self.ca_cert:
|
||||||
client.verify = self.ca_cert
|
client.verify = self.ca_cert
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
from typing import Any, Dict, TypeVar
|
from typing import Any, Dict, TypeVar
|
||||||
|
|
||||||
BytesOrDict = TypeVar("BytesOrDict", bytes, Dict[str, Any])
|
StrOrDict = TypeVar("StrOrDict", str, Dict[str, Any])
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
try:
|
try:
|
||||||
from ._version import __version__
|
from ._version import __version__ # type: ignore[import]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
try:
|
try:
|
||||||
# importlib.metadata available in Python 3.8+, the fallback (0.0.0)
|
# importlib.metadata available in Python 3.8+, the fallback (0.0.0)
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,22 @@ ignore = [
|
||||||
|
|
||||||
[tool.ruff.per-file-ignores]
|
[tool.ruff.per-file-ignores]
|
||||||
"**/__init__.py" = ["F401"]
|
"**/__init__.py" = ["F401"]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.8"
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = [
|
||||||
|
"docker.api.*",
|
||||||
|
"docker.context.*",
|
||||||
|
"docker.credentials.*",
|
||||||
|
"docker.models.*",
|
||||||
|
"docker.transport.*",
|
||||||
|
"docker.types.*",
|
||||||
|
"docker.utils.*",
|
||||||
|
]
|
||||||
|
ignore_errors = true
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = "docker.api.client"
|
||||||
|
ignore_errors = false
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@ pywin32==304; sys_platform == 'win32'
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
urllib3==1.26.11
|
urllib3==1.26.11
|
||||||
websocket-client==1.3.3
|
websocket-client==1.3.3
|
||||||
typing_extensions>=3.10.0.0; python_version < '3.8'
|
typing_extensions>=3.10.0.0; python_version < '3.11'
|
||||||
|
|
|
||||||
2
setup.py
2
setup.py
|
|
@ -14,7 +14,7 @@ requirements = [
|
||||||
'requests >= 2.26.0',
|
'requests >= 2.26.0',
|
||||||
'urllib3 >= 1.26.0',
|
'urllib3 >= 1.26.0',
|
||||||
'websocket-client >= 0.32.0',
|
'websocket-client >= 0.32.0',
|
||||||
'typing_extensions>=3.10.0.0; python_version < "3.8"',
|
'typing_extensions>=3.10.0.0; python_version < "3.11"',
|
||||||
]
|
]
|
||||||
|
|
||||||
extras_require = {
|
extras_require = {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
setuptools==65.5.1
|
setuptools==65.5.1
|
||||||
coverage==6.4.2
|
coverage==6.4.2
|
||||||
ruff==0.0.284
|
ruff==0.0.284
|
||||||
|
mypy==1.5.1
|
||||||
pytest==7.1.2
|
pytest==7.1.2
|
||||||
pytest-cov==3.0.0
|
pytest-cov==3.0.0
|
||||||
pytest-timeout==2.1.0
|
pytest-timeout==2.1.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue