Lint certbot code on Python 3, and update Pylint to the latest version (#7551)

Part of #7550

This PR makes appropriate corrections to run pylint on Python 3.

Why not keeping the dependencies unchanged and just run pylint on Python 3?
Because the old version of pylint breaks horribly on Python 3 because of unsupported version of astroid.

Why updating pylint + astroid to the latest version ?
Because this version only fixes some internal errors occuring during the lint of Certbot code, and is also ready to run gracefully on Python 3.8.

Why upgrading mypy ?
Because the old version does not support the new version of astroid required to run pylint correctly.

Why not upgrading mypy to its latest version ?
Because this latest version includes a new typshed version, that adds a lot of new type definitions, and brings dozens of new errors on the Certbot codebase. I would like to fix that in a future PR.

That said so, the work has been to find the correct set of new dependency versions, then configure pylint for sane configuration errors in our situation, disable irrelevant lintings errors, then fixing (or ignoring for good reason) the remaining mypy errors.

I also made PyLint and MyPy checks run correctly on Windows.

* Start configuration

* Reconfigure travis

* Suspend a check specific to python 3. Start fixing code.

* Repair call_args

* Fix return + elif lints

* Reconfigure development to run mainly on python3

* Remove incompatible Python 3.4 jobs

* Suspend pylint in some assertions

* Remove pylint in dev

* Take first mypy that supports typed-ast>=1.4.0 to limit the migration path

* Various return + else lint errors

* Find a set of deps that is working with current mypy version

* Update local oldest requirements

* Remove all current pylint errors

* Rebuild letsencrypt-auto

* Update mypy to fix pylint with new astroid version, and fix mypy issues

* Explain type: ignore

* Reconfigure tox, fix none path

* Simplify pinning

* Remove useless directive

* Remove debugging code

* Remove continue

* Update requirements

* Disable unsubscriptable-object check

* Disable one check, enabling two more

* Plug certbot dev version for oldest requirements

* Remove useless disable directives

* Remove useless no-member disable

* Remove no-else-* checks. Use elif in symetric branches.

* Add back assertion

* Add new line

* Remove unused pylint disable

* Remove other pylint disable
This commit is contained in:
Adrien Ferrand
2019-12-10 14:12:50 -08:00
committed by Brad Warren
parent e048da1e38
commit 9e5bca4bbf
99 changed files with 304 additions and 344 deletions
+13 -4
View File
@@ -24,6 +24,11 @@ persistent=yes
# usually to register additional checkers. # usually to register additional checkers.
load-plugins=linter_plugin load-plugins=linter_plugin
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=pywintypes,win32api,win32file,win32security
[MESSAGES CONTROL] [MESSAGES CONTROL]
@@ -41,10 +46,14 @@ load-plugins=linter_plugin
# --enable=similarities". If you want to run only the classes checker, but have # --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes # no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W" # --disable=W"
disable=fixme,locally-disabled,locally-enabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,no-self-use,invalid-name,cyclic-import,duplicate-code,design # CERTBOT COMMENT
# abstract-class-not-used cannot be disabled locally (at least in # 1) Once certbot codebase is claimed to be compatible exclusively with Python 3,
# pylint 1.4.1), same for abstract-class-little-used # the useless-object-inheritance check can be enabled again, and code fixed accordingly.
# 2) Check unsubscriptable-object tends to create a lot of false positives. Let's disable it.
# See https://github.com/PyCQA/pylint/issues/1498.
# 3) Same as point 2 for no-value-for-parameter.
# See https://github.com/PyCQA/pylint/issues/2820.
disable=fixme,locally-disabled,locally-enabled,bad-continuation,no-self-use,invalid-name,cyclic-import,duplicate-code,design,import-outside-toplevel,useless-object-inheritance,unsubscriptable-object,no-value-for-parameter,no-else-return,no-else-raise,no-else-break,no-else-continue
[REPORTS] [REPORTS]
+1 -4
View File
@@ -46,12 +46,9 @@ matrix:
- python: "2.7" - python: "2.7"
env: TOXENV=py27-cover FYI="py27 tests + code coverage" env: TOXENV=py27-cover FYI="py27 tests + code coverage"
- python: "2.7" - python: "3.7"
env: TOXENV=lint env: TOXENV=lint
<<: *not-on-master <<: *not-on-master
- python: "3.4"
env: TOXENV=mypy
<<: *not-on-master
- python: "3.5" - python: "3.5"
env: TOXENV=mypy env: TOXENV=mypy
<<: *not-on-master <<: *not-on-master
+4 -5
View File
@@ -6,16 +6,15 @@ EXPOSE 80 443
WORKDIR /opt/certbot/src WORKDIR /opt/certbot/src
# TODO: Install Apache/Nginx for plugin development.
COPY . . COPY . .
RUN apt-get update && \ RUN apt-get update && \
apt-get install apache2 git nginx-light -y && \ apt-get install apache2 git python3-dev python3-venv gcc libaugeas0 \
letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ libssl-dev libffi-dev ca-certificates openssl nginx-light -y && \
apt-get clean && \ apt-get clean && \
rm -rf /var/lib/apt/lists/* \ rm -rf /var/lib/apt/lists/* \
/tmp/* \ /tmp/* \
/var/tmp/* /var/tmp/*
RUN VENV_NAME="../venv" python tools/venv.py RUN VENV_NAME="../venv3" python3 tools/venv3.py
ENV PATH /opt/certbot/venv/bin:$PATH ENV PATH /opt/certbot/venv3/bin:$PATH
+2 -3
View File
@@ -54,8 +54,7 @@ class UnrecognizedChallenge(Challenge):
object.__setattr__(self, "jobj", jobj) object.__setattr__(self, "jobj", jobj)
def to_partial_json(self): def to_partial_json(self):
# pylint: disable=no-member return self.jobj # pylint: disable=no-member
return self.jobj
@classmethod @classmethod
def from_json(cls, jobj): def from_json(cls, jobj):
@@ -113,7 +112,7 @@ class KeyAuthorizationChallengeResponse(ChallengeResponse):
:rtype: bool :rtype: bool
""" """
parts = self.key_authorization.split('.') # pylint: disable=no-member parts = self.key_authorization.split('.')
if len(parts) != 2: if len(parts) != 2:
logger.debug("Key authorization (%r) is not well formed", logger.debug("Key authorization (%r) is not well formed",
self.key_authorization) self.key_authorization)
+5 -11
View File
@@ -34,7 +34,6 @@ logger = logging.getLogger(__name__)
# https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning # https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
if sys.version_info < (2, 7, 9): # pragma: no cover if sys.version_info < (2, 7, 9): # pragma: no cover
try: try:
# pylint: disable=no-member
requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3() # type: ignore requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3() # type: ignore
except AttributeError: except AttributeError:
import urllib3.contrib.pyopenssl # pylint: disable=import-error import urllib3.contrib.pyopenssl # pylint: disable=import-error
@@ -280,7 +279,6 @@ class Client(ClientBase):
assert response.status_code == http_client.CREATED assert response.status_code == http_client.CREATED
# "Instance of 'Field' has no key/contact member" bug: # "Instance of 'Field' has no key/contact member" bug:
# pylint: disable=no-member
return self._regr_from_response(response) return self._regr_from_response(response)
def query_registration(self, regr): def query_registration(self, regr):
@@ -465,7 +463,6 @@ class Client(ClientBase):
updated[authzr] = updated_authzr updated[authzr] = updated_authzr
attempts[authzr] += 1 attempts[authzr] += 1
# pylint: disable=no-member
if updated_authzr.body.status not in ( if updated_authzr.body.status not in (
messages.STATUS_VALID, messages.STATUS_INVALID): messages.STATUS_VALID, messages.STATUS_INVALID):
if attempts[authzr] < max_attempts: if attempts[authzr] < max_attempts:
@@ -606,7 +603,6 @@ class ClientV2(ClientBase):
if response.status_code == 200 and 'Location' in response.headers: if response.status_code == 200 and 'Location' in response.headers:
raise errors.ConflictError(response.headers.get('Location')) raise errors.ConflictError(response.headers.get('Location'))
# "Instance of 'Field' has no key/contact member" bug: # "Instance of 'Field' has no key/contact member" bug:
# pylint: disable=no-member
regr = self._regr_from_response(response) regr = self._regr_from_response(response)
self.net.account = regr self.net.account = regr
return regr return regr
@@ -730,7 +726,7 @@ class ClientV2(ClientBase):
for authzr in responses: for authzr in responses:
if authzr.body.status != messages.STATUS_VALID: if authzr.body.status != messages.STATUS_VALID:
for chall in authzr.body.challenges: for chall in authzr.body.challenges:
if chall.error != None: if chall.error is not None:
failed.append(authzr) failed.append(authzr)
if failed: if failed:
raise errors.ValidationError(failed) raise errors.ValidationError(failed)
@@ -1125,10 +1121,9 @@ class ClientNetwork(object):
err_regex = r".*host='(\S*)'.*Max retries exceeded with url\: (\/\w*).*(\[Errno \d+\])([A-Za-z ]*)" err_regex = r".*host='(\S*)'.*Max retries exceeded with url\: (\/\w*).*(\[Errno \d+\])([A-Za-z ]*)"
m = re.match(err_regex, str(e)) m = re.match(err_regex, str(e))
if m is None: if m is None:
raise # pragma: no cover raise # pragma: no cover
else: host, path, _err_no, err_msg = m.groups()
host, path, _err_no, err_msg = m.groups() raise ValueError("Requesting {0}{1}:{2}".format(host, path, err_msg))
raise ValueError("Requesting {0}{1}:{2}".format(host, path, err_msg))
# If content is DER, log the base64 of it instead of raw bytes, to keep # If content is DER, log the base64 of it instead of raw bytes, to keep
# binary data out of the logs. # binary data out of the logs.
@@ -1194,8 +1189,7 @@ class ClientNetwork(object):
if error.code == 'badNonce': if error.code == 'badNonce':
logger.debug('Retrying request after error:\n%s', error) logger.debug('Retrying request after error:\n%s', error)
return self._post_once(*args, **kwargs) return self._post_once(*args, **kwargs)
else: raise
raise
def _post_once(self, url, obj, content_type=JOSE_CONTENT_TYPE, def _post_once(self, url, obj, content_type=JOSE_CONTENT_TYPE,
acme_version=1, **kwargs): acme_version=1, **kwargs):
+13 -3
View File
@@ -29,7 +29,12 @@ class NonceError(ClientError):
class BadNonce(NonceError): class BadNonce(NonceError):
"""Bad nonce error.""" """Bad nonce error."""
def __init__(self, nonce, error, *args, **kwargs): def __init__(self, nonce, error, *args, **kwargs):
super(BadNonce, self).__init__(*args, **kwargs) # MyPy complains here that there is too many arguments for BaseException constructor.
# This is an error fixed in typeshed, see https://github.com/python/mypy/issues/4183
# The fix is included in MyPy>=0.740, but upgrading it would bring dozen of errors due to
# new types definitions. So we ignore the error until the code base is fixed to match
# with MyPy>=0.740 referential.
super(BadNonce, self).__init__(*args, **kwargs) # type: ignore
self.nonce = nonce self.nonce = nonce
self.error = error self.error = error
@@ -48,7 +53,8 @@ class MissingNonce(NonceError):
""" """
def __init__(self, response, *args, **kwargs): def __init__(self, response, *args, **kwargs):
super(MissingNonce, self).__init__(*args, **kwargs) # See comment in BadNonce constructor above for an explanation of type: ignore here.
super(MissingNonce, self).__init__(*args, **kwargs) # type: ignore
self.response = response self.response = response
def __str__(self): def __str__(self):
@@ -83,6 +89,7 @@ class PollError(ClientError):
return '{0}(exhausted={1!r}, updated={2!r})'.format( return '{0}(exhausted={1!r}, updated={2!r})'.format(
self.__class__.__name__, self.exhausted, self.updated) self.__class__.__name__, self.exhausted, self.updated)
class ValidationError(Error): class ValidationError(Error):
"""Error for authorization failures. Contains a list of authorization """Error for authorization failures. Contains a list of authorization
resources, each of which is invalid and should have an error field. resources, each of which is invalid and should have an error field.
@@ -91,9 +98,11 @@ class ValidationError(Error):
self.failed_authzrs = failed_authzrs self.failed_authzrs = failed_authzrs
super(ValidationError, self).__init__() super(ValidationError, self).__init__()
class TimeoutError(Error):
class TimeoutError(Error): # pylint: disable=redefined-builtin
"""Error for when polling an authorization or an order times out.""" """Error for when polling an authorization or an order times out."""
class IssuanceError(Error): class IssuanceError(Error):
"""Error sent by the server after requesting issuance of a certificate.""" """Error sent by the server after requesting issuance of a certificate."""
@@ -105,6 +114,7 @@ class IssuanceError(Error):
self.error = error self.error = error
super(IssuanceError, self).__init__() super(IssuanceError, self).__init__()
class ConflictError(ClientError): class ConflictError(ClientError):
"""Error for when the server returns a 409 (Conflict) HTTP status. """Error for when the server returns a 409 (Conflict) HTTP status.
+1 -1
View File
@@ -40,7 +40,7 @@ class Signature(jose.Signature):
class JWS(jose.JWS): class JWS(jose.JWS):
"""ACME-specific JWS. Includes none, url, and kid in protected header.""" """ACME-specific JWS. Includes none, url, and kid in protected header."""
signature_cls = Signature signature_cls = Signature
__slots__ = jose.JWS._orig_slots # pylint: disable=no-member __slots__ = jose.JWS._orig_slots
@classmethod @classmethod
# pylint: disable=arguments-differ # pylint: disable=arguments-differ
+1 -1
View File
@@ -146,7 +146,7 @@ class _Constant(jose.JSONDeSerializable, Hashable): # type: ignore
if jobj not in cls.POSSIBLE_NAMES: # pylint: disable=unsupported-membership-test if jobj not in cls.POSSIBLE_NAMES: # pylint: disable=unsupported-membership-test
raise jose.DeserializationError( raise jose.DeserializationError(
'{0} not recognized'.format(cls.__name__)) '{0} not recognized'.format(cls.__name__))
return cls.POSSIBLE_NAMES[jobj] # pylint: disable=unsubscriptable-object return cls.POSSIBLE_NAMES[jobj]
def __repr__(self): def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, self.name) return '{0}({1})'.format(self.__class__.__name__, self.name)
+1 -2
View File
@@ -44,7 +44,7 @@ class TLSServer(socketserver.TCPServer):
return socketserver.TCPServer.server_bind(self) return socketserver.TCPServer.server_bind(self)
class ACMEServerMixin: # pylint: disable=old-style-class class ACMEServerMixin:
"""ACME server common settings mixin.""" """ACME server common settings mixin."""
# TODO: c.f. #858 # TODO: c.f. #858
server_version = "ACME client standalone challenge solver" server_version = "ACME client standalone challenge solver"
@@ -105,7 +105,6 @@ class BaseDualNetworkedServers(object):
"""Wraps socketserver.TCPServer.serve_forever""" """Wraps socketserver.TCPServer.serve_forever"""
for server in self.servers: for server in self.servers:
thread = threading.Thread( thread = threading.Thread(
# pylint: disable=no-member
target=server.serve_forever) target=server.serve_forever)
thread.start() thread.start()
self.threads.append(thread) self.threads.append(thread)
+1 -2
View File
@@ -4,7 +4,7 @@ import unittest
import josepy as jose import josepy as jose
import mock import mock
import requests import requests
from six.moves.urllib import parse as urllib_parse # pylint: disable=relative-import from six.moves.urllib import parse as urllib_parse
import test_util import test_util
@@ -18,7 +18,6 @@ class ChallengeTest(unittest.TestCase):
from acme.challenges import Challenge from acme.challenges import Challenge
from acme.challenges import UnrecognizedChallenge from acme.challenges import UnrecognizedChallenge
chall = UnrecognizedChallenge({"type": "foo"}) chall = UnrecognizedChallenge({"type": "foo"})
# pylint: disable=no-member
self.assertEqual(chall, Challenge.from_json(chall.jobj)) self.assertEqual(chall, Challenge.from_json(chall.jobj))
+7 -7
View File
@@ -61,7 +61,7 @@ class ClientTestBase(unittest.TestCase):
self.contact = ('mailto:cert-admin@example.com', 'tel:+12025551212') self.contact = ('mailto:cert-admin@example.com', 'tel:+12025551212')
reg = messages.Registration( reg = messages.Registration(
contact=self.contact, key=KEY.public_key()) contact=self.contact, key=KEY.public_key())
the_arg = dict(reg) # type: Dict the_arg = dict(reg) # type: Dict
self.new_reg = messages.NewRegistration(**the_arg) self.new_reg = messages.NewRegistration(**the_arg)
self.regr = messages.RegistrationResource( self.regr = messages.RegistrationResource(
body=reg, uri='https://www.letsencrypt-demo.org/acme/reg/1') body=reg, uri='https://www.letsencrypt-demo.org/acme/reg/1')
@@ -963,8 +963,8 @@ class ClientNetworkTest(unittest.TestCase):
def test_check_response_not_ok_jobj_error(self): def test_check_response_not_ok_jobj_error(self):
self.response.ok = False self.response.ok = False
self.response.json.return_value = messages.Error( self.response.json.return_value = messages.Error.with_code(
detail='foo', typ='serverInternal', title='some title').to_json() 'serverInternal', detail='foo', title='some title').to_json()
# pylint: disable=protected-access # pylint: disable=protected-access
self.assertRaises( self.assertRaises(
messages.Error, self.net._check_response, self.response) messages.Error, self.net._check_response, self.response)
@@ -989,7 +989,7 @@ class ClientNetworkTest(unittest.TestCase):
self.response.json.side_effect = ValueError self.response.json.side_effect = ValueError
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']: for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
self.response.headers['Content-Type'] = response_ct self.response.headers['Content-Type'] = response_ct
# pylint: disable=protected-access,no-value-for-parameter # pylint: disable=protected-access
self.assertEqual( self.assertEqual(
self.response, self.net._check_response(self.response)) self.response, self.net._check_response(self.response))
@@ -1003,7 +1003,7 @@ class ClientNetworkTest(unittest.TestCase):
self.response.json.return_value = {} self.response.json.return_value = {}
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']: for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
self.response.headers['Content-Type'] = response_ct self.response.headers['Content-Type'] = response_ct
# pylint: disable=protected-access,no-value-for-parameter # pylint: disable=protected-access
self.assertEqual( self.assertEqual(
self.response, self.net._check_response(self.response)) self.response, self.net._check_response(self.response))
@@ -1128,8 +1128,8 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
self.response.headers = {} self.response.headers = {}
self.response.links = {} self.response.links = {}
self.response.checked = False self.response.checked = False
self.acmev1_nonce_response = mock.MagicMock(ok=False, self.acmev1_nonce_response = mock.MagicMock(
status_code=http_client.METHOD_NOT_ALLOWED) ok=False, status_code=http_client.METHOD_NOT_ALLOWED)
self.acmev1_nonce_response.headers = {} self.acmev1_nonce_response.headers = {}
self.obj = mock.MagicMock() self.obj = mock.MagicMock()
self.wrapped_obj = mock.MagicMock() self.wrapped_obj = mock.MagicMock()
+1 -2
View File
@@ -38,7 +38,6 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
self.server = _TestServer(('', 0), socketserver.BaseRequestHandler) self.server = _TestServer(('', 0), socketserver.BaseRequestHandler)
self.port = self.server.socket.getsockname()[1] self.port = self.server.socket.getsockname()[1]
self.server_thread = threading.Thread( self.server_thread = threading.Thread(
# pylint: disable=no-member
target=self.server.handle_request) target=self.server.handle_request)
def tearDown(self): def tearDown(self):
@@ -65,7 +64,7 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
def test_probe_connection_error(self): def test_probe_connection_error(self):
# pylint has a hard time with six # pylint has a hard time with six
self.server.server_close() # pylint: disable=no-member self.server.server_close()
original_timeout = socket.getdefaulttimeout() original_timeout = socket.getdefaulttimeout()
try: try:
socket.setdefaulttimeout(1) socket.setdefaulttimeout(1)
+4 -5
View File
@@ -21,11 +21,10 @@ class JoseTest(unittest.TestCase):
# We use the imports below with eval, but pylint doesn't # We use the imports below with eval, but pylint doesn't
# understand that. # understand that.
# pylint: disable=eval-used,unused-variable import acme # pylint: disable=unused-import
import acme import josepy # pylint: disable=unused-import
import josepy acme_jose_mod = eval(acme_jose_path) # pylint: disable=eval-used
acme_jose_mod = eval(acme_jose_path) josepy_mod = eval(josepy_path) # pylint: disable=eval-used
josepy_mod = eval(josepy_path)
self.assertIs(acme_jose_mod, josepy_mod) self.assertIs(acme_jose_mod, josepy_mod)
self.assertIs(getattr(acme_jose_mod, attribute), getattr(josepy_mod, attribute)) self.assertIs(getattr(acme_jose_mod, attribute), getattr(josepy_mod, attribute))
+8 -12
View File
@@ -18,8 +18,7 @@ class ErrorTest(unittest.TestCase):
def setUp(self): def setUp(self):
from acme.messages import Error, ERROR_PREFIX from acme.messages import Error, ERROR_PREFIX
self.error = Error( self.error = Error.with_code('malformed', detail='foo', title='title')
detail='foo', typ=ERROR_PREFIX + 'malformed', title='title')
self.jobj = { self.jobj = {
'detail': 'foo', 'detail': 'foo',
'title': 'some title', 'title': 'some title',
@@ -27,7 +26,6 @@ class ErrorTest(unittest.TestCase):
} }
self.error_custom = Error(typ='custom', detail='bar') self.error_custom = Error(typ='custom', detail='bar')
self.empty_error = Error() self.empty_error = Error()
self.jobj_custom = {'type': 'custom', 'detail': 'bar'}
def test_default_typ(self): def test_default_typ(self):
from acme.messages import Error from acme.messages import Error
@@ -42,8 +40,7 @@ class ErrorTest(unittest.TestCase):
hash(Error.from_json(self.error.to_json())) hash(Error.from_json(self.error.to_json()))
def test_description(self): def test_description(self):
self.assertEqual( self.assertEqual('The request message was malformed', self.error.description)
'The request message was malformed', self.error.description)
self.assertTrue(self.error_custom.description is None) self.assertTrue(self.error_custom.description is None)
def test_code(self): def test_code(self):
@@ -53,17 +50,17 @@ class ErrorTest(unittest.TestCase):
self.assertEqual(None, Error().code) self.assertEqual(None, Error().code)
def test_is_acme_error(self): def test_is_acme_error(self):
from acme.messages import is_acme_error from acme.messages import is_acme_error, Error
self.assertTrue(is_acme_error(self.error)) self.assertTrue(is_acme_error(self.error))
self.assertFalse(is_acme_error(self.error_custom)) self.assertFalse(is_acme_error(self.error_custom))
self.assertFalse(is_acme_error(Error()))
self.assertFalse(is_acme_error(self.empty_error)) self.assertFalse(is_acme_error(self.empty_error))
self.assertFalse(is_acme_error("must pet all the {dogs|rabbits}")) self.assertFalse(is_acme_error("must pet all the {dogs|rabbits}"))
def test_unicode_error(self): def test_unicode_error(self):
from acme.messages import Error, ERROR_PREFIX, is_acme_error from acme.messages import Error, is_acme_error
arabic_error = Error( arabic_error = Error.with_code(
detail=u'\u0639\u062f\u0627\u0644\u0629', typ=ERROR_PREFIX + 'malformed', 'malformed', detail=u'\u0639\u062f\u0627\u0644\u0629', title='title')
title='title')
self.assertTrue(is_acme_error(arabic_error)) self.assertTrue(is_acme_error(arabic_error))
def test_with_code(self): def test_with_code(self):
@@ -304,8 +301,7 @@ class ChallengeBodyTest(unittest.TestCase):
from acme.messages import Error from acme.messages import Error
from acme.messages import STATUS_INVALID from acme.messages import STATUS_INVALID
self.status = STATUS_INVALID self.status = STATUS_INVALID
error = Error(typ='urn:ietf:params:acme:error:serverInternal', error = Error.with_code('serverInternal', detail='Unable to communicate with DNS server')
detail='Unable to communicate with DNS server')
self.challb = ChallengeBody( self.challb = ChallengeBody(
uri='http://challb', chall=self.chall, status=self.status, uri='http://challb', chall=self.chall, status=self.status,
error=error) error=error)
+1 -2
View File
@@ -25,8 +25,7 @@ def _guess_loader(filename, loader_pem, loader_der):
return loader_pem return loader_pem
elif ext.lower() == '.der': elif ext.lower() == '.der':
return loader_der return loader_der
else: # pragma: no cover raise ValueError("Loader could not be recognized based on extension") # pragma: no cover
raise ValueError("Loader could not be recognized based on extension")
def load_cert(*names): def load_cert(*names):
@@ -449,7 +449,7 @@ class ApacheConfigurator(common.Installer):
filtered_vhosts[name] = vhost filtered_vhosts[name] = vhost
# Only unique VHost objects # Only unique VHost objects
dialog_input = set([vhost for vhost in filtered_vhosts.values()]) dialog_input = set(filtered_vhosts.values())
# Ask the user which of names to enable, expect list of names back # Ask the user which of names to enable, expect list of names back
dialog_output = display_ops.select_vhost_multiple(list(dialog_input)) dialog_output = display_ops.select_vhost_multiple(list(dialog_input))
@@ -600,9 +600,9 @@ class ApacheConfigurator(common.Installer):
"in the Apache config.", "in the Apache config.",
target_name) target_name)
raise errors.PluginError("No vhost selected") raise errors.PluginError("No vhost selected")
elif temp: if temp:
return vhost return vhost
elif not vhost.ssl: if not vhost.ssl:
addrs = self._get_proposed_addrs(vhost, "443") addrs = self._get_proposed_addrs(vhost, "443")
# TODO: Conflicts is too conservative # TODO: Conflicts is too conservative
if not any(vhost.enabled and vhost.conflicts(addrs) for if not any(vhost.enabled and vhost.conflicts(addrs) for
@@ -951,13 +951,12 @@ class ApacheConfigurator(common.Installer):
loc = parser.get_aug_path(self.parser.loc["name"]) loc = parser.get_aug_path(self.parser.loc["name"])
if addr.get_port() == "443": if addr.get_port() == "443":
path = self.parser.add_dir_to_ifmodssl( self.parser.add_dir_to_ifmodssl(
loc, "NameVirtualHost", [str(addr)]) loc, "NameVirtualHost", [str(addr)])
else: else:
path = self.parser.add_dir(loc, "NameVirtualHost", [str(addr)]) self.parser.add_dir(loc, "NameVirtualHost", [str(addr)])
msg = ("Setting %s to be NameBasedVirtualHost\n" msg = "Setting {0} to be NameBasedVirtualHost\n".format(addr)
"\tDirective added to %s\n" % (addr, path))
logger.debug(msg) logger.debug(msg)
self.save_notes += msg self.save_notes += msg
@@ -1365,12 +1364,9 @@ class ApacheConfigurator(common.Installer):
result.append(comment) result.append(comment)
sift = True sift = True
result.append('\n'.join( result.append('\n'.join(['# ' + l for l in chunk]))
['# ' + l for l in chunk]))
continue
else: else:
result.append('\n'.join(chunk)) result.append('\n'.join(chunk))
continue
return result, sift return result, sift
def _get_vhost_block(self, vhost): def _get_vhost_block(self, vhost):
@@ -2513,4 +2509,4 @@ class ApacheConfigurator(common.Installer):
self._autohsts_save_state() self._autohsts_save_state()
AutoHSTSEnhancement.register(ApacheConfigurator) # pylint: disable=no-member AutoHSTSEnhancement.register(ApacheConfigurator)
@@ -194,8 +194,8 @@ class ApacheHttp01(common.ChallengePerformer):
if vhost not in self.moded_vhosts: if vhost not in self.moded_vhosts:
logger.debug( logger.debug(
"Adding a temporary challenge validation Include for name: %s " + "Adding a temporary challenge validation Include for name: %s in: %s",
"in: %s", vhost.name, vhost.filep) vhost.name, vhost.filep)
self.configurator.parser.add_dir_beginning( self.configurator.parser.add_dir_beginning(
vhost.path, "Include", self.challenge_conf_pre) vhost.path, "Include", self.challenge_conf_pre)
self.configurator.parser.add_dir( self.configurator.parser.add_dir(
@@ -70,15 +70,14 @@ class DebianConfigurator(configurator.ApacheConfigurator):
# Already in shape # Already in shape
vhost.enabled = True vhost.enabled = True
return None return None
else: logger.warning(
logger.warning( "Could not symlink %s to %s, got error: %s", enabled_path,
"Could not symlink %s to %s, got error: %s", enabled_path, vhost.filep, err.strerror)
vhost.filep, err.strerror) errstring = ("Encountered error while trying to enable a " +
errstring = ("Encountered error while trying to enable a " + "newly created VirtualHost located at {0} by " +
"newly created VirtualHost located at {0} by " + "linking to it from {1}")
"linking to it from {1}") raise errors.NotSupportedError(errstring.format(vhost.filep,
raise errors.NotSupportedError(errstring.format(vhost.filep, enabled_path))
enabled_path))
vhost.enabled = True vhost.enabled = True
logger.info("Enabling available site: %s", vhost.filep) logger.info("Enabling available site: %s", vhost.filep)
self.save_notes += "Enabled site %s\n" % vhost.filep self.save_notes += "Enabled site %s\n" % vhost.filep
@@ -284,8 +284,8 @@ class ApacheParser(object):
mods.add(mod_name) mods.add(mod_name)
mods.add(os.path.basename(mod_filename)[:-2] + "c") mods.add(os.path.basename(mod_filename)[:-2] + "c")
else: else:
logger.debug("Could not read LoadModule directive from " + logger.debug("Could not read LoadModule directive from Augeas path: %s",
"Augeas path: %s", match_name[6:]) match_name[6:])
self.modules.update(mods) self.modules.update(mods)
def update_runtime_variables(self): def update_runtime_variables(self):
@@ -625,7 +625,7 @@ class ApacheParser(object):
# https://httpd.apache.org/docs/2.4/mod/core.html#include # https://httpd.apache.org/docs/2.4/mod/core.html#include
for match in matches: for match in matches:
dir_ = self.aug.get(match).lower() dir_ = self.aug.get(match).lower()
if dir_ == "include" or dir_ == "includeoptional": if dir_ in ("include", "includeoptional"):
ordered_matches.extend(self.find_dir( ordered_matches.extend(self.find_dir(
directive, arg, directive, arg,
self._get_include_path(self.get_arg(match + "/arg")), self._get_include_path(self.get_arg(match + "/arg")),
@@ -665,8 +665,7 @@ class ApacheParser(object):
# e.g. strip now, not later # e.g. strip now, not later
if not value: if not value:
return None return None
else: value = value.strip("'\"")
value = value.strip("'\"")
variables = ApacheParser.arg_var_interpreter.findall(value) variables = ApacheParser.arg_var_interpreter.findall(value)
+1 -1
View File
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.29.0 acme[dev]==0.29.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.29.0', 'acme>=0.29.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'mock', 'mock',
'python-augeas', 'python-augeas',
'setuptools', 'setuptools',
@@ -43,8 +43,7 @@ class Proxy(object):
method = getattr(self._configurator, name, None) method = getattr(self._configurator, name, None)
if callable(method): if callable(method):
return method return method
else: raise AttributeError()
raise AttributeError()
def has_more_configs(self): def has_more_configs(self):
"""Returns true if there are more configs to test""" """Returns true if there are more configs to test"""
@@ -82,8 +81,7 @@ class Proxy(object):
"""Returns the set of domain names that the plugin should find""" """Returns the set of domain names that the plugin should find"""
if self._all_names: if self._all_names:
return self._all_names return self._all_names
else: raise errors.Error("No configuration file loaded")
raise errors.Error("No configuration file loaded")
def get_testable_domain_names(self): def get_testable_domain_names(self):
"""Returns the set of domain names that can be tested against""" """Returns the set of domain names that can be tested against"""
@@ -10,7 +10,6 @@ import tempfile
import time import time
import OpenSSL import OpenSSL
from six.moves import xrange # pylint: disable=import-error,redefined-builtin
from urllib3.util import connection from urllib3.util import connection
from acme import challenges from acme import challenges
@@ -57,26 +56,27 @@ def test_authenticator(plugin, config, temp_dir):
return False return False
success = True success = True
for i in xrange(len(responses)): for i, response in enumerate(responses):
if not responses[i]: achall = achalls[i]
if not response:
logger.error( logger.error(
"Plugin failed to complete %s for %s in %s", "Plugin failed to complete %s for %s in %s",
type(achalls[i]), achalls[i].domain, config) type(achall), achall.domain, config)
success = False success = False
elif isinstance(responses[i], challenges.HTTP01Response): elif isinstance(response, challenges.HTTP01Response):
# We fake the DNS resolution to ensure that any domain is resolved # We fake the DNS resolution to ensure that any domain is resolved
# to the local HTTP server setup for the compatibility tests # to the local HTTP server setup for the compatibility tests
with _fake_dns_resolution("127.0.0.1"): with _fake_dns_resolution("127.0.0.1"):
verified = responses[i].simple_verify( verified = response.simple_verify(
achalls[i].chall, achalls[i].domain, achall.chall, achall.domain,
util.JWK.public_key(), port=plugin.http_port) util.JWK.public_key(), port=plugin.http_port)
if verified: if verified:
logger.info( logger.info(
"http-01 verification for %s succeeded", achalls[i].domain) "http-01 verification for %s succeeded", achall.domain)
else: else:
logger.error( logger.error(
"**** http-01 verification for %s in %s failed", "**** http-01 verification for %s in %s failed",
achalls[i].domain, config) achall.domain, config)
success = False success = False
if success: if success:
@@ -89,8 +89,7 @@ def test_authenticator(plugin, config, temp_dir):
if _dirs_are_unequal(config, backup): if _dirs_are_unequal(config, backup):
logger.error("Challenge cleanup failed for %s", config) logger.error("Challenge cleanup failed for %s", config)
return False return False
else: logger.info("Challenge cleanup succeeded")
logger.info("Challenge cleanup succeeded")
return success return success
@@ -305,7 +304,7 @@ def get_args():
"-e", "--enhance", action="store_true", help="tests the enhancements " "-e", "--enhance", action="store_true", help="tests the enhancements "
"the plugin supports (implicitly includes installer tests)") "the plugin supports (implicitly includes installer tests)")
for plugin in PLUGINS.itervalues(): for plugin in PLUGINS.values():
plugin.add_parser_arguments(parser) plugin.add_parser_arguments(parser)
args = parser.parse_args() args = parser.parse_args()
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.29.0 acme[dev]==0.29.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.29.0', 'acme>=0.29.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'cloudflare>=1.5.1', 'cloudflare>=1.5.1',
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.29.0 acme[dev]==0.29.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.29.0', 'acme>=0.29.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'mock', 'mock',
'python-digitalocean>=1.11', 'python-digitalocean>=1.11',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -11,7 +11,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'mock', 'mock',
'setuptools', 'setuptools',
'zope.interface', 'zope.interface',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -9,7 +9,7 @@ version = '1.1.0.dev0'
# Please update tox.ini when modifying dependency version requirements # Please update tox.ini when modifying dependency version requirements
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.1.22', 'dns-lexicon>=2.1.22',
'mock', 'mock',
'setuptools', 'setuptools',
@@ -235,7 +235,7 @@ class _GoogleClient(object):
:rtype: `list` of `string` or `None` :rtype: `list` of `string` or `None`
""" """
rrs_request = self.dns.resourceRecordSets() # pylint: disable=no-member rrs_request = self.dns.resourceRecordSets()
request = rrs_request.list(managedZone=zone_id, project=self.project_id) request = rrs_request.list(managedZone=zone_id, project=self.project_id)
# Add dot as the API returns absolute domains # Add dot as the API returns absolute domains
record_name += "." record_name += "."
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.29.0 acme[dev]==0.29.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.29.0', 'acme>=0.29.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'google-api-python-client>=1.5.5', 'google-api-python-client>=1.5.5',
'mock', 'mock',
'oauth2client>=4.0', 'oauth2client>=4.0',
@@ -288,7 +288,6 @@ class GoogleClientTest(unittest.TestCase):
def test_get_existing_fallback(self, unused_credential_mock): def test_get_existing_fallback(self, unused_credential_mock):
client, unused_changes = self._setUp_client_with_mock( client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}]) [{'managedZones': [{'id': self.zone}]}])
# pylint: disable=no-member
mock_execute = client.dns.resourceRecordSets.return_value.list.return_value.execute mock_execute = client.dns.resourceRecordSets.return_value.list.return_value.execute
mock_execute.side_effect = API_ERROR mock_execute.side_effect = API_ERROR
@@ -1,4 +1,4 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
dns-lexicon==2.2.3 dns-lexicon==2.2.3
+1 -1
View File
@@ -9,7 +9,7 @@ version = '1.1.0.dev0'
# Please update tox.ini when modifying dependency version requirements # Please update tox.ini when modifying dependency version requirements
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.2.3', 'dns-lexicon>=2.2.3',
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name 'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,4 +1,4 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
dns-lexicon==2.7.14 dns-lexicon==2.7.14
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.7.14', # Correct proxy use on OVH provider 'dns-lexicon>=2.7.14', # Correct proxy use on OVH provider
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.29.0 acme[dev]==0.29.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.29.0', 'acme>=0.29.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dnspython', 'dnspython',
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.29.0 acme[dev]==0.29.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=0.29.0', 'acme>=0.29.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'boto3', 'boto3',
'mock', 'mock',
'setuptools', 'setuptools',
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==0.31.0 acme[dev]==0.31.0
certbot[dev]==0.39.0 -e certbot[dev]
+1 -1
View File
@@ -9,7 +9,7 @@ version = '1.1.0.dev0'
# Please update tox.ini when modifying dependency version requirements # Please update tox.ini when modifying dependency version requirements
install_requires = [ install_requires = [
'acme>=0.31.0', 'acme>=0.31.0',
'certbot>=0.39.0', 'certbot>=1.0.0.dev0',
'dns-lexicon>=2.1.23', 'dns-lexicon>=2.1.23',
'mock', 'mock',
'setuptools', 'setuptools',
@@ -283,7 +283,7 @@ class NginxConfigurator(common.Installer):
filtered_vhosts[name] = vhost filtered_vhosts[name] = vhost
# Only unique VHost objects # Only unique VHost objects
dialog_input = set([vhost for vhost in filtered_vhosts.values()]) dialog_input = set(filtered_vhosts.values())
# Ask the user which of names to enable, expect list of names back # Ask the user which of names to enable, expect list of names back
return_vhosts = display_ops.select_vhost_multiple(list(dialog_input)) return_vhosts = display_ops.select_vhost_multiple(list(dialog_input))
@@ -186,12 +186,11 @@ class UnspacedList(list):
""" """
if not isinstance(inbound, list): # str or None if not isinstance(inbound, list): # str or None
return (inbound, inbound) return inbound, inbound
else: else:
if not hasattr(inbound, "spaced"): if not hasattr(inbound, "spaced"):
inbound = UnspacedList(inbound) inbound = UnspacedList(inbound)
return (inbound, inbound.spaced) return inbound, inbound.spaced
def insert(self, i, x): def insert(self, i, x):
item, spaced_item = self._coerce(x) item, spaced_item = self._coerce(x)
@@ -275,7 +275,7 @@ class NginxParser(object):
for directive in server: for directive in server:
if not directive: if not directive:
continue continue
elif _is_ssl_on_directive(directive): if _is_ssl_on_directive(directive):
return True return True
return False return False
@@ -489,7 +489,7 @@ def get_best_match(target_name, names):
def _exact_match(target_name, name): def _exact_match(target_name, name):
return target_name == name or '.' + target_name == name return name in (target_name, '.' + target_name)
def _wildcard_match(target_name, name, start): def _wildcard_match(target_name, name, start):
@@ -507,7 +507,7 @@ def _wildcard_match(target_name, name, start):
# The first part must be a wildcard or blank, e.g. '.eff.org' # The first part must be a wildcard or blank, e.g. '.eff.org'
first = match_parts.pop(0) first = match_parts.pop(0)
if first != '*' and first != '': if first not in ('*', ''):
return False return False
target_name = '.'.join(parts) target_name = '.'.join(parts)
@@ -585,7 +585,7 @@ def comment_directive(block, location):
if isinstance(next_entry, list) and next_entry: if isinstance(next_entry, list) and next_entry:
if len(next_entry) >= 2 and next_entry[-2] == "#" and COMMENT in next_entry[-1]: if len(next_entry) >= 2 and next_entry[-2] == "#" and COMMENT in next_entry[-1]:
return return
elif isinstance(next_entry, nginxparser.UnspacedList): if isinstance(next_entry, nginxparser.UnspacedList):
next_entry = next_entry.spaced[0] next_entry = next_entry.spaced[0]
else: else:
next_entry = next_entry[0] next_entry = next_entry[0]
@@ -661,13 +661,12 @@ def _add_directive(block, directive, insert_at_top):
for included_directive in included_directives: for included_directive in included_directives:
included_dir_loc = _find_location(block, included_directive[0]) included_dir_loc = _find_location(block, included_directive[0])
included_dir_name = included_directive[0] included_dir_name = included_directive[0]
if not _is_whitespace_or_comment(included_directive) \ if (not _is_whitespace_or_comment(included_directive)
and not can_append(included_dir_loc, included_dir_name): and not can_append(included_dir_loc, included_dir_name)):
if block[included_dir_loc] != included_directive: if block[included_dir_loc] != included_directive:
raise errors.MisconfigurationError(err_fmt.format(included_directive, raise errors.MisconfigurationError(err_fmt.format(included_directive,
block[included_dir_loc])) block[included_dir_loc]))
else: _comment_out_directive(block, included_dir_loc, directive[1])
_comment_out_directive(block, included_dir_loc, directive[1])
if can_append(location, directive_name): if can_append(location, directive_name):
if insert_at_top: if insert_at_top:
+1 -1
View File
@@ -1,3 +1,3 @@
# Remember to update setup.py to match the package versions below. # Remember to update setup.py to match the package versions below.
acme[dev]==1.0.0 acme[dev]==1.0.0
certbot[dev]==1.0.0 -e certbot[dev]
+1 -1
View File
@@ -10,7 +10,7 @@ version = '1.1.0.dev0'
# acme/certbot version. # acme/certbot version.
install_requires = [ install_requires = [
'acme>=1.0.0', 'acme>=1.0.0',
'certbot>=1.0.0', 'certbot>=1.0.0.dev0',
'mock', 'mock',
'PyOpenSSL', 'PyOpenSSL',
'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary? 'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary?
+1 -1
View File
@@ -1091,7 +1091,7 @@ class DetermineDefaultServerRootTest(certbot_test_util.ConfigTestCase):
self.assertIn("/usr/local/etc/nginx", server_root) self.assertIn("/usr/local/etc/nginx", server_root)
self.assertIn("/etc/nginx", server_root) self.assertIn("/etc/nginx", server_root)
else: else:
self.assertTrue(server_root == "/etc/nginx" or server_root == "/usr/local/etc/nginx") self.assertTrue(server_root in ("/etc/nginx", "/usr/local/etc/nginx"))
if __name__ == "__main__": if __name__ == "__main__":
+10 -10
View File
@@ -49,16 +49,16 @@ class NginxParserTest(util.NginxTest):
""" """
nparser = parser.NginxParser(self.config_path) nparser = parser.NginxParser(self.config_path)
nparser.load() nparser.load()
self.assertEqual(set([nparser.abs_path(x) for x in self.assertEqual({nparser.abs_path(x) for x in
['foo.conf', 'nginx.conf', 'server.conf', ['foo.conf', 'nginx.conf', 'server.conf',
'sites-enabled/default', 'sites-enabled/default',
'sites-enabled/example.com', 'sites-enabled/example.com',
'sites-enabled/headers.com', 'sites-enabled/headers.com',
'sites-enabled/migration.com', 'sites-enabled/migration.com',
'sites-enabled/sslon.com', 'sites-enabled/sslon.com',
'sites-enabled/globalssl.com', 'sites-enabled/globalssl.com',
'sites-enabled/ipv6.com', 'sites-enabled/ipv6.com',
'sites-enabled/ipv6ssl.com']]), 'sites-enabled/ipv6ssl.com']},
set(nparser.parsed.keys())) set(nparser.parsed.keys()))
self.assertEqual([['server_name', 'somename', 'alias', 'another.alias']], self.assertEqual([['server_name', 'somename', 'alias', 'another.alias']],
nparser.parsed[nparser.abs_path('server.conf')]) nparser.parsed[nparser.abs_path('server.conf')])
+2 -3
View File
@@ -216,9 +216,8 @@ class AccountFileStorage(interfaces.AccountStorage):
else: else:
self._symlink_to_accounts_dir(prev_server_path, server_path) self._symlink_to_accounts_dir(prev_server_path, server_path)
return prev_loaded_account return prev_loaded_account
else: raise errors.AccountNotFound(
raise errors.AccountNotFound( "Account at %s does not exist" % account_dir_path)
"Account at %s does not exist" % account_dir_path)
try: try:
with open(self._regr_path(account_dir_path)) as regr_file: with open(self._regr_path(account_dir_path)) as regr_file:
+2 -3
View File
@@ -285,9 +285,8 @@ def challb_to_achall(challb, account_key, domain):
challb=challb, domain=domain, account_key=account_key) challb=challb, domain=domain, account_key=account_key)
elif isinstance(chall, challenges.DNS): elif isinstance(chall, challenges.DNS):
return achallenges.DNS(challb=challb, domain=domain) return achallenges.DNS(challb=challb, domain=domain)
else: raise errors.Error(
raise errors.Error( "Received unsupported challenge of type: {0}".format(chall.typ))
"Received unsupported challenge of type: {0}".format(chall.typ))
def gen_challenge_path(challbs, preferences, combinations): def gen_challenge_path(challbs, preferences, combinations):
+2 -2
View File
@@ -242,8 +242,8 @@ def match_and_check_overlaps(cli_config, acceptable_matches, match_func, rv_func
raise errors.Error("No match found for cert-path {0}!".format(cli_config.cert_path[0])) raise errors.Error("No match found for cert-path {0}!".format(cli_config.cert_path[0]))
elif len(matched) > 1: elif len(matched) > 1:
raise errors.OverlappingMatchFound() raise errors.OverlappingMatchFound()
else: return matched
return matched
def human_readable_cert_info(config, cert, skip_filter_checks=False): def human_readable_cert_info(config, cert, skip_filter_checks=False):
""" Returns a human readable description of info about a RenewableCert object""" """ Returns a human readable description of info about a RenewableCert object"""
+6 -7
View File
@@ -287,10 +287,9 @@ def flag_default(name):
def config_help(name, hidden=False): def config_help(name, hidden=False):
"""Extract the help message for an `.IConfig` attribute.""" """Extract the help message for an `.IConfig` attribute."""
# pylint: disable=no-member
if hidden: if hidden:
return argparse.SUPPRESS return argparse.SUPPRESS
field = interfaces.IConfig.__getitem__(name) # type: zope.interface.interface.Attribute # pylint: disable=no-value-for-parameter field = interfaces.IConfig.__getitem__(name) # type: zope.interface.interface.Attribute
return field.__doc__ return field.__doc__
@@ -674,7 +673,7 @@ class HelpfulArgumentParser(object):
parsed_args.actual_csr = (csr, typ) parsed_args.actual_csr = (csr, typ)
csr_domains = set([d.lower() for d in domains]) csr_domains = {d.lower() for d in domains}
config_domains = set(parsed_args.domains) config_domains = set(parsed_args.domains)
if csr_domains != config_domains: if csr_domains != config_domains:
raise errors.ConfigurationError( raise errors.ConfigurationError(
@@ -847,11 +846,11 @@ class HelpfulArgumentParser(object):
chosen_topic = "run" chosen_topic = "run"
if chosen_topic == "all": if chosen_topic == "all":
# Addition of condition closes #6209 (removal of duplicate route53 option). # Addition of condition closes #6209 (removal of duplicate route53 option).
return dict([(t, True) if t != 'certbot-route53:auth' else (t, False) return {t: t != 'certbot-route53:auth' for t in self.help_topics}
for t in self.help_topics])
elif not chosen_topic: elif not chosen_topic:
return dict([(t, False) for t in self.help_topics]) return {t: False for t in self.help_topics}
return dict([(t, t == chosen_topic) for t in self.help_topics]) return {t: t == chosen_topic for t in self.help_topics}
def _add_all_groups(helpful): def _add_all_groups(helpful):
helpful.add_group("automation", description="Flags for automating execution & other tweaks") helpful.add_group("automation", description="Flags for automating execution & other tweaks")
+3 -8
View File
@@ -224,11 +224,9 @@ def perform_registration(acme, config, tos_cb):
"Please ensure it is a valid email and attempt " "Please ensure it is a valid email and attempt "
"registration again." % config.email) "registration again." % config.email)
raise errors.Error(msg) raise errors.Error(msg)
else: config.email = display_ops.get_email(invalid=True)
config.email = display_ops.get_email(invalid=True) return perform_registration(acme, config, tos_cb)
return perform_registration(acme, config, tos_cb) raise
else:
raise
class Client(object): class Client(object):
@@ -360,7 +358,6 @@ class Client(object):
return self.obtain_certificate(successful_domains) return self.obtain_certificate(successful_domains)
else: else:
cert, chain = self.obtain_certificate_from_csr(csr, orderr) cert, chain = self.obtain_certificate_from_csr(csr, orderr)
return cert, chain, key, csr return cert, chain, key, csr
def _get_order_and_authorizations(self, csr_pem, best_effort): def _get_order_and_authorizations(self, csr_pem, best_effort):
@@ -393,8 +390,6 @@ class Client(object):
authzr = self.auth_handler.handle_authorizations(orderr, best_effort) authzr = self.auth_handler.handle_authorizations(orderr, best_effort)
return orderr.update(authorizations=authzr) return orderr.update(authorizations=authzr)
# pylint: disable=no-member
def obtain_and_enroll_certificate(self, domains, certname): def obtain_and_enroll_certificate(self, domains, certname):
"""Obtain and enroll certificate. """Obtain and enroll certificate.
+1 -1
View File
@@ -1,7 +1,7 @@
"""Certbot user-supplied configuration.""" """Certbot user-supplied configuration."""
import copy import copy
from six.moves.urllib import parse # pylint: disable=relative-import from six.moves.urllib import parse
import zope.interface import zope.interface
from certbot import errors from certbot import errors
+1 -1
View File
@@ -93,7 +93,7 @@ class ErrorHandler(object):
# SystemExit is ignored to properly handle forks that don't exec # SystemExit is ignored to properly handle forks that don't exec
if exec_type is SystemExit: if exec_type is SystemExit:
return retval return retval
elif exec_type is None: if exec_type is None:
if not self.call_on_regular_exit: if not self.call_on_regular_exit:
return retval return retval
elif exec_type is errors.SignalExit: elif exec_type is errors.SignalExit:
+3 -3
View File
@@ -102,9 +102,9 @@ def post_arg_parse_setup(config):
root_logger.addHandler(file_handler) root_logger.addHandler(file_handler)
root_logger.removeHandler(memory_handler) root_logger.removeHandler(memory_handler)
temp_handler = memory_handler.target temp_handler = memory_handler.target # pylint: disable=no-member
memory_handler.setTarget(file_handler) memory_handler.setTarget(file_handler) # pylint: disable=no-member
memory_handler.flush(force=True) memory_handler.flush(force=True) # pylint: disable=unexpected-keyword-arg
memory_handler.close() memory_handler.close()
temp_handler.close() temp_handler.close()
+29 -33
View File
@@ -121,7 +121,7 @@ def _get_and_save_cert(le_client, config, domains=None, certname=None, lineage=N
lineage = le_client.obtain_and_enroll_certificate(domains, certname) lineage = le_client.obtain_and_enroll_certificate(domains, certname)
if lineage is False: if lineage is False:
raise errors.Error("Certificate could not be obtained") raise errors.Error("Certificate could not be obtained")
elif lineage is not None: if lineage is not None:
hooks.deploy_hook(config, lineage.names(), lineage.live_dir) hooks.deploy_hook(config, lineage.names(), lineage.live_dir)
finally: finally:
hooks.post_hook(config) hooks.post_hook(config)
@@ -162,19 +162,18 @@ def _handle_subset_cert_request(config, domains, cert):
cli_flag="--expand", cli_flag="--expand",
force_interactive=True): force_interactive=True):
return "renew", cert return "renew", cert
else: reporter_util = zope.component.getUtility(interfaces.IReporter)
reporter_util = zope.component.getUtility(interfaces.IReporter) reporter_util.add_message(
reporter_util.add_message( "To obtain a new certificate that contains these names without "
"To obtain a new certificate that contains these names without " "replacing your existing certificate for {0}, you must use the "
"replacing your existing certificate for {0}, you must use the " "--duplicate option.{br}{br}"
"--duplicate option.{br}{br}" "For example:{br}{br}{1} --duplicate {2}".format(
"For example:{br}{br}{1} --duplicate {2}".format( existing,
existing, sys.argv[0], " ".join(sys.argv[1:]),
sys.argv[0], " ".join(sys.argv[1:]), br=os.linesep
br=os.linesep ),
), reporter_util.HIGH_PRIORITY)
reporter_util.HIGH_PRIORITY) raise errors.Error(USER_CANCELLED)
raise errors.Error(USER_CANCELLED)
def _handle_identical_cert_request(config, lineage): def _handle_identical_cert_request(config, lineage):
@@ -220,7 +219,7 @@ def _handle_identical_cert_request(config, lineage):
# skipping the menu for this case. # skipping the menu for this case.
raise errors.Error( raise errors.Error(
"Operation canceled. You may re-run the client.") "Operation canceled. You may re-run the client.")
elif response[1] == 0: if response[1] == 0:
return "reinstall", lineage return "reinstall", lineage
elif response[1] == 1: elif response[1] == 1:
return "renew", lineage return "renew", lineage
@@ -312,23 +311,20 @@ def _find_lineage_for_domains_and_certname(config, domains, certname):
""" """
if not certname: if not certname:
return _find_lineage_for_domains(config, domains) return _find_lineage_for_domains(config, domains)
else: lineage = cert_manager.lineage_for_certname(config, certname)
lineage = cert_manager.lineage_for_certname(config, certname) if lineage:
if lineage: if domains:
if domains: if set(cert_manager.domains_for_certname(config, certname)) != set(domains):
if set(cert_manager.domains_for_certname(config, certname)) != set(domains): _ask_user_to_confirm_new_names(config, domains, certname,
_ask_user_to_confirm_new_names(config, domains, certname, lineage.names()) # raises if no
lineage.names()) # raises if no return "renew", lineage
return "renew", lineage # unnecessarily specified domains or no domains specified
# unnecessarily specified domains or no domains specified return _handle_identical_cert_request(config, lineage)
return _handle_identical_cert_request(config, lineage) elif domains:
else: return "newcert", None
if domains: raise errors.ConfigurationError("No certificate with name {0} found. "
return "newcert", None "Use -d to specify domains, or run certbot certificates to see "
else: "possible certificate names.".format(certname))
raise errors.ConfigurationError("No certificate with name {0} found. "
"Use -d to specify domains, or run certbot certificates to see "
"possible certificate names.".format(certname))
def _get_added_removed(after, before): def _get_added_removed(after, before):
"""Get lists of items removed from `before` """Get lists of items removed from `before`
@@ -1338,7 +1334,7 @@ def main(cli_args=None):
make_or_verify_needed_dirs(config) make_or_verify_needed_dirs(config)
except errors.Error: except errors.Error:
# Let plugins_cmd be run as un-privileged user. # Let plugins_cmd be run as un-privileged user.
if config.func != plugins_cmd: if config.func != plugins_cmd: # pylint: disable=comparison-with-callable
raise raise
set_displayer(config) set_displayer(config)
+1 -1
View File
@@ -296,5 +296,5 @@ def _translate_ocsp_query(cert_path, ocsp_output, ocsp_errors):
return True return True
else: else:
logger.warning("Unable to properly parse OCSP output: %s\nstderr:%s", logger.warning("Unable to properly parse OCSP output: %s\nstderr:%s",
ocsp_output, ocsp_errors) ocsp_output, ocsp_errors)
return False return False
@@ -64,9 +64,8 @@ def get_unprepared_installer(config, plugins):
inst = list(installers.values())[0] inst = list(installers.values())[0]
logger.debug("Selecting plugin: %s", inst) logger.debug("Selecting plugin: %s", inst)
return inst.init(config) return inst.init(config)
else: raise errors.PluginSelectionError(
raise errors.PluginSelectionError( "Could not select or initialize the requested installer %s." % req_inst)
"Could not select or initialize the requested installer %s." % req_inst)
def pick_plugin(config, default, plugins, question, ifaces): def pick_plugin(config, default, plugins, question, ifaces):
"""Pick plugin. """Pick plugin.
@@ -209,7 +208,7 @@ def choose_configurator_plugins(config, plugins, verb):
need_inst = need_auth = False need_inst = need_auth = False
if verb == "certonly": if verb == "certonly":
need_auth = True need_auth = True
if verb == "install" or verb == "enhance": elif verb in ("install", "enhance"):
need_inst = True need_inst = True
if config.authenticator: if config.authenticator:
logger.warning("Specifying an authenticator doesn't make sense when " logger.warning("Specifying an authenticator doesn't make sense when "
@@ -76,7 +76,6 @@ class ServerManager(object):
servers.serve_forever() servers.serve_forever()
# if port == 0, then random free port on OS is taken # if port == 0, then random free port on OS is taken
# pylint: disable=no-member
# both servers, if they exist, have the same port # both servers, if they exist, have the same port
real_port = servers.getsocknames()[0][1] real_port = servers.getsocknames()[0][1]
self._instances[real_port] = servers self._instances[real_port] = servers
@@ -196,7 +195,7 @@ def _handle_perform_error(error):
"the appropriate permissions (for example, you " "the appropriate permissions (for example, you "
"aren't running this program as " "aren't running this program as "
"root).".format(error.port)) "root).".format(error.port))
elif error.socket_error.errno == socket_errors.EADDRINUSE: if error.socket_error.errno == socket_errors.EADDRINUSE:
display = zope.component.getUtility(interfaces.IDisplay) display = zope.component.getUtility(interfaces.IDisplay)
msg = ( msg = (
"Could not bind TCP port {0} because it is already in " "Could not bind TCP port {0} because it is already in "
+5 -8
View File
@@ -135,8 +135,7 @@ to serve all files under specified web root ({0})."""
raise errors.PluginError( raise errors.PluginError(
"Every requested domain must have a " "Every requested domain must have a "
"webroot when using the webroot plugin.") "webroot when using the webroot plugin.")
else: # code == display_util.OK return None if index == 0 else known_webroots[index - 1] # code == display_util.OK
return None if index == 0 else known_webroots[index - 1]
def _prompt_for_new_webroot(self, domain, allowraise=False): def _prompt_for_new_webroot(self, domain, allowraise=False):
code, webroot = ops.validated_directory( code, webroot = ops.validated_directory(
@@ -146,12 +145,10 @@ to serve all files under specified web root ({0})."""
if code == display_util.CANCEL: if code == display_util.CANCEL:
if not allowraise: if not allowraise:
return None return None
else: raise errors.PluginError(
raise errors.PluginError( "Every requested domain must have a "
"Every requested domain must have a " "webroot when using the webroot plugin.")
"webroot when using the webroot plugin.") return _validate_webroot(webroot) # code == display_util.OK
else: # code == display_util.OK
return _validate_webroot(webroot)
def _create_challenge_dirs(self): def _create_challenge_dirs(self):
path_map = self.conf("map") path_map = self.conf("map")
+1 -2
View File
@@ -471,5 +471,4 @@ def handle_renewal_request(config):
if renew_failures or parse_failures: if renew_failures or parse_failures:
raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format( raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format(
len(renew_failures), len(parse_failures))) len(renew_failures), len(parse_failures)))
else: logger.debug("no renewal failures")
logger.debug("no renewal failures")
+4 -8
View File
@@ -1014,10 +1014,8 @@ class RenewableCert(interfaces.RenewableCert):
"directory %s created.", archive, live_dir) "directory %s created.", archive, live_dir)
# Put the data into the appropriate files on disk # Put the data into the appropriate files on disk
target = dict([(kind, os.path.join(live_dir, kind + ".pem")) target = {kind: os.path.join(live_dir, kind + ".pem") for kind in ALL_FOUR}
for kind in ALL_FOUR]) archive_target = {kind: os.path.join(archive, kind + "1.pem") for kind in ALL_FOUR}
archive_target = dict([(kind, os.path.join(archive, kind + "1.pem"))
for kind in ALL_FOUR])
for kind in ALL_FOUR: for kind in ALL_FOUR:
os.symlink(_relpath_from_file(archive_target[kind], target[kind]), target[kind]) os.symlink(_relpath_from_file(archive_target[kind], target[kind]), target[kind])
with open(target["cert"], "wb") as f: with open(target["cert"], "wb") as f:
@@ -1082,10 +1080,8 @@ class RenewableCert(interfaces.RenewableCert):
self.cli_config = cli_config self.cli_config = cli_config
target_version = self.next_free_version() target_version = self.next_free_version()
target = dict( target = {kind: os.path.join(self.archive_dir, "{0}{1}.pem".format(kind, target_version))
[(kind, for kind in ALL_FOUR}
os.path.join(self.archive_dir, "{0}{1}.pem".format(kind, target_version)))
for kind in ALL_FOUR])
old_privkey = os.path.join( old_privkey = os.path.join(
self.archive_dir, "privkey{0}.pem".format(prior_version)) self.archive_dir, "privkey{0}.pem".format(prior_version))
+7 -1
View File
@@ -71,6 +71,9 @@ def copy_ownership_and_apply_mode(src, dst, mode, copy_user, copy_group):
stats = os.stat(src) stats = os.stat(src)
user_id = stats.st_uid if copy_user else -1 user_id = stats.st_uid if copy_user else -1
group_id = stats.st_gid if copy_group else -1 group_id = stats.st_gid if copy_group else -1
# On Windows, os.chown does not exist. This is checked through POSIX_MODE value,
# but MyPy/PyLint does not know it and raises an error here on Windows.
# We disable specifically the check to fix the issue.
os.chown(dst, user_id, group_id) os.chown(dst, user_id, group_id)
elif copy_user: elif copy_user:
# There is no group handling in Windows # There is no group handling in Windows
@@ -105,7 +108,10 @@ def check_owner(file_path):
:return: True if given file is owned by current user, False otherwise. :return: True if given file is owned by current user, False otherwise.
""" """
if POSIX_MODE: if POSIX_MODE:
return os.stat(file_path).st_uid == os.getuid() # On Windows, os.getuid does not exist. This is checked through POSIX_MODE value,
# but MyPy/PyLint does not know it and raises an error here on Windows.
# We disable specifically the check to fix the issue.
return os.stat(file_path).st_uid == os.getuid() # type: ignore
# Get owner sid of the file # Get owner sid of the file
security = win32security.GetFileSecurity(file_path, win32security.OWNER_SECURITY_INFORMATION) security = win32security.GetFileSecurity(file_path, win32security.OWNER_SECURITY_INFORMATION)
+3 -4
View File
@@ -60,11 +60,10 @@ def get_email(invalid=False, optional=True):
raise errors.Error( raise errors.Error(
"An e-mail address or " "An e-mail address or "
"--register-unsafely-without-email must be provided.") "--register-unsafely-without-email must be provided.")
else: raise errors.Error("An e-mail address must be provided.")
raise errors.Error("An e-mail address must be provided.") if util.safe_email(email):
elif util.safe_email(email):
return email return email
elif suggest_unsafe: if suggest_unsafe:
msg += unsafe_suggestion msg += unsafe_suggestion
suggest_unsafe = False # add this message at most once suggest_unsafe = False # add this message at most once
+10 -12
View File
@@ -177,7 +177,7 @@ class FileDisplay(object):
message = _wrap_lines("%s (Enter 'c' to cancel):" % message) + " " message = _wrap_lines("%s (Enter 'c' to cancel):" % message) + " "
ans = input_with_timeout(message) ans = input_with_timeout(message)
if ans == "c" or ans == "C": if ans in ("c", "C"):
return CANCEL, "-1" return CANCEL, "-1"
return OK, ans return OK, ans
@@ -258,10 +258,9 @@ class FileDisplay(object):
selected_tags = self._scrub_checklist_input(indices, tags) selected_tags = self._scrub_checklist_input(indices, tags)
if selected_tags: if selected_tags:
return code, selected_tags return code, selected_tags
else: self.outfile.write(
self.outfile.write( "** Error - Invalid selection **%s" % os.linesep)
"** Error - Invalid selection **%s" % os.linesep) self.outfile.flush()
self.outfile.flush()
else: else:
return code, [] return code, []
@@ -282,18 +281,17 @@ class FileDisplay(object):
# assert_valid_call(prompt, default, cli_flag, force_interactive) # assert_valid_call(prompt, default, cli_flag, force_interactive)
if self._can_interact(force_interactive): if self._can_interact(force_interactive):
return False return False
elif default is None: if default is None:
msg = "Unable to get an answer for the question:\n{0}".format(prompt) msg = "Unable to get an answer for the question:\n{0}".format(prompt)
if cli_flag: if cli_flag:
msg += ( msg += (
"\nYou can provide an answer on the " "\nYou can provide an answer on the "
"command line with the {0} flag.".format(cli_flag)) "command line with the {0} flag.".format(cli_flag))
raise errors.Error(msg) raise errors.Error(msg)
else: logger.debug(
logger.debug( "Falling back to default %s for the prompt:\n%s",
"Falling back to default %s for the prompt:\n%s", default, prompt)
default, prompt) return True
return True
def _can_interact(self, force_interactive): def _can_interact(self, force_interactive):
"""Can we safely interact with the user? """Can we safely interact with the user?
@@ -308,7 +306,7 @@ class FileDisplay(object):
if (self.force_interactive or force_interactive or if (self.force_interactive or force_interactive or
sys.stdin.isatty() and self.outfile.isatty()): sys.stdin.isatty() and self.outfile.isatty()):
return True return True
elif not self.skipped_interaction: if not self.skipped_interaction:
logger.warning( logger.warning(
"Skipped user interaction because Certbot doesn't appear to " "Skipped user interaction because Certbot doesn't appear to "
"be running in a terminal. You should probably include " "be running in a terminal. You should probably include "
+4 -3
View File
@@ -33,6 +33,7 @@ def dest_namespace(name):
"""ArgumentParser dest namespace (prefix of all destinations).""" """ArgumentParser dest namespace (prefix of all destinations)."""
return name.replace("-", "_") + "_" return name.replace("-", "_") + "_"
private_ips_regex = re.compile( private_ips_regex = re.compile(
r"(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|" r"(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|"
r"(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)") r"(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)")
@@ -294,7 +295,7 @@ class Addr(object):
# appended to the end # appended to the end
append_to_end = True append_to_end = True
continue continue
elif len(block) > 1: if len(block) > 1:
# remove leading zeros # remove leading zeros
block = block.lstrip("0") block = block.lstrip("0")
if not append_to_end: if not append_to_end:
@@ -373,9 +374,9 @@ def install_version_controlled_file(dest_path, digest_path, src_path, all_hashes
active_file_digest = crypto_util.sha256sum(dest_path) active_file_digest = crypto_util.sha256sum(dest_path)
if active_file_digest == current_hash: # already up to date if active_file_digest == current_hash: # already up to date
return return
elif active_file_digest in all_hashes: # safe to update if active_file_digest in all_hashes: # safe to update
_install_current_file() _install_current_file()
else: # has been manually modified, not safe to update else: # has been manually modified, not safe to update
# did they modify the current version or an old version? # did they modify the current version or an old version?
if os.path.isfile(digest_path): if os.path.isfile(digest_path):
with open(digest_path, "r") as f: with open(digest_path, "r") as f:
+2 -4
View File
@@ -197,8 +197,7 @@ class DNSAuthenticator(common.Plugin):
if code == display_util.OK: if code == display_util.OK:
return response return response
else: raise errors.PluginError('{0} required to proceed.'.format(label))
raise errors.PluginError('{0} required to proceed.'.format(label))
@staticmethod @staticmethod
def _prompt_for_file(label, validator=None): def _prompt_for_file(label, validator=None):
@@ -231,8 +230,7 @@ class DNSAuthenticator(common.Plugin):
if code == display_util.OK: if code == display_util.OK:
return response return response
else: raise errors.PluginError('{0} required to proceed.'.format(label))
raise errors.PluginError('{0} required to proceed.'.format(label))
class CredentialsConfiguration(object): class CredentialsConfiguration(object):
@@ -100,7 +100,7 @@ class LexiconClient(object):
result = self._handle_general_error(e, domain_name) result = self._handle_general_error(e, domain_name)
if result: if result:
raise result raise result # pylint: disable=raising-bad-type
raise errors.PluginError('Unable to determine zone identifier for {0} using zone names: {1}' raise errors.PluginError('Unable to determine zone identifier for {0} using zone names: {1}'
.format(domain, domain_name_guesses)) .format(domain, domain_name_guesses))
+3 -7
View File
@@ -28,18 +28,14 @@ class BaseAuthenticatorTest(object):
challb=acme_util.DNS01, domain=DOMAIN, account_key=KEY) challb=acme_util.DNS01, domain=DOMAIN, account_key=KEY)
def test_more_info(self): def test_more_info(self):
# pylint: disable=no-member self.assertTrue(isinstance(self.auth.more_info(), six.string_types)) # pylint: disable=no-member
self.assertTrue(isinstance(self.auth.more_info(), six.string_types))
def test_get_chall_pref(self): def test_get_chall_pref(self):
# pylint: disable=no-member self.assertEqual(self.auth.get_chall_pref(None), [challenges.DNS01]) # pylint: disable=no-member
self.assertEqual(self.auth.get_chall_pref(None), [challenges.DNS01])
def test_parser_arguments(self): def test_parser_arguments(self):
m = mock.MagicMock() m = mock.MagicMock()
self.auth.add_parser_arguments(m) # pylint: disable=no-member
# pylint: disable=no-member
self.auth.add_parser_arguments(m)
m.assert_any_call('propagation-seconds', type=int, default=mock.ANY, help=mock.ANY) m.assert_any_call('propagation-seconds', type=int, default=mock.ANY, help=mock.ANY)
+4 -6
View File
@@ -57,8 +57,7 @@ def _guess_loader(filename, loader_pem, loader_der):
return loader_pem return loader_pem
elif ext.lower() == '.der': elif ext.lower() == '.der':
return loader_der return loader_der
else: # pragma: no cover raise ValueError("Loader could not be recognized based on extension") # pragma: no cover
raise ValueError("Loader could not be recognized based on extension")
def load_cert(*names): def load_cert(*names):
@@ -235,8 +234,7 @@ class FreezableMock(object):
if self._frozen: if self._frozen:
if name in self._frozen_set: if name in self._frozen_set:
raise AttributeError('Cannot change frozen attribute ' + name) raise AttributeError('Cannot change frozen attribute ' + name)
else: return setattr(self._mock, name, value)
return setattr(self._mock, name, value)
if name != '_frozen_set': if name != '_frozen_set':
self._frozen_set.add(name) self._frozen_set.add(name)
@@ -250,7 +248,7 @@ class FreezableMock(object):
def _create_get_utility_mock(): def _create_get_utility_mock():
display = FreezableMock() display = FreezableMock()
# Use pylint code for disable to keep on single line under line length limit # Use pylint code for disable to keep on single line under line length limit
for name in interfaces.IDisplay.names(): # pylint: disable=no-member,E1120 for name in interfaces.IDisplay.names(): # pylint: E1120
if name != 'notification': if name != 'notification':
frozen_mock = FreezableMock(frozen=True, func=_assert_valid_call) frozen_mock = FreezableMock(frozen=True, func=_assert_valid_call)
setattr(display, name, frozen_mock) setattr(display, name, frozen_mock)
@@ -275,7 +273,7 @@ def _create_get_utility_mock_with_stdout(stdout):
display = FreezableMock() display = FreezableMock()
# Use pylint code for disable to keep on single line under line length limit # Use pylint code for disable to keep on single line under line length limit
for name in interfaces.IDisplay.names(): # pylint: disable=no-member,E1120 for name in interfaces.IDisplay.names(): # pylint: E1120
if name == 'notification': if name == 'notification':
frozen_mock = FreezableMock(frozen=True, frozen_mock = FreezableMock(frozen=True,
func=_write_msg) func=_write_msg)
+5 -8
View File
@@ -26,7 +26,7 @@ from certbot.compat import filesystem
from certbot.compat import os from certbot.compat import os
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
import distro import distro # pylint: disable=import-error
_USE_DISTRO = True _USE_DISTRO = True
else: else:
_USE_DISTRO = False _USE_DISTRO = False
@@ -105,10 +105,9 @@ def exe_exists(exe):
path, _ = os.path.split(exe) path, _ = os.path.split(exe)
if path: if path:
return filesystem.is_executable(exe) return filesystem.is_executable(exe)
else: for path in os.environ["PATH"].split(os.pathsep):
for path in os.environ["PATH"].split(os.pathsep): if filesystem.is_executable(os.path.join(path, exe)):
if filesystem.is_executable(os.path.join(path, exe)): return True
return True
return False return False
@@ -436,7 +435,6 @@ def add_deprecated_argument(add_argument, argument_name, nargs):
# In version 0.12.0 ACTION_TYPES_THAT_DONT_NEED_A_VALUE was # In version 0.12.0 ACTION_TYPES_THAT_DONT_NEED_A_VALUE was
# changed from a set to a tuple. # changed from a set to a tuple.
if isinstance(configargparse.ACTION_TYPES_THAT_DONT_NEED_A_VALUE, set): if isinstance(configargparse.ACTION_TYPES_THAT_DONT_NEED_A_VALUE, set):
# pylint: disable=no-member
configargparse.ACTION_TYPES_THAT_DONT_NEED_A_VALUE.add( configargparse.ACTION_TYPES_THAT_DONT_NEED_A_VALUE.add(
_ShowWarning) _ShowWarning)
else: else:
@@ -537,7 +535,7 @@ def enforce_domain_sanity(domain):
for l in labels: for l in labels:
if not l: if not l:
raise errors.ConfigurationError("{0} it contains an empty label.".format(msg)) raise errors.ConfigurationError("{0} it contains an empty label.".format(msg))
elif len(l) > 63: if len(l) > 63:
raise errors.ConfigurationError("{0} label {1} is too long.".format(msg, l)) raise errors.ConfigurationError("{0} label {1} is too long.".format(msg, l))
return domain return domain
@@ -571,7 +569,6 @@ def get_strict_version(normalized):
""" """
# strict version ending with "a" and a number designates a pre-release # strict version ending with "a" and a number designates a pre-release
# pylint: disable=no-member
return distutils.version.StrictVersion(normalized.replace(".dev", "a")) return distutils.version.StrictVersion(normalized.replace(".dev", "a"))
+3 -3
View File
@@ -74,21 +74,21 @@ elif os.name == 'nt':
install_requires.append(pywin32_req) install_requires.append(pywin32_req)
dev_extras = [ dev_extras = [
'astroid==1.6.5',
'coverage', 'coverage',
'ipdb', 'ipdb',
'pytest', 'pytest',
'pytest-cov', 'pytest-cov',
'pytest-xdist', 'pytest-xdist',
'pylint==1.9.4',
'tox', 'tox',
'twine', 'twine',
'wheel', 'wheel',
] ]
dev3_extras = [ dev3_extras = [
'astroid',
'mypy', 'mypy',
'typing', # for python3.4 'pylint',
'typing', # for python3.4
] ]
docs_extras = [ docs_extras = [
+1 -1
View File
@@ -494,7 +494,7 @@ class ReportFailedAuthzrsTest(unittest.TestCase):
self.authzr1.body.identifier.value = 'example.com' self.authzr1.body.identifier.value = 'example.com'
self.authzr1.body.challenges = [http_01, http_01] self.authzr1.body.challenges = [http_01, http_01]
kwargs["error"] = messages.Error(typ="dnssec", detail="detail") kwargs["error"] = messages.Error.with_code("dnssec", detail="detail")
http_01_diff = messages.ChallengeBody(**kwargs) http_01_diff = messages.ChallengeBody(**kwargs)
self.authzr2 = mock.MagicMock() self.authzr2 = mock.MagicMock()
+3 -3
View File
@@ -178,7 +178,7 @@ class CertificatesTest(BaseCertManagerTest):
mock_verifier.return_value = None mock_verifier.return_value = None
mock_report.return_value = "" mock_report.return_value = ""
self._certificates(self.config) self._certificates(self.config)
self.assertFalse(mock_logger.warning.called) #pylint: disable=no-member self.assertFalse(mock_logger.warning.called)
self.assertTrue(mock_report.called) self.assertTrue(mock_report.called)
self.assertTrue(mock_utility.called) self.assertTrue(mock_utility.called)
self.assertTrue(mock_renewable_cert.called) self.assertTrue(mock_renewable_cert.called)
@@ -196,7 +196,7 @@ class CertificatesTest(BaseCertManagerTest):
filesystem.makedirs(empty_config.renewal_configs_dir) filesystem.makedirs(empty_config.renewal_configs_dir)
self._certificates(empty_config) self._certificates(empty_config)
self.assertFalse(mock_logger.warning.called) #pylint: disable=no-member self.assertFalse(mock_logger.warning.called)
self.assertTrue(mock_utility.called) self.assertTrue(mock_utility.called)
shutil.rmtree(empty_tempdir) shutil.rmtree(empty_tempdir)
@@ -240,7 +240,7 @@ class CertificatesTest(BaseCertManagerTest):
# pylint: disable=protected-access # pylint: disable=protected-access
out = get_report() out = get_report()
self.assertTrue('3 days' in out) self.assertTrue('3 days' in out)
self.assertTrue('VALID' in out and 'INVALID' not in out) self.assertTrue('VALID' in out and 'INVALID' not in out)
cert.is_test_cert = True cert.is_test_cert = True
mock_revoked.return_value = True mock_revoked.return_value = True
+1 -1
View File
@@ -205,7 +205,7 @@ class RegisterTest(test_util.ConfigTestCase):
def test_unsupported_error(self): def test_unsupported_error(self):
from acme import messages from acme import messages
msg = "Test" msg = "Test"
mx_err = messages.Error(detail=msg, typ="malformed", title="title") mx_err = messages.Error.with_code("malformed", detail=msg, title="title")
with mock.patch("certbot._internal.client.acme_client.BackwardsCompatibleClientV2") as mock_client: with mock.patch("certbot._internal.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
mock_client().client.directory.__getitem__ = mock.Mock( mock_client().client.directory.__getitem__ = mock.Mock(
side_effect=self._new_acct_dir_mock side_effect=self._new_acct_dir_mock
+2
View File
@@ -363,6 +363,8 @@ class CheckPermissionsTest(test_util.TempDirTestCase):
self.assertTrue(filesystem.check_owner(self.probe_path)) self.assertTrue(filesystem.check_owner(self.probe_path))
import os as std_os # pylint: disable=os-module-forbidden import os as std_os # pylint: disable=os-module-forbidden
# See related inline comment in certbot.compat.filesystem.check_owner method
# that explains why MyPy/PyLint check disable is needed here.
uid = std_os.getuid() uid = std_os.getuid()
with mock.patch('os.getuid') as mock_uid: with mock.patch('os.getuid') as mock_uid:
+7 -7
View File
@@ -311,12 +311,12 @@ class FileOutputDisplayTest(unittest.TestCase):
def test_methods_take_force_interactive(self): def test_methods_take_force_interactive(self):
# Every IDisplay method implemented by FileDisplay must take # Every IDisplay method implemented by FileDisplay must take
# force_interactive to prevent workflow regressions. # force_interactive to prevent workflow regressions.
for name in interfaces.IDisplay.names(): # pylint: disable=no-member,no-value-for-parameter for name in interfaces.IDisplay.names():
if six.PY2: if six.PY2:
getargspec = inspect.getargspec # pylint: disable=no-member getargspec = inspect.getargspec
else: else:
getargspec = inspect.getfullargspec # pylint: disable=no-member getargspec = inspect.getfullargspec
arg_spec = getargspec(getattr(self.displayer, name)) arg_spec = getargspec(getattr(self.displayer, name)) # pylint: disable=deprecated-method
self.assertTrue("force_interactive" in arg_spec.args) self.assertTrue("force_interactive" in arg_spec.args)
@@ -372,14 +372,14 @@ class NoninteractiveDisplayTest(unittest.TestCase):
# NoninteractiveDisplay. # NoninteractiveDisplay.
# Use pylint code for disable to keep on single line under line length limit # Use pylint code for disable to keep on single line under line length limit
for name in interfaces.IDisplay.names(): # pylint: disable=no-member,E1120 for name in interfaces.IDisplay.names(): # pylint: disable=E1120
method = getattr(self.displayer, name) method = getattr(self.displayer, name)
# asserts method accepts arbitrary keyword arguments # asserts method accepts arbitrary keyword arguments
if six.PY2: if six.PY2:
result = inspect.getargspec(method).keywords # pylint: disable=no-member result = inspect.getargspec(method).keywords # pylint:deprecated-method
self.assertFalse(result is None) self.assertFalse(result is None)
else: else:
result = inspect.getfullargspec(method).varkw # pylint: disable=no-member result = inspect.getfullargspec(method).varkw
self.assertFalse(result is None) self.assertFalse(result is None)
+5 -5
View File
@@ -111,7 +111,7 @@ class SubscribeTest(unittest.TestCase):
@test_util.patch_get_utility() @test_util.patch_get_utility()
def test_bad_status(self, mock_get_utility): def test_bad_status(self, mock_get_utility):
self.json['status'] = False self.json['status'] = False
self._call() # pylint: disable=no-value-for-parameter self._call()
actual = self._get_reported_message(mock_get_utility) actual = self._get_reported_message(mock_get_utility)
expected_part = 'because your e-mail address appears to be invalid.' expected_part = 'because your e-mail address appears to be invalid.'
self.assertTrue(expected_part in actual) self.assertTrue(expected_part in actual)
@@ -120,7 +120,7 @@ class SubscribeTest(unittest.TestCase):
def test_not_ok(self, mock_get_utility): def test_not_ok(self, mock_get_utility):
self.response.ok = False self.response.ok = False
self.response.raise_for_status.side_effect = requests.exceptions.HTTPError self.response.raise_for_status.side_effect = requests.exceptions.HTTPError
self._call() # pylint: disable=no-value-for-parameter self._call()
actual = self._get_reported_message(mock_get_utility) actual = self._get_reported_message(mock_get_utility)
unexpected_part = 'because' unexpected_part = 'because'
self.assertFalse(unexpected_part in actual) self.assertFalse(unexpected_part in actual)
@@ -128,7 +128,7 @@ class SubscribeTest(unittest.TestCase):
@test_util.patch_get_utility() @test_util.patch_get_utility()
def test_response_not_json(self, mock_get_utility): def test_response_not_json(self, mock_get_utility):
self.response.json.side_effect = ValueError() self.response.json.side_effect = ValueError()
self._call() # pylint: disable=no-value-for-parameter self._call()
actual = self._get_reported_message(mock_get_utility) actual = self._get_reported_message(mock_get_utility)
expected_part = 'problem' expected_part = 'problem'
self.assertTrue(expected_part in actual) self.assertTrue(expected_part in actual)
@@ -136,7 +136,7 @@ class SubscribeTest(unittest.TestCase):
@test_util.patch_get_utility() @test_util.patch_get_utility()
def test_response_json_missing_status_element(self, mock_get_utility): def test_response_json_missing_status_element(self, mock_get_utility):
self.json.clear() self.json.clear()
self._call() # pylint: disable=no-value-for-parameter self._call()
actual = self._get_reported_message(mock_get_utility) actual = self._get_reported_message(mock_get_utility)
expected_part = 'problem' expected_part = 'problem'
self.assertTrue(expected_part in actual) self.assertTrue(expected_part in actual)
@@ -147,7 +147,7 @@ class SubscribeTest(unittest.TestCase):
@test_util.patch_get_utility() @test_util.patch_get_utility()
def test_subscribe(self, mock_get_utility): def test_subscribe(self, mock_get_utility):
self._call() # pylint: disable=no-value-for-parameter self._call()
self.assertFalse(mock_get_utility.called) self.assertFalse(mock_get_utility.called)
+8 -6
View File
@@ -13,25 +13,27 @@ class FailedChallengesTest(unittest.TestCase):
def setUp(self): def setUp(self):
from certbot.errors import FailedChallenges from certbot.errors import FailedChallenges
self.error = FailedChallenges(set([achallenges.DNS( self.error = FailedChallenges({achallenges.DNS(
domain="example.com", challb=messages.ChallengeBody( domain="example.com", challb=messages.ChallengeBody(
chall=acme_util.DNS01, uri=None, chall=acme_util.DNS01, uri=None,
error=messages.Error(typ="tls", detail="detail")))])) error=messages.Error.with_code("tls", detail="detail")))})
def test_str(self): def test_str(self):
self.assertTrue(str(self.error).startswith( self.assertTrue(str(self.error).startswith(
"Failed authorization procedure. example.com (dns-01): tls")) "Failed authorization procedure. example.com (dns-01): "
"urn:ietf:params:acme:error:tls"))
def test_unicode(self): def test_unicode(self):
from certbot.errors import FailedChallenges from certbot.errors import FailedChallenges
arabic_detail = u'\u0639\u062f\u0627\u0644\u0629' arabic_detail = u'\u0639\u062f\u0627\u0644\u0629'
arabic_error = FailedChallenges(set([achallenges.DNS( arabic_error = FailedChallenges({achallenges.DNS(
domain="example.com", challb=messages.ChallengeBody( domain="example.com", challb=messages.ChallengeBody(
chall=acme_util.DNS01, uri=None, chall=acme_util.DNS01, uri=None,
error=messages.Error(typ="tls", detail=arabic_detail)))])) error=messages.Error.with_code("tls", detail=arabic_detail)))})
self.assertTrue(str(arabic_error).startswith( self.assertTrue(str(arabic_error).startswith(
"Failed authorization procedure. example.com (dns-01): tls")) "Failed authorization procedure. example.com (dns-01): "
"urn:ietf:params:acme:error:tls"))
class StandaloneBindErrorTest(unittest.TestCase): class StandaloneBindErrorTest(unittest.TestCase):
+2 -4
View File
@@ -37,12 +37,10 @@ class EnhancementTest(test_util.ConfigTestCase):
self.assertTrue([i for i in enabled if i["name"] == "somethingelse"]) self.assertTrue([i for i in enabled if i["name"] == "somethingelse"])
def test_are_requested(self): def test_are_requested(self):
self.assertEqual( self.assertEqual(len(list(enhancements.enabled_enhancements(self.config))), 0)
len([i for i in enhancements.enabled_enhancements(self.config)]), 0)
self.assertFalse(enhancements.are_requested(self.config)) self.assertFalse(enhancements.are_requested(self.config))
self.config.auto_hsts = True self.config.auto_hsts = True
self.assertEqual( self.assertEqual(len(list(enhancements.enabled_enhancements(self.config))), 1)
len([i for i in enhancements.enabled_enhancements(self.config)]), 1)
self.assertTrue(enhancements.are_requested(self.config)) self.assertTrue(enhancements.are_requested(self.config))
def test_are_supported(self): def test_are_supported(self):
+2 -2
View File
@@ -37,7 +37,7 @@ class ServerManagerTest(unittest.TestCase):
def _test_run_stop(self, challenge_type): def _test_run_stop(self, challenge_type):
server = self.mgr.run(port=0, challenge_type=challenge_type) server = self.mgr.run(port=0, challenge_type=challenge_type)
port = server.getsocknames()[0][1] # pylint: disable=no-member port = server.getsocknames()[0][1]
self.assertEqual(self.mgr.running(), {port: server}) self.assertEqual(self.mgr.running(), {port: server})
self.mgr.stop(port=port) self.mgr.stop(port=port)
self.assertEqual(self.mgr.running(), {}) self.assertEqual(self.mgr.running(), {})
@@ -47,7 +47,7 @@ class ServerManagerTest(unittest.TestCase):
def test_run_idempotent(self): def test_run_idempotent(self):
server = self.mgr.run(port=0, challenge_type=challenges.HTTP01) server = self.mgr.run(port=0, challenge_type=challenges.HTTP01)
port = server.getsocknames()[0][1] # pylint: disable=no-member port = server.getsocknames()[0][1]
server2 = self.mgr.run(port=port, challenge_type=challenges.HTTP01) server2 = self.mgr.run(port=port, challenge_type=challenges.HTTP01)
self.assertEqual(self.mgr.running(), {port: server}) self.assertEqual(self.mgr.running(), {port: server})
self.assertTrue(server is server2) self.assertTrue(server is server2)
+2 -4
View File
@@ -356,8 +356,7 @@ class RenewableCertTests(BaseRenewableCertTest):
basename = os.path.basename(path) basename = os.path.basename(path)
if "fullchain" in basename and basename.startswith("prev"): if "fullchain" in basename and basename.startswith("prev"):
raise ValueError raise ValueError
else: real_unlink(path)
real_unlink(path)
self._write_out_ex_kinds() self._write_out_ex_kinds()
with mock.patch("certbot._internal.storage.os.unlink") as mock_unlink: with mock.patch("certbot._internal.storage.os.unlink") as mock_unlink:
@@ -372,8 +371,7 @@ class RenewableCertTests(BaseRenewableCertTest):
# pylint: disable=missing-docstring # pylint: disable=missing-docstring
if "fullchain" in os.path.basename(path): if "fullchain" in os.path.basename(path):
raise ValueError raise ValueError
else: real_unlink(path)
real_unlink(path)
self._write_out_ex_kinds() self._write_out_ex_kinds()
with mock.patch("certbot._internal.storage.os.unlink") as mock_unlink: with mock.patch("certbot._internal.storage.os.unlink") as mock_unlink:
+2 -2
View File
@@ -74,7 +74,7 @@ def make_and_verify_selection(server_root, temp_dir):
ans = six.moves.input("(Y)es/(N)o: ").lower() ans = six.moves.input("(Y)es/(N)o: ").lower()
if ans.startswith("y"): if ans.startswith("y"):
return return
elif ans.startswith("n"): if ans.startswith("n"):
sys.exit("Your files were not submitted") sys.exit("Your files were not submitted")
@@ -159,7 +159,7 @@ def safe_config_file(config_file):
empty_or_all_comments = False empty_or_all_comments = False
if line.startswith("-----BEGIN"): if line.startswith("-----BEGIN"):
return False return False
elif ":" not in line: if ":" not in line:
possible_password_file = False possible_password_file = False
# If file isn't empty or commented out and could be a password file, # If file isn't empty or commented out and could be a password file,
# don't include it in selection. It is safe to include the file if # don't include it in selection. It is safe to include the file if
+1 -1
View File
@@ -173,7 +173,7 @@ def setup_certificate(workspace):
key_path = os.path.join(workspace, 'cert.key') key_path = os.path.join(workspace, 'cert.key')
with open(key_path, 'wb') as file_handle: with open(key_path, 'wb') as file_handle:
file_handle.write(private_key.private_bytes( file_handle.write(private_key.private_bytes( # type: ignore
encoding=serialization.Encoding.PEM, encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL, format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption() encryption_algorithm=serialization.NoEncryption()
+8 -7
View File
@@ -6,7 +6,7 @@ alabaster==0.7.10
apipkg==1.4 apipkg==1.4
appnope==0.1.0 appnope==0.1.0
asn1crypto==0.22.0 asn1crypto==0.22.0
astroid==1.6.5 astroid==2.3.3
attrs==17.3.0 attrs==17.3.0
Babel==2.5.1 Babel==2.5.1
backports.functools-lru-cache==1.5 backports.functools-lru-cache==1.5
@@ -33,17 +33,18 @@ importlib-metadata==0.23
ipdb==0.10.2 ipdb==0.10.2
ipython==5.5.0 ipython==5.5.0
ipython-genutils==0.2.0 ipython-genutils==0.2.0
isort==4.2.5 isort==4.3.21
Jinja2==2.9.6 Jinja2==2.9.6
jmespath==0.9.3 jmespath==0.9.3
josepy==1.1.0 josepy==1.1.0
lazy-object-proxy==1.3.1 lazy-object-proxy==1.4.3
logger==1.4 logger==1.4
logilab-common==1.4.1 logilab-common==1.4.1
MarkupSafe==1.0 MarkupSafe==1.0
mccabe==0.6.1 mccabe==0.6.1
more-itertools==5.0.0 more-itertools==5.0.0
mypy==0.600 mypy==0.710
mypy-extensions==0.4.3
ndg-httpsclient==0.3.2 ndg-httpsclient==0.3.2
oauth2client==4.0.0 oauth2client==4.0.0
packaging==19.2 packaging==19.2
@@ -58,7 +59,7 @@ py==1.8.0
pyasn1==0.1.9 pyasn1==0.1.9
pyasn1-modules==0.0.10 pyasn1-modules==0.0.10
Pygments==2.2.0 Pygments==2.2.0
pylint==1.9.4 pylint==2.4.3
# If pynsist version is upgraded, our NSIS template windows-installer/template.nsi # If pynsist version is upgraded, our NSIS template windows-installer/template.nsi
# must be upgraded if necessary using the new built-in one from pynsist. # must be upgraded if necessary using the new built-in one from pynsist.
pynsist==2.4 pynsist==2.4
@@ -90,10 +91,10 @@ tox==3.14.0
tqdm==4.19.4 tqdm==4.19.4
traitlets==4.3.2 traitlets==4.3.2
twine==1.11.0 twine==1.11.0
typed-ast==1.1.0 typed-ast==1.4.0
typing==3.6.4 typing==3.6.4
uritemplate==3.0.0 uritemplate==3.0.0
virtualenv==16.6.2 virtualenv==16.6.2
wcwidth==0.1.7 wcwidth==0.1.7
wrapt==1.11.1 wrapt==1.11.2
zipp==0.6.0 zipp==0.6.0
+2 -1
View File
@@ -122,12 +122,13 @@ commands =
python tox.cover.py python tox.cover.py
[testenv:lint] [testenv:lint]
basepython = python2.7 basepython = python3
# separating into multiple invocations disables cross package # separating into multiple invocations disables cross package
# duplicate code checking; if one of the commands fails, others will # duplicate code checking; if one of the commands fails, others will
# continue, but tox return code will reflect previous error # continue, but tox return code will reflect previous error
commands = commands =
{[base]install_packages} {[base]install_packages}
{[base]pip_install} certbot[dev3]
python -m pylint --reports=n --rcfile=.pylintrc {[base]source_paths} python -m pylint --reports=n --rcfile=.pylintrc {[base]source_paths}
[testenv:mypy] [testenv:mypy]