Merge remote-tracking branch 'goog/mirror-aosp-master' into bp_timeout am: e2a8f4c150

Original change: https://googleplex-android-review.googlesource.com/c/platform/external/python/timeout-decorator/+/18819311

Change-Id: I0671d8d0948d4b32b40fb4a1aefa854efa9a592e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0b7661f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+*.py[cod]
+*.swp
+*~
+venv
+.env
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+.cache
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+.ropeproject/
+.vscode/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..02adf35
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,21 @@
+language: python
+sudo: false
+python:
+- '2.7'
+- '3.6'
+- '3.7'
+- '3.8'
+install:
+- pip install python-coveralls tox tox-travis
+script: tox --recreate
+after_success:
+- pip install -e .
+- py.test --cov=timeout_decorator --cov-report=term-missing tests
+- coveralls
+deploy:
+  provider: pypi
+  user: png
+  password:
+    secure: ZXoq3kgfu+IICjhhmQZr0s0xE6bvWzH04GjdE/VL4BxdDdGI4fHEwudGEjzLXJbt2d09vNOO67Nqam+MwPWtq+WZEP69g/Fhyy4kbkuUl/CMeqashQzU/N+3lwv97Y2qvzTUwDnSoz4zyBFu67SSrovKruFsYaiH00bwvWcvLa0=
+  on:
+    python: 2.7
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..535ec1d
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// 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.
+
+package {
+    default_applicable_licenses: ["external_python_timeout_decorator_license"],
+}
+
+license {
+    name: "external_python_timeout_decorator_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-MIT",
+    ],
+    license_text: [
+        "LICENSE",
+    ],
+}
diff --git a/CHANGES.rst b/CHANGES.rst
new file mode 100644
index 0000000..1fd3175
--- /dev/null
+++ b/CHANGES.rst
@@ -0,0 +1,18 @@
+Changelog
+=========
+
+0.3.1
+-----
+- Fixed issue with PicklingError causes the timeout to never be reached.
+
+0.3.0
+-----
+
+- Added optional threading support via python multiprocessing (bubenkoff)
+- Switched to pytest test runner (bubenkoff)
+
+
+0.2.1
+-----
+
+- Initial public release
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..85de3d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE.txt
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..671c599
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2014 Patrick Ng
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..bb37a27
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include *.rst
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..5323ad6
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,16 @@
+name: "timeout-decorator"
+description:
+    "timeout-decorator"
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://github.com/pnpnpn/timeout-decorator"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/pnpnpn/timeout-decorator"
+  }
+  version: "9fbc3ef5b6f8f8cba2eb7ba795813d6ec543e265"
+  last_upgrade_date { year: 2022 month: 4 day: 25 }
+  license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..017607e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+# create virtual environment
+venv:
+	virtualenv venv
+
+# install all needed for development
+develop: venv
+	venv/bin/pip install -e . -r requirements-testing.txt tox
+
+# clean the development envrironment
+clean:
+	-rm -rf venv
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..671c599
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2014 Patrick Ng
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..eb86f14
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,8 @@
+# Android side engprod team
+jdesprez@google.com
+frankfeng@google.com
+murj@google.com
+
+# Mobly team - use for mobly bugs
+angli@google.com
+lancefluger@google.com
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..2dcea7b
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,111 @@
+Timeout decorator
+=================
+
+|Build Status| |Pypi Status| |Coveralls Status|
+
+Installation
+------------
+
+From source code:
+
+::
+
+    python setup.py install
+
+From pypi:
+
+::
+
+    pip install timeout-decorator
+
+Usage
+-----
+
+::
+
+    import time
+    import timeout_decorator
+
+    @timeout_decorator.timeout(5)
+    def mytest():
+        print("Start")
+        for i in range(1,10):
+            time.sleep(1)
+            print("{} seconds have passed".format(i))
+
+    if __name__ == '__main__':
+        mytest()
+
+Specify an alternate exception to raise on timeout:
+
+::
+
+    import time
+    import timeout_decorator
+
+    @timeout_decorator.timeout(5, timeout_exception=StopIteration)
+    def mytest():
+        print("Start")
+        for i in range(1,10):
+            time.sleep(1)
+            print("{} seconds have passed".format(i))
+
+    if __name__ == '__main__':
+        mytest()
+
+Multithreading
+--------------
+
+By default, timeout-decorator uses signals to limit the execution time
+of the given function. This appoach does not work if your function is
+executed not in a main thread (for example if it's a worker thread of
+the web application). There is alternative timeout strategy for this
+case - by using multiprocessing. To use it, just pass
+``use_signals=False`` to the timeout decorator function:
+
+::
+
+    import time
+    import timeout_decorator
+
+    @timeout_decorator.timeout(5, use_signals=False)
+    def mytest():
+        print "Start"
+        for i in range(1,10):
+            time.sleep(1)
+            print("{} seconds have passed".format(i))
+
+    if __name__ == '__main__':
+        mytest()
+
+.. warning::
+    Make sure that in case of multiprocessing strategy for timeout, your function does not return objects which cannot
+    be pickled, otherwise it will fail at marshalling it between master and child processes.
+
+
+Acknowledgement
+---------------
+
+Derived from
+http://www.saltycrane.com/blog/2010/04/using-python-timeout-decorator-uploading-s3/
+and https://code.google.com/p/verse-quiz/source/browse/trunk/timeout.py
+
+Contribute
+----------
+
+I would love for you to fork and send me pull request for this project.
+Please contribute.
+
+License
+-------
+
+This software is licensed under the `MIT license <http://en.wikipedia.org/wiki/MIT_License>`_
+
+See `License file <https://github.com/pnpnpn/timeout-decorator/blob/master/LICENSE.txt>`_
+
+.. |Build Status| image:: https://travis-ci.org/pnpnpn/timeout-decorator.svg?branch=master
+   :target: https://travis-ci.org/pnpnpn/timeout-decorator
+.. |Pypi Status| image:: https://badge.fury.io/py/timeout-decorator.svg
+    :target: https://badge.fury.io/py/timeout-decorator
+.. |Coveralls Status| image:: https://coveralls.io/repos/pnpnpn/timeout-decorator/badge.png?branch=master
+    :target: https://coveralls.io/r/pnpnpn/timeout-decorator
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..32771a8
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,38 @@
+"""Setuptools entry point."""
+import codecs
+import os
+
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+
+
+CLASSIFIERS = [
+    'Development Status :: 4 - Beta',
+    'Intended Audience :: Developers',
+    'License :: OSI Approved :: MIT License',
+    'Natural Language :: English',
+    'Operating System :: OS Independent',
+    'Programming Language :: Python',
+    'Topic :: Software Development :: Libraries :: Python Modules'
+]
+
+dirname = os.path.dirname(__file__)
+
+long_description = (
+    codecs.open(os.path.join(dirname, 'README.rst'), encoding='utf-8').read() + '\n' +
+    codecs.open(os.path.join(dirname, 'CHANGES.rst'), encoding='utf-8').read()
+)
+
+setup(
+    name='timeout-decorator',
+    version='0.5.0',
+    description='Timeout decorator',
+    long_description=long_description,
+    author='Patrick Ng',
+    author_email='pn.appdev@gmail.com',
+    url='https://github.com/pnpnpn/timeout-decorator',
+    packages=['timeout_decorator'],
+    install_requires=[],
+    classifiers=CLASSIFIERS)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/test_timeout_decorator.py b/tests/test_timeout_decorator.py
new file mode 100644
index 0000000..2c2d9c7
--- /dev/null
+++ b/tests/test_timeout_decorator.py
@@ -0,0 +1,120 @@
+"""Timeout decorator tests."""
+import time
+
+import pytest
+
+from timeout_decorator import timeout, TimeoutError
+
+
+@pytest.fixture(params=[False, True])
+def use_signals(request):
+    """Use signals for timing out or not."""
+    return request.param
+
+
+def test_timeout_decorator_arg(use_signals):
+    @timeout(1, use_signals=use_signals)
+    def f():
+        time.sleep(2)
+    with pytest.raises(TimeoutError):
+        f()
+
+
+def test_timeout_class_method(use_signals):
+    class c():
+        @timeout(1, use_signals=use_signals)
+        def f(self):
+            time.sleep(2)
+    with pytest.raises(TimeoutError):
+        c().f()
+
+
+def test_timeout_kwargs(use_signals):
+    @timeout(3, use_signals=use_signals)
+    def f():
+        time.sleep(2)
+    with pytest.raises(TimeoutError):
+        f(timeout=1)
+
+
+def test_timeout_alternate_exception(use_signals):
+    @timeout(3, use_signals=use_signals, timeout_exception=StopIteration)
+    def f():
+        time.sleep(2)
+    with pytest.raises(StopIteration):
+        f(timeout=1)
+
+
+def test_timeout_kwargs_with_initial_timeout_none(use_signals):
+    @timeout(use_signals=use_signals)
+    def f():
+        time.sleep(2)
+    with pytest.raises(TimeoutError):
+        f(timeout=1)
+
+
+def test_timeout_no_seconds(use_signals):
+    @timeout(use_signals=use_signals)
+    def f():
+        time.sleep(0.1)
+    f()
+
+
+def test_timeout_partial_seconds(use_signals):
+    @timeout(0.2, use_signals=use_signals)
+    def f():
+        time.sleep(0.5)
+    with pytest.raises(TimeoutError):
+        f()
+
+
+def test_timeout_ok(use_signals):
+    @timeout(seconds=2, use_signals=use_signals)
+    def f():
+        time.sleep(1)
+    f()
+
+
+def test_function_name(use_signals):
+    @timeout(seconds=2, use_signals=use_signals)
+    def func_name():
+        pass
+
+    assert func_name.__name__ == 'func_name'
+
+
+def test_timeout_pickle_error():
+    """Test that when a pickle error occurs a timeout error is raised."""
+    @timeout(seconds=1, use_signals=False)
+    def f():
+        time.sleep(0.1)
+
+        class Test(object):
+            pass
+        return Test()
+    with pytest.raises(TimeoutError):
+        f()
+
+
+def test_timeout_custom_exception_message():
+    @timeout(seconds=1, exception_message="Custom fail message")
+    def f():
+        time.sleep(2)
+    with pytest.raises(TimeoutError, match="Custom fail message"):
+        f()
+
+
+def test_timeout_custom_exception_with_message():
+    @timeout(seconds=1, timeout_exception=RuntimeError, exception_message="Custom fail message")
+    def f():
+        time.sleep(2)
+    with pytest.raises(RuntimeError, match="Custom fail message"):
+        f()
+
+
+def test_timeout_default_exception_message():
+    @timeout(seconds=1)
+    def f():
+        time.sleep(2)
+    with pytest.raises(TimeoutError, match="Timed Out"):
+        f()
diff --git a/timeout_decorator/Android.bp b/timeout_decorator/Android.bp
new file mode 100644
index 0000000..b70e430
--- /dev/null
+++ b/timeout_decorator/Android.bp
@@ -0,0 +1,33 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// 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.
+package {
+    default_applicable_licenses: ["external_python_timeout_decorator_license"],
+}
+
+python_library {
+    name: "py-timeout-decorator",
+    host_supported: true,
+    srcs: [
+        "timeout_decorator.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+    pkg_path: "timeout_decorator",
+}
diff --git a/timeout_decorator/__init__.py b/timeout_decorator/__init__.py
new file mode 100644
index 0000000..4c8254b
--- /dev/null
+++ b/timeout_decorator/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+from .timeout_decorator import timeout
+from .timeout_decorator import TimeoutError
+
+__title__ = 'timeout_decorator'
+__version__ = '0.5.0'
diff --git a/timeout_decorator/timeout_decorator.py b/timeout_decorator/timeout_decorator.py
new file mode 100644
index 0000000..42b6686
--- /dev/null
+++ b/timeout_decorator/timeout_decorator.py
@@ -0,0 +1,175 @@
+"""
+Timeout decorator.
+
+    :copyright: (c) 2012-2013 by PN.
+    :license: MIT, see LICENSE for more details.
+"""
+
+from __future__ import print_function
+from __future__ import unicode_literals
+from __future__ import division
+
+import sys
+import time
+import multiprocessing
+import signal
+from functools import wraps
+
+############################################################
+# Timeout
+############################################################
+
+# http://www.saltycrane.com/blog/2010/04/using-python-timeout-decorator-uploading-s3/
+# Used work of Stephen "Zero" Chappell <Noctis.Skytower@gmail.com>
+# in https://code.google.com/p/verse-quiz/source/browse/trunk/timeout.py
+
+
+class TimeoutError(AssertionError):
+
+    """Thrown when a timeout occurs in the `timeout` context manager."""
+
+    def __init__(self, value="Timed Out"):
+        self.value = value
+
+    def __str__(self):
+        return repr(self.value)
+
+
+def _raise_exception(exception, exception_message):
+    """ This function checks if a exception message is given.
+
+    If there is no exception message, the default behaviour is maintained.
+    If there is an exception message, the message is passed to the exception with the 'value' keyword.
+    """
+    if exception_message is None:
+        raise exception()
+    else:
+        raise exception(exception_message)
+
+
+def timeout(seconds=None, use_signals=True, timeout_exception=TimeoutError, exception_message=None):
+    """Add a timeout parameter to a function and return it.
+
+    :param seconds: optional time limit in seconds or fractions of a second. If None is passed, no timeout is applied.
+        This adds some flexibility to the usage: you can disable timing out depending on the settings.
+    :type seconds: float
+    :param use_signals: flag indicating whether signals should be used for timing function out or the multiprocessing
+        When using multiprocessing, timeout granularity is limited to 10ths of a second.
+    :type use_signals: bool
+
+    :raises: TimeoutError if time limit is reached
+
+    It is illegal to pass anything other than a function as the first
+    parameter. The function is wrapped and returned to the caller.
+    """
+    def decorate(function):
+
+        if use_signals:
+            def handler(signum, frame):
+                _raise_exception(timeout_exception, exception_message)
+
+            @wraps(function)
+            def new_function(*args, **kwargs):
+                new_seconds = kwargs.pop('timeout', seconds)
+                if new_seconds:
+                    old = signal.signal(signal.SIGALRM, handler)
+                    signal.setitimer(signal.ITIMER_REAL, new_seconds)
+
+                if not seconds:
+                    return function(*args, **kwargs)
+
+                try:
+                    return function(*args, **kwargs)
+                finally:
+                    if new_seconds:
+                        signal.setitimer(signal.ITIMER_REAL, 0)
+                        signal.signal(signal.SIGALRM, old)
+            return new_function
+        else:
+            @wraps(function)
+            def new_function(*args, **kwargs):
+                timeout_wrapper = _Timeout(function, timeout_exception, exception_message, seconds)
+                return timeout_wrapper(*args, **kwargs)
+            return new_function
+
+    return decorate
+
+
+def _target(queue, function, *args, **kwargs):
+    """Run a function with arguments and return output via a queue.
+
+    This is a helper function for the Process created in _Timeout. It runs
+    the function with positional arguments and keyword arguments and then
+    returns the function's output by way of a queue. If an exception gets
+    raised, it is returned to _Timeout to be raised by the value property.
+    """
+    try:
+        queue.put((True, function(*args, **kwargs)))
+    except:
+        queue.put((False, sys.exc_info()[1]))
+
+
+class _Timeout(object):
+
+    """Wrap a function and add a timeout (limit) attribute to it.
+
+    Instances of this class are automatically generated by the add_timeout
+    function defined above. Wrapping a function allows asynchronous calls
+    to be made and termination of execution after a timeout has passed.
+    """
+
+    def __init__(self, function, timeout_exception, exception_message, limit):
+        """Initialize instance in preparation for being called."""
+        self.__limit = limit
+        self.__function = function
+        self.__timeout_exception = timeout_exception
+        self.__exception_message = exception_message
+        self.__name__ = function.__name__
+        self.__doc__ = function.__doc__
+        self.__timeout = time.time()
+        self.__process = multiprocessing.Process()
+        self.__queue = multiprocessing.Queue()
+
+    def __call__(self, *args, **kwargs):
+        """Execute the embedded function object asynchronously.
+
+        The function given to the constructor is transparently called and
+        requires that "ready" be intermittently polled. If and when it is
+        True, the "value" property may then be checked for returned data.
+        """
+        self.__limit = kwargs.pop('timeout', self.__limit)
+        self.__queue = multiprocessing.Queue(1)
+        args = (self.__queue, self.__function) + args
+        self.__process = multiprocessing.Process(target=_target,
+                                                 args=args,
+                                                 kwargs=kwargs)
+        self.__process.daemon = True
+        self.__process.start()
+        if self.__limit is not None:
+            self.__timeout = self.__limit + time.time()
+        while not self.ready:
+            time.sleep(0.01)
+        return self.value
+
+    def cancel(self):
+        """Terminate any possible execution of the embedded function."""
+        if self.__process.is_alive():
+            self.__process.terminate()
+
+        _raise_exception(self.__timeout_exception, self.__exception_message)
+
+    @property
+    def ready(self):
+        """Read-only property indicating status of "value" property."""
+        if self.__limit and self.__timeout < time.time():
+            self.cancel()
+        return self.__queue.full() and not self.__queue.empty()
+
+    @property
+    def value(self):
+        """Read-only property containing data returned from function."""
+        if self.ready is True:
+            flag, load = self.__queue.get()
+            if flag:
+                return load
+            raise load
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..232b10b
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,17 @@
+[tox]
+distshare={homedir}/.tox/distshare
+envlist=py{27,36,37,38}
+skip_missing_interpreters=true
+indexserver=
+    pypi = https://pypi.python.org/simple
+
+[testenv]
+commands=
+    py.test timeout_decorator tests
+deps =
+    pytest
+    pytest-pep8
+
+[pytest]
+addopts = -vvl
+pep8maxlinelength=120