diff --git a/README.rst b/README.rst index 65aac1d..c7469ab 100644 --- a/README.rst +++ b/README.rst @@ -121,12 +121,14 @@ Main methods are `execute`, `check_call` and `check_stderr` for simple executing and executing, checking return code and checking for empty stderr output. This methods are almost the same for `SSHCleint` and `Subprocess`, except specific flags. +.. note:: By default ALL methods have timeout 1 hour, infinite waiting can be enabled, but it's special case. + .. code-block:: python result = helper.execute( command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=1 * 60 * 60, # type: typing.Optional[int] **kwargs ) @@ -136,7 +138,7 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif result = helper.check_call( command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=1 * 60 * 60, # type: typing.Optional[int] error_info=None, # type: typing.Optional[str] expected=None, # type: typing.Optional[typing.Iterable[int]] raise_on_err=True, # type: bool @@ -148,7 +150,7 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif result = helper.check_stderr( command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=1 * 60 * 60, # type: typing.Optional[int] error_info=None, # type: typing.Optional[str] raise_on_err=True, # type: bool ) @@ -193,7 +195,7 @@ Possible to call commands in parallel on multiple hosts if it's not produce huge results = SSHClient.execute_together( remotes, # type: typing.Iterable[SSHClient] command, # type: str - timeout=None, # type: typing.Optional[int] + timeout=1 * 60 * 60, # type: typing.Optional[int] expected=None, # type: typing.Optional[typing.Iterable[int]] raise_on_err=True # type: bool ) @@ -211,7 +213,7 @@ For execute through SSH host can be used `execute_through_host` method: command, # type: str auth=None, # type: typing.Optional[SSHAuth] target_port=22, # type: int - timeout=None, # type: typing.Optional[int] + timeout=1 * 60 * 60, # type: typing.Optional[int] verbose=False, # type: bool get_pty=False, # type: bool ) diff --git a/doc/source/SSHClient.rst b/doc/source/SSHClient.rst index 9f35c9b..c840d49 100644 --- a/doc/source/SSHClient.rst +++ b/doc/source/SSHClient.rst @@ -79,14 +79,14 @@ API: SSHClient and SSHAuth. Open context manager - .. versionchanged:: 1.1.0 - lock on enter + .. versionchanged:: 1.1.0 lock on enter .. py:method:: __exit__(self, exc_type, exc_val, exc_tb) Close context manager and disconnect - .. versionchanged:: 1.0.0 - disconnect enforced on close - .. versionchanged:: 1.1.0 - release lock on exit + .. versionchanged:: 1.0.0 disconnect enforced on close + .. versionchanged:: 1.1.0 release lock on exit .. py:method:: sudo(enforce=None) @@ -109,9 +109,9 @@ API: SSHClient and SSHAuth. :type open_stderr: bool :rtype: ``typing.Tuple[paramiko.Channel, paramiko.ChannelFile, paramiko.ChannelFile, paramiko.ChannelFile]`` - .. versionchanged:: 1.2.0 - open_stdout and open_stderr flags + .. versionchanged:: 1.2.0 open_stdout and open_stderr flags - .. py:method:: execute(command, verbose=False, timeout=None, **kwargs) + .. py:method:: execute(command, verbose=False, timeout=1*60*60, **kwargs) Execute command and wait for return code. @@ -124,7 +124,9 @@ API: SSHClient and SSHAuth. :rtype: ExecResult :raises: ExecHelperTimeoutError - .. py:method:: check_call(command, verbose=False, timeout=None, error_info=None, expected=None, raise_on_err=True, **kwargs) + .. versionchanged:: 1.2.0 default timeout 1 hour + + .. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, **kwargs) Execute command and check for return code. @@ -143,7 +145,9 @@ API: SSHClient and SSHAuth. :rtype: ExecResult :raises: CalledProcessError - .. py:method:: check_stderr(command, verbose=False, timeout=None, error_info=None, raise_on_err=True, **kwargs) + .. versionchanged:: 1.2.0 default timeout 1 hour + + .. py:method:: check_stderr(command, verbose=False, timeout=1*60*60, error_info=None, raise_on_err=True, **kwargs) Execute command expecting return code 0 and empty STDERR. @@ -161,8 +165,9 @@ API: SSHClient and SSHAuth. :raises: CalledProcessError .. note:: expected return codes can be overridden via kwargs. + .. versionchanged:: 1.2.0 default timeout 1 hour - .. py:method:: execute_through_host(hostname, command, auth=None, target_port=22, verbose=False, timeout=None, get_pty=False, **kwargs) + .. py:method:: execute_through_host(hostname, command, auth=None, target_port=22, verbose=False, timeout=1*60*60, get_pty=False, **kwargs) Execute command on remote host through currently connected host. @@ -183,7 +188,9 @@ API: SSHClient and SSHAuth. :rtype: ExecResult :raises: ExecHelperTimeoutError - .. py:classmethod:: execute_together(remotes, command, timeout=None, expected=None, raise_on_err=True, **kwargs) + .. versionchanged:: 1.2.0 default timeout 1 hour + + .. py:classmethod:: execute_together(remotes, command, timeout=1*60*60, expected=None, raise_on_err=True, **kwargs) Execute command on multiple remotes in async mode. @@ -202,6 +209,8 @@ API: SSHClient and SSHAuth. :raises: ParallelCallProcessError :raises: ParallelCallExceptions + .. versionchanged:: 1.2.0 default timeout 1 hour + .. py:method:: open(path, mode='r') Open file on remote using SFTP session. diff --git a/doc/source/Subprocess.rst b/doc/source/Subprocess.rst index cf09b98..de86282 100644 --- a/doc/source/Subprocess.rst +++ b/doc/source/Subprocess.rst @@ -16,15 +16,15 @@ API: Subprocess Open context manager - .. versionchanged:: 1.1.0 - lock on enter + .. versionchanged:: 1.1.0 lock on enter .. py:method:: __exit__(self, exc_type, exc_val, exc_tb) Close context manager - .. versionchanged:: 1.1.0 - release lock on exit + .. versionchanged:: 1.1.0 release lock on exit - .. py:method:: execute(command, verbose=False, timeout=None, **kwargs) + .. py:method:: execute(command, verbose=False, timeout=1*60*60, **kwargs) Execute command and wait for return code. @@ -37,10 +37,13 @@ API: Subprocess :rtype: ExecResult :raises: ExecHelperTimeoutError - .. versionchanged:: 1.1.0 - make method - .. versionchanged:: 1.2.0 - open_stdout and open_stderr flags + .. versionchanged:: 1.1.0 make method + .. versionchanged:: 1.2.0 - .. py:method:: check_call(command, verbose=False, timeout=None, error_info=None, expected=None, raise_on_err=True, **kwargs) + open_stdout and open_stderr flags + default timeout 1 hour + + .. py:method:: check_call(command, verbose=False, timeout=1*60*60, error_info=None, expected=None, raise_on_err=True, **kwargs) Execute command and check for return code. @@ -59,9 +62,10 @@ API: Subprocess :rtype: ExecResult :raises: CalledProcessError - .. versionchanged:: 1.1.0 - make method + .. versionchanged:: 1.1.0 make method + .. versionchanged:: 1.2.0 default timeout 1 hour - .. py:method:: check_stderr(command, verbose=False, timeout=None, error_info=None, raise_on_err=True, **kwargs) + .. py:method:: check_stderr(command, verbose=False, timeout=1*60*60, error_info=None, raise_on_err=True, **kwargs) Execute command expecting return code 0 and empty STDERR. @@ -80,4 +84,5 @@ API: Subprocess .. note:: expected return codes can be overridden via kwargs. - .. versionchanged:: 1.1.0 - make method + .. versionchanged:: 1.1.0 make method + .. versionchanged:: 1.2.0 default timeout 1 hour diff --git a/exec_helpers/_log_templates.py b/exec_helpers/_log_templates.py index 0bef7d4..d3f8c3b 100644 --- a/exec_helpers/_log_templates.py +++ b/exec_helpers/_log_templates.py @@ -17,6 +17,7 @@ """Text templates for logging.""" from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals CMD_EXEC = "Executing command:\n{cmd!s}\n" diff --git a/exec_helpers/_ssh_client_base.py b/exec_helpers/_ssh_client_base.py index a8c22d4..2d5f9ef 100644 --- a/exec_helpers/_ssh_client_base.py +++ b/exec_helpers/_ssh_client_base.py @@ -16,6 +16,8 @@ """SSH client helper based on Paramiko. Base class.""" +from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals import base64 @@ -37,11 +39,12 @@ import threaded import six -from exec_helpers import exceptions +from exec_helpers import constants from exec_helpers import exec_result -from exec_helpers import _log_templates +from exec_helpers import exceptions from exec_helpers import proc_enums from exec_helpers import ssh_auth +from exec_helpers import _log_templates __all__ = ('SSHClientBase', ) @@ -474,7 +477,7 @@ def __del__(self): def __enter__(self): """Get context manager. - .. versionchanged:: 1.1.0 - lock on enter + .. versionchanged:: 1.1.0 lock on enter """ self.lock.acquire() return self @@ -482,8 +485,8 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): """Exit context manager. - .. versionchanged:: 1.0.0 - disconnect enforced on close - .. versionchanged:: 1.1.0 - release lock on exit + .. versionchanged:: 1.0.0 disconnect enforced on close + .. versionchanged:: 1.1.0 release lock on exit """ self.close() self.lock.release() @@ -534,7 +537,7 @@ def execute_async( typing.Optional[paramiko.ChannelFile], ] - .. versionchanged:: 1.2.0 - open_stdout and open_stderr flags + .. versionchanged:: 1.2.0 open_stdout and open_stderr flags """ message = _log_templates.CMD_EXEC.format(cmd=command.rstrip()) self.logger.debug(message) @@ -690,7 +693,7 @@ def execute( self, command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] **kwargs ): # type: (...) -> exec_result.ExecResult """Execute command and wait for return code. @@ -703,6 +706,8 @@ def execute( :type timeout: typing.Optional[int] :rtype: ExecResult :raises: ExecHelperTimeoutError + + .. versionchanged:: 1.2.0 default timeout 1 hour """ ( chan, # type: paramiko.channel.Channel @@ -726,7 +731,7 @@ def check_call( self, command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] error_info=None, # type: typing.Optional[str] expected=None, # type: typing.Optional[typing.Iterable[]] raise_on_err=True, # type: bool @@ -748,6 +753,8 @@ def check_call( :type raise_on_err: bool :rtype: ExecResult :raises: CalledProcessError + + .. versionchanged:: 1.2.0 default timeout 1 hour """ expected = proc_enums.exit_codes_to_enums(expected) ret = self.execute(command, verbose, timeout, **kwargs) @@ -770,7 +777,7 @@ def check_stderr( self, command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] error_info=None, # type: typing.Optional[str] raise_on_err=True, # type: bool **kwargs @@ -791,6 +798,7 @@ def check_stderr( :raises: CalledProcessError .. note:: expected return codes can be overridden via kwargs. + .. versionchanged:: 1.2.0 default timeout 1 hour """ ret = self.check_call( command, verbose, timeout=timeout, @@ -816,7 +824,7 @@ def execute_through_host( auth=None, # type: typing.Optional[ssh_auth.SSHAuth] target_port=22, # type: int verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] get_pty=False, # type: bool **kwargs ): # type: (...) -> exec_result.ExecResult @@ -838,6 +846,8 @@ def execute_through_host( :type get_pty: bool :rtype: ExecResult :raises: ExecHelperTimeoutError + + .. versionchanged:: 1.2.0 default timeout 1 hour """ if auth is None: auth = self.auth @@ -880,7 +890,7 @@ def execute_together( cls, remotes, # type: typing.Iterable[SSHClientBase] command, # type: str - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] expected=None, # type: typing.Optional[typing.Iterable[]] raise_on_err=True, # type: bool **kwargs @@ -901,6 +911,8 @@ def execute_together( :rtype: typing.Dict[typing.Tuple[str, int], exec_result.ExecResult] :raises: ParallelCallProcessError :raises: ParallelCallExceptions + + .. versionchanged:: 1.2.0 default timeout 1 hour """ @threaded.threadpooled def get_result( diff --git a/exec_helpers/constants.py b/exec_helpers/constants.py new file mode 100644 index 0000000..6254ff7 --- /dev/null +++ b/exec_helpers/constants.py @@ -0,0 +1,27 @@ +# Copyright 2018 Alexey Stepanov aka penguinolog. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Global constants.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import unicode_literals + + +MINUTE = 60 +HOUR = 60 * MINUTE + + +# Default command timeout +DEFAULT_TIMEOUT = 1 * HOUR diff --git a/exec_helpers/exceptions.py b/exec_helpers/exceptions.py index fac9883..457b847 100644 --- a/exec_helpers/exceptions.py +++ b/exec_helpers/exceptions.py @@ -15,6 +15,8 @@ """Package specific exceptions.""" from __future__ import absolute_import +from __future__ import division +from __future__ import unicode_literals import typing diff --git a/exec_helpers/exec_result.py b/exec_helpers/exec_result.py index 4ba7f0c..214e751 100644 --- a/exec_helpers/exec_result.py +++ b/exec_helpers/exec_result.py @@ -17,6 +17,7 @@ """Execution restult.""" from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals import datetime diff --git a/exec_helpers/proc_enums.py b/exec_helpers/proc_enums.py index eaaaf70..df25a5c 100644 --- a/exec_helpers/proc_enums.py +++ b/exec_helpers/proc_enums.py @@ -20,6 +20,7 @@ """ from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals import enum diff --git a/exec_helpers/ssh_auth.py b/exec_helpers/ssh_auth.py index ac1df0b..0c2e285 100644 --- a/exec_helpers/ssh_auth.py +++ b/exec_helpers/ssh_auth.py @@ -16,6 +16,8 @@ """SSH client credentials class.""" +from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals import copy diff --git a/exec_helpers/ssh_client.py b/exec_helpers/ssh_client.py index f2e48bf..3232596 100644 --- a/exec_helpers/ssh_client.py +++ b/exec_helpers/ssh_client.py @@ -17,6 +17,7 @@ """SSH client helper based on Paramiko. Extended API helpers.""" from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals import logging diff --git a/exec_helpers/subprocess_runner.py b/exec_helpers/subprocess_runner.py index 0d32860..4858bf1 100644 --- a/exec_helpers/subprocess_runner.py +++ b/exec_helpers/subprocess_runner.py @@ -17,6 +17,7 @@ """Python subprocess.Popen wrapper.""" from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals import logging @@ -31,10 +32,11 @@ import six import threaded -from exec_helpers import exceptions +from exec_helpers import constants from exec_helpers import exec_result -from exec_helpers import _log_templates +from exec_helpers import exceptions from exec_helpers import proc_enums +from exec_helpers import _log_templates logger = logging.getLogger(__name__) devnull = open(os.devnull) # subprocess.DEVNULL is py3.3+ @@ -165,7 +167,7 @@ def __exec_command( command, # type: str cwd=None, # type: typing.Optional[str] env=None, # type: typing.Optional[typing.Dict[str, typing.Any]] - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] verbose=False, # type: bool open_stdout=True, # type: bool open_stderr=True, # type: bool @@ -182,7 +184,8 @@ def __exec_command( :type open_stderr: bool :rtype: ExecResult - .. versionchanged:: 1.2.0 - open_stdout and open_stderr flags + .. versionchanged:: 1.2.0 open_stdout and open_stderr flags + .. versionchanged:: 1.2.0 default timeout 1 hour """ def poll_streams( result, # type: exec_result.ExecResult @@ -313,7 +316,7 @@ def execute( self, command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] **kwargs ): # type: (...) -> exec_result.ExecResult """Execute command and wait for return code. @@ -325,6 +328,8 @@ def execute( :type timeout: typing.Optional[int] :rtype: ExecResult :raises: ExecHelperTimeoutError + + .. versionchanged:: 1.2.0 default timeout 1 hour """ result = self.__exec_command(command=command, timeout=timeout, verbose=verbose, **kwargs) @@ -340,7 +345,7 @@ def check_call( self, command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] error_info=None, # type: typing.Optional[str] expected=None, # type: _type_expected raise_on_err=True, # type: bool @@ -358,6 +363,8 @@ def check_call( :type raise_on_err: bool :rtype: ExecResult :raises: DevopsCalledProcessError + + .. versionchanged:: 1.2.0 default timeout 1 hour """ expected = proc_enums.exit_codes_to_enums(expected) ret = self.execute(command, verbose, timeout, **kwargs) @@ -380,7 +387,7 @@ def check_stderr( self, command, # type: str verbose=False, # type: bool - timeout=None, # type: typing.Optional[int] + timeout=constants.DEFAULT_TIMEOUT, # type: typing.Optional[int] error_info=None, # type: typing.Optional[str] raise_on_err=True, # type: bool **kwargs @@ -396,6 +403,8 @@ def check_stderr( :type raise_on_err: bool :rtype: ExecResult :raises: DevopsCalledProcessError + + .. versionchanged:: 1.2.0 default timeout 1 hour """ ret = self.check_call( command, verbose, timeout=timeout, diff --git a/setup.py b/setup.py index 2e56a0d..3550094 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,10 @@ """Execution helpers for simplified usage of subprocess and ssh.""" +from __future__ import absolute_import +from __future__ import division from __future__ import print_function +from __future__ import unicode_literals import ast import collections @@ -52,10 +55,15 @@ def _extension(modpath): """Make setuptools.Extension.""" - return setuptools.Extension(modpath, [modpath.replace('.', '/') + '.py']) + source_path = modpath.replace('.', '/') + '.py' + return setuptools.Extension( + modpath if PY3 else modpath.encode('utf-8'), + [source_path if PY3 else source_path.encode('utf-8')] + ) requires_optimization = [ + _extension('exec_helpers.constants'), _extension('exec_helpers._log_templates'), _extension('exec_helpers.exceptions'), _extension('exec_helpers.exec_result'), diff --git a/test/test_exec_result.py b/test/test_exec_result.py index a90a1f2..a5fbef8 100644 --- a/test/test_exec_result.py +++ b/test/test_exec_result.py @@ -15,6 +15,7 @@ # under the License. from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals # pylint: disable=no-self-use diff --git a/test/test_ssh_client.py b/test/test_ssh_client.py index 57bc104..a724820 100644 --- a/test/test_ssh_client.py +++ b/test/test_ssh_client.py @@ -17,6 +17,7 @@ # under the License. from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals # pylint: disable=no-self-use @@ -32,6 +33,7 @@ import paramiko import exec_helpers +from exec_helpers import constants from exec_helpers import exec_result @@ -724,8 +726,7 @@ def test_execute_timeout_fail( execute_async.assert_called_once_with(command) chan.assert_has_calls((mock.call.status_event.is_set(), )) - @mock.patch( - 'exec_helpers.ssh_client.SSHClient.execute_async') + @mock.patch('exec_helpers.ssh_client.SSHClient.execute_async') def test_execute_together(self, execute_async, client, policy, logger): ( chan, _stdin, _, stderr, stdout @@ -754,10 +755,10 @@ def test_execute_together(self, execute_async, client, policy, logger): self.assertEqual( sorted(chan.mock_calls), sorted(( - mock.call.status_event.wait(None), + mock.call.status_event.wait(constants.DEFAULT_TIMEOUT), mock.call.recv_exit_status(), mock.call.close(), - mock.call.status_event.wait(None), + mock.call.status_event.wait(constants.DEFAULT_TIMEOUT), mock.call.recv_exit_status(), mock.call.close() )) @@ -776,8 +777,7 @@ def test_execute_together(self, execute_async, client, policy, logger): exec_helpers.SSHClient.execute_together( remotes=remotes, command=command, expected=[1]) - @mock.patch( - 'exec_helpers.ssh_client.SSHClient.execute_async') + @mock.patch('exec_helpers.ssh_client.SSHClient.execute_async') def test_execute_together_exceptions( self, execute_async, # type: mock.Mock @@ -815,8 +815,7 @@ def test_execute_together_exceptions( for exception in exc.exceptions.values(): self.assertIsInstance(exception, RuntimeError) - @mock.patch( - 'exec_helpers.ssh_client.SSHClient.execute') + @mock.patch('exec_helpers.ssh_client.SSHClient.execute') def test_check_call(self, execute, client, policy, logger): exit_code = 0 return_value = exec_result.ExecResult( @@ -855,8 +854,7 @@ def test_check_call(self, execute, client, policy, logger): self.assertEqual(exc.stderr, stderr_str) execute.assert_called_once_with(command, verbose, None) - @mock.patch( - 'exec_helpers.ssh_client.SSHClient.execute') + @mock.patch('exec_helpers.ssh_client.SSHClient.execute') def test_check_call_expected(self, execute, client, policy, logger): exit_code = 0 return_value = exec_result.ExecResult( @@ -894,8 +892,7 @@ def test_check_call_expected(self, execute, client, policy, logger): ) execute.assert_called_once_with(command, verbose, None) - @mock.patch( - 'exec_helpers.ssh_client.SSHClient.check_call') + @mock.patch('exec_helpers.ssh_client.SSHClient.check_call') def test_check_stderr(self, check_call, client, policy, logger): return_value = exec_result.ExecResult( cmd=command, diff --git a/test/test_ssh_client_init.py b/test/test_ssh_client_init.py index 7b77d06..feffdc8 100644 --- a/test/test_ssh_client_init.py +++ b/test/test_ssh_client_init.py @@ -17,6 +17,7 @@ # under the License. from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals # pylint: disable=no-self-use diff --git a/test/test_sshauth.py b/test/test_sshauth.py index 3c3431d..0c4ce5f 100644 --- a/test/test_sshauth.py +++ b/test/test_sshauth.py @@ -17,6 +17,7 @@ # under the License. from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals # pylint: disable=no-self-use diff --git a/test/test_subprocess_runner.py b/test/test_subprocess_runner.py index 483f9fe..a642d76 100644 --- a/test/test_subprocess_runner.py +++ b/test/test_subprocess_runner.py @@ -17,6 +17,7 @@ # under the License. from __future__ import absolute_import +from __future__ import division from __future__ import unicode_literals import logging diff --git a/tox.ini b/tox.ini index 235ecd3..c1ac392 100644 --- a/tox.ini +++ b/tox.ini @@ -70,6 +70,7 @@ pypy3 = install, pypy3, [testenv:pep8] deps = flake8 + flake8-future-import usedevelop = False commands = flake8 @@ -106,6 +107,28 @@ exclude = __init__.py, docs ignore = + # Expected + # __future__ import "division" present + FI50 + # __future__ import "absolute_import" present + FI51 + # Setup only: + # __future__ import "print_function" present + FI53 + # __future__ import "unicode_literals" present + FI54 + + # Expected missind (not used or required for not supported versions): + # __future__ import "with_statement" missing + FI12 + # __future__ import "print_function" missing + FI13 + # __future__ import "generator_stop" missing + FI15 + # __future__ import "nested_scopes" missing + FI16 + # __future__ import "generators" missing + FI17 show-pep8 = True show-source = True count = True