diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..3866aa0 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,7 @@ +[run] +branch = True +source = positional +omit = positional/tests/* + +[report] +ignore_errors = True diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6e1e71 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +.coverage +.testrepository +subunit.log +.venv +*,cover +cover +*.pyc +.idea +*.sw? +*.egg +*~ +.tox +AUTHORS +ChangeLog +build +dist +python_positional.egg-info +positional.egg-info +positional/versioninfo +doc/source/api +# Development environment files +.project +.pydevproject +# Temporary files created during test, but not removed +examples/pki/certs/tmp* +# Files created by releasenotes build +releasenotes/build diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 0000000..f89a3bf --- /dev/null +++ b/.testr.conf @@ -0,0 +1,4 @@ +[DEFAULT] +test_command=${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./positional/tests} $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9f2ef38 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: python +install: + - pip install tox + - pip install -r requirements.txt -r test-requirements.txt +script: + - tox +env: + - TOXENV=pep8 + - TOXENV=py27 + - TOXENV=py34 \ No newline at end of file diff --git a/LICENSE b/LICENSE index 32b6611..6b34f86 100644 --- a/LICENSE +++ b/LICENSE @@ -177,33 +177,4 @@ All rights reserved. of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - ---- License for python-keystoneclient versions prior to 2.1 --- - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of this project nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/positional/__init__.py b/positional/__init__.py index 4962111..8abc358 100644 --- a/positional/__init__.py +++ b/positional/__init__.py @@ -170,4 +170,4 @@ class positional(object): return func(*args, **kwargs) - return inner \ No newline at end of file + return inner diff --git a/positional/tests/__init__.py b/positional/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/positional/tests/test_positional.py b/positional/tests/test_positional.py new file mode 100644 index 0000000..783f4ab --- /dev/null +++ b/positional/tests/test_positional.py @@ -0,0 +1,90 @@ +# 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. + +import logging + +import six +import testtools + +import positional + + +class TestPositional(testtools.TestCase): + + @positional.positional(1) + def no_vars(self): + # positional doesn't enforce anything here + return True + + @positional.positional(3, positional.positional.EXCEPT) + def mixed_except(self, arg, kwarg1=None, kwarg2=None): + # self, arg, and kwarg1 may be passed positionally + return (arg, kwarg1, kwarg2) + + @positional.positional(3, positional.positional.WARN) + def mixed_warn(self, arg, kwarg1=None, kwarg2=None): + # self, arg, and kwarg1 may be passed positionally, only a warning + # is emitted + return (arg, kwarg1, kwarg2) + + def test_nothing(self): + self.assertTrue(self.no_vars()) + + def test_mixed_except(self): + self.assertEqual((1, 2, 3), self.mixed_except(1, 2, kwarg2=3)) + self.assertEqual((1, 2, 3), self.mixed_except(1, kwarg1=2, kwarg2=3)) + self.assertEqual((1, None, None), self.mixed_except(1)) + self.assertRaises(TypeError, self.mixed_except, 1, 2, 3) + + def test_mixed_warn(self): + logger_message = six.moves.cStringIO() + handler = logging.StreamHandler(logger_message) + handler.setLevel(logging.DEBUG) + + logger = logging.getLogger(positional.__name__) + level = logger.getEffectiveLevel() + logger.setLevel(logging.DEBUG) + logger.addHandler(handler) + + self.addCleanup(logger.removeHandler, handler) + self.addCleanup(logger.setLevel, level) + + self.mixed_warn(1, 2, 3) + + self.assertIn('takes at most 3 positional', logger_message.getvalue()) + + @positional.positional(enforcement=positional.positional.EXCEPT) + def inspect_func(self, arg, kwarg=None): + return (arg, kwarg) + + def test_inspect_positions(self): + self.assertEqual((1, None), self.inspect_func(1)) + self.assertEqual((1, 2), self.inspect_func(1, kwarg=2)) + self.assertRaises(TypeError, self.inspect_func) + self.assertRaises(TypeError, self.inspect_func, 1, 2) + + @positional.positional.classmethod(1) + def class_method(cls, a, b): + return (cls, a, b) + + @positional.positional.method(1) + def normal_method(self, a, b): + self.assertIsInstance(self, TestPositional) + return (self, a, b) + + def test_class_method(self): + self.assertEqual((TestPositional, 1, 2), self.class_method(1, b=2)) + self.assertRaises(TypeError, self.class_method, 1, 2) + + def test_normal_method(self): + self.assertEqual((self, 1, 2), self.normal_method(1, b=2)) + self.assertRaises(TypeError, self.normal_method, 1, 2) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py index 782bb21..ddd1771 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# # 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 diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..1a7b27d --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,14 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. + +hacking<0.11,>=0.10.0 +flake8-docstrings==0.2.1.post1 + +coverage>=3.6 + +six + +testrepository>=0.0.18 +testresources>=0.2.4 +testtools>=1.4.0 \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..2520aae --- /dev/null +++ b/tox.ini @@ -0,0 +1,49 @@ +[tox] +minversion = 1.6 +skipsdist = True +envlist = py34,py27,pep8,releasenotes + +[testenv] +usedevelop = True +install_command = pip install -U {opts} {packages} +setenv = VIRTUAL_ENV={envdir} + OS_STDOUT_NOCAPTURE=False + OS_STDERR_NOCAPTURE=False + +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = find . -type f -name "*.pyc" -delete + python setup.py testr --slowest --testr-args='{posargs}' +whitelist_externals = find + +[testenv:pep8] +commands = + flake8 + +[testenv:venv] +commands = {posargs} + +[testenv:cover] +commands = python setup.py testr --coverage --testr-args='{posargs}' + +[flake8] +# D100: Missing docstring in public module +# D101: Missing docstring in public class +# D102: Missing docstring in public method +# D103: Missing docstring in public function +# D104: Missing docstring in public package +# D105: Missing docstring in magic method +# D200: One-line docstring should fit on one line with quotes +# D202: No blank lines allowed after function docstring +# D203: 1 blank required before class docstring. +# D204: 1 blank required after class docstring +# D205: Blank line required between one-line summary and description. +# D207: Docstring is under-indented +# D208: Docstring is over-indented +# D211: No blank lines allowed before class docstring +# D301: Use r”“” if any backslashes in a docstring +# D400: First line should end with a period. +# D401: First line should be in imperative mood. +ignore = D100,D101,D102,D103,D104 +show-source = True +exclude = .venv,.tox,dist,doc,*egg,build