{"id":13486581,"url":"https://github.com/kislyuk/ensure","last_synced_at":"2025-04-05T05:07:30.010Z","repository":{"id":11162577,"uuid":"13535120","full_name":"kislyuk/ensure","owner":"kislyuk","description":"Validate conditions, Python style.","archived":false,"fork":false,"pushed_at":"2023-12-10T03:32:50.000Z","size":2793,"stargazers_count":150,"open_issues_count":2,"forks_count":18,"subscribers_count":9,"default_branch":"main","last_synced_at":"2024-10-13T02:09:29.209Z","etag":null,"topics":["bdd","expect","literate-programming","literate-testing","python-test","python-unittest"],"latest_commit_sha":null,"homepage":"https://kislyuk.github.io/ensure","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kislyuk.png","metadata":{"files":{"readme":"README.rst","changelog":"Changes.rst","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2013-10-13T07:11:53.000Z","updated_at":"2024-10-07T22:09:37.000Z","dependencies_parsed_at":"2024-01-16T09:01:04.292Z","dependency_job_id":"804a94c3-195c-4f33-b8ad-46c2493244a7","html_url":"https://github.com/kislyuk/ensure","commit_stats":{"total_commits":203,"total_committers":8,"mean_commits":25.375,"dds":"0.13793103448275867","last_synced_commit":"ffa68ef7ae18cbcdda1ec35a70a270af6f793703"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kislyuk%2Fensure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kislyuk%2Fensure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kislyuk%2Fensure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kislyuk%2Fensure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kislyuk","download_url":"https://codeload.github.com/kislyuk/ensure/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289428,"owners_count":20914464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bdd","expect","literate-programming","literate-testing","python-test","python-unittest"],"created_at":"2024-07-31T18:00:48.620Z","updated_at":"2025-04-05T05:07:29.984Z","avatar_url":"https://github.com/kislyuk.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"ensure: Literate assertions in Python\n=====================================\n*ensure* is a set of simple assertion helpers that let you write more expressive, literate, concise, and readable\nPythonic code for validating conditions. It's inspired by `should.js \u003chttps://github.com/shouldjs/should.js\u003e`_,\n`expect.js \u003chttps://github.com/Automattic/expect.js\u003e`_, and builds on top of the\n`unittest/JUnit assert helpers \u003chttp://docs.python.org/2/library/unittest.html#assert-methods\u003e`_.\n\nIf you use Python 3, you can use *ensure* to enforce your **function signature annotations**: see\n`PEP 3107 \u003chttp://www.python.org/dev/peps/pep-3107/\u003e`_ and the\n@ensure_annotations decorator below.\n\nBecause *ensure* is fast, is a standalone library (not part of a test framework), doesn't monkey-patch anything or use DSLs,\nand doesn't use the assert statement (which is liable to be turned off with the ``-O`` flag), it can be used to validate\nconditions in production code, not just for testing (though it certainly works as a BDD test utility library).\n\nAside from better looking code, a big reason to use *ensure* is that it provides more consistent, readable, and\ninformative error messages when things go wrong. See\n`Motivation and Goals \u003chttps://github.com/kislyuk/ensure#motivation-and-goals\u003e`_ for more.\n\nInstallation\n------------\n::\n\n    pip install ensure\n\nSynopsis\n--------\n\n.. code-block:: python\n\n    from ensure import ensure\n\n    ensure(1).is_an(int)\n    ensure({1: {2: 3}}).equals({1: {2: 3}}).also.contains(1)\n    ensure({1: \"a\"}).has_key(1).whose_value.has_length(1)\n    ensure.each_of([{1: 2}, {3: 4}]).is_a(dict).of(int).to(int)\n    ensure(int).called_with(\"1100101\", base=2).returns(101)\n    ensure(dict).called_with(1, 2).raises(TypeError)\n    check(1).is_a(float).or_raise(Exception, \"An error happened: {msg}. See http://example.com for more information.\")\n\nIn Python 3:\n\n.. code-block:: python\n\n    from ensure import ensure_annotations\n\n    @ensure_annotations\n    def f(x: int, y: float) -\u003e float:\n        return x+y\n\nSee **More examples** below.\n\nNotes\n~~~~~\nThe ``ensure`` module exports the ``Ensure`` class and its convenience instance ``ensure``. Instances of the class are\ncallable, and the call will reset the contents that the instance is inspecting, so you can reuse it for many checks (as\nseen above).\n\nThe class raises ``EnsureError`` (a subclass of ``AssertionError``) by default.\n\nThere are several ways to **chain clauses**, depending on the grammatical context: ``.also``, ``.which``, and\n``.whose_value`` are available per examples below.\n\nRaising custom exceptions\n~~~~~~~~~~~~~~~~~~~~~~~~~\nYou can pass a callable or exception class as the ``error_factory`` keyword argument to ``Ensure()``, or you can use the\n``Check`` class or its convenience instance ``check()``. This class behaves like ``Ensure``, but does not raise errors\nimmediately. It saves them and chains the methods ``otherwise()``, ``or_raise()`` and ``or_call()`` to the end of the\nclauses.\n\n.. code-block:: python\n\n    from ensure import check\n\n    check(\"w00t\").is_an(int).or_raise(Exception)\n    check(1).is_a(float).or_raise(Exception, \"An error happened: {msg}. See http://example.com for more information.\")\n    check(\"w00t\").is_an(int).or_raise(MyException, 1, 2, x=3, y=4)\n\n.. code-block:: python\n\n    def build_fancy_exception(original_exception):\n        return MyException(original_exception)\n\n    check(\"w00t\").is_an(int).otherwise(build_fancy_exception)\n    check(\"w00t\").is_an(int).or_call(build_fancy_exception, *args, **kwargs)\n\nMore examples\n-------------\n\n.. code-block:: python\n\n    ensure({1: {2: 3}}).is_not_equal_to({1: {2: 4}})\n    ensure(True).does_not_equal(False)\n    ensure(1).is_in(range(10))\n    ensure(True).is_a(bool)\n    ensure(True).is_(True)\n    ensure(True).is_not(False)\n\n.. code-block:: python\n\n    ensure([\"train\", \"boat\"]).contains_one_of([\"train\"])\n    ensure(range(8)).contains(5)\n    ensure([\"spam\"]).contains_none_of([\"eggs\", \"ham\"])\n    ensure(\"abcdef\").contains_some_of(\"abcxyz\")\n    ensure(\"abcdef\").contains_one_or_more_of(\"abcxyz\")\n    ensure(\"abcdef\").contains_all_of(\"acf\")\n    ensure(\"abcd\").contains_only(\"dcba\")\n    ensure(\"abc\").does_not_contain(\"xyz\")\n    ensure([1, 2, 3]).contains_no(float)\n    ensure(1).is_in(range(10))\n    ensure(\"z\").is_not_in(\"abc\")\n    ensure(None).is_not_in([])\n    ensure(dict).has_attribute('__contains__').which.is_callable()\n    ensure({1: \"a\", 2: \"b\", 3: \"c\"}).has_keys([1, 2])\n    ensure({1: \"a\", 2: \"b\"}).has_only_keys([1, 2])\n\n.. code-block:: python\n\n    ensure(1).is_true()\n    ensure(0).is_false()\n    ensure(None).is_none()\n    ensure(1).is_not_none()\n    ensure(\"\").is_empty()\n    ensure([1, 2]).is_nonempty().also.has_length(2)\n    ensure(1.1).is_a(float).which.equals(1.10)\n    ensure(KeyError()).is_an(Exception)\n    ensure({x: str(x) for x in range(5)}).is_a_nonempty(dict).of(int).to(str)\n    ensure({}).is_an_empty(dict)\n    ensure(None).is_not_a(list)\n\n.. code-block:: python\n\n    import re\n    ensure(\"abc\").matches(\"A\", flags=re.IGNORECASE)\n    ensure([1, 2, 3]).is_an_iterable_of(int)\n    ensure([1, 2, 3]).is_a_list_of(int)\n    ensure({1, 2, 3}).is_a_set_of(int)\n    ensure({1: 2, 3: 4}).is_a_mapping_of(int).to(int)\n    ensure({1: 2, 3: 4}).is_a_dict_of(int).to(int)\n    ensure({1: 2, 3: 4}).is_a(dict).of(int).to(int)\n    ensure(10**100).is_numeric()\n    ensure(lambda: 1).is_callable()\n    ensure(\"abc\").has_length(3)\n    ensure(\"abc\").has_length(min=3, max=8)\n    ensure(1).is_greater_than(0)\n    ensure(1).exceeds(0)\n    ensure(0).is_less_than(1)\n    ensure(1).is_greater_than_or_equal_to(1)\n    ensure(0).is_less_than_or_equal_to(0)\n    ensure(1).is_positive()\n    ensure(1.1).is_a_positive(float)\n    ensure(-1).is_negative()\n    ensure(-1).is_a_negative(int)\n    ensure(0).is_nonnegative()\n    ensure(0).is_a_nonnegative(int)\n    ensure([1,2,3]).is_sorted()\n\n.. code-block:: python\n\n    ensure(\"{x} {y}\".format).called_with(x=1, y=2).equals(\"1 2\")\n    ensure(int).called_with(\"1100101\", base=2).returns(101)\n    ensure(\"{x} {y}\".format).with_args(x=1, y=2).is_a(str)\n    with ensure().raises(ZeroDivisionError):\n        1/0\n    with ensure().raises_regex(NameError, \"'w00t' is not defined\"):\n        w00t\n\nSee `complete API documentation \u003chttps://kislyuk.github.io/ensure/#module-ensure\u003e`_.\n\nEnforcing function annotations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nUse the ``@ensure_annotations`` decorator to enforce\n`function signature annotations \u003chttp://www.python.org/dev/peps/pep-3107/\u003e`_:\n\n.. code-block:: python\n\n    from ensure import ensure_annotations\n\n    @ensure_annotations\n    def f(x: int, y: float) -\u003e float:\n        return x+y\n\n    f(1, 2.3)\n\n::\n\n    \u003e\u003e\u003e 3.3\n\n.. code-block:: python\n\n    f(1, 2)\n\n::\n\n    \u003e\u003e\u003e ensure.EnsureError: Argument y to \u003cfunction f at 0x109b7c710\u003e does not match annotation type \u003cclass 'float'\u003e\n\nCompare this runtime type checking to compile-time checking in `Mypy \u003chttp://www.mypy-lang.org/\u003e`_ and `type hinting in PEP 484/Python 3.5+ \u003chttps://www.python.org/dev/peps/pep-0484/\u003e`_.\n\nMotivation and goals\n~~~~~~~~~~~~~~~~~~~~\nMany BDD assertion libraries suffer from an excess of magic, or end up having to construct statements that don't parse\nas English easily. *ensure* is deliberately kept simple to avoid succumbing to either issue. The\n`source \u003chttps://github.com/kislyuk/ensure/blob/master/ensure/__init__.py\u003e`_ is easy to read and extend.\n\nWork remains to make error messages raised by *ensure* even more readable, informative, and consistent. Going forward,\nability to introspect exceptions to extract structured error information will be a major development\nfocus. You will be in control of how much information is presented in each error, which context it's thrown from, and\nwhat introspection capabilities the exception object will have.\n\nThe original use case for *ensure* is as an I/O validation helper for API endpoints, where the client needs to be sent a\nvery clear message about what went wrong, some structured information (such as an HTTP error code and machine-readable\nreference to a failing element) may need to be added, and some information may need to be hidden from the client. To\nfurther improve on that, we will work on better error translation, marshalling, message formatting, and schema\nvalidation helpers.\n\nAuthors\n-------\n* Andrey Kislyuk\n* Harrison Metzger\n\nLinks\n-----\n* `Project home page (GitHub) \u003chttps://github.com/kislyuk/ensure\u003e`_\n* `Documentation \u003chttps://kislyuk.github.io/ensure/\u003e`_\n* `Package distribution (PyPI) \u003chttps://pypi.python.org/pypi/ensure\u003e`_\n\nBugs\n~~~~\nPlease report bugs, issues, feature requests, etc. on `GitHub \u003chttps://github.com/kislyuk/ensure/issues\u003e`_.\n\nLicense\n-------\nLicensed under the terms of the `Apache License, Version 2.0 \u003chttp://www.apache.org/licenses/LICENSE-2.0\u003e`_.\n\n.. image:: https://github.com/kislyuk/ensure/workflows/CI/badge.svg\n        :target: https://github.com/kislyuk/ensure/actions\n.. image:: https://codecov.io/github/kislyuk/ensure/coverage.svg?branch=master\n        :target: https://codecov.io/github/kislyuk/ensure?branch=master\n.. image:: https://img.shields.io/pypi/v/ensure.svg\n        :target: https://pypi.python.org/pypi/ensure\n.. image:: https://img.shields.io/pypi/l/ensure.svg\n        :target: https://pypi.python.org/pypi/ensure\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkislyuk%2Fensure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkislyuk%2Fensure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkislyuk%2Fensure/lists"}