Prepare certbot module for mypy check untyped defs (#6005)

* Prepare certbot module for mypy check untyped defs

* Fix #5952

* Bump mypy to version 0.600 and fix associated bugs

* Fix pylint bugs after introducing mypy

* Implement Brad's suggestions

* Reenabling pylint and adding nginx mypy back
This commit is contained in:
Dmitry Figol
2018-05-18 06:28:17 -07:00
committed by Brad Warren
parent 250c0d6691
commit 36dfd06503
36 changed files with 316 additions and 214 deletions
+4 -2
View File
@@ -11,6 +11,8 @@ import zope.interface
from josepy import util as jose_util
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from certbot import achallenges # pylint: disable=unused-import
from certbot import constants
from certbot import crypto_util
from certbot import errors
@@ -331,8 +333,8 @@ class ChallengePerformer(object):
def __init__(self, configurator):
self.configurator = configurator
self.achalls = []
self.indices = []
self.achalls = [] # type: List[achallenges.KeyAuthorizationAnnotatedChallenge]
self.indices = [] # type: List[int]
def add_chall(self, achall, idx=None):
"""Store challenge to be performed when perform() is called.
+2 -1
View File
@@ -10,6 +10,7 @@ from collections import OrderedDict
import zope.interface
import zope.interface.verify
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
from certbot import constants
from certbot import errors
from certbot import interfaces
@@ -189,7 +190,7 @@ class PluginsRegistry(collections.Mapping):
@classmethod
def find_all(cls):
"""Find plugins using setuptools entry points."""
plugins = {}
plugins = {} # type: Dict[str, PluginEntryPoint]
# pylint: disable=not-callable
entry_points = itertools.chain(
pkg_resources.iter_entry_points(
+2 -1
View File
@@ -8,6 +8,7 @@ import pkg_resources
import six
import zope.interface
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from certbot import errors
from certbot import interfaces
@@ -250,7 +251,7 @@ class PluginsRegistryTest(unittest.TestCase):
self.plugin_ep.prepare.assert_called_once_with()
def test_prepare_order(self):
order = []
order = [] # type: List[str]
plugins = dict(
(c, mock.MagicMock(prepare=functools.partial(order.append, c)))
for c in string.ascii_letters)
+4 -1
View File
@@ -5,7 +5,9 @@ import zope.component
import zope.interface
from acme import challenges
from acme.magic_typing import Dict # pylint: disable=unused-import, no-name-in-module
from certbot import achallenges # pylint: disable=unused-import
from certbot import interfaces
from certbot import errors
from certbot import hooks
@@ -98,7 +100,8 @@ when it receives a TLS ClientHello with the SNI extension set to
super(Authenticator, self).__init__(*args, **kwargs)
self.reverter = reverter.Reverter(self.config)
self.reverter.recovery_routine()
self.env = dict()
self.env = dict() \
# type: Dict[achallenges.KeyAuthorizationAnnotatedChallenge, Dict[str, str]]
self.tls_sni_01 = None
@classmethod
+2 -1
View File
@@ -6,6 +6,7 @@ import unittest
import mock
import zope.component
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from certbot.display import util as display_util
from certbot.tests import util as test_util
from certbot import interfaces
@@ -47,7 +48,7 @@ class PickPluginTest(unittest.TestCase):
self.default = None
self.reg = mock.MagicMock()
self.question = "Question?"
self.ifaces = []
self.ifaces = [] # type: List[interfaces.IPlugin]
def _call(self):
from certbot.plugins.selection import pick_plugin
+21 -8
View File
@@ -3,6 +3,8 @@ import argparse
import collections
import logging
import socket
# https://github.com/python/typeshed/blob/master/stdlib/2and3/socket.pyi
from socket import errno as socket_errors # type: ignore
import OpenSSL
import six
@@ -10,7 +12,10 @@ import zope.interface
from acme import challenges
from acme import standalone as acme_standalone
# pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import DefaultDict, Dict, Set, Tuple, List, Type, TYPE_CHECKING
from certbot import achallenges # pylint: disable=unused-import
from certbot import errors
from certbot import interfaces
@@ -18,6 +23,11 @@ from certbot.plugins import common
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
ServedType = DefaultDict[
acme_standalone.BaseDualNetworkedServers,
Set[achallenges.KeyAuthorizationAnnotatedChallenge]
]
class ServerManager(object):
"""Standalone servers manager.
@@ -33,7 +43,7 @@ class ServerManager(object):
"""
def __init__(self, certs, http_01_resources):
self._instances = {}
self._instances = {} # type: Dict[int, acme_standalone.BaseDualNetworkedServers]
self.certs = certs
self.http_01_resources = http_01_resources
@@ -59,7 +69,8 @@ class ServerManager(object):
address = (listenaddr, port)
try:
if challenge_type is challenges.TLSSNI01:
servers = acme_standalone.TLSSNI01DualNetworkedServers(address, self.certs)
servers = acme_standalone.TLSSNI01DualNetworkedServers(
address, self.certs) # type: acme_standalone.BaseDualNetworkedServers
else: # challenges.HTTP01
servers = acme_standalone.HTTP01DualNetworkedServers(
address, self.http_01_resources)
@@ -103,7 +114,8 @@ class ServerManager(object):
return self._instances.copy()
SUPPORTED_CHALLENGES = [challenges.TLSSNI01, challenges.HTTP01]
SUPPORTED_CHALLENGES = [challenges.TLSSNI01, challenges.HTTP01] \
# type: List[Type[challenges.KeyAuthorizationChallenge]]
class SupportedChallengesAction(argparse.Action):
@@ -179,14 +191,15 @@ class Authenticator(common.Plugin):
self.key = OpenSSL.crypto.PKey()
self.key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
self.served = collections.defaultdict(set)
self.served = collections.defaultdict(set) # type: ServedType
# Stuff below is shared across threads (i.e. servers read
# values, main thread writes). Due to the nature of CPython's
# GIL, the operations are safe, c.f.
# https://docs.python.org/2/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
self.certs = {}
self.http_01_resources = set()
self.certs = {} # type: Dict[bytes, Tuple[OpenSSL.crypto.PKey, OpenSSL.crypto.X509]]
self.http_01_resources = set() \
# type: Set[acme_standalone.HTTP01RequestHandler.HTTP01Resource]
self.servers = ServerManager(self.certs, self.http_01_resources)
@@ -265,13 +278,13 @@ class Authenticator(common.Plugin):
def _handle_perform_error(error):
if error.socket_error.errno == socket.errno.EACCES:
if error.socket_error.errno == socket_errors.EACCES:
raise errors.PluginError(
"Could not bind TCP port {0} because you don't have "
"the appropriate permissions (for example, you "
"aren't running this program as "
"root).".format(error.port))
elif error.socket_error.errno == socket.errno.EADDRINUSE:
elif error.socket_error.errno == socket_errors.EADDRINUSE:
display = zope.component.getUtility(interfaces.IDisplay)
msg = (
"Could not bind TCP port {0} because it is already in "
+13 -6
View File
@@ -2,12 +2,18 @@
import argparse
import socket
import unittest
# https://github.com/python/typeshed/blob/master/stdlib/2and3/socket.pyi
from socket import errno as socket_errors # type: ignore
import josepy as jose
import mock
import six
import OpenSSL.crypto # pylint: disable=unused-import
from acme import challenges
from acme import standalone as acme_standalone # pylint: disable=unused-import
from acme.magic_typing import Dict, Tuple, Set # pylint: disable=unused-import, no-name-in-module
from certbot import achallenges
from certbot import errors
@@ -21,8 +27,9 @@ class ServerManagerTest(unittest.TestCase):
def setUp(self):
from certbot.plugins.standalone import ServerManager
self.certs = {}
self.http_01_resources = {}
self.certs = {} # type: Dict[bytes, Tuple[OpenSSL.crypto.PKey, OpenSSL.crypto.X509]]
self.http_01_resources = {} \
# type: Set[acme_standalone.HTTP01RequestHandler.HTTP01Resource]
self.mgr = ServerManager(self.certs, self.http_01_resources)
def test_init(self):
@@ -159,7 +166,7 @@ class AuthenticatorTest(unittest.TestCase):
@test_util.patch_get_utility()
def test_perform_eaddrinuse_retry(self, mock_get_utility):
mock_utility = mock_get_utility()
errno = socket.errno.EADDRINUSE
errno = socket_errors.EADDRINUSE
error = errors.StandaloneBindError(mock.MagicMock(errno=errno), -1)
self.auth.servers.run.side_effect = [error] + 2 * [mock.MagicMock()]
mock_yesno = mock_utility.yesno
@@ -174,7 +181,7 @@ class AuthenticatorTest(unittest.TestCase):
mock_yesno = mock_utility.yesno
mock_yesno.return_value = False
errno = socket.errno.EADDRINUSE
errno = socket_errors.EADDRINUSE
self.assertRaises(errors.PluginError, self._fail_perform, errno)
self._assert_correct_yesno_call(mock_yesno)
@@ -184,11 +191,11 @@ class AuthenticatorTest(unittest.TestCase):
self.assertFalse(yesno_kwargs.get("default", True))
def test_perform_eacces(self):
errno = socket.errno.EACCES
errno = socket_errors.EACCES
self.assertRaises(errors.PluginError, self._fail_perform, errno)
def test_perform_unexpected_socket_error(self):
errno = socket.errno.ENOTCONN
errno = socket_errors.ENOTCONN
self.assertRaises(
errors.StandaloneBindError, self._fail_perform, errno)
+2 -1
View File
@@ -3,6 +3,7 @@ import json
import logging
import os
from acme.magic_typing import Any, Dict # pylint: disable=unused-import, no-name-in-module
from certbot import errors
logger = logging.getLogger(__name__)
@@ -38,7 +39,7 @@ class PluginStorage(object):
:raises .errors.PluginStorageError: when unable to open or read the file
"""
data = dict()
data = dict() # type: Dict[str, Any]
filedata = ""
try:
with open(self._storagepath, 'r') as fh:
+10 -7
View File
@@ -10,8 +10,12 @@ import six
import zope.component
import zope.interface
from acme import challenges
from acme import challenges # pylint: disable=unused-import
# pylint: disable=unused-import, no-name-in-module
from acme.magic_typing import Dict, Set, DefaultDict, List
# pylint: enable=unused-import, no-name-in-module
from certbot import achallenges # pylint: disable=unused-import
from certbot import cli
from certbot import errors
from certbot import interfaces
@@ -64,10 +68,11 @@ to serve all files under specified web root ({0})."""
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
self.full_roots = {}
self.performed = collections.defaultdict(set)
self.full_roots = {} # type: Dict[str, str]
self.performed = collections.defaultdict(set) \
# type: DefaultDict[str, Set[achallenges.KeyAuthorizationAnnotatedChallenge]]
# stack of dirs successfully created by this authenticator
self._created_dirs = []
self._created_dirs = [] # type: List[str]
def prepare(self): # pylint: disable=missing-docstring
pass
@@ -156,7 +161,6 @@ to serve all files under specified web root ({0})."""
" --help webroot for examples.")
for name, path in path_map.items():
self.full_roots[name] = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH)
logger.debug("Creating root challenges validation dir at %s",
self.full_roots[name])
@@ -207,7 +211,6 @@ to serve all files under specified web root ({0})."""
os.umask(old_umask)
self.performed[root_path].add(achall)
return response
def cleanup(self, achalls): # pylint: disable=missing-docstring
@@ -219,7 +222,7 @@ to serve all files under specified web root ({0})."""
os.remove(validation_path)
self.performed[root_path].remove(achall)
not_removed = []
not_removed = [] # type: List[str]
while len(self._created_dirs) > 0:
path = self._created_dirs.pop()
try: