Repository: codedawi/pytest-schema (language: python)

Issue:
Enum not working in schema validation
Thanks a lot for this cool repo, I have an issue whenever I want to use Enum, I'm running into a TypeError. I used eventually the example from the docs and even there it doesn't work as expected. 

```>>> def test_users_endpoint():
...     response = [
...         {
...             "id": 2,
...             "name": "Sue",
...             "age": 28,
...             "email": "sue@gmail.com",
...             "gender": "female",
...             "role": "admin",
...             "friends": [5, 6],
...             "address": {
...                 "street": "123 Washington Ave.",
...                 "city": "New York",
...                 "state": "NY",
...                 "zipcode": "099012",
...             }
...         },
...         {
...             "id": 5,
...             "name": "Sam",
...             "age": 42,
...             "email": "sam@aol.com",
...             "role": "user",
...             "friends": [2, 6, 7],
...             "address": {
...                 "street": "5 Sunset St.",
...                 "street2": "Apt # 55-b",
...                 "city": "San Jose",
...                 "state": "CA",
...                 "zipcode": "054053",
...             }
...         }]
...     assert schema(users) == response
...
>>> test_users_endpoint()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 33, in test_users_endpoint
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/pytest_schema/schema.py", line 45, in __eq__
    return bool(self.validate(value))
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/schema.py", line 366, in validate
    return type(data)(o.validate(d, **kwargs) for d in data)
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/schema.py", line 366, in <genexpr>
    return type(data)(o.validate(d, **kwargs) for d in data)
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/schema.py", line 169, in validate
    [self._error.format(data) if self._error else None] + errors,
schema.SchemaError: Or({'id': <class 'int'>, 'name': <class 'str'>, 'description': Or(None, <class 'str'>), 'email': Regex('.*?@.*?\\.[A-Za-z]{2,6}'), 'age': And(<class 'int'>, <function <lambda> at 0x7fe8e66ef598>), Optional('gender'): <class 'str'>, 'role': Enum(['user', 'super-user', 'admin']), 'friends': [<class 'int'>], 'address': {'street': <class 'str'>, Optional('street2'): <class 'str'>, 'city': <class 'str'>, 'state': And(<class 'str'>, <function <lambda> at 0x7fe8e66ef620>), 'zipcode': <class 'str'>}}) did not validate {'id': 2, 'name': 'Sue', 'age': 28, 'email': 'sue@gmail.com', 'gender': 'female', 'role': 'admin', 'friends': [5, 6], 'address': {'street': '123 Washington Ave.', 'city': 'New York', 'state': 'NY', 'zipcode': '099012'}}
Key 'role' error:
Enum(['user', 'super-user', 'admin']).validate('admin') raised TypeError("__init__() got an unexpected keyword argument 'error'",)```
Enum not working in schema validation
Thanks a lot for this cool repo, I have an issue whenever I want to use Enum, I'm running into a TypeError. I used eventually the example from the docs and even there it doesn't work as expected. 

```>>> def test_users_endpoint():
...     response = [
...         {
...             "id": 2,
...             "name": "Sue",
...             "age": 28,
...             "email": "sue@gmail.com",
...             "gender": "female",
...             "role": "admin",
...             "friends": [5, 6],
...             "address": {
...                 "street": "123 Washington Ave.",
...                 "city": "New York",
...                 "state": "NY",
...                 "zipcode": "099012",
...             }
...         },
...         {
...             "id": 5,
...             "name": "Sam",
...             "age": 42,
...             "email": "sam@aol.com",
...             "role": "user",
...             "friends": [2, 6, 7],
...             "address": {
...                 "street": "5 Sunset St.",
...                 "street2": "Apt # 55-b",
...                 "city": "San Jose",
...                 "state": "CA",
...                 "zipcode": "054053",
...             }
...         }]
...     assert schema(users) == response
...
>>> test_users_endpoint()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 33, in test_users_endpoint
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/pytest_schema/schema.py", line 45, in __eq__
    return bool(self.validate(value))
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/schema.py", line 366, in validate
    return type(data)(o.validate(d, **kwargs) for d in data)
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/schema.py", line 366, in <genexpr>
    return type(data)(o.validate(d, **kwargs) for d in data)
  File "/opt/apps/k2view/.local/lib/python3.6/site-packages/schema.py", line 169, in validate
    [self._error.format(data) if self._error else None] + errors,
schema.SchemaError: Or({'id': <class 'int'>, 'name': <class 'str'>, 'description': Or(None, <class 'str'>), 'email': Regex('.*?@.*?\\.[A-Za-z]{2,6}'), 'age': And(<class 'int'>, <function <lambda> at 0x7fe8e66ef598>), Optional('gender'): <class 'str'>, 'role': Enum(['user', 'super-user', 'admin']), 'friends': [<class 'int'>], 'address': {'street': <class 'str'>, Optional('street2'): <class 'str'>, 'city': <class 'str'>, 'state': And(<class 'str'>, <function <lambda> at 0x7fe8e66ef620>), 'zipcode': <class 'str'>}}) did not validate {'id': 2, 'name': 'Sue', 'age': 28, 'email': 'sue@gmail.com', 'gender': 'female', 'role': 'admin', 'friends': [5, 6], 'address': {'street': '123 Washington Ave.', 'city': 'New York', 'state': 'NY', 'zipcode': '099012'}}
Key 'role' error:
Enum(['user', 'super-user', 'admin']).validate('admin') raised TypeError("__init__() got an unexpected keyword argument 'error'",)```


Discussion / hints:
My apologies just seeing this now. I will look into it!
My apologies just seeing this now. I will look into it!

Fix patch:
diff --git a/README.md b/README.md
index 5c25d6d..09958b7 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ pip install pytest-schema
 
 Here is a quick example of using **`schema`**:
 ```python
-from pytest_schema import schema
+from pytest_schema import schema, exact, like
 
 article_v1 = {
     "id": int,
@@ -34,13 +34,26 @@ article_v1 = {
 
 def test_article_v1_endpoint(test_client):
     """
-    Test calling a article endpoint and validating its
-    response for a article is correctly formatted.
+    Test calling v1 endpoint and validating the response
+    is in the correctly/expected format.
     """
+    response_v1 = test_client.get("/api/v1/article/1")
+    assert exact(article_v1) == response_v1
+    # Same as:
+    # assert schema(article_v1) == response_v1
+
+article_v2 = {
+    **article_v1,
+    "someNewField": int
+}
 
-    response = test_client.get("/api/v1/article/1")
+def test_article_v2_endpoint(test_client):
+    """
+    Test calling v2 endpoint is backwards compatible with v1
+    """
+    response_v2 = test_client.get("/api/v2/article/1")
 
-    assert schema(article_v1) == response
+    assert like(article_v1) == value
 
 ```
 ## Full Example


Test patch:
diff --git a/src/pytest_schema/__init__.py b/src/pytest_schema/__init__.py
index 4e88550..27a2a14 100644
--- a/src/pytest_schema/__init__.py
+++ b/src/pytest_schema/__init__.py
@@ -17,6 +17,8 @@
     "schema",
     "like_schema",
     "exact_schema",
+    "like",
+    "exact",
     "And",
     "Enum",
     "Forbidden",
diff --git a/src/pytest_schema/helpers.py b/src/pytest_schema/helpers.py
index 6a96146..8432da0 100644
--- a/src/pytest_schema/helpers.py
+++ b/src/pytest_schema/helpers.py
@@ -1,12 +1,12 @@
-from typing import Any
+from typing import Any, List
+from schema import Or
 
 from pytest_schema.schema import Schema
 
 
 def schema(value: Any, **kargs) -> Schema:
     """
-    Helper to create an Schema class, accepting
-    all kargs to configure underlying Schema class.
+    Helper to create an Schema class, accepting all kargs to configure underlying Schema class.
 
     Args:
         value (Any): schema value to validation data against
@@ -60,3 +60,39 @@ def like_schema(value: Any) -> Schema:
         ✅ assert like_schema({ "status": int }) == {"status": 404, "timestamp": 1594358256}
     """
     return schema(value, ignore_extra_keys=True)
