# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['pytest_embrace']

package_data = \
{'': ['*']}

install_requires = \
['pydantic>=1.9.1,<2.0.0', 'pyperclip>=1.8.2,<2.0.0', 'pytest>=7.0,<8.0']

extras_require = \
{':python_version == "3.8"': ['typing-extensions>=4.2.0,<5.0.0']}

entry_points = \
{'console_scripts': ['embrace = pytest_embrace.cli:main'],
 'pytest11': ['pytest_embrace = pytest_embrace.plugin']}

setup_kwargs = {
    'name': 'pytest-embrace',
    'version': '1.0.1',
    'description': '💝  Dataclasses-as-tests. Describe the runtime once and multiply coverage with no boilerplate.',
    'long_description': '# pytest-embrace :gift_heart:\n\n[![PyPI version](https://badge.fury.io/py/pytest-embrace.svg)](https://badge.fury.io/py/pytest-embrace) [![PyPI pyversions](https://img.shields.io/pypi/pyversions/pytest-embrace.svg)](https://pypi.python.org/pypi/pytest-embrace/) ![CI](https://github.com/ainsleymcgrath/pytest-embrace/actions/workflows/ci.yml/badge.svg)\n\nThe `pytest-embrace` plugin enables judicious, repeatable, lucid unit testing.\n\n> Reject boilerplate. Embrace complexity.\n\n[TOC]\n\n# Philosophy :bulb:\n\n1. [Table-oriented](https://dave.cheney.net/2019/05/07/prefer-table-driven-tests) ([parametrized](https://docs.pytest.org/en/6.2.x/parametrize.html)) tests are indespensible.\n\n2. Type hints and modern Python [dataclasses](https://docs.python.org/3/library/dataclasses.html) are very good.\n\n3. As sayeth the [Zen](https://peps.python.org/pep-0020/):\n   ```\n   Flat is better than nested.\n   Sparse is better than dense.\n   ...\n   There should be one-- and preferably only one --obvious way to do it.\n   ...\n   Namespaces are one honking great idea -- let\'s do more of those!\n   ```\n\n4. Code generation is *really* [underrated](https://github.com/copier-org/copier/).\n\n5. The wave of [type-driven](https://pydantic-docs.helpmanual.io/) [Python](https://github.com/beartype/beartype) [tools](https://typer.tiangolo.com/) [is](https://peps.python.org/pep-0593/) [very](https://github.com/python/mypy) cowabunga––and only just beginning :ocean:\n\n# Features :white_check_mark:\n\n- [x] Completely customizable test design\n- [x] Type hints everywhere\n- [x] Table-oriented testing\n- [x] Strongly-typed test namespaces-as-modules.\n- [x] Highly cusomizable code generation––powered by Pep 593\n- [x] Readable errors, early and often\n\n# Basic Usage :wave:\n\nLike any pytest plugin, `pytest-embrace` is configured in `conftest.py`.\n\nThe main ingredients are:\n\n1. The "case" –– which can be any class decorated with `builtins.dataclasses.dataclass`.\n2. The "runner" –– which is just a tricked out Pytest fixture to run assertions against your case.\n3. The "caller" –– which is is another tricked out fixture that your tests will use.\n\n```python\n# conftest.py\nfrom dataclasses import dataclass\nfrom typing import Callable\n\nfrom pytest_embrace import Embrace\n\n\n@dataclass\nclass Case:\n    arg: str\n    func: Callable\n    expect: str\n\n\nembrace = Embrace(Case)\n\n@embrace.register_case_runner\ndef run_simple(case: Case):\n    result = case.func(case.arg)\n    assert result == case.expect\n    return result\n\n\nsimple_case = embrace.caller_fixture_factory(\'simple_case\')\n```\n\nWith the above conftest, you can write tests like so:\n\n1. Make a module with attributes matching your `Embrace()`\'d object\n2. Request your caller fixture in normal Pytest fashion\n\n```python\n# test_func.py\narg = \'Ainsley\'\nfunc = lambda x: x * 2\nexpect = \'AinsleyAinsley\'\n\n\ndef test(simple_case):\n\t...\n```\n\nIf *"flat is better than nested,"* then it serves to reason that the flattest structure of all, the module, must be the best of all!\n\nOr you can go [table-oriented](https://dave.cheney.net/2019/05/07/prefer-table-driven-tests) and run many tests from one module––just like with `pytest.mark.parametrize`.\n\nWhen `pytest-embrace` sees a `table` attribute full of case objects, it will generate a test for each member of the list.\n\n```python\n# test_many_func.py\nfrom conftest import Case\n\ntable = [\n    Case(arg="haha", func=lambda x: x.upper(), expect="HAHA"),\n    Case(arg="wow damn", func=lambda x: len(x), expect=8),\n    Case(arg="sure", func=lambda x: hasattr(x, "beep"), expect=False),\n]\n\n\ndef test(simple_case):\n    ...\n```\n\n:point_up_2: This runs 3 independent tests.\n\n## Strongly Typed Namespaces :muscle:\n\nBefore even completing the setup phase of your `Embrace()`\'d tests, this plugin uses [Pydantic](https://pydantic-docs.helpmanual.io/) to validate the values set in your test modules. No type hints required.\n\nThat means there\'s no waiting around for expensive setups before catching simple mistakes.\n\n```python\n# given this case...\narg = "Ainsley"\nfunc = lambda x: x * 2\nexpect = b"AinsleyAinsley"\n\n\ndef test(simple_case):\n    ...\n```\n\nRunning the above immediately produces this error:\n\n```python\nE   pytest_embrace.exc.CaseConfigurationError: 1 invalid attr values in module \'test_wow\':\nE       Variable \'expect\' should be of type str\n```\n\nThe auxilary benefit of this feature is hardening the design of your code\'s interfaces––even interfaces that exist beyond the "vanishing point" of incoming data that you can\'t be certain of: Command line inputs, incoming HTTP requests, structured file inputs, etc.\n\nCases in `table` are checked as well.\n\n# Code Generation :robot:\n\nInstalling `pytest-embrace` adds a flag to `pytest` called `--embrace`.\n\nIt can be used to scaffold tests based on any of your registered cases.\n\nWith the example from above, you can do this out of the box:\n\n```shell\npytest --embrace simple_case\n```\n\nWhich puts this in your clipboard:\n\n```python\n# test_more.py\nfrom pytest_embrace import CaseArtifact\nfrom conftest import Case\n\narg: str\nfunc: "Callable"\nexpect: str\n\n\ndef test(simple_case: CaseArtifact[Case]):\n    ...\n```\n\nCopypasta\'d test cases like this can also be table-style: **[Coming soon!]**\n\n```shell\npytest --embrace-table 3\n```\n\nThe value passed to the `--embrace-table` flag will produce that many rows.\n\n```python\n# test_table_style.py\nfrom pytest_embrace import CaseArtifact\nfrom conftest import Case\n\ntable = [\n    # Case(arg=..., func=..., expect=...),\n    # Case(arg=..., func=..., expect=...),\n    # Case(arg=..., func=..., expect=...),\n]\n\ndef test(simple_case: CaseArtifact[Case]):\n    ...\n```\n\nBy default, each item is commented out so you don\'t end up with linter errors upon opening your new file.\n\nIf that\'s not cool, don\'t worry! It\'s configurable. :sunglasses:\n\n## Config With Pep 593 :star2:\n\nIn order to customize the behavior of your test cases, `pytest-embrace` :flushed: embraces :flushed:  the new `Annotated` type.\n\n> :information_source:\n> If you\'ve never heard of Pep 593 or `Annotated`, the **tl;dr** is that `Annotated[<type>, ...]` takes any number of arguments after the first one (the actual hint) that developers (me) can use at rumtime.\n>\n> **Also this only works on Python >3.8.**\n\nThe `pytest_embrace.anno` namespace provides a number of utilities for controlling test parsing and code generation via `Annotated`.\n\n### Comments in Generated Tests :pencil:\n\nHere\'s an example of using `anno.Comment` to put comments in generated test suites:\n\n```python\nfrom dataclasses import dataclass\n\nfrom pytest_embrace import Embrace, anno\n\n\n@dataclass\nclass AnnotatedCase:\n    name: Annotated[str, anno.Comment("This is ... pretty cool.")]\n\n\nembrace = Embrace(AnnotatedCase)\n\n\n@embrace.register_case_runner\ndef run(case: AnnotatedCase, fix: str) -> None:\n    pass\n\n\nanno_case = embrace.caller_fixture_factory("anno_case")\n```\n\nCalling the generation utility...\n\n```shell\npytest --embrace anno_case\n```\n\nGets you this:\n\n```python\nfrom pytest_embrace import CaseArtifact\n\nimport AnnotatedCase from conftest\n\n\nname: str  # This is ... pretty cool.\n\n\ndef test(anno_case: CaseArtifact[AnnotatedCase]) -> None:\n    ...\n```\n\n\n\n# Complexity :lollipop:\n\n## Table Cases vs Module Cases :balance_scale:\n\nAs we\'ve seen `pytest-embrace` can produce and execute 2 types of cases:\n\n1. *"Module cases"* where a test is a single file\n2. *"Table cases"* where the  `table` attribute generates as many tests as there are members of the `table` list.\n\nThere are pros and cons to each approach:\n\n|                          | **Table**                                                    | **Module**                                                   |\n| -----------------------: | ------------------------------------------------------------ | ------------------------------------------------------------ |\n|         **Pros** :smile: | Everything is in one place.<br /><br />The format is a bit more familiar to veteran `parametrize` users.<br /><br />Writing many cases in one file at once can be easier than jumping around. | It\'s easier to get going with module tests generated by `--embrace`.<br /><br />A directory full of tests is more discoverable than a big literal list of tests.<br /><br />You can select one test at a time with modules. |\n| **Cons** :frowning_face: | A gigantic list of cases can get out of hand and become very difficult to grok.<br /><br />Over time, if the expectation of 1 case diverges from its siblings, things can get messy.<br /><br />Repetitive tests (e.g. they all share similar default attributes) are ugly. | Many developers detest "death by 1000 files."<br /><br />Sharing configuration among similar tests requires finesse.<br /><br />Repetitive tests can be hard to name well and keep track of. |\n\nThis framework was born from several iterations of essentially the same concept across a couple repos at [Amper](https://www.amper.xyz/) and in my hobby projects. Some versions supported the table, some did not, and some did an opinionated mix––where certain module-level attributes could "trickle down" to cases or certain attributes were *only* allowed at the module level and applied to all table cases.\n\nTo address the module-vs-table tension, `pytest-embrace` provides a utility to design tests that freely mix table and module style: `trickles()`.\n\nJust use it as the default value for attributes of your case where you would like the module-level attribute to "trickle down" to cases in tables.\n\nBy default, table cases can set that attribute themselves to override the module value. If that\'s not ok, pass `no_override=True` and then overriding will throw an error.\n\n```python\nfrom dataclasses import dataclass\nimport pytest\n\nfrom pytest_embrace import  Embrace\nfrom pytest_embrace.case import CaseArtifact, trickles\n\n\n@dataclass\nclass TrickleCase:\n    snack: str\n    beverage: str = trickles()\n    ounces_of_beverage: int = trickles(no_override=True)\n\n\nembrace = Embrace(TrickleCase)\n\n\n@embrace.register_case_runner\ndef my_runner(case: TrickleCase) -> None:\n    pass\n\n\ntrickle_case = embrace.caller_fixture_factory("trickle_case")\n```\n\nAnd here\'s how that looks in a test:\n\n```python\nfrom conftest import TrickleCase\n\nbeverage = \'just water, thanks\'\nounces_of_beverage = 16\n\ntable = [\n    TrickleCase(snack=\'do you have any dates?\'),\n    TrickleCase(snack="I\'m stuffed! No thanks.", beverage=\'espresso\')\n]\n\ndef test(trickle_case):\n    bev = trickle_case.case.beverage\n    snack = trickle_case.case.snack\n\n    if bev == \'just water, thanks\':\n        assert snack == \'do you have any dates?\'\n    elif bev == \'espresso\':\n        assert snack == "I\'m stuffed! No thanks."\n```\n\nAnd this one would throw an error:\n\n```python\nfrom conftest import TrickleCase\n\n\ntable = [\n    TrickleCase(\n        snack="do you have any dates?",\n        ounces_of_beverage = 1\n    ),\n]\n\n\ndef test(trickle_case):\n    ...\n```\n\n## Config With Pep 593 :star2:\n\nWe\'ve already seen `anno.Comment()` for manipulating code generation.\n\nBut annotations can also control the setup phase of `Embrace()`\'d tests.\n\n### Deriving Attributes from Filenames :crystal_ball:\n\nThe `DeriveFromFileName` object in the `anno` namespace allows you to lift the determination of case attributes out of the case and *into the name of the test file.*\n\nHere\'s how it works:\n\n```python\n# conftest.py\nfrom dataclasses import dataclass\n\nfrom pytest_embrace import Embrace, anno\n\n\n@dataclass\nclass DeriveCase:\n    name: Annotated[str, anno.DeriveFromFileName()]\n\n\nembrace = Embrace(DeriveCase)\n\n\n@embrace.register_case_runner\ndef run(case: AnnotatedCase) -> None:\n    pass\n\n\nderive_case = embrace.caller_fixture_factory("derive_case")\n```\n\nThe default derivation simply extracts the substring after `test_` in your module\'s name.\n\nSo, using `derive_case`, you don\'t need to put `name` in the module.\n\n```python\n# test_something.py\nfrom conftest import DeriveCase\n\n\ndef test(derive_case: CaseArtifact[DeriveCase]):\n    assert derive_case.case.name == \'something\'\n```\n',
    'author': 'Ainsley McGrath',
    'author_email': 'mcgrath.ainsley@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': None,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'entry_points': entry_points,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
