├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CHANGES.rst ├── CONTRIBUTING.rst ├── LICENSE.rst ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── api.rst ├── authors.rst ├── changelog.rst ├── conf.py ├── contributing.rst ├── index.rst ├── install.rst ├── license.rst └── versioning.rst ├── makefile ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── fixtures.py └── test_verify.py ├── tox.ini └── verify ├── __init__.py ├── __meta__.py ├── base.py ├── containers.py ├── equality.py ├── logic.py ├── numbers.py ├── runners.py └── types.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | .idea 38 | 39 | # virtualenv 40 | /env* 41 | 42 | # NodeJS 43 | node_modules 44 | 45 | # Docs 46 | /docs/_build 47 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - "2.7" 5 | - "3.3" 6 | - "3.4" 7 | install: 8 | - make travisci-install 9 | script: 10 | - make travisci-test 11 | after_success: 12 | - pip install python-coveralls --use-mirrors 13 | - coveralls 14 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | 5 | Lead 6 | ---- 7 | 8 | - Derrick Gilland, dgilland@gmail.com, `dgilland@github `_ 9 | 10 | 11 | Contributors 12 | ------------ 13 | 14 | - Szczepan Cieślik, szczepan.cieslik@gmail.com, `beregond@github `_ 15 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | .. _changelog: 2 | 3 | Changelog 4 | ========= 5 | 6 | 7 | v1.1.1 (2017-05-09) 8 | ------------------- 9 | 10 | - Fix compatibility with pydash v4. 11 | 12 | 13 | v1.1.0 (2015-07-23) 14 | ------------------- 15 | 16 | - Add ``ensure`` as alias of ``expect``. 17 | - Add ``to_be_*`` and ``is_*`` aliases for all assertions. 18 | 19 | 20 | v1.0.0 (2015-05-15) 21 | ------------------- 22 | 23 | - Add ``NotLength``. 24 | - Make assertions accept an optional argument, ``msg``, that overrides the default assert message on a per call basis. 25 | - Make ``Between`` and ``Length`` only accept keyword arguments ``min`` and ``max``. (**breaking change**) 26 | 27 | 28 | v0.6.0 (2015-05-14) 29 | ------------------- 30 | 31 | - Make ``expect`` into a class and support method chaining of assertions. Original usage is still supported. 32 | - Make ``expect`` wrap external predicate functions with ``Predicate`` for evaluation. (**breaking change**) 33 | - Make ``Predicate`` catch ``AssertionError`` thrown by `comparable` and return ``False``. (**breaking change**) 34 | - Make ``Predicate`` treat a `comparable` that returns ``None`` as passing. (**breaking change**) 35 | - Rename ``InstanceOf`` and ``NotInstanceOf`` to ``Type`` and ``NotType``. (**breaking change**) 36 | 37 | 38 | v0.5.0 (2015-05-12) 39 | ------------------- 40 | 41 | - Add ``NotEqual``. 42 | - Add ``NotMatch``. 43 | - Add ``NotBetween``. 44 | - Add ``IsNot``. 45 | - Add ``IsNotTrue``. 46 | - Add ``IsNotFalse``. 47 | - Add ``IsNotNone``. 48 | - Add ``NotAll``. 49 | - Add ``NotAny``. 50 | - Add ``NotIn``. 51 | - Add ``NotContains``. 52 | - Add ``NotContainsOnly``. 53 | - Add ``NotSubset``. 54 | - Add ``NotSuperset``. 55 | - Add ``NotUnique``. 56 | - Add ``NotInstanceOf``. 57 | - Add ``NotBoolean``. 58 | - Add ``NotString``. 59 | - Add ``NotDict``. 60 | - Add ``NotList``. 61 | - Add ``NotTuple``. 62 | - Add ``NotDate``. 63 | - Add ``NotDateString``. 64 | - Add ``NotInt``. 65 | - Add ``NotFloat``. 66 | - Rename ``NaN`` to ``NotNumber``. (**breaking change**) 67 | 68 | 69 | v0.4.0 (2015-05-12) 70 | ------------------- 71 | 72 | - Make ``Between`` accept keyword arguments for ``min`` and ``max``. 73 | - Make ``Length`` function like ``Between`` and allow comparison over range of lengths. If a single comparable value is passed in, then comparison uses the value as a max length. Previously, a single comparable value performed an equality check for length. (**breaking change**) 74 | - Make ``Match`` accept keyword argument ``flags`` for use with string based regular expression. 75 | 76 | 77 | v0.3.0 (2015-05-11) 78 | ------------------- 79 | 80 | - Add ``Match``. 81 | - Add ``Subset``. 82 | - Add ``Superset``. 83 | - Add ``Unique``. 84 | - Add ``Date``. 85 | - Add ``DateString``. 86 | - Add ``Positive``. 87 | - Add ``Negative``. 88 | - Add ``Even``. 89 | - Add ``Odd``. 90 | - Add ``Monotone``. 91 | - Add ``Increasing``. 92 | - Add ``StrictlyIncreasing``. 93 | - Add ``Decreasing``. 94 | - Add ``StrictlyDecreasing``. 95 | 96 | 97 | v0.2.0 (2015-05-11) 98 | ------------------- 99 | 100 | - Add ``All``. 101 | - Add ``Any``. 102 | - Add ``Between``. 103 | - Add ``Contains``. 104 | - Add ``ContainsOnly``. 105 | - Add ``Length``. 106 | - Make ``Not`` compatible with bare predicate functions by return the evaluation of the `comparable`. 107 | 108 | 109 | v0.1.1 (2015-05-08) 110 | ------------------- 111 | 112 | - Make ``expect`` include an assertion message on failure. Without it, a cryptic ``NameError`` is thrown when a plain predicate function fails due to a generator being used in the ``all()`` call. 113 | 114 | 115 | v0.1.0 (2015-05-08) 116 | ------------------- 117 | 118 | - Add ``Boolean``. 119 | - Add ``Dict``. 120 | - Add ``Float``. 121 | - Add ``Int``. 122 | - Add ``IsTrue``. 123 | - Add ``IsFalse``. 124 | - Add ``List``. 125 | - Add ``NaN``. 126 | - Add ``Number``. 127 | - Add ``Predicate``. 128 | - Add ``String``. 129 | - Add ``Tuple``. 130 | - Rename ``Except`` to ``except``. (**breaking change**) 131 | - Make ``except`` **not** call `value` if it's callable. (**breaking change**) 132 | - Make ``except`` return ``True`` if all assertions pass. 133 | 134 | 135 | v0.0.1 (2015-05-07) 136 | ------------------- 137 | 138 | - First release. 139 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | How to Contribute 2 | ================= 3 | 4 | - Overview_ 5 | - Guidelines_ 6 | - Branching_ 7 | - `Continuous Integration`_ 8 | - `Project CLI`_ 9 | 10 | 11 | Overview 12 | -------- 13 | 14 | 1. Fork the repo. 15 | 2. Build development environment run tests to ensure a clean, working slate. 16 | 3. Improve/fix the code. 17 | 4. Add test cases if new functionality introduced or bug fixed (100% test coverage). 18 | 5. Ensure tests pass. 19 | 6. Add yourself to ``AUTHORS.rst``. 20 | 7. Push to your fork and submit a pull request to the ``develop`` branch. 21 | 22 | 23 | Guidelines 24 | ---------- 25 | 26 | Some simple guidelines to follow when contributing code: 27 | 28 | - Adhere to `PEP8`_. 29 | - Clean, well documented code. 30 | - All tests must pass. 31 | - 100% test coverage. 32 | 33 | 34 | Branching 35 | --------- 36 | 37 | There are two main development branches: ``master`` and ``develop``. ``master`` represents the currently released version while ``develop`` is the latest development work. When submitting a pull request, be sure to submit to ``develop``. The originating branch you submit from can be any name though. 38 | 39 | 40 | Continuous Integration 41 | ---------------------- 42 | 43 | Integration testing is provided by `Travis-CI`_ at https://travis-ci.org/dgilland/verify. 44 | 45 | Test coverage reporting is provided by `Coveralls`_ at https://coveralls.io/r/dgilland/verify. 46 | 47 | 48 | Project CLI 49 | ----------- 50 | 51 | Some useful CLI commands when working on the project are below. **NOTE:** All commands are run from the root of the project and require ``make``. 52 | 53 | make build 54 | ++++++++++ 55 | 56 | Run the ``clean`` and ``install`` commands. 57 | 58 | :: 59 | 60 | make build 61 | 62 | 63 | make install 64 | ++++++++++++ 65 | 66 | Install Python dependencies into virtualenv located at ``env/``. 67 | 68 | :: 69 | 70 | make install 71 | 72 | 73 | make clean 74 | ++++++++++ 75 | 76 | Remove build/test related temporary files like ``env/``, ``.tox``, ``.coverage``, and ``__pycache__``. 77 | 78 | :: 79 | 80 | make clean 81 | 82 | 83 | make test 84 | +++++++++ 85 | 86 | Run unittests under the virtualenv's default Python version. Does not test all support Python versions. To test all supported versions, see `make test-full`_. 87 | 88 | :: 89 | 90 | make test 91 | 92 | 93 | make test-full 94 | ++++++++++++++ 95 | 96 | Run unittest and linting for all supported Python versions. **NOTE:** This will fail if you do not have all Python versions installed on your system. If you are on an Ubuntu based system, the `Dead Snakes PPA`_ is a good resource for easily installing multiple Python versions. If for whatever reason you're unable to have all Python versions on your development machine, note that Travis-CI will run full integration tests on all pull requests. 97 | 98 | :: 99 | 100 | make test-full 101 | 102 | 103 | make lint 104 | +++++++++ 105 | 106 | Run ``make pylint`` and ``make pep8`` commands. 107 | 108 | :: 109 | 110 | make lint 111 | 112 | 113 | make pylint 114 | +++++++++++ 115 | 116 | Run ``pylint`` compliance check on code base. 117 | 118 | :: 119 | 120 | make pylint 121 | 122 | 123 | make pep8 124 | +++++++++ 125 | 126 | Run `PEP8`_ compliance check on code base. 127 | 128 | :: 129 | 130 | make pep8 131 | 132 | 133 | make docs 134 | +++++++++ 135 | 136 | Build documentation to ``docs/_build/``. 137 | 138 | :: 139 | 140 | make docs 141 | 142 | 143 | .. _Travis-CI: https://travis-ci.org/ 144 | .. _Coveralls: https://coveralls.io/ 145 | .. _Dead Snakes PPA: https://launchpad.net/~fkrull/+archive/deadsnakes 146 | .. _PEP8: http://legacy.python.org/dev/peps/pep-0008/ 147 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2015 Derrick Gilland 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.rst README.rst CHANGES.rst -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ****** 2 | verify 3 | ****** 4 | 5 | |version| |travis| |coveralls| |license| 6 | 7 | Verify is a painless assertion library for Python. 8 | 9 | 10 | Links 11 | ===== 12 | 13 | - Project: https://github.com/dgilland/verify 14 | - Documentation: http://verify.readthedocs.org 15 | - PyPI: https://pypi.python.org/pypi/verify/ 16 | - TravisCI: https://travis-ci.org/dgilland/verify 17 | 18 | 19 | Quickstart 20 | ========== 21 | 22 | Install using pip: 23 | 24 | 25 | :: 26 | 27 | pip install verify 28 | 29 | 30 | Verify some value using multiple assertions: 31 | 32 | 33 | .. code-block:: python 34 | 35 | from verify import expect, Not, Truthy, Falsy, Less, Greater 36 | 37 | expect(5 * 5, 38 | Truthy(), 39 | Not(Falsy), 40 | Greater(15), 41 | Less(30)) 42 | 43 | 44 | Verify using your own assert functions: 45 | 46 | 47 | .. code-block:: python 48 | 49 | def is_just_right(value): 50 | assert value == 'just right', 'Not just right!' 51 | 52 | # Passes 53 | expect('just right', is_just_right) 54 | 55 | # Fails 56 | try: 57 | expect('too cold', is_just_right) 58 | except AssertionError: 59 | raise 60 | 61 | **NOTE:** The assert function should return a truthy value, otherwise, ``expect`` will treat the falsy return from the function as an indication that it failed and subsequently raise it's own ``AssertionError``. 62 | 63 | 64 | Verify using your own predicate functions: 65 | 66 | 67 | .. code-block:: python 68 | 69 | def is_awesome(value): 70 | return 'awesome' in value 71 | 72 | def is_more_awesome(value): 73 | return value > 'awesome' 74 | 75 | expect('so awesome', is_awesome, is_more_awesome) 76 | 77 | 78 | Verify using chaining syntax: 79 | 80 | 81 | .. code-block:: python 82 | 83 | expect(1).Truthy().Number().NotBoolean().Not(is_awesome) 84 | 85 | 86 | Verify without ``expect`` since the ``verify`` assertions can be used on their own: 87 | 88 | 89 | .. code-block:: python 90 | 91 | import verify 92 | 93 | # These would pass. 94 | verify.Truthy(1) 95 | verify.Equal(2, 2) 96 | verify.Greater(3, 2) 97 | 98 | # These would fail with an AssertionError 99 | verify.Truthy(0) 100 | verify.Equal(2, 3) 101 | verify.Greater(2, 3) 102 | 103 | 104 | If you'd prefer to see ``assert`` being used, all ``verify`` assertions will return ``True`` if no ``AssertionError`` is raised: 105 | 106 | 107 | .. code-block:: python 108 | 109 | assert Truthy(1) 110 | assert expect(1, Truthy(), Number()) 111 | 112 | 113 | Multiple Syntax Styles 114 | ====================== 115 | 116 | There are several syntax styles available to help construct more natural sounding assertion chains. 117 | 118 | 119 | Expect...To Be 120 | -------------- 121 | 122 | Use ``expect`` with the ``to_be`` aliases. All Pascal case assertions have ``to_be_*`` and ``to_not_be_*`` prefixes (with a few expections). 123 | 124 | .. code-block:: python 125 | 126 | expect(something).to_be_int().to_be_less_or_equal(5).to_be_greater_or_equal(1) 127 | expect(something_else).to_not_be_float().to_be_number() 128 | 129 | 130 | Ensure...Is 131 | ----------- 132 | 133 | Use ``ensure`` with ``is`` aliases. All Pascal case assertions have ``is_*`` and ``is_not_*`` prefixes (with a few expections). 134 | 135 | 136 | .. code-block:: python 137 | 138 | ensure(something).is_int().is_less_or_equal(5).is_greater_or_equal(1) 139 | ensure(something_else).is_not_float().is_number() 140 | 141 | 142 | Classical 143 | --------- 144 | 145 | Use ``expect`` or ``ensure`` with the Pascal case assertions. 146 | 147 | .. code-block:: python 148 | 149 | ensure(something).Int().LessOrEqual(5).GreaterOrEqual(1) 150 | expect(something_else).Float().Number() 151 | 152 | 153 | **NOTE:** While it's suggested to not mix styles, each of the assertion syntaxes are available with both ``expect`` and ``ensure``. So you can call ``expect(..).is_int()`` as well as ``ensure(..).to_be_int()``. 154 | 155 | 156 | Naming Convention Exceptions 157 | ---------------------------- 158 | 159 | As mentioned above, there are some assertions that have nonstandard aliases: 160 | 161 | - ``Not``: ``not_``, ``does_not``, ``to_fail``, and ``fails`` 162 | - ``Predicate``: ``does``, ``to_pass``, and ``passes`` 163 | - ``All``: ``all_``, ``does_all``, and ``passes_all`` 164 | - ``NotAll``: ``not_all``, ``does_not_all``, and ``fails_all`` 165 | - ``Any``: ``any_``, ``does_any``, and ``passes_any`` 166 | - ``NotAny``: ``not_any``, ``does_not_any``, and ``fails_any`` 167 | - ``Match``: ``to_match``, ``is_match`` and ``matches`` 168 | - ``NotMatch``: ``to_not_match``, ``is_not_match`` and ``does_not_match`` 169 | - ``Is``: ``to_be`` and ``is_`` 170 | - ``Contains``: ``to_contain`` and ``contains`` 171 | - ``NotContains``: ``to_not_contain`` and ``does_not_contain`` 172 | - ``ContainsOnly``: ``to_contain_only`` and ``contains_only`` 173 | - ``NotContainsOnly``: ``to_not_contain_only`` and ``does_not_contain_only`` 174 | - ``Length``: ``to_have_length`` and ``has_length`` 175 | - ``NotLength``: ``to_not_have_length`` and ``does_not_have_length`` 176 | 177 | 178 | Validators 179 | ========== 180 | 181 | All of the validators in ``verify`` are callables that can be used in two contexts: 182 | 183 | 1. By themselves as in ``Equal(a, b)`` which will raise an ``AssertionError`` if false. 184 | 2. In combination with ``expect`` as in ``expect(a, Equal(b))`` which could also raise an ``AssertionError``. 185 | 186 | The available validators are: 187 | 188 | =================================== =========== 189 | Validator Description 190 | =================================== =========== 191 | ``Truthy`` Assert that ``bool(a)``. 192 | ``Falsy`` Assert that ``not bool(a)``. 193 | ``Not`` Assert that a callable doesn't raise an ``AssertionError``. 194 | ``Predicate`` Assert that ``predicate(a)``. 195 | ``All`` Assert that all of the list of predicates evaluate ``a`` as truthy. 196 | ``NotAll`` Assert ``not All``. 197 | ``Any`` Assert that any of the list of predicates evaluate ``a`` as truthy. 198 | ``NotAny`` Assert ``not Any``. 199 | ``Equal`` Assert that ``a == b``. 200 | ``NotEqual`` Assert ``not Equal``. 201 | ``Match`` Assert that ``a`` matches regular expression ``b``. 202 | ``NotMatch`` Assert ``not Match``. 203 | ``Is`` Assert that ``a is b``. 204 | ``IsNot`` Assert ``not Is``. 205 | ``IsTrue`` Assert that ``a is True``. 206 | ``IsNotTrue`` Assert ``not IsTrue``. 207 | ``IsFalse`` Assert that ``a is False``. 208 | ``IsNotFalse`` Assert ``not IsFalse``. 209 | ``IsNone`` Assert that ``a is None``. 210 | ``IsNotNone`` Assert ``not IsNone``. 211 | ``Type`` Assert that ``isinstance(a, b)``. 212 | ``NotType`` Assert ``not Type``. 213 | ``Boolean`` Assert that ``isinstance(a, bool)``. 214 | ``NotBoolean`` Assert ``not Boolean``. 215 | ``String`` Assert that ``isinstance(a, (str, unicode))``. 216 | ``NotString`` Assert ``not String``. 217 | ``Dict`` Assert that ``isinstance(a, dict)``. 218 | ``NotDict`` Assert ``not Dict``. 219 | ``List`` Assert that ``isinstance(a, list)``. 220 | ``NotList`` Assert ``not List``. 221 | ``Tuple`` Assert that ``isinstance(a, tuple)``. 222 | ``NotTuple`` Assert ``not Tuple``. 223 | ``Date`` Assert that ``isinstance(a, datetime.date)``. 224 | ``NotDate`` Assert ``not Date``. 225 | ``DateString`` Assert that ``a`` matches the datetime format string ``b``. 226 | ``NotDateString`` Assert ``not DateString``. 227 | ``Int`` Assert that ``isinstance(a, int)``. 228 | ``NotInt`` Assert ``not Int``. 229 | ``Float`` Assert that ``isinstance(a, float)``. 230 | ``NotFloat`` Assert ``not Float``. 231 | ``Number`` Assert that ``isinstance(a, (int, float, Decimal, long))``. 232 | ``NotNumber`` Assert ``not Number``. 233 | ``In`` Assert that ``a in b``. 234 | ``NotIn`` Assert ``not In``. 235 | ``Contains`` Assert that ``b in a``. 236 | ``NotContains`` Assert ``not Contains``. 237 | ``ContainsOnly`` Assert that values from ``b`` are the only ones contained in ``a``. 238 | ``NotContainsOnly`` Assert ``not ContainsOnly``. 239 | ``Subset`` Assert that ``a`` is a subset of ``b``. 240 | ``NotSubset`` Assert ``not Subset``. 241 | ``Superset`` Assert that ``a`` is a superset of ``b``. 242 | ``NotSuperset`` Assert ``not Superset``. 243 | ``Unique`` Assert that ``a`` contains unique items. 244 | ``NotUnique`` Assert ``not Unique``. 245 | ``Length`` Assert that ``b <= len(a) <= c``. 246 | ``NotLength`` Assert that ``not Length``. 247 | ``Greater``/``GreaterThan`` Assert that ``a > b``. 248 | ``GreaterEqual``/``GreaterOrEqual`` Assert that ``a >= b``. 249 | ``Less``/``LessThan`` Assert that ``a < b``. 250 | ``LessEqual``/``LessOrEqual`` Assert that ``a <= b``. 251 | ``Between`` Assert that ``b <= a <= c``. 252 | ``NotBetween`` Assert ``not Between``. 253 | ``Positive`` Assert that ``a > 0``. 254 | ``Negative`` Assert that ``a < 0``. 255 | ``Even`` Assert that ``a % 2 == 0``. 256 | ``Odd`` Assert that ``a % 2 != 1``. 257 | ``Monotone`` Assert that ``a`` is monotonic with respect to ``b()``. 258 | ``Increasing`` Assert that ``a`` is monotonically increasing. 259 | ``StrictlyIncreasing`` Assert that ``a`` is strictly increasing. 260 | ``Decreasing`` Assert that ``a`` is monotonically decreasing. 261 | ``StrictlyDecreasing`` Assert that ``a`` is strictly decreasing. 262 | =================================== =========== 263 | 264 | 265 | For more details, please see the full documentation at http://verify.readthedocs.org. 266 | 267 | 268 | .. |version| image:: https://img.shields.io/pypi/v/verify.svg?style=flat-square 269 | :target: https://pypi.python.org/pypi/verify/ 270 | 271 | .. |travis| image:: https://img.shields.io/travis/dgilland/verify/master.svg?style=flat-square 272 | :target: https://travis-ci.org/dgilland/verify 273 | 274 | .. |coveralls| image:: https://img.shields.io/coveralls/dgilland/verify/master.svg?style=flat-square 275 | :target: https://coveralls.io/r/dgilland/verify 276 | 277 | .. |license| image:: https://img.shields.io/pypi/l/verify.svg?style=flat-square 278 | :target: https://pypi.python.org/pypi/verify/ 279 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pushjack.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pushjack.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pushjack" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pushjack" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | API Reference 4 | ============= 5 | 6 | .. testsetup:: 7 | 8 | from verify import * 9 | 10 | 11 | .. automodule:: verify 12 | 13 | 14 | Assertion Runner 15 | ---------------- 16 | 17 | The :class:`.expect` class is basically an assertion runner that takes an input `value` and passes it through any number of assertions or predicate functions. If all assertions pass **and** return truthy, then all is well and ``True`` is returned. Otherwise, either one of the assertion functions will raise an ``AssertionError`` or no exceptiosn were raised but at least one of the functions returned a non-truthy value which means that :func:`.expect` will return ``False``. 18 | 19 | The :class:`.expect` has alias in the same module under name of ``ensure``, so 20 | you can use both of these names according to your needs. 21 | 22 | .. autoclass:: verify.runners.expect 23 | :members: 24 | 25 | 26 | Assertions 27 | ---------- 28 | 29 | For all assertion classes, the `value` argument is optional, but when provided the assertion will be evaluated immediately. When passing both the `value` and `comparable` arguments, be sure that `value` comes first even though `comparable` is listed as the first argument. Internally, when both variables are passed in, `value` and `comparable` are swapped in order to support late evaulation, i.e., all of the following are equivalent ways to assert validity: 30 | 31 | 32 | .. doctest:: 33 | 34 | >>> Less(5, 10) 35 | 36 | >>> Less(10)(5) 37 | True 38 | >>> expect(5, Less(10)) 39 | 40 | >>> Truthy(5) 41 | 42 | >>> Truthy()(5) 43 | True 44 | >>> expect(5, Truthy()) 45 | 46 | 47 | Below are the various assertion classes that can be used for validation. 48 | 49 | 50 | Base Classes 51 | ++++++++++++ 52 | 53 | .. automodule:: verify.base 54 | :members: 55 | 56 | 57 | Logic 58 | +++++ 59 | 60 | .. automodule:: verify.logic 61 | :members: 62 | :exclude-members: op 63 | 64 | 65 | Equality 66 | ++++++++ 67 | 68 | .. automodule:: verify.equality 69 | :members: 70 | :exclude-members: op 71 | 72 | 73 | Types 74 | +++++ 75 | 76 | .. automodule:: verify.types 77 | :members: 78 | :exclude-members: op 79 | 80 | 81 | Containers 82 | ++++++++++ 83 | 84 | .. automodule:: verify.containers 85 | :members: 86 | :exclude-members: op 87 | 88 | Numbers 89 | +++++++ 90 | 91 | .. automodule:: verify.numbers 92 | :members: 93 | :exclude-members: op 94 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CHANGES.rst 2 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # verify documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Mar 25 13:45:41 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | 19 | # On_rtd is whether we are on readthedocs.org, this line of code grabbed from 20 | # docs.readthedocs.org. 21 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 22 | 23 | 24 | # If extensions (or modules to document with autodoc) are in another directory, 25 | # add these directories to sys.path here. If the directory is relative to the 26 | # documentation root, use os.path.abspath to make it absolute, like shown here. 27 | sys.path.insert(0, os.path.abspath('..')) 28 | import verify 29 | 30 | # -- General configuration ------------------------------------------------ 31 | 32 | # If your documentation needs a minimal Sphinx version, state it here. 33 | #needs_sphinx = '1.0' 34 | 35 | # Add any Sphinx extension module names here, as strings. They can be 36 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 37 | # ones. 38 | extensions = [ 39 | 'sphinx.ext.autodoc', 40 | 'sphinx.ext.doctest', 41 | 'sphinx.ext.coverage', 42 | 'sphinx.ext.viewcode', 43 | 'sphinx.ext.napoleon' 44 | ] 45 | 46 | # Add any paths that contain templates here, relative to this directory. 47 | templates_path = ['_templates'] 48 | 49 | # The suffix of source filenames. 50 | source_suffix = '.rst' 51 | 52 | # The encoding of source files. 53 | #source_encoding = 'utf-8-sig' 54 | 55 | # The master toctree document. 56 | master_doc = 'index' 57 | 58 | # General information about the project. 59 | project = u'verify' 60 | copyright = u'2015, Derrick Gilland' 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = release = verify.__version__ 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | #language = None 72 | 73 | # There are two options for replacing |today|: either, you set today to some 74 | # non-false value, then it is used: 75 | #today = '' 76 | # Else, today_fmt is used as the format for a strftime call. 77 | #today_fmt = '%B %d, %Y' 78 | 79 | # List of patterns, relative to source directory, that match files and 80 | # directories to ignore when looking for source files. 81 | exclude_patterns = ['_build'] 82 | 83 | # The reST default role (used for this markup: `text`) to use for all 84 | # documents. 85 | #default_role = None 86 | 87 | # If true, '()' will be appended to :func: etc. cross-reference text. 88 | #add_function_parentheses = True 89 | 90 | # If true, the current module name will be prepended to all description 91 | # unit titles (such as .. function::). 92 | #add_module_names = True 93 | 94 | # If true, sectionauthor and moduleauthor directives will be shown in the 95 | # output. They are ignored by default. 96 | #show_authors = False 97 | 98 | # The name of the Pygments (syntax highlighting) style to use. 99 | pygments_style = 'sphinx' 100 | 101 | # A list of ignored prefixes for module index sorting. 102 | #modindex_common_prefix = [] 103 | 104 | # If true, keep warnings as "system message" paragraphs in the built documents. 105 | #keep_warnings = False 106 | 107 | 108 | # -- Options for HTML output ---------------------------------------------- 109 | 110 | # The theme to use for HTML and HTML Help pages. See the documentation for 111 | # a list of builtin themes. 112 | if on_rtd: 113 | html_theme = 'default' 114 | else: 115 | import sphinx_rtd_theme 116 | html_theme = 'sphinx_rtd_theme' 117 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 118 | 119 | 120 | # Theme options are theme-specific and customize the look and feel of a theme 121 | # further. For a list of options available for each theme, see the 122 | # documentation. 123 | #html_theme_options = {} 124 | 125 | # Add any paths that contain custom themes here, relative to this directory. 126 | #html_theme_path = [] 127 | 128 | # The name for this set of Sphinx documents. If None, it defaults to 129 | # " v documentation". 130 | #html_title = None 131 | 132 | # A shorter title for the navigation bar. Default is the same as html_title. 133 | #html_short_title = None 134 | 135 | # The name of an image file (relative to this directory) to place at the top 136 | # of the sidebar. 137 | #html_logo = None 138 | 139 | # The name of an image file (within the static path) to use as favicon of the 140 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 141 | # pixels large. 142 | #html_favicon = None 143 | 144 | # Add any paths that contain custom static files (such as style sheets) here, 145 | # relative to this directory. They are copied after the builtin static files, 146 | # so a file named "default.css" will overwrite the builtin "default.css". 147 | html_static_path = ['_static'] 148 | 149 | # Add any extra paths that contain custom files (such as robots.txt or 150 | # .htaccess) here, relative to this directory. These files are copied 151 | # directly to the root of the documentation. 152 | #html_extra_path = [] 153 | 154 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 155 | # using the given strftime format. 156 | #html_last_updated_fmt = '%b %d, %Y' 157 | 158 | # If true, SmartyPants will be used to convert quotes and dashes to 159 | # typographically correct entities. 160 | #html_use_smartypants = True 161 | 162 | # Custom sidebar templates, maps document names to template names. 163 | #html_sidebars = {} 164 | 165 | # Additional templates that should be rendered to pages, maps page names to 166 | # template names. 167 | #html_additional_pages = {} 168 | 169 | # If false, no module index is generated. 170 | #html_domain_indices = True 171 | 172 | # If false, no index is generated. 173 | #html_use_index = True 174 | 175 | # If true, the index is split into individual pages for each letter. 176 | #html_split_index = False 177 | 178 | # If true, links to the reST sources are added to the pages. 179 | #html_show_sourcelink = True 180 | 181 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 182 | #html_show_sphinx = True 183 | 184 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 185 | #html_show_copyright = True 186 | 187 | # If true, an OpenSearch description file will be output, and all pages will 188 | # contain a tag referring to it. The value of this option must be the 189 | # base URL from which the finished HTML is served. 190 | #html_use_opensearch = '' 191 | 192 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 193 | #html_file_suffix = None 194 | 195 | # Output file base name for HTML help builder. 196 | htmlhelp_basename = 'verifydoc' 197 | 198 | 199 | # -- Options for LaTeX output --------------------------------------------- 200 | 201 | latex_elements = { 202 | # The paper size ('letterpaper' or 'a4paper'). 203 | #'papersize': 'letterpaper', 204 | 205 | # The font size ('10pt', '11pt' or '12pt'). 206 | #'pointsize': '10pt', 207 | 208 | # Additional stuff for the LaTeX preamble. 209 | #'preamble': '', 210 | } 211 | 212 | # Grouping the document tree into LaTeX files. List of tuples 213 | # (source start file, target name, title, 214 | # author, documentclass [howto, manual, or own class]). 215 | latex_documents = [ 216 | ('index', 'verify.tex', u'Verify Documentation', 217 | u'Derrick Gilland', 'manual'), 218 | ] 219 | 220 | # The name of an image file (relative to this directory) to place at the top of 221 | # the title page. 222 | #latex_logo = None 223 | 224 | # For "manual" documents, if this is true, then toplevel headings are parts, 225 | # not chapters. 226 | #latex_use_parts = False 227 | 228 | # If true, show page references after internal links. 229 | #latex_show_pagerefs = False 230 | 231 | # If true, show URL addresses after external links. 232 | #latex_show_urls = False 233 | 234 | # Documents to append as an appendix to all manuals. 235 | #latex_appendices = [] 236 | 237 | # If false, no module index is generated. 238 | #latex_domain_indices = True 239 | 240 | 241 | # -- Options for manual page output --------------------------------------- 242 | 243 | # One entry per manual page. List of tuples 244 | # (source start file, name, description, authors, manual section). 245 | man_pages = [ 246 | ('index', 'verify', u'Verify Documentation', 247 | [u'Derrick Gilland'], 1) 248 | ] 249 | 250 | # If true, show URL addresses after external links. 251 | #man_show_urls = False 252 | 253 | 254 | # -- Options for Texinfo output ------------------------------------------- 255 | 256 | # Grouping the document tree into Texinfo files. List of tuples 257 | # (source start file, target name, title, author, 258 | # dir menu entry, description, category) 259 | texinfo_documents = [ 260 | ('index', 'verify', u'Verify Documentation', 261 | u'Derrick Gilland', 'verify', 'A painless assertion library for Python.', 262 | 'Miscellaneous'), 263 | ] 264 | 265 | # Documents to append as an appendix to all manuals. 266 | #texinfo_appendices = [] 267 | 268 | # If false, no module index is generated. 269 | #texinfo_domain_indices = True 270 | 271 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 272 | #texinfo_show_urls = 'footnote' 273 | 274 | # If true, do not generate a @detailmenu in the "Top" node's menu. 275 | #texinfo_no_detailmenu = False 276 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. pushjack documentation master file 2 | 3 | .. include:: ../README.rst 4 | 5 | 6 | Guide 7 | ===== 8 | 9 | .. toctree:: 10 | :maxdepth: 3 11 | 12 | install 13 | api 14 | 15 | 16 | Project Info 17 | ============ 18 | 19 | .. toctree:: 20 | :maxdepth: 1 21 | 22 | license 23 | versioning 24 | changelog 25 | authors 26 | contributing 27 | 28 | 29 | Indices and Tables 30 | ================== 31 | 32 | - :ref:`genindex` 33 | - :ref:`modindex` 34 | - :ref:`search` 35 | 36 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Verify requires Python >= 2.7 or >= 3.3. 5 | 6 | To install from `PyPI `_: 7 | 8 | :: 9 | 10 | pip install verify 11 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../LICENSE.rst -------------------------------------------------------------------------------- /docs/versioning.rst: -------------------------------------------------------------------------------- 1 | Versioning 2 | ========== 3 | 4 | This project follows `Semantic Versioning`_ with the following caveats: 5 | 6 | - Only the public API (i.e. the objects imported into the ``verify`` module) will maintain backwards compatibility between MINOR version bumps. 7 | - Objects within any other parts of the library are not guaranteed to not break between MINOR version bumps. 8 | 9 | With that in mind, it is recommended to only use or import objects from the main module, ``verify``. 10 | 11 | 12 | .. _Semantic Versioning: http://semver.org/ 13 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | ## 2 | # Variables 3 | ## 4 | 5 | ENV_NAME = env 6 | ENV_ACT = . env/bin/activate; 7 | PIP = $(ENV_NAME)/bin/pip 8 | PYTEST_ARGS = --doctest-modules -v -s 9 | PYTEST_TARGET = verify tests 10 | COVERAGE_ARGS = --cov-config setup.cfg --cov-report term-missing --cov 11 | COVERAGE_TARGET = verify 12 | 13 | 14 | ## 15 | # Targets 16 | ## 17 | 18 | .PHONY: build 19 | build: clean install 20 | 21 | .PHONY: clean 22 | clean: clean-env clean-files 23 | 24 | .PHONY: clean-env 25 | clean-env: 26 | rm -rf $(ENV_NAME) 27 | 28 | .PHONY: clean-files 29 | clean-files: 30 | rm -rf .tox 31 | rm -rf .coverage 32 | find . -name \*.pyc -type f -delete 33 | find . -name \*.test.db -type f -delete 34 | find . -depth -name __pycache__ -type d -exec rm -rf {} \; 35 | rm -rf dist *.egg* build 36 | 37 | .PHONY: install 38 | install: 39 | rm -rf $(ENV_NAME) 40 | virtualenv --no-site-packages $(ENV_NAME) 41 | $(PIP) install -r requirements.txt 42 | 43 | .PHONY: test 44 | test: pylint-errors pep8 pytest 45 | 46 | .PHONY: pytest 47 | pytest: 48 | $(ENV_ACT) py.test $(PYTEST_ARGS) $(COVERAGE_ARGS) $(COVERAGE_TARGET) $(PYTEST_TARGET) 49 | 50 | .PHONY: test-full 51 | test-full: pylint-errors test-setuppy clean-files 52 | 53 | .PHONY: test-setuppy 54 | test-setuppy: 55 | python setup.py test 56 | 57 | 58 | .PHONY: lint 59 | lint: pylint pep8 60 | 61 | .PHONY: pep8 62 | pep8: 63 | $(ENV_ACT) pep8 $(PYTEST_TARGET) 64 | 65 | .PHONY: pylint 66 | pylint: 67 | $(ENV_ACT) pylint $(COVERAGE_TARGET) 68 | 69 | .PHONY: pylint-errors 70 | pylint-errors: 71 | $(ENV_ACT) pylint -E $(COVERAGE_TARGET) 72 | 73 | 74 | .PHONY: master 75 | master: 76 | git checkout master 77 | git merge develop 78 | git push origin develop master --tags 79 | git checkout develop 80 | 81 | .PHONY: release 82 | release: 83 | $(ENV_ACT) python setup.py sdist bdist_wheel 84 | $(ENV_ACT) twine upload dist/* 85 | rm -rf dist *.egg* build 86 | 87 | .PHONY: docs 88 | docs: 89 | touch docs/_build 90 | rm -r docs/_build 91 | $(ENV_ACT) cd docs; make doctest 92 | $(ENV_ACT) cd docs; make html 93 | 94 | .PHONY: serve-docs 95 | serve-docs: 96 | cd docs/_build/html; python2 -m SimpleHTTPServer 8000 97 | 98 | .PHONY: reload-docs 99 | reload-docs: docs serve-docs 100 | 101 | 102 | ## 103 | # TravisCI 104 | ## 105 | 106 | .PHONY: travisci-install 107 | travisci-install: 108 | pip install -r requirements.txt 109 | 110 | .PHONY: travisci-test 111 | travisci-test: 112 | pep8 $(PYTEST_TARGET) 113 | pylint -E $(COVERAGE_TARGET) 114 | py.test $(PYTEST_ARGS) $(COVERAGE_ARGS) $(COVERAGE_TARGET) $(PYTEST_TARGET) 115 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | astroid==1.3.6 2 | pep8==1.6.2 3 | pip-tools==0.3.6 4 | pydash>=4.0.3 5 | pylint==1.4.3 6 | pytest-cov==1.8.1 7 | pytest==2.7.0 8 | Sphinx==1.3.1 9 | tox==1.9.2 10 | twine==1.5.0 11 | wheel==0.24.0 12 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [pytest] 2 | norecursedirs = env 3 | addopts = --doctest-modules -v -s 4 | 5 | # pytest coverage options 6 | [run] 7 | omit = 8 | */tests/* 9 | */test_* 10 | */_compat.py 11 | 12 | [bidst_wheel] 13 | universal = 1 14 | 15 | [pep8] 16 | exclude = .tox,env 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | ****** 3 | verify 4 | ****** 5 | 6 | A painless assertion and validation library for Python. 7 | 8 | Project: https://github.com/dgilland/verify 9 | 10 | Documentation: http://verify.readthedocs.org/ 11 | """ 12 | 13 | import os 14 | import sys 15 | from setuptools import setup, find_packages 16 | from setuptools.command.test import test as TestCommand 17 | 18 | 19 | def read(fname): 20 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 21 | 22 | 23 | meta = {} 24 | exec(read('verify/__meta__.py'), meta) 25 | 26 | 27 | class Tox(TestCommand): 28 | user_options = [ 29 | ('tox-args=', 'a', "Arguments to pass to tox") 30 | ] 31 | 32 | def initialize_options(self): 33 | TestCommand.initialize_options(self) 34 | self.tox_args = '-c tox.ini' 35 | 36 | def finalize_options(self): 37 | TestCommand.finalize_options(self) 38 | self.test_args = [] 39 | self.test_suite = True 40 | 41 | def run_tests(self): 42 | # Import here because outside the eggs aren't loaded. 43 | import tox 44 | import shlex 45 | 46 | errno = tox.cmdline(args=shlex.split(self.tox_args)) 47 | sys.exit(errno) 48 | 49 | 50 | setup( 51 | name=meta['__title__'], 52 | version=meta['__version__'], 53 | url=meta['__url__'], 54 | license=meta['__license__'], 55 | author=meta['__author__'], 56 | author_email=meta['__email__'], 57 | description=meta['__summary__'], 58 | long_description=read('README.rst'), 59 | packages=find_packages(exclude=['tests']), 60 | install_requires=meta['__install_requires__'], 61 | tests_require=['tox'], 62 | cmdclass={'test': Tox}, 63 | test_suite='tests', 64 | keywords='test testing assert assertion validation unittest', 65 | classifiers=[ 66 | 'Development Status :: 3 - Alpha', 67 | 'Intended Audience :: Developers', 68 | 'Operating System :: OS Independent', 69 | 'Programming Language :: Python', 70 | 'License :: OSI Approved :: MIT License', 71 | 'Topic :: Software Development :: Libraries', 72 | 'Topic :: Software Development :: Libraries :: Python Modules', 73 | 'Topic :: Software Development :: Testing', 74 | 'Topic :: Utilities', 75 | 'Programming Language :: Python', 76 | 'Programming Language :: Python :: 2', 77 | 'Programming Language :: Python :: 2.7', 78 | 'Programming Language :: Python :: 3', 79 | 'Programming Language :: Python :: 3.3', 80 | 'Programming Language :: Python :: 3.4', 81 | ] 82 | ) 83 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgilland/verify/d39ff729d34f2d2200e27c153d6337adf2908bd5/tests/__init__.py -------------------------------------------------------------------------------- /tests/fixtures.py: -------------------------------------------------------------------------------- 1 | 2 | import datetime 3 | from decimal import Decimal 4 | import operator 5 | import re 6 | 7 | import pytest 8 | import pydash 9 | 10 | import verify as v 11 | from verify import Not 12 | 13 | 14 | class Arg(object): 15 | def __init__(self, *args, **kargs): 16 | self.args = args 17 | self.kargs = kargs 18 | 19 | def __repr__(self): 20 | return '{0}-{1}'.format(self.args, self.kargs) 21 | __str__ = __repr__ 22 | 23 | 24 | def assert_truthy(value): 25 | assert value 26 | 27 | 28 | def make_parametrize_id(argvalue): 29 | """Return custom parameter id for test reporting.""" 30 | if isinstance(argvalue, Arg): 31 | return str(argvalue) 32 | elif hasattr(argvalue, '__name__'): 33 | return '-' + argvalue.__name__ 34 | else: 35 | return str(argvalue) 36 | 37 | 38 | def raises_assertion(): 39 | """Expect AssertionError to be raised.""" 40 | return pytest.raises(AssertionError) 41 | 42 | 43 | METHOD_CALL_CASES = [ 44 | (v.Not, False, Arg(v.Truthy)), 45 | (v.Not, True, Arg(v.Falsy)), 46 | (v.Not, 1, Arg(pydash.is_boolean)), 47 | (v.Predicate, True, Arg(pydash.is_boolean)), 48 | (v.Predicate, 1, Arg(pydash.is_number)), 49 | (v.Equal, 1, Arg(1)), 50 | (v.Equal, True, Arg(True)), 51 | (v.Equal, 1, Arg(True)), 52 | (v.Equal, 0, Arg(False)), 53 | (v.Equal, 'abc', Arg('abc')), 54 | (v.Match, 'abc', Arg(r'\w+')), 55 | (v.Match, 'abc', Arg(re.compile(r'\w+'))), 56 | (v.Greater, 5, Arg(4)), 57 | (v.Greater, 10, Arg(-10)), 58 | (v.Greater, 'b', Arg('a')), 59 | (v.Greater, True, Arg(False)), 60 | (v.GreaterEqual, 5, Arg(5)), 61 | (v.GreaterEqual, 5, Arg(4)), 62 | (v.GreaterEqual, 10, Arg(-10)), 63 | (v.GreaterEqual, 'b', Arg('a')), 64 | (v.GreaterEqual, True, Arg(False)), 65 | (v.Less, -10, Arg(10)), 66 | (v.Less, 4, Arg(5)), 67 | (v.Less, 'a', Arg('b')), 68 | (v.LessEqual, 5, Arg(5)), 69 | (v.LessEqual, 4, Arg(5)), 70 | (v.LessEqual, 'a', Arg('b')), 71 | (v.Between, 5, Arg(min=4, max=5)), 72 | (v.Between, 5, Arg(max=5)), 73 | (v.Between, 5, Arg(min=5)), 74 | (v.Between, 5, Arg(min=5, max=5)), 75 | (v.Between, 5, Arg(max=6)), 76 | (v.Length, [1, 2, 3, 4], Arg(max=4)), 77 | (v.Length, (1, 2, 3), Arg(max=3)), 78 | (v.Length, [1, 2, 3, 4], Arg(min=3, max=5)), 79 | (v.Length, [1, 2, 3, 4], Arg(min=3)), 80 | (v.Length, [1, 2, 3, 4], Arg(max=5)), 81 | (v.Is, True, Arg(True)), 82 | (v.Is, False, Arg(False)), 83 | (v.Is, None, Arg(None)), 84 | (v.Is, 1, Arg(1)), 85 | (v.Is, 'a', Arg('a')), 86 | (v.IsTrue, True, Arg()), 87 | (v.IsFalse, False, Arg()), 88 | (v.IsNone, None, Arg()), 89 | (v.All, True, Arg([pydash.is_boolean, pydash.is_nan])), 90 | (v.Any, True, Arg([pydash.is_boolean, pydash.is_number])), 91 | (v.In, 1, Arg([0, 1, 2])), 92 | (v.In, 'a', Arg(('a', 'b', 'c'))), 93 | (v.In, 'a', Arg('abc')), 94 | (v.Contains, [1, 2, 3], Arg(2)), 95 | (v.Contains, {'one': 1, 'two': 2}, Arg('two')), 96 | (v.ContainsOnly, [1, 1, 1], Arg([1])), 97 | (v.ContainsOnly, [1, 0, 1], Arg((1, 0))), 98 | (v.Subset, {'b': 2}, Arg({'a': 1, 'b': 2})), 99 | (v.Subset, 100 | {'a': [{'b': [{'d': 4}]}]}, 101 | Arg({'a': [{'b': [{'c': 3, 'd': 4}]}]})), 102 | (v.Subset, [1, 2], Arg([1, 2, 3])), 103 | (v.Superset, {'a': 1, 'b': 2}, Arg({'b': 2})), 104 | (v.Superset, 105 | {'a': [{'b': [{'c': 3, 'd': 4}]}]}, 106 | Arg({'a': [{'b': [{'d': 4}]}]})), 107 | (v.Superset, [1, 2, 3], Arg([1, 2])), 108 | (v.Unique, [1, 2, 3, 4], Arg()), 109 | (v.Unique, {'one': 1, 'two': 2, 'thr': 3}, Arg()), 110 | (v.Type, True, Arg(bool)), 111 | (v.Type, 'abc', Arg(str)), 112 | (v.Type, 1, Arg(int)), 113 | (v.Truthy, True, Arg()), 114 | (v.Truthy, 1, Arg()), 115 | (v.Truthy, 'verify', Arg()), 116 | (v.Falsy, False, Arg()), 117 | (v.Falsy, None, Arg()), 118 | (v.Falsy, 0, Arg()), 119 | (v.Falsy, '', Arg()), 120 | (v.Boolean, True, Arg()), 121 | (v.Boolean, False, Arg()), 122 | (v.String, '', Arg()), 123 | (v.Dict, {}, Arg()), 124 | (v.List, [], Arg()), 125 | (v.Tuple, (), Arg()), 126 | (v.Date, datetime.date.today(), Arg()), 127 | (v.Date, datetime.datetime.now(), Arg()), 128 | (v.DateString, '2015-01-01', Arg('%Y-%m-%d')), 129 | (v.DateString, '2015-01-01T01:00:59', Arg('%Y-%m-%dT%H:%M:%S')), 130 | (v.Int, 1, Arg()), 131 | (v.Float, 1.1, Arg()), 132 | (v.Number, 1, Arg()), 133 | (v.Number, 0, Arg()), 134 | (v.Number, -1, Arg()), 135 | (v.Number, 1.05, Arg()), 136 | (v.Number, Decimal('1.05'), Arg()), 137 | (v.Positive, 1, Arg()), 138 | (v.Positive, 100, Arg()), 139 | (v.Negative, -1, Arg()), 140 | (v.Negative, -100, Arg()), 141 | (v.Even, 2, Arg()), 142 | (v.Even, -8, Arg()), 143 | (v.Odd, 1, Arg()), 144 | (v.Odd, -5, Arg()), 145 | (v.Monotone, [1, 1, 3, 5], Arg(operator.le)), 146 | (v.Monotone, [1, 2, 10, 20], Arg(operator.lt)), 147 | (v.Increasing, [1, 1, 3, 5], Arg()), 148 | (v.StrictlyIncreasing, [1, 5, 10], Arg()), 149 | (v.Decreasing, [5, 3, 1, 1], Arg()), 150 | (v.StrictlyDecreasing, [5, 4, 2, 1], Arg()), 151 | (v.NotEqual, 1, Arg(2)), 152 | (v.NotEqual, True, Arg(False)), 153 | (v.NotEqual, 'abc', Arg('cba')), 154 | (v.NotMatch, '###', Arg(r'\w+')), 155 | (v.NotMatch, '###', Arg(re.compile(r'\w+'))), 156 | (v.NotMatch, 1, Arg(r'\w+')), 157 | (v.NotBetween, 5, Arg(max=4)), 158 | (v.NotBetween, 5, Arg(min=1, max=4)), 159 | (v.NotLength, [1, 2, 3, 4], Arg(min=5)), 160 | (v.NotLength, [1, 2, 3, 4], Arg(max=2)), 161 | (v.IsNot, 1, Arg(2)), 162 | (v.IsNot, 1, Arg(True)), 163 | (v.IsNot, 0, Arg(False)), 164 | (v.IsNot, 'a', Arg('b')), 165 | (v.IsNotTrue, False, Arg()), 166 | (v.IsNotTrue, None, Arg()), 167 | (v.IsNotTrue, 0, Arg()), 168 | (v.IsNotTrue, '', Arg()), 169 | (v.IsNotFalse, True, Arg()), 170 | (v.IsNotFalse, 1, Arg()), 171 | (v.IsNotFalse, 'verify', Arg()), 172 | (v.IsNotNone, True, Arg()), 173 | (v.IsNotNone, 1, Arg()), 174 | (v.IsNotNone, 'verify', Arg()), 175 | (v.NotAll, True, Arg([pydash.is_boolean, pydash.is_number])), 176 | (v.NotAny, True, Arg([pydash.is_none, pydash.is_number])), 177 | (v.NotIn, 1, Arg([0, 0, 2])), 178 | (v.NotIn, 'a', Arg(('b', 'b', 'c'))), 179 | (v.NotIn, 1, Arg(2)), 180 | (v.NotContains, [1, 2, 3], Arg(4)), 181 | (v.NotContains, {'one': 1, 'two': 2}, Arg(2)), 182 | (v.NotContains, 4, Arg(4)), 183 | (v.NotContainsOnly, 1, Arg(1)), 184 | (v.NotContainsOnly, [1, 0], Arg([1])), 185 | (v.NotSubset, {'a': 1, 'b': 2}, Arg({'b': 2})), 186 | (v.NotSubset, 187 | {'a': [{'b': [{'c': 3, 'd': 4}]}]}, 188 | Arg({'a': [{'b': [{'d': 4}]}]})), 189 | (v.NotSubset, [1, 2, 3], Arg([1, 2])), 190 | (v.NotSuperset, {'b': 2}, Arg({'a': 1, 'b': 2})), 191 | (v.NotSuperset, 192 | {'a': [{'b': [{'d': 4}]}]}, 193 | Arg({'a': [{'b': [{'c': 3, 'd': 4}]}]})), 194 | (v.NotSuperset, [1, 2], Arg([1, 2, 3])), 195 | (v.NotUnique, [1, 1, 2], Arg()), 196 | (v.NotUnique, {'one': 1, 'uno': 1}, Arg()), 197 | (v.NotType, True, Arg(str)), 198 | (v.NotType, 'abc', Arg(int)), 199 | (v.NotType, 1, Arg(str)), 200 | (v.NotBoolean, None, Arg()), 201 | (v.NotBoolean, 1, Arg()), 202 | (v.NotBoolean, '', Arg()), 203 | (v.NotBoolean, [], Arg()), 204 | (v.NotString, True, Arg()), 205 | (v.NotString, [], Arg()), 206 | (v.NotDict, [], Arg()), 207 | (v.NotDict, (), Arg()), 208 | (v.NotDict, False, Arg()), 209 | (v.NotList, {}, Arg()), 210 | (v.NotList, (), Arg()), 211 | (v.NotList, False, Arg()), 212 | (v.NotTuple, {}, Arg()), 213 | (v.NotTuple, [], Arg()), 214 | (v.NotTuple, '', Arg()), 215 | (v.NotDate, '', Arg()), 216 | (v.NotDate, '2015-01-01', Arg()), 217 | (v.NotDateString, 2015, Arg('%Y')), 218 | (v.NotDateString, '2015-29-01', Arg('%Y-%m-%d')), 219 | (v.NotInt, '', Arg()), 220 | (v.NotInt, False, Arg()), 221 | (v.NotInt, 1.1, Arg()), 222 | (v.NotFloat, 1, Arg()), 223 | (v.NotFloat, '', Arg()), 224 | (v.NotNumber, '', Arg()), 225 | (v.NotNumber, True, Arg()), 226 | (v.NotNumber, {}, Arg()), 227 | ] 228 | 229 | 230 | METHOD_RAISE_CASES = [ 231 | (v.Not, True, Arg(v.Truthy)), 232 | (v.Not, False, Arg(v.Falsy)), 233 | (v.Not, True, Arg(pydash.is_boolean)), 234 | (v.Predicate, 1, Arg(pydash.is_boolean)), 235 | (v.Predicate, True, Arg(pydash.is_number)), 236 | (v.Predicate, False, Arg(assert_truthy)), 237 | (v.Equal, 1, Arg(2)), 238 | (v.Equal, True, Arg(False)), 239 | (v.Equal, 'abc', Arg('cba')), 240 | (v.Match, '###', Arg(r'\w+')), 241 | (v.Match, '###', Arg(re.compile(r'\w+'))), 242 | (v.Match, 1, Arg(r'\w+')), 243 | (v.Greater, 5, Arg(5)), 244 | (v.Greater, 4, Arg(5)), 245 | (v.Greater, 'a', Arg('b')), 246 | (v.GreaterEqual, 4, Arg(5)), 247 | (v.GreaterEqual, 'a', Arg('b')), 248 | (v.GreaterEqual, False, Arg(True)), 249 | (v.Less, 5, Arg(4)), 250 | (v.Less, 10, Arg(-10)), 251 | (v.Less, 'b', Arg('a')), 252 | (v.Less, True, Arg(False)), 253 | (v.LessEqual, 5, Arg(4)), 254 | (v.LessEqual, 10, Arg(-10)), 255 | (v.LessEqual, 'b', Arg('a')), 256 | (v.LessEqual, True, Arg(False)), 257 | (v.Between, 5, Arg(max=4)), 258 | (v.Between, 5, Arg(min=1, max=4)), 259 | (v.Length, [1, 2, 3, 4], Arg(min=3, max=3)), 260 | (v.Length, (1, 2, 3), Arg(min=2, max=2)), 261 | (v.Length, 1, Arg(min=1, max=1)), 262 | (v.Length, [1, 2, 3, 4], Arg(max=3)), 263 | (v.Is, 1, Arg(2)), 264 | (v.Is, 1, Arg(True)), 265 | (v.Is, 0, Arg(False)), 266 | (v.Is, 'a', Arg('b')), 267 | (v.IsTrue, False, Arg()), 268 | (v.IsTrue, None, Arg()), 269 | (v.IsTrue, 0, Arg()), 270 | (v.IsTrue, '', Arg()), 271 | (v.IsFalse, True, Arg()), 272 | (v.IsFalse, 1, Arg()), 273 | (v.IsFalse, 'verify', Arg()), 274 | (v.IsNone, True, Arg()), 275 | (v.IsNone, 1, Arg()), 276 | (v.IsNone, 'verify', Arg()), 277 | (v.All, True, Arg([pydash.is_boolean, pydash.is_number])), 278 | (v.Any, True, Arg([pydash.is_none, pydash.is_number])), 279 | (v.In, 1, Arg([0, 0, 2])), 280 | (v.In, 'a', Arg(('b', 'b', 'c'))), 281 | (v.In, 1, Arg(2)), 282 | (v.Contains, [1, 2, 3], Arg(4)), 283 | (v.Contains, {'one': 1, 'two': 2}, Arg(2)), 284 | (v.Contains, 4, Arg(4)), 285 | (v.ContainsOnly, 1, Arg(1)), 286 | (v.ContainsOnly, [1, 0], Arg([1])), 287 | (v.Subset, {'a': 1, 'b': 2}, Arg({'b': 2})), 288 | (v.Subset, 289 | {'a': [{'b': [{'c': 3, 'd': 4}]}]}, 290 | Arg({'a': [{'b': [{'d': 4}]}]})), 291 | (v.Subset, [1, 2, 3], Arg([1, 2])), 292 | (v.Superset, {'b': 2}, Arg({'a': 1, 'b': 2})), 293 | (v.Superset, 294 | {'a': [{'b': [{'d': 4}]}]}, 295 | Arg({'a': [{'b': [{'c': 3, 'd': 4}]}]})), 296 | (v.Superset, [1, 2], Arg([1, 2, 3])), 297 | (v.Unique, [1, 1, 2], Arg()), 298 | (v.Unique, {'one': 1, 'uno': 1}, Arg()), 299 | (v.Type, True, Arg(str)), 300 | (v.Type, 'abc', Arg(int)), 301 | (v.Type, 1, Arg(str)), 302 | (v.Truthy, False, Arg()), 303 | (v.Truthy, None, Arg()), 304 | (v.Truthy, 0, Arg()), 305 | (v.Truthy, '', Arg()), 306 | (v.Falsy, True, Arg()), 307 | (v.Falsy, 1, Arg()), 308 | (v.Falsy, 'verify', Arg()), 309 | (v.Boolean, None, Arg()), 310 | (v.Boolean, 1, Arg()), 311 | (v.Boolean, '', Arg()), 312 | (v.Boolean, [], Arg()), 313 | (v.String, True, Arg()), 314 | (v.String, [], Arg()), 315 | (v.Dict, [], Arg()), 316 | (v.Dict, (), Arg()), 317 | (v.Dict, False, Arg()), 318 | (v.List, {}, Arg()), 319 | (v.List, (), Arg()), 320 | (v.List, False, Arg()), 321 | (v.Tuple, {}, Arg()), 322 | (v.Tuple, [], Arg()), 323 | (v.Tuple, '', Arg()), 324 | (v.Date, '', Arg()), 325 | (v.Date, '2015-01-01', Arg()), 326 | (v.DateString, 2015, Arg('%Y')), 327 | (v.DateString, '2015-29-01', Arg('%Y-%m-%d')), 328 | (v.Int, '', Arg()), 329 | (v.Int, False, Arg()), 330 | (v.Int, 1.1, Arg()), 331 | (v.Float, 1, Arg()), 332 | (v.Float, '', Arg()), 333 | (v.Number, '', Arg()), 334 | (v.Number, False, Arg()), 335 | (v.Number, None, Arg()), 336 | (v.Number, {}, Arg()), 337 | (v.Number, [], Arg()), 338 | (v.Positive, -1, Arg()), 339 | (v.Positive, -100, Arg()), 340 | (v.Negative, 1, Arg()), 341 | (v.Negative, 100, Arg()), 342 | (v.Even, 1, Arg()), 343 | (v.Even, -5, Arg()), 344 | (v.Odd, 2, Arg()), 345 | (v.Odd, -8, Arg()), 346 | (v.Monotone, [1, 0, 3, 5], Arg(operator.le)), 347 | (v.Monotone, [1, 2, 0, 20], Arg(operator.lt)), 348 | (v.Increasing, [1, 0, 3, 5], Arg()), 349 | (v.StrictlyIncreasing, [1, 1, 10], Arg()), 350 | (v.Decreasing, [5, 3, 0, 1], Arg()), 351 | (v.StrictlyDecreasing, [5, 5, 2, 1], Arg()), 352 | (v.NotEqual, 1, Arg(1)), 353 | (v.NotEqual, True, Arg(True)), 354 | (v.NotEqual, 1, Arg(True)), 355 | (v.NotEqual, 0, Arg(False)), 356 | (v.NotEqual, 'abc', Arg('abc')), 357 | (v.NotMatch, 'abc', Arg(r'\w+')), 358 | (v.NotMatch, 'abc', Arg(re.compile(r'\w+'))), 359 | (v.NotBetween, 5, Arg(min=4, max=5)), 360 | (v.NotBetween, 5, Arg(max=5)), 361 | (v.NotBetween, 5, Arg(min=5)), 362 | (v.NotBetween, 5, Arg(min=5, max=5)), 363 | (v.NotBetween, 5, Arg(max=6)), 364 | (v.NotLength, [1, 2, 3, 4], Arg(max=4)), 365 | (v.NotLength, (1, 2, 3), Arg(max=3)), 366 | (v.NotLength, [1, 2, 3, 4], Arg(min=3, max=5)), 367 | (v.NotLength, [1, 2, 3, 4], Arg(min=3)), 368 | (v.NotLength, [1, 2, 3, 4], Arg(max=5)), 369 | (v.IsNot, True, Arg(True)), 370 | (v.IsNot, False, Arg(False)), 371 | (v.IsNot, None, Arg(None)), 372 | (v.IsNot, 1, Arg(1)), 373 | (v.IsNot, 'a', Arg('a')), 374 | (v.IsNotTrue, True, Arg()), 375 | (v.IsNotFalse, False, Arg()), 376 | (v.IsNotNone, None, Arg()), 377 | (v.NotAll, True, Arg([pydash.is_boolean, pydash.is_nan])), 378 | (v.NotAny, True, Arg([pydash.is_boolean, pydash.is_number])), 379 | (v.NotIn, 1, Arg([0, 1, 2])), 380 | (v.NotIn, 'a', Arg(('a', 'b', 'c'))), 381 | (v.NotIn, 'a', Arg('abc')), 382 | (v.NotContains, [1, 2, 3], Arg(2)), 383 | (v.NotContains, {'one': 1, 'two': 2}, Arg('two')), 384 | (v.NotContainsOnly, [1, 1, 1], Arg([1])), 385 | (v.NotContainsOnly, [1, 0, 1], Arg((1, 0))), 386 | (v.NotSubset, {'b': 2}, Arg({'a': 1, 'b': 2})), 387 | (v.NotSubset, 388 | {'a': [{'b': [{'d': 4}]}]}, 389 | Arg({'a': [{'b': [{'c': 3, 'd': 4}]}]})), 390 | (v.NotSubset, [1, 2], Arg([1, 2, 3])), 391 | (v.NotSuperset, {'a': 1, 'b': 2}, Arg({'b': 2})), 392 | (v.NotSuperset, 393 | {'a': [{'b': [{'c': 3, 'd': 4}]}]}, 394 | Arg({'a': [{'b': [{'d': 4}]}]})), 395 | (v.NotSuperset, [1, 2, 3], Arg([1, 2])), 396 | (v.NotUnique, [1, 2, 3, 4], Arg()), 397 | (v.NotUnique, {'one': 1, 'two': 2, 'thr': 3}, Arg()), 398 | (v.NotType, True, Arg(bool)), 399 | (v.NotType, 'abc', Arg(str)), 400 | (v.NotType, 1, Arg(int)), 401 | (v.NotBoolean, True, Arg()), 402 | (v.NotBoolean, False, Arg()), 403 | (v.NotString, '', Arg()), 404 | (v.NotDict, {}, Arg()), 405 | (v.NotList, [], Arg()), 406 | (v.NotTuple, (), Arg()), 407 | (v.NotDate, datetime.date.today(), Arg()), 408 | (v.NotDate, datetime.datetime.now(), Arg()), 409 | (v.NotDateString, '2015-01-01', Arg('%Y-%m-%d')), 410 | (v.NotDateString, '2015-01-01T01:00:59', Arg('%Y-%m-%dT%H:%M:%S')), 411 | (v.NotInt, 1, Arg()), 412 | (v.NotFloat, 1.1, Arg()), 413 | (v.NotNumber, 1, Arg()), 414 | (v.NotNumber, 0, Arg()), 415 | (v.NotNumber, -1, Arg()), 416 | (v.NotNumber, 1.05, Arg()), 417 | (v.NotNumber, Decimal('1.05'), Arg()), 418 | ] 419 | 420 | 421 | METHOD_ALIAS_CASES = [ 422 | (v.expect, v.ensure), 423 | (v.Greater, v.GreaterThan), 424 | (v.GreaterEqual, v.GreaterOrEqual), 425 | (v.Less, v.LessThan), 426 | (v.LessEqual, v.LessOrEqual), 427 | (v.In, v.to_be_in), 428 | (v.In, v.is_in), 429 | (v.NotIn, v.to_not_be_in), 430 | (v.NotIn, v.is_not_in), 431 | (v.Contains, v.to_contain), 432 | (v.Contains, v.contains), 433 | (v.NotContains, v.to_not_contain), 434 | (v.NotContains, v.does_not_contain), 435 | (v.ContainsOnly, v.to_contain_only), 436 | (v.ContainsOnly, v.contains_only), 437 | (v.NotContainsOnly, v.to_not_contain_only), 438 | (v.NotContainsOnly, v.does_not_contain_only), 439 | (v.Subset, v.to_be_subset), 440 | (v.Subset, v.is_subset), 441 | (v.NotSubset, v.to_not_be_subset), 442 | (v.NotSubset, v.is_not_subset), 443 | (v.Superset, v.to_be_superset), 444 | (v.Superset, v.is_superset), 445 | (v.NotSuperset, v.to_not_be_superset), 446 | (v.NotSuperset, v.is_not_superset), 447 | (v.Unique, v.to_be_unique), 448 | (v.Unique, v.is_unique), 449 | (v.NotUnique, v.to_not_be_unique), 450 | (v.NotUnique, v.is_not_unique), 451 | (v.Length, v.to_have_length), 452 | (v.Length, v.has_length), 453 | (v.NotLength, v.to_not_have_length), 454 | (v.NotLength, v.does_not_have_length), 455 | (v.Equal, v.to_be_equal), 456 | (v.Equal, v.is_equal), 457 | (v.NotEqual, v.to_not_be_equal), 458 | (v.NotEqual, v.is_not_equal), 459 | (v.Match, v.to_match), 460 | (v.Match, v.is_match), 461 | (v.Match, v.matches), 462 | (v.NotMatch, v.to_not_match), 463 | (v.Match, v.is_not_match), 464 | (v.NotMatch, v.does_not_match), 465 | (v.Is, v.to_be), 466 | (v.Is, v.is_), 467 | (v.IsNot, v.to_not_be), 468 | (v.IsNot, v.is_not), 469 | (v.IsTrue, v.to_be_true), 470 | (v.IsTrue, v.is_true), 471 | (v.IsNotTrue, v.to_not_be_true), 472 | (v.IsNotTrue, v.is_not_true), 473 | (v.IsFalse, v.to_be_false), 474 | (v.IsFalse, v.is_false), 475 | (v.IsNotFalse, v.to_not_be_false), 476 | (v.IsNotFalse, v.is_not_false), 477 | (v.IsNone, v.to_be_none), 478 | (v.IsNone, v.is_none), 479 | (v.IsNotNone, v.to_not_be_none), 480 | (v.IsNotNone, v.is_not_none), 481 | (v.Truthy, v.to_be_truthy), 482 | (v.Truthy, v.is_truthy), 483 | (v.Falsy, v.to_be_falsy), 484 | (v.Falsy, v.is_falsy), 485 | (v.Not, v.not_), 486 | (v.Not, v.does_not), 487 | (v.Not, v.to_fail), 488 | (v.Not, v.fails), 489 | (v.Predicate, v.does), 490 | (v.Predicate, v.to_pass), 491 | (v.Predicate, v.passes), 492 | (v.All, v.all_), 493 | (v.All, v.expect(None).all.assertion), 494 | (v.All, v.does_all), 495 | (v.All, v.passes_all), 496 | (v.NotAll, v.not_all), 497 | (v.NotAll, v.does_not_all), 498 | (v.NotAll, v.fails_all), 499 | (v.Any, v.any_), 500 | (v.Any, v.expect(None).any.assertion), 501 | (v.Any, v.does_any), 502 | (v.Any, v.passes_any), 503 | (v.NotAny, v.not_any), 504 | (v.NotAny, v.does_not_any), 505 | (v.NotAny, v.fails_any), 506 | (v.Greater, v.to_be_greater), 507 | (v.Greater, v.to_be_greater_than), 508 | (v.Greater, v.is_greater), 509 | (v.Greater, v.is_greater_than), 510 | (v.GreaterEqual, v.to_be_greater_equal), 511 | (v.GreaterEqual, v.to_be_greater_or_equal), 512 | (v.GreaterEqual, v.is_greqter_equal), 513 | (v.GreaterEqual, v.is_greater_or_equal), 514 | (v.Less, v.to_be_less), 515 | (v.Less, v.to_be_less_than), 516 | (v.Less, v.is_less), 517 | (v.Less, v.is_less_than), 518 | (v.LessEqual, v.to_be_less_equal), 519 | (v.LessEqual, v.to_be_less_or_equal), 520 | (v.LessEqual, v.is_less_equal), 521 | (v.LessEqual, v.is_less_or_equal), 522 | (v.Between, v.to_be_between), 523 | (v.Between, v.is_between), 524 | (v.NotBetween, v.to_not_be_between), 525 | (v.NotBetween, v.is_not_between), 526 | (v.Positive, v.to_be_positive), 527 | (v.Positive, v.is_positive), 528 | (v.Negative, v.to_be_negative), 529 | (v.Negative, v.is_negative), 530 | (v.Even, v.to_be_even), 531 | (v.Even, v.is_even), 532 | (v.Odd, v.to_be_odd), 533 | (v.Odd, v.is_odd), 534 | (v.Monotone, v.to_be_monotone), 535 | (v.Monotone, v.is_monotone), 536 | (v.Increasing, v.to_be_increasing), 537 | (v.Increasing, v.is_increasing), 538 | (v.StrictlyIncreasing, v.to_be_strictly_increasing), 539 | (v.StrictlyIncreasing, v.is_strictly_increasing), 540 | (v.Decreasing, v.to_be_decreasing), 541 | (v.Decreasing, v.is_decreasing), 542 | (v.StrictlyDecreasing, v.to_be_strictly_decreasing), 543 | (v.StrictlyDecreasing, v.is_strictly_decreasing), 544 | (v.Type, v.to_be_type), 545 | (v.Type, v.is_type), 546 | (v.NotType, v.to_not_be_type), 547 | (v.NotType, v.is_not_type), 548 | (v.Boolean, v.to_be_boolean), 549 | (v.Boolean, v.is_boolean), 550 | (v.NotBoolean, v.to_not_be_boolean), 551 | (v.NotBoolean, v.is_not_boolean), 552 | (v.String, v.to_be_string), 553 | (v.String, v.is_string), 554 | (v.NotString, v.to_not_be_string), 555 | (v.NotString, v.is_not_string), 556 | (v.Dict, v.to_be_dict), 557 | (v.Dict, v.is_dict), 558 | (v.NotDict, v.to_not_be_dict), 559 | (v.NotDict, v.is_not_dict), 560 | (v.List, v.to_be_list), 561 | (v.List, v.is_list), 562 | (v.NotList, v.to_not_be_list), 563 | (v.NotList, v.is_not_list), 564 | (v.Tuple, v.to_be_tuple), 565 | (v.Tuple, v.is_tuple), 566 | (v.NotTuple, v.to_not_be_tuple), 567 | (v.NotTuple, v.is_not_tuple), 568 | (v.Date, v.to_be_date), 569 | (v.Date, v.is_date), 570 | (v.NotDate, v.to_not_be_date), 571 | (v.NotDate, v.is_not_date), 572 | (v.DateString, v.to_be_date_string), 573 | (v.DateString, v.is_date_string), 574 | (v.NotDateString, v.to_not_be_date_string), 575 | (v.NotDateString, v.is_not_date_string), 576 | (v.Int, v.to_be_int), 577 | (v.Int, v.is_int), 578 | (v.NotInt, v.to_not_be_int), 579 | (v.NotInt, v.is_not_int), 580 | (v.Float, v.to_be_float), 581 | (v.Float, v.is_float), 582 | (v.NotFloat, v.to_not_be_float), 583 | (v.NotFloat, v.is_not_float), 584 | (v.Number, v.to_be_number), 585 | (v.Number, v.is_number), 586 | (v.NotNumber, v.to_not_be_number), 587 | (v.NotNumber, v.is_not_number), 588 | ] 589 | -------------------------------------------------------------------------------- /tests/test_verify.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | import pydash 5 | 6 | import verify as v 7 | from verify import expect, ensure 8 | 9 | from .fixtures import ( 10 | METHOD_CALL_CASES, 11 | METHOD_RAISE_CASES, 12 | METHOD_ALIAS_CASES, 13 | raises_assertion, 14 | assert_truthy, 15 | make_parametrize_id 16 | ) 17 | 18 | 19 | @pytest.mark.parametrize('value,assertions', [ 20 | (5, (v.Greater(4), v.Less(6))), 21 | ]) 22 | def test_expect_multiple_assertions(value, assertions): 23 | """Test that Expect handles multiple assertions.""" 24 | assert expect(value, *assertions) 25 | 26 | 27 | @pytest.mark.parametrize('value,predicates', [ 28 | (True, (pydash.is_boolean, pydash.identity)), 29 | ]) 30 | def test_expect_predicates(value, predicates): 31 | """Test that Expect handles multiple predicates that returns boolean 32 | values. 33 | """ 34 | assert expect(value, *predicates) 35 | 36 | 37 | @pytest.mark.parametrize('value,predicates', [ 38 | (True, (pydash.is_boolean, pydash.is_number)), 39 | (True, (pydash.is_integer, pydash.identity)), 40 | ]) 41 | def test_expect_predicates_raises(value, predicates): 42 | """Test that Expect handles multiple predicates that returns boolean 43 | values. 44 | """ 45 | with raises_assertion(): 46 | assert expect(value, *predicates) 47 | 48 | 49 | def test_expect_predicates_return_none(): 50 | assert expect(True, assert_truthy) 51 | 52 | 53 | def test_expect_chaining(): 54 | assert expect(True).Boolean()(assert_truthy) 55 | assert expect(True, v.Boolean(), assert_truthy).Truthy() 56 | 57 | 58 | def test_expect_chain_method_proxy(): 59 | for method in [method for method in dir(v) 60 | if method[0].isupper() or method[:2] in ('to', 'is')]: 61 | assert getattr(v, method) is getattr(expect(None), method).assertion 62 | 63 | 64 | @pytest.mark.parametrize('method', [ 65 | 'nosuchmethod', 66 | 'expect' 67 | ]) 68 | def test_expect_chain_invalid_method(method): 69 | with pytest.raises(AttributeError): 70 | getattr(expect(None), method) 71 | 72 | 73 | @pytest.mark.parametrize('meth,value,arg', 74 | METHOD_CALL_CASES, 75 | ids=make_parametrize_id) 76 | def test_assert_method(meth, value, arg): 77 | """Test that method passes when evaluated for comparables.""" 78 | assert expect(value, meth(*arg.args, **arg.kargs)) 79 | assert meth(value, *arg.args, **arg.kargs) 80 | 81 | 82 | @pytest.mark.parametrize('meth,value,arg', 83 | METHOD_RAISE_CASES, 84 | ids=make_parametrize_id) 85 | def test_assert_raises(meth, value, arg): 86 | """Test that method raises an assertion error when evaluated for 87 | comparables. 88 | """ 89 | with raises_assertion() as exc: 90 | expect(value, meth(*arg.args, **arg.kargs)) 91 | 92 | with raises_assertion() as exc: 93 | meth(value, *arg.args, **arg.kargs) 94 | 95 | with raises_assertion() as exc: 96 | opts = arg.kargs.copy() 97 | opts.update({'msg': 'TEST CUSTOM MESSAGE'}) 98 | meth(value, *arg.args, **opts) 99 | 100 | assert opts['msg'] in str(exc.value) 101 | 102 | 103 | @pytest.mark.parametrize('obj,alias', 104 | METHOD_ALIAS_CASES, 105 | ids=make_parametrize_id) 106 | def test_aliases(obj, alias): 107 | assert obj is alias 108 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = pep8, py26, py27, py33, py34 8 | 9 | [testenv] 10 | deps = 11 | pytest 12 | pytest-cov 13 | commands = py.test verify tests [] 14 | 15 | [testenv:pep8] 16 | deps = pep8 17 | commands = pep8 verify tests 18 | -------------------------------------------------------------------------------- /verify/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """The verify module is composed of various assertion callables (in this case, 3 | callable classes) that can be called in two contexts: 4 | 5 | 1. By themselves as in ``Equal(a, b)`` which will raise an ``AssertionError`` 6 | if ``a`` does not equal ``b``. 7 | 2. In combination with :func:`.expect` as in ``expect(a, Equal(b))`` which 8 | could also raise an ``AssertionError``. 9 | 10 | Thus, for all assertion classes below, the `value` argument defaults to 11 | ``NotSet`` which is a custom singleton to indicate that nothing was passed in 12 | for `value`. Whether `value` is set or ``NotSet`` is used to indicate which 13 | context the assertion class is being used. Whenever `value` is set, the 14 | `comparable` is swapped with `value` (internally inside the class' ``__init__`` 15 | method). This allows the assertion to be used in the two contexts above. 16 | 17 | This module's main focus is on testing, which is why all assertions raise an 18 | ``AssertionError`` on failure. Therefore, all assertion classes function 19 | similarly: 20 | 21 | - If the evaluation of `value` with `comparable` returns ``False``, then an 22 | ``AssertionError`` is raised with a custom message. 23 | - If the evaluation of `value` with `comparable` returns ``True`` and the class 24 | was only created (e.g. ``Equal(a, b)``), then nothing is raised or returned 25 | (obviously, since all we did was create a class instance). 26 | - If the evaluation of `value` with `comparable` returns ``True`` and the class 27 | was called (e.g. ``expect(a, Equal(b))`` or ``Equal(b)(a)``), then ``True`` 28 | is returned from the class call. 29 | 30 | There are two general types of assertions within this module: 31 | 32 | 1. Assertions that evaulate a single object: `value`. Referred to here as a 33 | plain assertion. 34 | 2. Assertions that evaulate two objects: `value` and `comparable`. Referred to 35 | here as a comparator assertion. 36 | 37 | When using plain assertions with :func:`.expect`, you can pass the bare 38 | assertion or initialize it. 39 | 40 | :: 41 | 42 | >>> expect(True, Truthy) 43 | 44 | >>> expect(True, Truthy()) 45 | 46 | 47 | When using any of the assertions, inserting ``assert`` in front is optional as 48 | each assertion will raise if the evaluation is false. However, having that 49 | ``assert`` in front may be aesthetically appealing to you, but keep in mind 50 | that any assert message included will not be shown since the assertion error 51 | will occur within the class itself and raised with it's own custom error 52 | message. 53 | 54 | :: 55 | 56 | >>> Truthy(True) 57 | 58 | >>> assert Truthy(True) 59 | 60 | :: 61 | 62 | # Both of these would raise an assertion error. 63 | >>> Falsy(True) 64 | Traceback (most recent call last): 65 | ... 66 | AssertionError: True is not falsy 67 | 68 | >>> assert Falsy(True) 69 | Traceback (most recent call last): 70 | ... 71 | AssertionError: True is not falsy 72 | 73 | # But assert messages will not make it to the traceback. 74 | >>> assert Falsy(True), 'this message will not be shown' 75 | Traceback (most recent call last): 76 | ... 77 | AssertionError: True is not falsy 78 | """ 79 | 80 | from .__meta__ import ( 81 | __title__, 82 | __summary__, 83 | __url__, 84 | __version__, 85 | __author__, 86 | __email__, 87 | __license__ 88 | ) 89 | 90 | 91 | from .runners import ( 92 | expect, 93 | ensure, 94 | ) 95 | 96 | from .containers import ( 97 | In, 98 | NotIn, 99 | Contains, 100 | NotContains, 101 | ContainsOnly, 102 | NotContainsOnly, 103 | Subset, 104 | NotSubset, 105 | Superset, 106 | NotSuperset, 107 | Unique, 108 | NotUnique, 109 | Length, 110 | NotLength, 111 | to_be_in, 112 | is_in, 113 | to_not_be_in, 114 | is_not_in, 115 | to_contain, 116 | contains, 117 | to_not_contain, 118 | does_not_contain, 119 | to_contain_only, 120 | contains_only, 121 | to_not_contain_only, 122 | does_not_contain_only, 123 | to_be_subset, 124 | is_subset, 125 | to_not_be_subset, 126 | is_not_subset, 127 | to_be_superset, 128 | is_superset, 129 | to_not_be_superset, 130 | is_not_superset, 131 | to_be_unique, 132 | is_unique, 133 | to_not_be_unique, 134 | is_not_unique, 135 | to_have_length, 136 | has_length, 137 | to_not_have_length, 138 | does_not_have_length, 139 | ) 140 | 141 | from .equality import ( 142 | Equal, 143 | NotEqual, 144 | Match, 145 | NotMatch, 146 | Is, 147 | IsNot, 148 | IsTrue, 149 | IsNotTrue, 150 | IsFalse, 151 | IsNotFalse, 152 | IsNone, 153 | IsNotNone, 154 | to_be_equal, 155 | is_equal, 156 | to_not_be_equal, 157 | is_not_equal, 158 | to_match, 159 | is_match, 160 | matches, 161 | to_not_match, 162 | is_not_match, 163 | does_not_match, 164 | to_be, 165 | is_, 166 | to_not_be, 167 | is_not, 168 | to_not_be, 169 | is_not, 170 | to_be_true, 171 | is_true, 172 | to_not_be_true, 173 | is_not_true, 174 | to_be_false, 175 | is_false, 176 | to_not_be_false, 177 | is_not_false, 178 | to_be_none, 179 | is_none, 180 | to_not_be_none, 181 | is_not_none, 182 | ) 183 | 184 | from .logic import ( 185 | Truthy, 186 | Falsy, 187 | Not, 188 | Predicate, 189 | All, 190 | NotAll, 191 | Any, 192 | NotAny, 193 | to_be_truthy, 194 | is_truthy, 195 | to_be_falsy, 196 | is_falsy, 197 | not_, 198 | does_not, 199 | to_fail, 200 | fails, 201 | does, 202 | to_pass, 203 | passes, 204 | all_, 205 | does_all, 206 | passes_all, 207 | not_all, 208 | does_not_all, 209 | fails_all, 210 | any_, 211 | does_any, 212 | passes_any, 213 | not_any, 214 | does_not_any, 215 | fails_any, 216 | ) 217 | 218 | from .numbers import ( 219 | Greater, 220 | GreaterThan, 221 | GreaterEqual, 222 | GreaterOrEqual, 223 | Less, 224 | LessThan, 225 | LessEqual, 226 | LessOrEqual, 227 | Between, 228 | NotBetween, 229 | Positive, 230 | Negative, 231 | Even, 232 | Odd, 233 | Monotone, 234 | Increasing, 235 | StrictlyIncreasing, 236 | Decreasing, 237 | StrictlyDecreasing, 238 | to_be_greater, 239 | to_be_greater_than, 240 | is_greater, 241 | is_greater_than, 242 | to_be_greater_equal, 243 | to_be_greater_or_equal, 244 | is_greqter_equal, 245 | is_greater_or_equal, 246 | to_be_less, 247 | to_be_less_than, 248 | is_less, 249 | is_less_than, 250 | to_be_less_equal, 251 | to_be_less_or_equal, 252 | is_less_equal, 253 | is_less_or_equal, 254 | to_be_between, 255 | is_between, 256 | to_not_be_between, 257 | is_not_between, 258 | to_be_positive, 259 | is_positive, 260 | to_be_negative, 261 | is_negative, 262 | to_be_even, 263 | is_even, 264 | to_be_odd, 265 | is_odd, 266 | to_be_monotone, 267 | is_monotone, 268 | to_be_increasing, 269 | is_increasing, 270 | to_be_strictly_increasing, 271 | is_strictly_increasing, 272 | to_be_decreasing, 273 | is_decreasing, 274 | to_be_strictly_decreasing, 275 | is_strictly_decreasing, 276 | ) 277 | 278 | from .types import ( 279 | Type, 280 | NotType, 281 | Boolean, 282 | NotBoolean, 283 | String, 284 | NotString, 285 | Dict, 286 | NotDict, 287 | List, 288 | NotList, 289 | Tuple, 290 | NotTuple, 291 | Date, 292 | NotDate, 293 | DateString, 294 | NotDateString, 295 | Int, 296 | NotInt, 297 | NotFloat, 298 | Float, 299 | Number, 300 | NotNumber, 301 | to_be_type, 302 | is_type, 303 | to_not_be_type, 304 | is_not_type, 305 | to_be_boolean, 306 | is_boolean, 307 | to_not_be_boolean, 308 | is_not_boolean, 309 | to_be_string, 310 | is_string, 311 | to_not_be_string, 312 | is_not_string, 313 | to_be_dict, 314 | is_dict, 315 | to_not_be_dict, 316 | is_not_dict, 317 | to_be_list, 318 | is_list, 319 | to_not_be_list, 320 | is_not_list, 321 | to_be_tuple, 322 | is_tuple, 323 | to_not_be_tuple, 324 | is_not_tuple, 325 | to_be_date, 326 | is_date, 327 | to_not_be_date, 328 | is_not_date, 329 | to_be_date_string, 330 | is_date_string, 331 | to_not_be_date_string, 332 | is_not_date_string, 333 | to_be_int, 334 | is_int, 335 | to_not_be_int, 336 | is_not_int, 337 | to_be_float, 338 | is_float, 339 | to_not_be_float, 340 | is_not_float, 341 | to_be_number, 342 | is_number, 343 | to_not_be_number, 344 | is_not_number, 345 | ) 346 | -------------------------------------------------------------------------------- /verify/__meta__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Define project metadata 3 | """ 4 | 5 | __title__ = 'verify' 6 | __summary__ = 'A painless assertion and validation library for Python.' 7 | __url__ = 'https://github.com/dgilland/verify' 8 | 9 | __version__ = '1.1.1' 10 | 11 | __install_requires__ = ['pydash>=4.0.3'] 12 | 13 | __author__ = 'Derrick Gilland' 14 | __email__ = 'dgilland@gmail.com' 15 | 16 | __license__ = 'MIT License' 17 | -------------------------------------------------------------------------------- /verify/base.py: -------------------------------------------------------------------------------- 1 | """Base classes and mixins. 2 | """ 3 | 4 | 5 | class _NotSet(object): 6 | """Represents an unset value.""" 7 | def __repr__(self): # pragma: no cover 8 | return 'NotSet' 9 | 10 | 11 | #: Singleton to indicate that a keyword argument was not provided. 12 | NotSet = _NotSet() 13 | 14 | 15 | class Assertion(object): 16 | """Base class for assertions. 17 | 18 | If `value` **is not** provided, then assertion isn't executed. This style 19 | of usage is used in conjuction with :class:`.expect`. 20 | 21 | If `value` **is** provided, then assertion is executed immediately. This 22 | style of usage is used when making assertions using only the class and not 23 | an assertion runner like :class:`.expect`. 24 | 25 | Keyword Arguments: 26 | msg (str, optional): Override assert message to use when performing 27 | assertion. 28 | """ 29 | #: Default format string used for assert message. 30 | reason = '' 31 | 32 | #: Operation to perform to determine whether `value` is valid. **This must 33 | #: be set in subclass**. 34 | op = None 35 | 36 | def __init__(self, value=NotSet, **opts): 37 | self.set_options(opts) 38 | 39 | if value is not NotSet: 40 | self(value, **opts) 41 | 42 | def set_options(self, opts): 43 | # Optional method that sets options as class instance variables for 44 | # use when calling operation. 45 | pass 46 | 47 | def format_msg(self, *args, **kargs): 48 | """Return formatted assert message. This is used to generate the assert 49 | message during :meth:`__call__`. If no ``msg`` keyword argument is 50 | provided, then :attr:`reason` will be used as the format string. By 51 | default, passed in ``args`` and ``kargs`` along with the classes 52 | ``__dict__`` dictionary are given to the format string. In all cases, 53 | ``arg[0]`` will be the `value` that is being validated. 54 | """ 55 | reason = kargs.pop('msg', None) or self.reason 56 | kargs.update(self.__dict__) 57 | return reason.format(*args, **kargs) 58 | 59 | def compare(self, value): # pragma: no cover 60 | # pylint: disable=not-callable 61 | return self.op(value) 62 | 63 | def __repr__(self): # pragma: no cover 64 | return '<{0}>'.format(self) 65 | 66 | def __str__(self): # pragma: no cover 67 | return '{0}()'.format(self.__class__.__name__) 68 | 69 | def __call__(self, *args, **opts): 70 | """Execute validation. 71 | 72 | Keyword Arguments: 73 | msg (str, optional): Override assert message to use when performing 74 | assertion. 75 | 76 | Returns: 77 | bool: ``True`` if comparison passes, otherwise, an 78 | ``AssertionError`` is raised. 79 | 80 | Raises: 81 | AssertionError: If comparison returns ``False``. 82 | """ 83 | fmt_kargs = {'msg': opts.pop('msg', None)} 84 | fmt_kargs.update(opts) 85 | 86 | assert self.compare(*args, **opts), self.format_msg(*args, **fmt_kargs) 87 | return True 88 | 89 | 90 | class Comparator(Assertion): 91 | """Base class for assertions that compare two values.""" 92 | def __init__(self, comparable, value=NotSet, **opts): 93 | if value is not NotSet: 94 | # Swap variables since the prescence of both inputs indicates we 95 | # are immediately executing validation. 96 | value, comparable = comparable, value 97 | 98 | # Whether we are validating now or later, set comparable on class since 99 | # self.compare() expects comparable to be an instance variable. 100 | self.comparable = comparable 101 | 102 | super(Comparator, self).__init__(value, **opts) 103 | 104 | def compare(self, value): 105 | # pylint: disable=not-callable 106 | return self.op(value, self.comparable) 107 | 108 | 109 | class Negate(object): 110 | """Mixin class that negates the results of :meth:`compare` from the parent 111 | class. 112 | """ 113 | def compare(self, *args, **opts): 114 | try: 115 | return not super(Negate, self).compare(*args, **opts) 116 | except AssertionError: # pragma: no cover 117 | return True 118 | 119 | 120 | def is_assertion(obj): 121 | """Return whether `obj` is either an instance or subclass of 122 | :class:`Assertion`. 123 | """ 124 | try: 125 | return isinstance(obj, Assertion) or issubclass(obj, Assertion) 126 | except TypeError: 127 | # Happens if `obj` isn't a class. 128 | return False 129 | -------------------------------------------------------------------------------- /verify/containers.py: -------------------------------------------------------------------------------- 1 | """Assertions related to containers/iterables. 2 | """ 3 | 4 | import operator 5 | 6 | import pydash 7 | 8 | from .base import Assertion, Comparator, Negate 9 | from .numbers import Between 10 | 11 | 12 | __all__ = ( 13 | 'In', 14 | 'NotIn', 15 | 'Contains', 16 | 'NotContains', 17 | 'ContainsOnly', 18 | 'NotContainsOnly', 19 | 'Subset', 20 | 'NotSubset', 21 | 'Superset', 22 | 'NotSuperset', 23 | 'Unique', 24 | 'NotUnique', 25 | 'Length', 26 | 'NotLength', 27 | ) 28 | 29 | 30 | class In(Comparator): 31 | """Asserts that `value` is in `comparable`. 32 | 33 | Aliases: 34 | - ``to_be_in`` 35 | - ``is_in`` 36 | 37 | .. versionadded:: 0.0.1 38 | """ 39 | #: 40 | reason = '{0} is not in {comparable}' 41 | 42 | @staticmethod 43 | def op(value, comparable): 44 | """Return whether `value` is contained in `comparable`.""" 45 | try: 46 | return value in comparable 47 | except (TypeError, ValueError): 48 | return False 49 | 50 | 51 | to_be_in = In 52 | is_in = In 53 | 54 | 55 | class NotIn(Negate, In): 56 | """Asserts that `value` is not in `comparable`. 57 | 58 | Aliases: 59 | - ``to_not_be_in`` 60 | - ``is_not_in`` 61 | 62 | .. versionadded:: 0.5.0 63 | """ 64 | #: 65 | reason = '{0} is in {comparable}' 66 | 67 | 68 | to_not_be_in = NotIn 69 | is_not_in = NotIn 70 | 71 | 72 | class Contains(Comparator): 73 | """Asserts that `value` is an iterable and contains `comparable`. 74 | 75 | Aliases: 76 | - ``to_contain`` 77 | - ``contains`` 78 | 79 | .. versionadded:: 0.2.0 80 | """ 81 | #: 82 | reason = '{0} does not contain {comparable}' 83 | 84 | @staticmethod 85 | def op(value, comparable): 86 | """Return whether `value` contains `comparable`.""" 87 | try: 88 | return comparable in value 89 | except (TypeError, ValueError): 90 | return False 91 | 92 | 93 | to_contain = Contains 94 | contains = Contains 95 | 96 | 97 | class NotContains(Negate, Contains): 98 | """Asserts that `value` does not contain `comparable`. 99 | 100 | Aliases: 101 | - ``to_not_contain`` 102 | - ``does_not_contain`` 103 | 104 | .. versionadded:: 0.5.0 105 | """ 106 | #: 107 | reason = '{0} contains {comparable}' 108 | 109 | 110 | to_not_contain = NotContains 111 | does_not_contain = NotContains 112 | 113 | 114 | class ContainsOnly(Comparator): 115 | """Asserts that `value` is an iterable and only contains `comparable`. 116 | 117 | Aliases: 118 | - ``to_contain_only`` 119 | - ``contains_only`` 120 | 121 | .. versionadded:: 0.2.0 122 | """ 123 | #: 124 | reason = '{0} does not only contain values in {comparable}' 125 | 126 | @staticmethod 127 | def op(value, comparable): 128 | """Return whether `value` contains only values in `comparable`.""" 129 | try: 130 | return all(val in comparable for val in value) 131 | except (TypeError, ValueError): 132 | return False 133 | 134 | 135 | to_contain_only = ContainsOnly 136 | contains_only = ContainsOnly 137 | 138 | 139 | class NotContainsOnly(Negate, ContainsOnly): 140 | """Asserts that `value` does not contain only `comparable`. 141 | 142 | Aliases: 143 | - ``to_not_contain_only`` 144 | - ``does_not_contain_only`` 145 | 146 | .. versionadded:: 0.5.0 147 | """ 148 | #: 149 | reason = '{0} contains only {comparable}' 150 | 151 | 152 | to_not_contain_only = NotContainsOnly 153 | does_not_contain_only = NotContainsOnly 154 | 155 | 156 | class Subset(Comparator): 157 | """Asserts that `value` is a subset of `comparable`. Comparison supports 158 | nested ``dict``, ``list``, and ``tuple`` objects. 159 | 160 | Aliases: 161 | - ``to_be_subset`` 162 | - ``is_subset`` 163 | 164 | .. versionadded:: 0.3.0 165 | """ 166 | #: 167 | reason = '{0} is not a subset of {comparable}' 168 | op = pydash.rearg(pydash.is_match, 1, 0) 169 | 170 | 171 | to_be_subset = Subset 172 | is_subset = Subset 173 | 174 | 175 | class NotSubset(Negate, Subset): 176 | """Asserts that `value` is a not a subset of `comparable`. 177 | 178 | Aliases: 179 | - ``to_not_be_subset`` 180 | - ``is_not_subset`` 181 | 182 | .. versionadded:: 0.5.0 183 | """ 184 | #: 185 | reason = '{0} is a subset of {comparable}' 186 | 187 | 188 | to_not_be_subset = NotSubset 189 | is_not_subset = NotSubset 190 | 191 | 192 | class Superset(Comparator): 193 | """Asserts that `value` is a superset of `comparable`. Comparison supports 194 | nested ``dict``, ``list``, and ``tuple`` objects. 195 | 196 | Aliases: 197 | - ``to_be_superset`` 198 | - ``is_superset`` 199 | 200 | .. versionadded:: 0.3.0 201 | """ 202 | #: 203 | reason = '{0} is not a supserset of {comparable}' 204 | op = staticmethod(pydash.is_match) 205 | 206 | 207 | to_be_superset = Superset 208 | is_superset = Superset 209 | 210 | 211 | class NotSuperset(Negate, Superset): 212 | """Asserts that `value` is a not a superset of `comparable`. 213 | 214 | Aliases: 215 | - ``to_not_be_superset`` 216 | - ``is_not_superset`` 217 | 218 | .. versionadded:: 0.5.0 219 | """ 220 | #: 221 | reason = '{0} is a superset of {comparable}' 222 | 223 | 224 | to_not_be_superset = NotSuperset 225 | is_not_superset = NotSuperset 226 | 227 | 228 | class Unique(Assertion): 229 | """Asserts that `value` contains only unique values. If `value` is a 230 | ``dict``, then its ``values()`` will be compared. 231 | 232 | Aliases: 233 | - ``to_be_unique`` 234 | - ``is_unique`` 235 | 236 | .. versionadded:: 0.3.0 237 | """ 238 | #: 239 | reason = '{0} contains duplicate items' 240 | 241 | @staticmethod 242 | def op(value): 243 | if isinstance(value, dict): 244 | value = value.values() 245 | 246 | is_unique = True 247 | seen = [] 248 | 249 | for item in value: 250 | if item in seen: 251 | is_unique = False 252 | break 253 | seen.append(item) 254 | 255 | return is_unique 256 | 257 | 258 | to_be_unique = Unique 259 | is_unique = Unique 260 | 261 | 262 | class NotUnique(Negate, Unique): 263 | """Asserts that `value` is a not a unique. 264 | 265 | Aliases: 266 | - ``to_not_be_unique`` 267 | - ``is_not_unique`` 268 | 269 | .. versionadded:: 0.5.0 270 | """ 271 | #: 272 | reason = '{0} is unique' 273 | 274 | 275 | to_not_be_unique = NotUnique 276 | is_not_unique = NotUnique 277 | 278 | 279 | class Length(Between): 280 | """Asserts that `value` is an iterable with length between `min` and `max` 281 | inclusively. 282 | 283 | Examples: 284 | 285 | These will pass: 286 | 287 | >>> assert Length([1, 2, 3], min=3, max=3) # 3 <= len(a) <= 3 288 | >>> assert Length([1, 2, 3, 4, 5], min=5, max=6) # 5 <= len(a) <= 6 289 | >>> assert Length([1, 2, 3], max=6) # len(a) <= 6 290 | >>> assert Length([1, 2, 3, 4], min=4) # len(a) >= 4 291 | 292 | This will fail: 293 | 294 | >>> Length([1, 2, 4], max=2) # len(a) <= 2 295 | Traceback (most recent call last): 296 | ... 297 | AssertionError... 298 | 299 | Args: 300 | value (mixed, optional): Value to compare. 301 | 302 | Keyword Args: 303 | min (int, optional): Minimum value that `value` must be greater than or 304 | equal to. 305 | max (int, optional): Maximum value that `value` must be less than or 306 | equal to. 307 | 308 | Aliases: 309 | - ``to_have_length`` 310 | - ``has_length`` 311 | 312 | .. versionadded:: 0.2.0 313 | 314 | .. versionchanged:: 0.4.0 315 | 316 | - Change comparison to function like :class:`Between` meaning length is 317 | compared to min and max values. 318 | - Allow keyword arguments ``min`` and ``max`` to be used in place of 319 | positional tuple 320 | 321 | .. versionchanged:: 1.0.0 322 | Removed positional tuple argument and only support ``min`` and ``max`` 323 | keyword arguments. 324 | """ 325 | #: 326 | reason = '{0} does not have length between {min} and {max}' 327 | 328 | @staticmethod 329 | def op(value, min=None, max=None): 330 | try: 331 | return Between.op(len(value), min=min, max=max) 332 | except (TypeError, ValueError): 333 | return False 334 | 335 | 336 | to_have_length = Length 337 | has_length = Length 338 | 339 | 340 | class NotLength(Negate, Length): 341 | """Asserts that `value` is an iterable with length not between `min` and 342 | `max` inclusively. 343 | 344 | Aliases: 345 | - ``to_not_have_length`` 346 | - ``does_not_have_length`` 347 | 348 | .. versionadded:: 1.0.0 349 | """ 350 | #: 351 | reason = '{0} has length between {min} and {max}' 352 | 353 | 354 | to_not_have_length = NotLength 355 | does_not_have_length = NotLength 356 | -------------------------------------------------------------------------------- /verify/equality.py: -------------------------------------------------------------------------------- 1 | """Assertions related to equality. 2 | """ 3 | 4 | import operator 5 | from functools import partial 6 | import re 7 | 8 | import pydash 9 | 10 | from .base import Assertion, Comparator, Negate, NotSet 11 | 12 | 13 | __all__ = ( 14 | 'Equal', 15 | 'NotEqual', 16 | 'Match', 17 | 'NotMatch', 18 | 'Is', 19 | 'IsNot', 20 | 'IsTrue', 21 | 'IsNotTrue', 22 | 'IsFalse', 23 | 'IsNotFalse', 24 | 'IsNotNone', 25 | 'IsNone', 26 | ) 27 | 28 | 29 | class Equal(Comparator): 30 | """Asserts that two values are equal. 31 | 32 | Aliases: 33 | - ``to_be_equal`` 34 | - ``is_equal`` 35 | 36 | .. versionadded:: 0.0.1 37 | """ 38 | #: 39 | reason = '{0} is not equal to {comparable}' 40 | op = operator.eq 41 | 42 | 43 | to_be_equal = Equal 44 | is_equal = Equal 45 | 46 | 47 | class NotEqual(Negate, Equal): 48 | """Asserts that two values are not equal. 49 | 50 | Aliases: 51 | - ``to_not_be_equal`` 52 | - ``is_not_equal`` 53 | 54 | .. versionadded:: 0.5.0 55 | """ 56 | #: 57 | reason = '{0} is equal to {comparable}' 58 | 59 | 60 | to_not_be_equal = NotEqual 61 | is_not_equal = NotEqual 62 | 63 | 64 | class Match(Comparator): 65 | """Asserts that `value` matches the regular expression `comparable`. 66 | 67 | Args: 68 | value (mixed, optional): Value to compare. 69 | comparable (str|RegExp): String or RegExp object used for matching. 70 | 71 | Keyword Args: 72 | flags (int, optional): Used when compiling regular expression when 73 | regular expression is a string. Defaults to ``0``. 74 | 75 | Aliases: 76 | - ``to_match`` 77 | - ``is_match`` 78 | - ``matches`` 79 | 80 | .. versionadded:: 0.3.0 81 | """ 82 | #: 83 | reason = '{0} does not match the regular expression {comparable}' 84 | 85 | def set_options(self, opts): 86 | self.flags = opts.pop('flags', 0) 87 | 88 | def compare(self, value): 89 | return self.op(value, self.comparable, flags=self.flags) 90 | 91 | @staticmethod 92 | def op(value, comparable, flags=0): 93 | if pydash.is_string(comparable): 94 | pattern = re.compile(comparable, flags) 95 | else: 96 | pattern = comparable 97 | 98 | try: 99 | match = bool(pattern.match(value)) 100 | except (TypeError, ValueError): 101 | match = False 102 | 103 | return match 104 | 105 | 106 | to_match = Match 107 | is_match = Match 108 | matches = Match 109 | 110 | 111 | class NotMatch(Negate, Match): 112 | """Asserts that `value` does not match the regular expression `comparable`. 113 | 114 | Aliases: 115 | - ``to_not_be_match`` 116 | - ``is_not_match`` 117 | - ``not_matches`` 118 | 119 | .. versionadded:: 0.5.0 120 | """ 121 | #: 122 | reason = '{0} matches the regular expression {comparable}' 123 | 124 | 125 | to_not_match = NotMatch 126 | is_not_match = Match 127 | does_not_match = NotMatch 128 | 129 | 130 | class Is(Comparator): 131 | """Asserts that `value` is `comparable`. 132 | 133 | Aliases: 134 | - ``to_be`` 135 | - ``is_`` 136 | 137 | .. versionadded:: 0.0.1 138 | """ 139 | #: 140 | reason = '{0} is not {comparable}' 141 | op = operator.is_ 142 | 143 | 144 | to_be = Is 145 | is_ = Is 146 | 147 | 148 | class IsNot(Negate, Is): 149 | """Asserts that `value` is not `comparable`. 150 | 151 | Aliases: 152 | - ``to_not_be`` 153 | - ``is_not`` 154 | 155 | .. versionadded:: 0.5.0 156 | """ 157 | #: 158 | reason = '{0} is {comparable}' 159 | 160 | 161 | to_not_be = IsNot 162 | is_not = IsNot 163 | 164 | 165 | class IsTrue(Assertion): 166 | """Asserts that `value` is ``True``. 167 | 168 | Aliases: 169 | - ``to_be_true`` 170 | - ``is_true`` 171 | 172 | .. versionadded:: 0.1.0 173 | """ 174 | #: 175 | reason = '{0} is not True' 176 | op = partial(operator.is_, True) 177 | 178 | 179 | to_be_true = IsTrue 180 | is_true = IsTrue 181 | 182 | 183 | class IsNotTrue(Negate, IsTrue): 184 | """Asserts that `value` is not ``True``. 185 | 186 | Aliases: 187 | - ``to_not_be_true`` 188 | - ``is_not_true`` 189 | 190 | .. versionadded:: 0.5.0 191 | """ 192 | #: 193 | reason = '{0} is True' 194 | 195 | 196 | to_not_be_true = IsNotTrue 197 | is_not_true = IsNotTrue 198 | 199 | 200 | class IsFalse(Assertion): 201 | """Asserts that `value` is ``False``. 202 | 203 | Aliases: 204 | - ``to_be_false`` 205 | - ``is_false`` 206 | 207 | .. versionadded:: 0.1.0 208 | """ 209 | #: 210 | reason = '{0} is not False' 211 | op = partial(operator.is_, False) 212 | 213 | 214 | to_be_false = IsFalse 215 | is_false = IsFalse 216 | 217 | 218 | class IsNotFalse(Negate, IsFalse): 219 | """Asserts that `value` is not ``False``. 220 | 221 | Aliases: 222 | - ``to_not_be_false`` 223 | - ``is_not_false`` 224 | 225 | .. versionadded:: 0.5.0 226 | """ 227 | #: 228 | reason = '{0} is False' 229 | 230 | 231 | to_not_be_false = IsNotFalse 232 | is_not_false = IsNotFalse 233 | 234 | 235 | class IsNone(Assertion): 236 | """Asserts that `value` is ``None``. 237 | 238 | Aliases: 239 | - ``to_be_none`` 240 | - ``is_none`` 241 | 242 | .. versionadded:: 0.0.1 243 | """ 244 | #: 245 | reason = '{0} is not None' 246 | op = staticmethod(pydash.is_none) 247 | 248 | 249 | to_be_none = IsNone 250 | is_none = IsNone 251 | 252 | 253 | class IsNotNone(Negate, IsNone): 254 | """Asserts that `value` is not ``None``. 255 | 256 | Aliases: 257 | - ``to_be_not_none`` 258 | - ``is_not_none`` 259 | 260 | .. versionadded:: 0.5.0 261 | """ 262 | #: 263 | reason = '{0} is None' 264 | 265 | 266 | to_not_be_none = IsNotNone 267 | is_not_none = IsNotNone 268 | -------------------------------------------------------------------------------- /verify/logic.py: -------------------------------------------------------------------------------- 1 | """Assertions related to logical operations. 2 | """ 3 | 4 | import pydash 5 | 6 | from .base import Assertion, Comparator, Negate 7 | 8 | 9 | __all__ = ( 10 | 'Truthy', 11 | 'Falsy', 12 | 'Not', 13 | 'Predicate', 14 | 'All', 15 | 'NotAll', 16 | 'Any', 17 | 'NotAny', 18 | ) 19 | 20 | 21 | class Truthy(Assertion): 22 | """Asserts that `value` is truthy. 23 | 24 | Aliases: 25 | - ``to_be_truthy`` 26 | - ``is_truthy`` 27 | 28 | .. versionadded:: 0.0.1 29 | """ 30 | #: 31 | reason = '{0} is not truthy' 32 | op = bool 33 | 34 | 35 | to_be_truthy = Truthy 36 | is_truthy = Truthy 37 | 38 | 39 | class Falsy(Assertion): 40 | """Asserts that `value` is falsy. 41 | 42 | Aliases: 43 | - ``to_be_falsy`` 44 | - ``is_falsy`` 45 | 46 | .. versionadded:: 0.0.1 47 | """ 48 | #: 49 | reason = '{0} is not falsy' 50 | op = pydash.negate(bool) 51 | 52 | 53 | to_be_falsy = Falsy 54 | is_falsy = Falsy 55 | 56 | 57 | class Not(Comparator): 58 | """Asserts that `comparable` doesn't raise an ``AssertionError``. Can be 59 | used to create "opposite" comparators. 60 | 61 | Examples: 62 | 63 | >>> from verify import * 64 | >>> expect(5, Not(In([1, 2, 3]))) 65 | 66 | >>> Not(5, In([1, 2, 3])) 67 | 68 | >>> Not(In([1, 2, 3]))(5) 69 | True 70 | 71 | Aliases: 72 | - ``not_`` 73 | - ``does_not`` 74 | - ``to_fail`` 75 | - ``fails`` 76 | 77 | .. versionadded:: 0.0.1 78 | """ 79 | #: 80 | reason = ('The negation of {comparable} should not be true ' 81 | 'when evaluated with {0}') 82 | 83 | def compare(self, *args, **opts): 84 | try: 85 | return not self.comparable(*args, **opts) 86 | except AssertionError: 87 | return True 88 | 89 | 90 | not_ = Not 91 | does_not = Not 92 | to_fail = Not 93 | fails = Not 94 | 95 | 96 | class Predicate(Comparator): 97 | """Asserts that `value` evaluated by the predicate `comparable` is 98 | ``True``. 99 | 100 | Aliases: 101 | - ``does`` 102 | - ``to_pass`` 103 | - ``passes`` 104 | 105 | .. versionadded:: 0.1.0 106 | 107 | .. versionchanged:: 0.6.0 108 | Catch ``AssertionError`` thrown by `comparable` and return ``False`` 109 | as comparison value instead. 110 | """ 111 | #: 112 | reason = 'The evaluation of {0} using {comparable} is false' 113 | 114 | def compare(self, *args, **opts): 115 | try: 116 | result = self.comparable(*args, **opts) 117 | except AssertionError as ex: 118 | # Catch AssertionError so that our class will emit it's own error 119 | # message when False is returned. 120 | result = False 121 | 122 | if result is None: 123 | # Consider predicates that return None to pass. This is done to 124 | # support predicates that assert internally but don't have a return 125 | # value. 126 | result = True 127 | 128 | return result 129 | 130 | 131 | does = Predicate 132 | to_pass = Predicate 133 | passes = Predicate 134 | 135 | 136 | class All(Comparator): 137 | """Asserts that `value` evaluates as truthy for **all** predicates in 138 | `comparable`. 139 | 140 | Aliases: 141 | - ``all_`` 142 | - ``does_all`` 143 | - ``passes_all`` 144 | 145 | .. versionadded:: 0.2.0 146 | """ 147 | #: 148 | reason = '{0} is not true for all {comparable}' 149 | 150 | @staticmethod 151 | def op(value, comparable): 152 | """Return whether all results from evaluating `value` in `comparable` 153 | predicates return truthy. 154 | """ 155 | return all(pydash.juxtapose(*comparable)(value)) 156 | 157 | all_ = All 158 | does_all = All 159 | passes_all = All 160 | 161 | 162 | class NotAll(Negate, All): 163 | """Asserts that `value` evaluates as falsy for **all** predicates in 164 | `comparable`. 165 | 166 | Aliases: 167 | - ``to_be_not_all`` 168 | - ``does_not_all`` 169 | - ``fails_all`` 170 | 171 | .. versionadded:: 0.5.0 172 | """ 173 | #: 174 | reason = '{0} is true for all {comparable}' 175 | 176 | 177 | not_all = NotAll 178 | does_not_all = NotAll 179 | fails_all = NotAll 180 | 181 | 182 | class Any(Comparator): 183 | """Asserts that `value` evaluates as truthy for **any** predicates in 184 | `comparable`. 185 | 186 | Aliases: 187 | - ``any_`` 188 | - ``does_any`` 189 | - ``passes_any`` 190 | 191 | .. versionadded:: 0.2.0 192 | """ 193 | #: 194 | reason = '{0} is not true for any {comparable}' 195 | 196 | @staticmethod 197 | def op(value, comparable): 198 | """Return whether any results from evaluating `value` in `comparable` 199 | predicates return truthy. 200 | """ 201 | return any(pydash.juxtapose(*comparable)(value)) 202 | 203 | 204 | any_ = Any 205 | does_any = Any 206 | passes_any = Any 207 | 208 | 209 | class NotAny(Negate, Any): 210 | """Asserts that `value` evaluates as falsy for **any** predicates in 211 | `comparable`. 212 | 213 | Aliases: 214 | - ``not_any`` 215 | - ``does_not_any`` 216 | - ``fails_any`` 217 | 218 | .. versionadded:: 0.5.0 219 | """ 220 | #: 221 | reason = '{0} is true for some {comparable}' 222 | 223 | 224 | not_any = NotAny 225 | does_not_any = NotAny 226 | fails_any = NotAny 227 | -------------------------------------------------------------------------------- /verify/numbers.py: -------------------------------------------------------------------------------- 1 | """Assertions related to numbers. 2 | """ 3 | 4 | import operator 5 | 6 | import pydash 7 | 8 | from .base import Assertion, Comparator, Negate, NotSet 9 | 10 | 11 | __all__ = ( 12 | 'Greater', 13 | 'GreaterThan', 14 | 'GreaterEqual', 15 | 'GreaterOrEqual', 16 | 'Less', 17 | 'LessThan', 18 | 'LessEqual', 19 | 'LessOrEqual', 20 | 'Between', 21 | 'NotBetween', 22 | 'Positive', 23 | 'Negative', 24 | 'Even', 25 | 'Odd', 26 | 'Monotone', 27 | 'Increasing', 28 | 'StrictlyIncreasing', 29 | 'Decreasing', 30 | 'StrictlyDecreasing', 31 | ) 32 | 33 | 34 | class Greater(Comparator): 35 | """Asserts that `value` is greater than `comparable`. 36 | 37 | Aliases: 38 | - ``GreaterThan`` 39 | - ``to_be_greater`` 40 | - ``to_be_greater_than`` 41 | - ``is_greater`` 42 | - ``is_greater_than`` 43 | 44 | .. versionadded:: 0.0.1 45 | """ 46 | #: 47 | reason = '{0} is not greater than {comparable}' 48 | op = operator.gt 49 | 50 | 51 | GreaterThan = Greater 52 | to_be_greater = Greater 53 | to_be_greater_than = Greater 54 | is_greater = Greater 55 | is_greater_than = Greater 56 | 57 | 58 | class GreaterEqual(Comparator): 59 | """Asserts that `value` is greater than or equal to `comparable`. 60 | 61 | Aliases: 62 | - ``GreaterThanEqual`` 63 | - ``to_be_greater_equal`` 64 | - ``to_be_greater_or_equal`` 65 | - ``is_greater_equal`` 66 | - ``is_greater_or_equal`` 67 | 68 | .. versionadded:: 0.0.1 69 | """ 70 | #: 71 | reason = '{0} is not greater than or equal to {comparable}' 72 | op = operator.ge 73 | 74 | 75 | GreaterOrEqual = GreaterEqual 76 | to_be_greater_equal = GreaterEqual 77 | to_be_greater_or_equal = GreaterEqual 78 | is_greqter_equal = GreaterEqual 79 | is_greater_or_equal = GreaterEqual 80 | 81 | 82 | class Less(Comparator): 83 | """Asserts that `value` is less than `comparable`. 84 | 85 | Aliases: 86 | - ``LessThan`` 87 | - ``to_be_less`` 88 | - ``to_be_less_than`` 89 | - ``is_less`` 90 | - ``is_less_than`` 91 | 92 | .. versionadded:: 0.0.1 93 | """ 94 | #: 95 | reason = '{0} is not less than {comparable}' 96 | op = operator.lt 97 | 98 | 99 | LessThan = Less 100 | to_be_less = Less 101 | to_be_less_than = Less 102 | is_less = Less 103 | is_less_than = Less 104 | 105 | 106 | class LessEqual(Comparator): 107 | """Asserts that `value` is less than or equal to `comparable`. 108 | 109 | Aliases: 110 | - ``LessThanEqual`` 111 | - ``to_be_less_equal`` 112 | - ``to_be_less_or_equal`` 113 | - ``is_less_equal`` 114 | - ``is_less_or_equal`` 115 | 116 | .. versionadded:: 0.0.1 117 | """ 118 | #: 119 | reason = '{0} is not less than or equal to {comparable}' 120 | op = operator.le 121 | 122 | 123 | LessOrEqual = LessEqual 124 | to_be_less_equal = LessEqual 125 | to_be_less_or_equal = LessEqual 126 | is_less_equal = LessEqual 127 | is_less_or_equal = LessEqual 128 | 129 | 130 | class Between(Assertion): 131 | """Asserts that `value` is between `min` and `max` inclusively. 132 | 133 | Examples: 134 | 135 | These will pass: 136 | 137 | >>> assert Between(5, min=4, max=6) # 4 <= 5 <= 6 138 | >>> assert Between(5, min=5, max=6) # 5 <= 5 <= 6 139 | >>> assert Between(5, max=6) # 5 <= 6 140 | >>> assert Between(5, min=4) # 5 >= 4 141 | 142 | This will fail: 143 | 144 | >>> Between(5, max=4) # 5 <= 4 145 | Traceback (most recent call last): 146 | ... 147 | AssertionError... 148 | 149 | Args: 150 | value (mixed, optional): Value to compare. 151 | 152 | Keyword Args: 153 | min (int, optional): Minimum value that `value` must be greater than or 154 | equal to. 155 | max (int, optional): Maximum value that `value` must be less than or 156 | equal to. 157 | 158 | Aliases: 159 | - ``to_be_between`` 160 | - ``is_between`` 161 | 162 | .. versionadded:: 0.2.0 163 | 164 | .. versionchanged:: 0.4.0 165 | Allow keyword arguments ``min`` and ``max`` to be used in place of 166 | positional tuple. 167 | 168 | .. versionchanged:: 1.0.0 169 | Removed positional tuple argument and only support ``min`` and ``max`` 170 | keyword arguments. 171 | """ 172 | #: 173 | reason = '{0} is not between {min} and {max}' 174 | 175 | def set_options(self, opts): 176 | self.min = opts.pop('min', None) 177 | self.max = opts.pop('max', None) 178 | 179 | def compare(self, value): 180 | return self.op(value, self.min, self.max) 181 | 182 | @staticmethod 183 | def op(value, min=None, max=None): 184 | ge_min = value >= min if min is not None else True 185 | le_max = value <= max if max is not None else True 186 | return ge_min and le_max 187 | 188 | 189 | to_be_between = Between 190 | is_between = Between 191 | 192 | 193 | class NotBetween(Negate, Between): 194 | """Asserts that `value` is not between `min` and `max` inclusively. 195 | 196 | Aliases: 197 | - ``to_not_be_between`` 198 | - ``is_not_between`` 199 | 200 | .. versionadded:: 0.5.0 201 | """ 202 | #: 203 | reason = '{0} is between {min} and {max}' 204 | 205 | 206 | to_not_be_between = NotBetween 207 | is_not_between = NotBetween 208 | 209 | 210 | class Positive(Assertion): 211 | """Asserts that `value` is a positive number. 212 | 213 | Aliases: 214 | - ``to_be_positive`` 215 | - ``is_positive`` 216 | 217 | .. versionadded:: 0.3.0 218 | """ 219 | #: 220 | reason = '{0} is not a positive number' 221 | op = staticmethod(pydash.is_positive) 222 | 223 | 224 | to_be_positive = Positive 225 | is_positive = Positive 226 | 227 | 228 | class Negative(Assertion): 229 | """Asserts that `value` is a negative number. 230 | 231 | Aliases: 232 | - ``to_be_negative`` 233 | - ``is_negative`` 234 | 235 | .. versionadded:: 0.3.0 236 | """ 237 | #: 238 | reason = '{0} is not a negative number' 239 | op = staticmethod(pydash.is_negative) 240 | 241 | 242 | to_be_negative = Negative 243 | is_negative = Negative 244 | 245 | 246 | class Even(Assertion): 247 | """Asserts that `value` is an even number. 248 | 249 | Aliases: 250 | - ``to_be_even`` 251 | - ``is_even`` 252 | 253 | .. versionadded:: 0.3.0 254 | """ 255 | #: 256 | reason = '{0} is not an even number' 257 | op = staticmethod(pydash.is_even) 258 | 259 | 260 | to_be_even = Even 261 | is_even = Even 262 | 263 | 264 | class Odd(Assertion): 265 | """Asserts that `value` is an odd number. 266 | 267 | Aliases: 268 | - ``to_be_odd`` 269 | - ``is_odd`` 270 | 271 | .. versionadded:: 0.3.0 272 | """ 273 | #: 274 | reason = '{0} is not an odd number' 275 | op = staticmethod(pydash.is_odd) 276 | 277 | 278 | to_be_odd = Odd 279 | is_odd = Odd 280 | 281 | 282 | class Monotone(Comparator): 283 | """Asserts that `value` is a monotonic with respect to `comparable`. 284 | 285 | Aliases: 286 | - ``to_be_monotone`` 287 | - ``is_monotone`` 288 | 289 | .. versionadded:: 0.3.0 290 | """ 291 | #: 292 | reason = '{0} is not monotonic as evaluated by {comparable}' 293 | op = staticmethod(pydash.is_monotone) 294 | 295 | 296 | to_be_monotone = Monotone 297 | is_monotone = Monotone 298 | 299 | 300 | class Increasing(Assertion): 301 | """Asserts that `value` is monotonically increasing. 302 | 303 | Aliases: 304 | - ``to_be_increasing`` 305 | - ``is_increasing`` 306 | 307 | .. versionadded:: 0.3.0 308 | """ 309 | #: 310 | reason = '{0} is not monotonically increasing' 311 | op = staticmethod(pydash.is_increasing) 312 | 313 | 314 | to_be_increasing = Increasing 315 | is_increasing = Increasing 316 | 317 | 318 | class StrictlyIncreasing(Assertion): 319 | """Asserts that `value` is strictly increasing. 320 | 321 | Aliases: 322 | - ``to_be_strictly_increasing`` 323 | - ``is_strictly_increasing`` 324 | 325 | .. versionadded:: 0.3.0 326 | """ 327 | #: 328 | reason = '{0} is not strictly increasing' 329 | op = staticmethod(pydash.is_strictly_increasing) 330 | 331 | 332 | to_be_strictly_increasing = StrictlyIncreasing 333 | is_strictly_increasing = StrictlyIncreasing 334 | 335 | 336 | class Decreasing(Assertion): 337 | """Asserts that `value` is monotonically decreasing. 338 | 339 | Aliases: 340 | - ``to_be_decreasing`` 341 | - ``is_decreasing`` 342 | 343 | .. versionadded:: 0.3.0 344 | """ 345 | #: 346 | reason = '{0} is not monotonically decreasing' 347 | op = staticmethod(pydash.is_decreasing) 348 | 349 | 350 | to_be_decreasing = Decreasing 351 | is_decreasing = Decreasing 352 | 353 | 354 | class StrictlyDecreasing(Assertion): 355 | """Asserts that `value` is strictly decreasing. 356 | 357 | Aliases: 358 | - ``to_be_strictly_decreasing`` 359 | - ``is_strictly_decreasing`` 360 | 361 | .. versionadded:: 0.3.0 362 | """ 363 | #: 364 | reason = '{0} is not strictly decreasing' 365 | op = staticmethod(pydash.is_strictly_decreasing) 366 | 367 | 368 | to_be_strictly_decreasing = StrictlyDecreasing 369 | is_strictly_decreasing = StrictlyDecreasing 370 | -------------------------------------------------------------------------------- /verify/runners.py: -------------------------------------------------------------------------------- 1 | """Assertion runners. 2 | """ 3 | 4 | import re 5 | 6 | import verify 7 | from .base import Assertion, is_assertion 8 | 9 | 10 | __all__ = ( 11 | 'ensure', 12 | 'expect', 13 | ) 14 | 15 | 16 | class expect(object): 17 | """Pass `value` through a set of assertable functions. 18 | 19 | There are two styles for invoking ``expect``: 20 | 21 | 1. Pass `value` and all `assertions` as arguments to the ``__init__`` 22 | method of ``expect``. 23 | 2. Pass `value` to the ``__init__`` method of ``expect`` and invoke 24 | assertions via method chaining. 25 | 26 | Examples: 27 | 28 | Passing `value` and `assertions` to ``expect.__init__``: 29 | 30 | >>> from verify import * 31 | >>> expect(5, Truthy(), Greater(4)) 32 | 33 | >>> expect(5, Falsy()) 34 | Traceback (most recent call last): 35 | ... 36 | AssertionError... 37 | 38 | Using method chaining: 39 | 40 | >>> expect(5).Truthy().Greater(4) 41 | 42 | >>> expect(5).Falsy() 43 | Traceback (most recent call last): 44 | ... 45 | AssertionError... 46 | 47 | 48 | Args: 49 | value (mixed): Value to test. 50 | *assertions (callable, optional): Callable objects that accept `value` 51 | as its first argument. It's expected that these callables assert 52 | something. 53 | 54 | Returns: 55 | self: Allows for method assertion chaining. 56 | 57 | Raises: 58 | AssertionError: If the evaluation of all assertions returns ``False``. 59 | 60 | Aliases: 61 | - ``ensure`` 62 | 63 | .. versionadded:: 0.0.1 64 | 65 | .. versionchanged:: 0.1.0 66 | 67 | - Rename from ``Expect`` to ``expect`` and change implementation from a 68 | class to a function. 69 | - Passed in `value` is no longer called if it's a callable. 70 | - Return ``True`` if all assertions pass. 71 | 72 | .. versionchanged:: 0.6.0 73 | 74 | - Re-implement as class. 75 | - Support method chaining of assertion classes. 76 | - Wrap assertions that are not derived from Assertion in 77 | :class:`.Predicate` for consistent behavior from external assertion 78 | functions. 79 | """ 80 | def __init__(self, value, *assertions): 81 | self.value = value 82 | 83 | if assertions: 84 | self(*assertions) 85 | 86 | def __repr__(self): # pragma: no cover 87 | return '<{0}>'.format(self) 88 | 89 | def __str__(self): # pragma: no cover 90 | return '{0}({1})'.format(self.__class__.__name__, self.value) 91 | 92 | def __getattr__(self, attr): 93 | """Invoke assertions via attribute access. All :mod:`verify` assertions 94 | are available. 95 | """ 96 | assertion = getattr(verify, attr, None) 97 | 98 | if not callable(assertion) and not attr.endswith('_'): 99 | # Alias method names not ending in underscore to their underscore 100 | # counterpart. This allows chaining of functions that have a name 101 | # conflict with builtins (e.g. "any_", "all_", etc). 102 | assertion = getattr(verify, attr + '_', None) 103 | 104 | if not is_assertion(assertion): 105 | raise AttributeError(('"{0}" is not a valid assertion method' 106 | .format(attr))) 107 | 108 | def chained_assertion(*args, **kargs): 109 | assertion(*args, **kargs)(self.value) 110 | return self 111 | chained_assertion.assertion = assertion 112 | 113 | return chained_assertion 114 | 115 | def __call__(self, *assertions): 116 | for assertion in assertions: 117 | if not is_assertion(assertion): 118 | # Wrap non-verify assertions in Predicate for consistent 119 | # behavior. 120 | assertion = verify.Predicate(assertion) 121 | assertion(self.value) 122 | return self 123 | 124 | 125 | ensure = expect 126 | -------------------------------------------------------------------------------- /verify/types.py: -------------------------------------------------------------------------------- 1 | """Assertions related to types. 2 | """ 3 | 4 | import datetime 5 | 6 | import pydash 7 | 8 | from .base import Assertion, Comparator, Negate 9 | 10 | 11 | __all__ = ( 12 | 'Type', 13 | 'NotType', 14 | 'Boolean', 15 | 'NotBoolean', 16 | 'String', 17 | 'NotString', 18 | 'Dict', 19 | 'NotDict', 20 | 'List', 21 | 'NotList', 22 | 'Tuple', 23 | 'NotTuple', 24 | 'Date', 25 | 'NotDate', 26 | 'DateString', 27 | 'NotDateString', 28 | 'Int', 29 | 'NotInt', 30 | 'NotFloat', 31 | 'Float', 32 | 'Number', 33 | 'NotNumber', 34 | ) 35 | 36 | 37 | class Type(Comparator): 38 | """Asserts that `value` is an instance of `comparable`. 39 | 40 | Aliases: 41 | - ``to_be_type`` 42 | - ``is_type`` 43 | 44 | .. versionadded:: 0.0.1 45 | 46 | .. versionchanged:: 0.6.0 47 | Renamed from ``InstanceOf`` to ``Type`` 48 | """ 49 | #: 50 | reason = '{0} is not an instance of {comparable}' 51 | op = isinstance 52 | 53 | 54 | to_be_type = Type 55 | is_type = Type 56 | 57 | 58 | class NotType(Negate, Type): 59 | """Asserts that `value` is a not an instance of `comparable`. 60 | 61 | Aliases: 62 | - ``to_be_not_type`` 63 | - ``is_not_type`` 64 | 65 | .. versionadded:: 0.5.0 66 | 67 | .. versionchanged:: 0.6.0 68 | Renamed from ``NotInstanceOf`` to ``NotType`` 69 | """ 70 | #: 71 | reason = '{0} is an instance of {comparable}' 72 | 73 | 74 | to_not_be_type = NotType 75 | is_not_type = NotType 76 | 77 | 78 | class Boolean(Assertion): 79 | """Asserts that `value` is a boolean. 80 | 81 | Aliases: 82 | - ``to_be_boolean`` 83 | - ``is_boolean`` 84 | 85 | .. versionadded:: 0.1.0 86 | """ 87 | #: 88 | reason = '{0} is not a boolean' 89 | op = staticmethod(pydash.is_boolean) 90 | 91 | 92 | to_be_boolean = Boolean 93 | is_boolean = Boolean 94 | 95 | 96 | class NotBoolean(Negate, Boolean): 97 | """Asserts that `value` is a not a boolean. 98 | 99 | Aliases: 100 | - ``to_be_not_boolean`` 101 | - ``is_not_boolean`` 102 | 103 | .. versionadded:: 0.5.0 104 | """ 105 | #: 106 | reason = '{0} is a boolean' 107 | 108 | 109 | to_not_be_boolean = NotBoolean 110 | is_not_boolean = NotBoolean 111 | 112 | 113 | class String(Assertion): 114 | """Asserts that `value` is a string (``str`` or ``unicode`` on Python 2). 115 | 116 | Aliases: 117 | - ``to_be_string`` 118 | - ``is_string`` 119 | 120 | .. versionadded:: 0.1.0 121 | """ 122 | #: 123 | reason = '{0} is not a string' 124 | op = staticmethod(pydash.is_string) 125 | 126 | 127 | to_be_string = String 128 | is_string = String 129 | 130 | 131 | class NotString(Negate, String): 132 | """Asserts that `value` is a not a string. 133 | 134 | Aliases: 135 | - ``to_be_not_string`` 136 | - ``is_not_string`` 137 | 138 | .. versionadded:: 0.5.0 139 | """ 140 | #: 141 | reason = '{0} is a string' 142 | 143 | 144 | to_not_be_string = NotString 145 | is_not_string = NotString 146 | 147 | 148 | class Dict(Assertion): 149 | """Asserts that `value` is a dictionary. 150 | 151 | Aliases: 152 | - ``to_be_dict`` 153 | - ``is_dict`` 154 | 155 | .. versionadded:: 0.1.0 156 | """ 157 | #: 158 | reason = '{0} is not a dictionary' 159 | op = staticmethod(pydash.is_dict) 160 | 161 | 162 | to_be_dict = Dict 163 | is_dict = Dict 164 | 165 | 166 | class NotDict(Negate, Dict): 167 | """Asserts that `value` is a not a dict. 168 | 169 | Aliases: 170 | - ``to_be_not_dict`` 171 | - ``is_dict`` 172 | 173 | .. versionadded:: 0.5.0 174 | """ 175 | #: 176 | reason = '{0} is a dict' 177 | 178 | 179 | to_not_be_dict = NotDict 180 | is_not_dict = NotDict 181 | 182 | 183 | class List(Assertion): 184 | """Asserts that `value` is a list. 185 | 186 | Aliases: 187 | - ``to_be_list`` 188 | - ``is_list`` 189 | 190 | .. versionadded:: 0.1.0 191 | """ 192 | #: 193 | reason = '{0} is not a list' 194 | op = staticmethod(pydash.is_list) 195 | 196 | 197 | to_be_list = List 198 | is_list = List 199 | 200 | 201 | class NotList(Negate, List): 202 | """Asserts that `value` is a not a list. 203 | 204 | Aliases: 205 | - ``to_be_not_list`` 206 | - ``is_not_list`` 207 | 208 | .. versionadded:: 0.5.0 209 | """ 210 | #: 211 | reason = '{0} is a list' 212 | 213 | 214 | to_not_be_list = NotList 215 | is_not_list = NotList 216 | 217 | 218 | class Tuple(Assertion): 219 | """Asserts that `value` is a tuple. 220 | 221 | Aliases: 222 | - ``to_be_tuple`` 223 | - ``is_tuple`` 224 | 225 | .. versionadded:: 0.1.0 226 | """ 227 | #: 228 | reason = '{0} is not a tuple' 229 | op = staticmethod(pydash.is_tuple) 230 | 231 | 232 | to_be_tuple = Tuple 233 | is_tuple = Tuple 234 | 235 | 236 | class NotTuple(Negate, Tuple): 237 | """Asserts that `value` is a not a tuple. 238 | 239 | Aliases: 240 | - ``to_be_not_tuple`` 241 | - ``is_not_tuple`` 242 | 243 | .. versionadded:: 0.5.0 244 | """ 245 | #: 246 | reason = '{0} is a tuple' 247 | 248 | 249 | to_not_be_tuple = NotTuple 250 | is_not_tuple = NotTuple 251 | 252 | 253 | class Date(Assertion): 254 | """Asserts that `value` is an instance of ``datetime.date`` or 255 | ``datetime.datetime``. 256 | 257 | Aliases: 258 | - ``to_be_date`` 259 | - ``is_date`` 260 | 261 | .. versionadded:: 0.3.0 262 | """ 263 | #: 264 | reason = '{0} is not a date or datetime object' 265 | op = staticmethod(pydash.is_date) 266 | 267 | 268 | to_be_date = Date 269 | is_date = Date 270 | 271 | 272 | class NotDate(Negate, Date): 273 | """Asserts that `value` is a not a date or datetime object. 274 | 275 | Aliases: 276 | - ``to_be_not_date`` 277 | - ``is_not_date`` 278 | 279 | .. versionadded:: 0.5.0 280 | """ 281 | #: 282 | reason = '{0} is a date or datetime object' 283 | 284 | 285 | to_not_be_date = NotDate 286 | is_not_date = NotDate 287 | 288 | 289 | class DateString(Comparator): 290 | """Asserts that `value` is matches the datetime format string `comparable`. 291 | 292 | Aliases: 293 | - ``to_be_date_string`` 294 | - ``is_date_string`` 295 | 296 | .. versionadded:: 0.3.0 297 | """ 298 | #: 299 | reason = '{0} does not match the datetime format {comparable}' 300 | 301 | @staticmethod 302 | def op(value, comparable): 303 | try: 304 | datetime.datetime.strptime(value, comparable) 305 | return True 306 | except (TypeError, ValueError): 307 | return False 308 | 309 | 310 | to_be_date_string = DateString 311 | is_date_string = DateString 312 | 313 | 314 | class NotDateString(Negate, DateString): 315 | """Asserts that `value` does not match datetime format string `comparable`. 316 | 317 | Aliases: 318 | - ``to_be_not_date_string`` 319 | - ``is_not_date_string`` 320 | 321 | .. versionadded:: 0.5.0 322 | """ 323 | #: 324 | reason = '{0} matches the datetime format {comparable}' 325 | 326 | 327 | to_not_be_date_string = NotDateString 328 | is_not_date_string = NotDateString 329 | 330 | 331 | class Int(Assertion): 332 | """Asserts that `value` is an integer. 333 | 334 | Aliases: 335 | - ``to_be_int`` 336 | - ``is_int`` 337 | 338 | .. versionadded:: 0.1.0 339 | """ 340 | #: 341 | reason = '{0} is not an integer' 342 | op = staticmethod(pydash.is_integer) 343 | 344 | 345 | to_be_int = Int 346 | is_int = Int 347 | 348 | 349 | class NotInt(Negate, Int): 350 | """Asserts that `value` is a not an integer. 351 | 352 | Aliases: 353 | - ``to_be_not_int`` 354 | - ``is_not_int`` 355 | 356 | .. versionadded:: 0.5.0 357 | """ 358 | #: 359 | reason = '{0} is an integer' 360 | 361 | 362 | to_not_be_int = NotInt 363 | is_not_int = NotInt 364 | 365 | 366 | class Float(Assertion): 367 | """Asserts that `value` is a float. 368 | 369 | Aliases: 370 | - ``to_be_float`` 371 | - ``is_float`` 372 | 373 | .. versionadded:: 0.1.0 374 | """ 375 | #: 376 | reason = '{0} is not a float' 377 | op = staticmethod(pydash.is_float) 378 | 379 | 380 | to_be_float = Float 381 | is_float = Float 382 | 383 | 384 | class NotFloat(Negate, Float): 385 | """Asserts that `value` is a not a float. 386 | 387 | Aliases: 388 | - ``to_be_not_float`` 389 | - ``is_not_float`` 390 | 391 | .. versionadded:: 0.5.0 392 | """ 393 | #: 394 | reason = '{0} is a float' 395 | 396 | 397 | to_not_be_float = NotFloat 398 | is_not_float = NotFloat 399 | 400 | 401 | class Number(Assertion): 402 | """Asserts that `value` is a number. 403 | 404 | Objects considered a number are: 405 | 406 | - ``int`` 407 | - ``float`` 408 | - ``decimal.Decimal`` 409 | - ``long (Python 2)`` 410 | 411 | Aliases: 412 | - ``to_be_number`` 413 | - ``is_number`` 414 | 415 | .. versionadded:: 0.1.0 416 | """ 417 | #: 418 | reason = '{0} is not a number' 419 | op = staticmethod(pydash.is_number) 420 | 421 | 422 | to_be_number = Number 423 | is_number = Number 424 | 425 | 426 | class NotNumber(Negate, Number): 427 | """Asserts that `value` is a not a number. 428 | 429 | Aliases: 430 | - ``to_be_not_number`` 431 | - ``is_not_number`` 432 | 433 | .. versionadded:: 0.1.0 434 | 435 | .. versionchanged:: 0.5.0 436 | Renamed from ``NaN`` to ``NotNumber``. 437 | """ 438 | #: 439 | reason = '{0} is a number' 440 | 441 | 442 | to_not_be_number = NotNumber 443 | is_not_number = NotNumber 444 | --------------------------------------------------------------------------------