+
+
+def like(value: Any) -> Schema:
+    """
+    Helper to create an Schema class with non exact match requirements.
+
+    Args:
+        value (Any): schema value to validation data against
+
+    Returns:
+        Schema: initialized and configured class with non exact match requirements
+
+    Example:
+        ✅ assert like({ "status": int }) == {"status": 404}
+        ❌ assert like({ "status": int }) == {"status": "404"}
+        ✅ assert like({ "status": int }) == {"status": 404, "timestamp": 1594358256}
+    """
+    return schema(value, ignore_extra_keys=True)
+
+
+def exact(value: Any) -> Schema:
+    """
+    Helper to create an Schema class with exact match requirements.
+
+    Args:
+        value (Any): schema value to validation data against
+
+    Returns:
+        Schema: initialized and configured class with exact match requirements
+
+    Example:
+        ✅ assert exact({ "status": int }) == {"status": 404}
+        ❌ assert exact({ "status": int }) == {"status": "404"}
+        ❌ assert exact({ "status": int }) == {"status": 404, "timestamp": 1594358256}
+    """
+    return schema(value, ignore_extra_keys=True)
\ No newline at end of file
diff --git a/src/pytest_schema/types.py b/src/pytest_schema/types.py
index 8f368ea..189eff0 100644
--- a/src/pytest_schema/types.py
+++ b/src/pytest_schema/types.py
@@ -1,25 +1,24 @@
 from schema import And, Forbidden, Hook, Literal, Optional, Or, Regex, Use
 
-from pytest_schema.schema import Schema
 
-
-class Enum(Schema):
-    """Simple interface to create Enum like schema."""
-
-    def __init__(self, *value):
-        """
-        Initialize schema with list or tuple like values.
-
-        Args:
-            *values (List[Any]): values in enum.
-
-        Example:
-
-            Enum([0, 1, 1, 2, 3, 5, 8])
-            Enum(["red", "blue", "green"])
-
-        """
-        if len(value) == 1 and hasattr(value[0], "__iter__"):
-            value = [v for v in value[0]]
-
-        super().__init__(value, ignore_extra_keys=False)
+__all__ = [
+    "And",
+    "Forbidden",
+    "Hook",
+    "Literal",
+    "Optional",
+    "Or",
+    "Regex",
+    "Use",
+    "Enum"
+]
+
+class Enum(Or):
+    """
+    Patch interface of `Or` class with plans to expand the
+    interface to support different Enum-like workflows.
+
+    TODO: Support List[Any] constructor input
+    TODO: Support python Enum constructor input,
+        with value or name option.
+    """
\ No newline at end of file
diff --git a/tests/test_enum.py b/tests/test_enum.py
new file mode 100644
index 0000000..c00e670
--- /dev/null
+++ b/tests/test_enum.py
@@ -0,0 +1,26 @@
+import pytest
+
+from pytest_schema import schema, Enum, SchemaError
+
+
+@pytest.mark.parametrize(
+    "values, expected",
+    [
+        (["red", "blue", "green"], "red"),
+        ([1, 2, 3], 3),
+    ],
+)
+def test_enum_valid(values, expected):
+    assert schema(Enum(*values)) == expected
+
+
+@pytest.mark.parametrize(
+    "values, expected",
+    [
+        (["red", "blue", "green"], "yellow"),
+        ([1, 2, 3], 5),
+    ],
+)
+def test_enum_not_valid(values, expected):
+    with pytest.raises(SchemaError):
+        assert schema(Enum(*values)) == expected