Hello community,

here is the log from the commit of package python-pampy for openSUSE:Factory 
checked in at 2020-03-11 18:55:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pampy (Old)
 and      /work/SRC/openSUSE:Factory/.python-pampy.new.3160 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pampy"

Wed Mar 11 18:55:36 2020 rev:2 rq:783882 version:0.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pampy/python-pampy.changes        
2019-01-24 14:11:26.763498723 +0100
+++ /work/SRC/openSUSE:Factory/.python-pampy.new.3160/python-pampy.changes      
2020-03-11 18:56:46.759713324 +0100
@@ -1,0 +2,9 @@
+Wed Mar 11 14:44:24 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com>
+
+- update to version 0.3.0
+  * Add type annotations support for matching
+  * Add support for callables which return Tuple[bool, List]
+  * Make match_value not depend on itself and add datetime example
+  * Add Enum support for matching
+
+-------------------------------------------------------------------

Old:
----
  pampy-0.2.1.tar.gz

New:
----
  pampy-0.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pampy.spec ++++++
--- /var/tmp/diff_new_pack.B4YgkH/_old  2020-03-11 18:56:47.143713496 +0100
+++ /var/tmp/diff_new_pack.B4YgkH/_new  2020-03-11 18:56:47.147713498 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-pampy
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %define skip_python2 1
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-pampy
-Version:        0.2.1
+Version:        0.3.0
 Release:        0
 Summary:        An alternate pattern matching for Python
 License:        MIT

++++++ pampy-0.2.1.tar.gz -> pampy-0.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/PKG-INFO new/pampy-0.3.0/PKG-INFO
--- old/pampy-0.2.1/PKG-INFO    2018-12-24 15:08:24.000000000 +0100
+++ new/pampy-0.3.0/PKG-INFO    2019-11-07 16:50:51.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pampy
-Version: 0.2.1
+Version: 0.3.0
 Summary: The Pattern Matching for Python you always dreamed of
 Home-page: https://github.com/santinic/pampy
 Author: Claudio Santini
@@ -162,6 +162,86 @@
         match(pet, Pet(_, _), lambda name, age: (name, age))            # => 
('rover', 7)
         ```
         
+        ## Using typing
+        Pampy supports typing annotations.
+        
+        ```python
+        
+        class Pet:          pass
+        class Dog(Pet):     pass
+        class Cat(Pet):     pass
+        class Hamster(Pet): pass
+        
+        timestamp = NewType("year", Union[int, float])
+        
+        def annotated(a: Tuple[int, float], b: str, c: E) -> timestamp:
+            pass
+        
+        match((1, 2), Tuple[int, int], lambda a, b: (a, b))             # => 
(1, 2)
+        match(1, Union[str, int], lambda x: x)                          # => 1
+        match('a', Union[str, int], lambda x: x)                        # => 
'a'
+        match('a', Optional[str], lambda x: x)                          # => 
'a'
+        match(None, Optional[str], lambda x: x)                         # => 
None
+        match(Pet, Type[Pet], lambda x: x)                              # => 
Pet
+        match(Cat, Type[Pet], lambda x: x)                              # => 
Cat
+        match(Dog, Any, lambda x: x)                                    # => 
Dog
+        match(Dog, Type[Any], lambda x: x)                              # => 
Dog
+        match(15, timestamp, lambda x: x)                               # => 15
+        match(10.0, timestamp, lambda x: x)                             # => 
10.0
+        match([1, 2, 3], List[int], lambda x: x)                        # => 
[1, 2, 3]
+        match({'a': 1, 'b': 2}, Dict[str, int], lambda x: x)            # => 
{'a': 1, 'b': 2}
+        match(annotated, 
+            Callable[[Tuple[int, float], str, Pet], timestamp], lambda x: x
+        )                                                               # => 
annotated
+        ```
+        For iterable generics actual type of value is guessed based on the 
first element. 
+        ```python
+        match([1, 2, 3], List[int], lambda x: x)                        # => 
[1, 2, 3]
+        match([1, "b", "a"], List[int], lambda x: x)                    # => 
[1, "b", "a"]
+        match(["a", "b", "c"], List[int], lambda x: x)                  # 
raises MatchError
+        match(["a", "b", "c"], List[Union[str, int]], lambda x: x)      # 
["a", "b", "c"]
+        
+        match({"a": 1, "b": 2}, Dict[str, int], lambda x: x)            # 
{"a": 1, "b": 2}
+        match({"a": 1, "b": "dog"}, Dict[str, int], lambda x: x)        # 
{"a": 1, "b": "dog"}
+        match({"a": 1, 1: 2}, Dict[str, int], lambda x: x)              # 
{"a": 1, 1: 2}
+        match({2: 1, 1: 2}, Dict[str, int], lambda x: x)                # 
raises MatchError
+        match({2: 1, 1: 2}, Dict[Union[str, int], int], lambda x: x)    # {2: 
1, 1: 2}
+        ```
+        Iterable generics also match with any of their subtypes.
+        ```python
+        match([1, 2, 3], Iterable[int], lambda x: x)                     # => 
[1, 2, 3]
+        match({1, 2, 3}, Iterable[int], lambda x: x)                     # => 
{1, 2, 3}
+        match(range(10), Iterable[int], lambda x: x)                     # => 
range(10)
+        
+        match([1, 2, 3], List[int], lambda x: x)                         # => 
[1, 2, 3]
+        match({1, 2, 3}, List[int], lambda x: x)                         # => 
raises MatchError
+        match(range(10), List[int], lambda x: x)                         # => 
raises MatchError
+        
+        match([1, 2, 3], Set[int], lambda x: x)                          # => 
raises MatchError
+        match({1, 2, 3}, Set[int], lambda x: x)                          # => 
{1, 2, 3}
+        match(range(10), Set[int], lambda x: x)                          # => 
raises MatchError
+        ```
+        For Callable any arg without annotation treated as Any. 
+        ```python
+        def annotated(a: int, b: int) -> float:
+            pass
+            
+        def not_annotated(a, b):
+            pass
+            
+        def partially_annotated(a, b: float):
+            pass
+        
+        match(annotated, Callable[[int, int], float], lambda x: x)     # => 
annotated
+        match(not_annotated, Callable[[int, int], float], lambda x: x) # => 
raises MatchError
+        match(not_annotated, Callable[[Any, Any], Any], lambda x: x)   # => 
not_annotated
+        match(annotated, Callable[[Any, Any], Any], lambda x: x)       # => 
raises MatchError
+        match(partially_annotated, 
+            Callable[[Any, float], Any], lambda x: x
+        )                                                              # => 
partially_annotated
+        ```
+        TypeVar is not supported.
+        
         ## All the things you can match
         
         As Pattern you can use any Python type, any class, or any Python value.
@@ -191,6 +271,15 @@
         | `{'type':'dog', age: int }` | Any dict with `type: "dog"` and with 
an `int` age | `{"type":"dog", "age": 3}` | `3` | `{"type":"dog", "age":2.3}` |
         | `re.compile('(\w+)-(\w+)-cat$')` | Any string that matches that 
regular expression expr | `"my-fuffy-cat"` | `"my"` and `"puffy"` | 
`"fuffy-dog"` | 
         | `Pet(name=_, age=7)` | Any Pet dataclass with `age == 7` | 
`Pet('rover', 7)` | `['rover']` | `Pet('rover', 8)` |
+        | `Any` | The same as `_` | | that value | |
+        | `Union[int, float, None]` | Any integer or float number or None | 
`2.35` | `2.35` | any other value |
+        | `Optional[int]` | The same as `Union[int, None]` | `2` | `2` | any 
other value |
+        | `Type[MyClass]` | Any subclass of MyClass. **And any class that 
extends MyClass.** | `MyClass` | that class | any other object |
+        | `Callable[[int], float]` | Any callable with exactly that signature 
| `def a(q:int) -> float: ...` | that function | `def a(q) -> float: ...` |
+        | `Tuple[MyClass, int, float]` | The same as `(MyClass, int, float)` | 
| | |
+        | `Mapping[str, int]` Any subtype of `Mapping` acceptable too | any 
mapping or subtype of mapping with string keys and integer values | `{'a': 2, 
'b': 3}` | that dict | `{'a': 'b', 'b': 'c'}` |
+        | `Iterable[int]` Any subtype of `Iterable` acceptable too | any 
iterable or subtype of iterable with integer values | `range(10)` and `[1, 2, 
3]` | that iterable | `['a', 'b', 'v']` |
+        
         
         ## Using default
         
@@ -234,7 +323,7 @@
         or
         ```$ pip3 install pampy```
         
-        ## If you really must to use Python2
+        ## If you really must use Python2
         Pampy is Python3-first, but you can use most of its features in 
Python2 via [this backport](https://pypi.org/project/backports.pampy/) by 
Manuel Barkhau:
         
         ```pip install backports.pampy```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/README.md new/pampy-0.3.0/README.md
--- old/pampy-0.2.1/README.md   2018-12-24 14:50:14.000000000 +0100
+++ new/pampy-0.3.0/README.md   2019-07-13 13:23:08.000000000 +0200
@@ -154,6 +154,86 @@
 match(pet, Pet(_, _), lambda name, age: (name, age))            # => ('rover', 
7)
 ```
 
+## Using typing
+Pampy supports typing annotations.
+
+```python
+
+class Pet:          pass
+class Dog(Pet):     pass
+class Cat(Pet):     pass
+class Hamster(Pet): pass
+
+timestamp = NewType("year", Union[int, float])
+
+def annotated(a: Tuple[int, float], b: str, c: E) -> timestamp:
+    pass
+
+match((1, 2), Tuple[int, int], lambda a, b: (a, b))             # => (1, 2)
+match(1, Union[str, int], lambda x: x)                          # => 1
+match('a', Union[str, int], lambda x: x)                        # => 'a'
+match('a', Optional[str], lambda x: x)                          # => 'a'
+match(None, Optional[str], lambda x: x)                         # => None
+match(Pet, Type[Pet], lambda x: x)                              # => Pet
+match(Cat, Type[Pet], lambda x: x)                              # => Cat
+match(Dog, Any, lambda x: x)                                    # => Dog
+match(Dog, Type[Any], lambda x: x)                              # => Dog
+match(15, timestamp, lambda x: x)                               # => 15
+match(10.0, timestamp, lambda x: x)                             # => 10.0
+match([1, 2, 3], List[int], lambda x: x)                        # => [1, 2, 3]
+match({'a': 1, 'b': 2}, Dict[str, int], lambda x: x)            # => {'a': 1, 
'b': 2}
+match(annotated, 
+    Callable[[Tuple[int, float], str, Pet], timestamp], lambda x: x
+)                                                               # => annotated
+```
+For iterable generics actual type of value is guessed based on the first 
element. 
+```python
+match([1, 2, 3], List[int], lambda x: x)                        # => [1, 2, 3]
+match([1, "b", "a"], List[int], lambda x: x)                    # => [1, "b", 
"a"]
+match(["a", "b", "c"], List[int], lambda x: x)                  # raises 
MatchError
+match(["a", "b", "c"], List[Union[str, int]], lambda x: x)      # ["a", "b", 
"c"]
+
+match({"a": 1, "b": 2}, Dict[str, int], lambda x: x)            # {"a": 1, 
"b": 2}
+match({"a": 1, "b": "dog"}, Dict[str, int], lambda x: x)        # {"a": 1, 
"b": "dog"}
+match({"a": 1, 1: 2}, Dict[str, int], lambda x: x)              # {"a": 1, 1: 
2}
+match({2: 1, 1: 2}, Dict[str, int], lambda x: x)                # raises 
MatchError
+match({2: 1, 1: 2}, Dict[Union[str, int], int], lambda x: x)    # {2: 1, 1: 2}
+```
+Iterable generics also match with any of their subtypes.
+```python
+match([1, 2, 3], Iterable[int], lambda x: x)                     # => [1, 2, 3]
+match({1, 2, 3}, Iterable[int], lambda x: x)                     # => {1, 2, 3}
+match(range(10), Iterable[int], lambda x: x)                     # => range(10)
+
+match([1, 2, 3], List[int], lambda x: x)                         # => [1, 2, 3]
+match({1, 2, 3}, List[int], lambda x: x)                         # => raises 
MatchError
+match(range(10), List[int], lambda x: x)                         # => raises 
MatchError
+
+match([1, 2, 3], Set[int], lambda x: x)                          # => raises 
MatchError
+match({1, 2, 3}, Set[int], lambda x: x)                          # => {1, 2, 3}
+match(range(10), Set[int], lambda x: x)                          # => raises 
MatchError
+```
+For Callable any arg without annotation treated as Any. 
+```python
+def annotated(a: int, b: int) -> float:
+    pass
+    
+def not_annotated(a, b):
+    pass
+    
+def partially_annotated(a, b: float):
+    pass
+
+match(annotated, Callable[[int, int], float], lambda x: x)     # => annotated
+match(not_annotated, Callable[[int, int], float], lambda x: x) # => raises 
MatchError
+match(not_annotated, Callable[[Any, Any], Any], lambda x: x)   # => 
not_annotated
+match(annotated, Callable[[Any, Any], Any], lambda x: x)       # => raises 
MatchError
+match(partially_annotated, 
+    Callable[[Any, float], Any], lambda x: x
+)                                                              # => 
partially_annotated
+```
+TypeVar is not supported.
+
 ## All the things you can match
 
 As Pattern you can use any Python type, any class, or any Python value.
@@ -183,6 +263,15 @@
 | `{'type':'dog', age: int }` | Any dict with `type: "dog"` and with an `int` 
age | `{"type":"dog", "age": 3}` | `3` | `{"type":"dog", "age":2.3}` |
 | `re.compile('(\w+)-(\w+)-cat$')` | Any string that matches that regular 
expression expr | `"my-fuffy-cat"` | `"my"` and `"puffy"` | `"fuffy-dog"` | 
 | `Pet(name=_, age=7)` | Any Pet dataclass with `age == 7` | `Pet('rover', 7)` 
| `['rover']` | `Pet('rover', 8)` |
+| `Any` | The same as `_` | | that value | |
+| `Union[int, float, None]` | Any integer or float number or None | `2.35` | 
`2.35` | any other value |
+| `Optional[int]` | The same as `Union[int, None]` | `2` | `2` | any other 
value |
+| `Type[MyClass]` | Any subclass of MyClass. **And any class that extends 
MyClass.** | `MyClass` | that class | any other object |
+| `Callable[[int], float]` | Any callable with exactly that signature | `def 
a(q:int) -> float: ...` | that function | `def a(q) -> float: ...` |
+| `Tuple[MyClass, int, float]` | The same as `(MyClass, int, float)` | | | |
+| `Mapping[str, int]` Any subtype of `Mapping` acceptable too | any mapping or 
subtype of mapping with string keys and integer values | `{'a': 2, 'b': 3}` | 
that dict | `{'a': 'b', 'b': 'c'}` |
+| `Iterable[int]` Any subtype of `Iterable` acceptable too | any iterable or 
subtype of iterable with integer values | `range(10)` and `[1, 2, 3]` | that 
iterable | `['a', 'b', 'v']` |
+
 
 ## Using default
 
@@ -226,7 +315,7 @@
 or
 ```$ pip3 install pampy```
 
-## If you really must to use Python2
+## If you really must use Python2
 Pampy is Python3-first, but you can use most of its features in Python2 via 
[this backport](https://pypi.org/project/backports.pampy/) by Manuel Barkhau:
 
 ```pip install backports.pampy```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/pampy/__init__.py 
new/pampy-0.3.0/pampy/__init__.py
--- old/pampy-0.2.1/pampy/__init__.py   2018-12-24 15:06:29.000000000 +0100
+++ new/pampy-0.3.0/pampy/__init__.py   2019-07-13 13:30:15.000000000 +0200
@@ -1,4 +1,4 @@
-__version__ = '0.2.1'
+__version__ = '0.3.0'
 
 import sys
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/pampy/helpers.py 
new/pampy-0.3.0/pampy/helpers.py
--- old/pampy-0.2.1/pampy/helpers.py    2018-12-24 12:08:44.000000000 +0100
+++ new/pampy-0.3.0/pampy/helpers.py    2019-07-13 13:23:08.000000000 +0200
@@ -1,4 +1,17 @@
 import inspect
+from typing import (
+    Union,
+    Any,
+    Iterable,
+    TypeVar,
+)
+
+try:
+    from typing import GenericMeta
+except ImportError:
+    from typing import _GenericAlias as GenericMeta
+
+T = TypeVar("T")
 
 
 class UnderscoreType:
@@ -62,3 +75,34 @@
         return is_dataclass(value)
     except ImportError:
         return False
+
+
+def get_extra(pattern):
+    return getattr(pattern, "__extra__", None) or getattr(pattern, 
"__origin__", None)
+
+
+def peek(iter_: Iterable[T]) -> T:
+    return next(iter(iter_))
+
+
+def is_newtype(pattern):
+    return inspect.isfunction(pattern) and hasattr(pattern, '__supertype__')
+
+
+def is_generic(pattern):
+    return isinstance(pattern, GenericMeta)
+
+
+def is_union(pattern):
+    return isinstance(pattern, Union.__class__) or getattr(pattern, 
"__origin__", None) == Union
+
+
+def is_typing_stuff(pattern):
+    return pattern == Any or is_generic(pattern) or is_union(pattern) or 
is_newtype(pattern)
+
+
+def get_real_type(subtype):
+    if is_newtype(subtype):
+        return get_real_type(subtype.__supertype__)
+    else:
+        return subtype
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/pampy/pampy.py 
new/pampy-0.3.0/pampy/pampy.py
--- old/pampy-0.2.1/pampy/pampy.py      2018-12-24 12:09:38.000000000 +0100
+++ new/pampy-0.3.0/pampy/pampy.py      2019-07-13 13:23:08.000000000 +0200
@@ -1,10 +1,41 @@
-from collections import Iterable
+from collections.abc import (
+    Iterable,
+    Mapping,
+    Callable as ACallable,
+)
 from itertools import zip_longest
-from typing import Tuple, List
-from typing import Pattern as RegexPattern
-
-from pampy.helpers import *
+from enum import Enum
+from typing import (
+    Any,
+    Generic,
+    TypeVar,
+    Tuple,
+    List,
+    Pattern as RegexPattern,
+    Callable,
+)
+import inspect
+
+from pampy.helpers import (
+    UnderscoreType,
+    HeadType,
+    TailType,
+    get_lambda_args_error_msg,
+    BoxedArgs,
+    PaddedValue,
+    NoDefault,
+    is_typing_stuff,
+    is_dataclass,
+    is_generic,
+    is_newtype,
+    is_union,
+    pairwise,
+    peek,
+    get_real_type,
+    get_extra,
+)
 
+T = TypeVar('T')
 _ = ANY = UnderscoreType()
 HEAD = HeadType()
 REST = TAIL = TailType()
@@ -28,7 +59,9 @@
 def match_value(pattern, value) -> Tuple[bool, List]:
     if value is PaddedValue:
         return False, []
-    elif isinstance(pattern, (int, float, str, bool)):
+    elif is_typing_stuff(pattern):
+        return match_typing_stuff(pattern, value)
+    elif isinstance(pattern, (int, float, str, bool, Enum)):
         eq = pattern == value
         type_eq = type(pattern) == type(value)
         return eq and type_eq, []
@@ -37,20 +70,21 @@
     elif isinstance(pattern, type):
         if isinstance(value, pattern):
             return True, [value]
-        else:
-            return False, []
     elif isinstance(pattern, (list, tuple)):
         return match_iterable(pattern, value)
     elif isinstance(pattern, dict):
         return match_dict(pattern, value)
     elif callable(pattern):
         return_value = pattern(value)
-        if return_value is True:
-            return True, [value]
-        elif return_value is False:
-            pass
+
+        if isinstance(return_value, bool):
+            return return_value, [value]
+        elif isinstance(return_value, tuple) and len(return_value) == 2 \
+                and isinstance(return_value[0], bool) and 
isinstance(return_value[1], list):
+            return return_value
         else:
-            raise MatchError("Warning! pattern function %s is not returning a 
boolean, but instead %s" %
+            raise MatchError("Warning! pattern function %s is not returning a 
boolean "
+                             "nor a tuple of (boolean, list), but instead %s" %
                              (pattern, return_value))
     elif isinstance(pattern, RegexPattern):
         rematch = pattern.search(value)
@@ -137,7 +171,90 @@
     return True, total_extracted
 
 
+def match_typing_stuff(pattern, value) -> Tuple[bool, List]:
+    if pattern == Any:
+        return match_value(ANY, value)
+    elif is_union(pattern):
+        for subpattern in pattern.__args__:
+            is_matched, extracted = match_value(subpattern, value)
+            if is_matched:
+                return True, extracted
+        else:
+            return False, []
+    elif is_newtype(pattern):
+        return match_value(pattern.__supertype__, value)
+    elif is_generic(pattern):
+        return match_generic(pattern, value)
+    else:
+        return False, []
+
+
+def match_generic(pattern: Generic[T], value) -> Tuple[bool, List]:
+    if get_extra(pattern) == type:       # Type[int] for example
+        real_value = None
+        if is_newtype(value):
+            real_value = value
+            value = get_real_type(value)
+        if not inspect.isclass(value):
+            return False, []
+
+        type_ = pattern.__args__[0]
+        if type_ == Any:
+            return True, [real_value or value]
+        if is_newtype(type_):   # NewType case
+            type_ = get_real_type(type_)
+
+        if issubclass(value, type_):
+            return True, [real_value or value]
+        else:
+            return False, []
+
+    elif get_extra(pattern) == ACallable:
+        if callable(value):
+            spec = inspect.getfullargspec(value)
+            annotations = spec.annotations
+            artgtypes = [annotations.get(arg, Any) for arg in spec.args]
+            ret_type = annotations.get('return', Any)
+            if pattern == Callable[[*artgtypes], ret_type]:
+                return True, [value]
+            else:
+                return False, []
+        else:
+            return False, []
+
+    elif get_extra(pattern) == tuple:
+        return match_value(pattern.__args__, value)
+
+    elif issubclass(get_extra(pattern), Mapping):
+        type_matched, _captured = match_value(get_extra(pattern), value)
+        if not type_matched:
+            return False, []
+        k_type, v_type = pattern.__args__
+
+        key_example = peek(value)
+        key_matched, _captured = match_value(k_type, key_example)
+        if not key_matched:
+            return False, []
+
+        value_matched, _captured = match_value(v_type, value[key_example])
+        if not value_matched:
+            return False, []
+        else:
+            return True, [value]
 
+    elif issubclass(get_extra(pattern), Iterable):
+        type_matched, _captured = match_value(get_extra(pattern), value)
+        if not type_matched:
+            return False, []
+        v_type, = pattern.__args__
+        v = peek(value)
+        value_matched, _captured = match_value(v_type, v)
+        if not value_matched:
+            return False, []
+        else:
+            return True, [value]
+    else:
+        return False, []
 
 
 def match(var, *args, default=NoDefault, strict=True):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/pampy.egg-info/PKG-INFO 
new/pampy-0.3.0/pampy.egg-info/PKG-INFO
--- old/pampy-0.2.1/pampy.egg-info/PKG-INFO     2018-12-24 15:08:24.000000000 
+0100
+++ new/pampy-0.3.0/pampy.egg-info/PKG-INFO     2019-11-07 16:50:51.000000000 
+0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pampy
-Version: 0.2.1
+Version: 0.3.0
 Summary: The Pattern Matching for Python you always dreamed of
 Home-page: https://github.com/santinic/pampy
 Author: Claudio Santini
@@ -162,6 +162,86 @@
         match(pet, Pet(_, _), lambda name, age: (name, age))            # => 
('rover', 7)
         ```
         
+        ## Using typing
+        Pampy supports typing annotations.
+        
+        ```python
+        
+        class Pet:          pass
+        class Dog(Pet):     pass
+        class Cat(Pet):     pass
+        class Hamster(Pet): pass
+        
+        timestamp = NewType("year", Union[int, float])
+        
+        def annotated(a: Tuple[int, float], b: str, c: E) -> timestamp:
+            pass
+        
+        match((1, 2), Tuple[int, int], lambda a, b: (a, b))             # => 
(1, 2)
+        match(1, Union[str, int], lambda x: x)                          # => 1
+        match('a', Union[str, int], lambda x: x)                        # => 
'a'
+        match('a', Optional[str], lambda x: x)                          # => 
'a'
+        match(None, Optional[str], lambda x: x)                         # => 
None
+        match(Pet, Type[Pet], lambda x: x)                              # => 
Pet
+        match(Cat, Type[Pet], lambda x: x)                              # => 
Cat
+        match(Dog, Any, lambda x: x)                                    # => 
Dog
+        match(Dog, Type[Any], lambda x: x)                              # => 
Dog
+        match(15, timestamp, lambda x: x)                               # => 15
+        match(10.0, timestamp, lambda x: x)                             # => 
10.0
+        match([1, 2, 3], List[int], lambda x: x)                        # => 
[1, 2, 3]
+        match({'a': 1, 'b': 2}, Dict[str, int], lambda x: x)            # => 
{'a': 1, 'b': 2}
+        match(annotated, 
+            Callable[[Tuple[int, float], str, Pet], timestamp], lambda x: x
+        )                                                               # => 
annotated
+        ```
+        For iterable generics actual type of value is guessed based on the 
first element. 
+        ```python
+        match([1, 2, 3], List[int], lambda x: x)                        # => 
[1, 2, 3]
+        match([1, "b", "a"], List[int], lambda x: x)                    # => 
[1, "b", "a"]
+        match(["a", "b", "c"], List[int], lambda x: x)                  # 
raises MatchError
+        match(["a", "b", "c"], List[Union[str, int]], lambda x: x)      # 
["a", "b", "c"]
+        
+        match({"a": 1, "b": 2}, Dict[str, int], lambda x: x)            # 
{"a": 1, "b": 2}
+        match({"a": 1, "b": "dog"}, Dict[str, int], lambda x: x)        # 
{"a": 1, "b": "dog"}
+        match({"a": 1, 1: 2}, Dict[str, int], lambda x: x)              # 
{"a": 1, 1: 2}
+        match({2: 1, 1: 2}, Dict[str, int], lambda x: x)                # 
raises MatchError
+        match({2: 1, 1: 2}, Dict[Union[str, int], int], lambda x: x)    # {2: 
1, 1: 2}
+        ```
+        Iterable generics also match with any of their subtypes.
+        ```python
+        match([1, 2, 3], Iterable[int], lambda x: x)                     # => 
[1, 2, 3]
+        match({1, 2, 3}, Iterable[int], lambda x: x)                     # => 
{1, 2, 3}
+        match(range(10), Iterable[int], lambda x: x)                     # => 
range(10)
+        
+        match([1, 2, 3], List[int], lambda x: x)                         # => 
[1, 2, 3]
+        match({1, 2, 3}, List[int], lambda x: x)                         # => 
raises MatchError
+        match(range(10), List[int], lambda x: x)                         # => 
raises MatchError
+        
+        match([1, 2, 3], Set[int], lambda x: x)                          # => 
raises MatchError
+        match({1, 2, 3}, Set[int], lambda x: x)                          # => 
{1, 2, 3}
+        match(range(10), Set[int], lambda x: x)                          # => 
raises MatchError
+        ```
+        For Callable any arg without annotation treated as Any. 
+        ```python
+        def annotated(a: int, b: int) -> float:
+            pass
+            
+        def not_annotated(a, b):
+            pass
+            
+        def partially_annotated(a, b: float):
+            pass
+        
+        match(annotated, Callable[[int, int], float], lambda x: x)     # => 
annotated
+        match(not_annotated, Callable[[int, int], float], lambda x: x) # => 
raises MatchError
+        match(not_annotated, Callable[[Any, Any], Any], lambda x: x)   # => 
not_annotated
+        match(annotated, Callable[[Any, Any], Any], lambda x: x)       # => 
raises MatchError
+        match(partially_annotated, 
+            Callable[[Any, float], Any], lambda x: x
+        )                                                              # => 
partially_annotated
+        ```
+        TypeVar is not supported.
+        
         ## All the things you can match
         
         As Pattern you can use any Python type, any class, or any Python value.
@@ -191,6 +271,15 @@
         | `{'type':'dog', age: int }` | Any dict with `type: "dog"` and with 
an `int` age | `{"type":"dog", "age": 3}` | `3` | `{"type":"dog", "age":2.3}` |
         | `re.compile('(\w+)-(\w+)-cat$')` | Any string that matches that 
regular expression expr | `"my-fuffy-cat"` | `"my"` and `"puffy"` | 
`"fuffy-dog"` | 
         | `Pet(name=_, age=7)` | Any Pet dataclass with `age == 7` | 
`Pet('rover', 7)` | `['rover']` | `Pet('rover', 8)` |
+        | `Any` | The same as `_` | | that value | |
+        | `Union[int, float, None]` | Any integer or float number or None | 
`2.35` | `2.35` | any other value |
+        | `Optional[int]` | The same as `Union[int, None]` | `2` | `2` | any 
other value |
+        | `Type[MyClass]` | Any subclass of MyClass. **And any class that 
extends MyClass.** | `MyClass` | that class | any other object |
+        | `Callable[[int], float]` | Any callable with exactly that signature 
| `def a(q:int) -> float: ...` | that function | `def a(q) -> float: ...` |
+        | `Tuple[MyClass, int, float]` | The same as `(MyClass, int, float)` | 
| | |
+        | `Mapping[str, int]` Any subtype of `Mapping` acceptable too | any 
mapping or subtype of mapping with string keys and integer values | `{'a': 2, 
'b': 3}` | that dict | `{'a': 'b', 'b': 'c'}` |
+        | `Iterable[int]` Any subtype of `Iterable` acceptable too | any 
iterable or subtype of iterable with integer values | `range(10)` and `[1, 2, 
3]` | that iterable | `['a', 'b', 'v']` |
+        
         
         ## Using default
         
@@ -234,7 +323,7 @@
         or
         ```$ pip3 install pampy```
         
-        ## If you really must to use Python2
+        ## If you really must use Python2
         Pampy is Python3-first, but you can use most of its features in 
Python2 via [this backport](https://pypi.org/project/backports.pampy/) by 
Manuel Barkhau:
         
         ```pip install backports.pampy```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/pampy.egg-info/SOURCES.txt 
new/pampy-0.3.0/pampy.egg-info/SOURCES.txt
--- old/pampy-0.2.1/pampy.egg-info/SOURCES.txt  2018-12-24 15:08:24.000000000 
+0100
+++ new/pampy-0.3.0/pampy.egg-info/SOURCES.txt  2019-11-07 16:50:51.000000000 
+0100
@@ -12,4 +12,5 @@
 tests/test_dataclass.py
 tests/test_dict.py
 tests/test_elaborate.py
-tests/test_iterable.py
\ No newline at end of file
+tests/test_iterable.py
+tests/test_typing.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/tests/test_basic.py 
new/pampy-0.3.0/tests/test_basic.py
--- old/pampy-0.2.1/tests/test_basic.py 2018-12-24 12:06:20.000000000 +0100
+++ new/pampy-0.3.0/tests/test_basic.py 2019-07-13 13:23:08.000000000 +0200
@@ -1,5 +1,5 @@
 import unittest
-
+from enum import Enum
 import re
 
 from pampy import match_value, match, HEAD, TAIL, _, MatchError
@@ -143,3 +143,12 @@
 
         self.assertEqual(what_is('my-fuffy-cat'), 'fuffy-cat')
 
+    def test_match_enum(self):
+        class Color(Enum):
+            RED = 1
+            GREEN = 2
+            BLUE = 3
+
+        self.assertEqual(match(Color.RED, Color.BLUE, "blue", Color.RED, 
"red", _, "else"), "red")
+        self.assertEqual(match(Color.RED, Color.BLUE, "blue", Color.GREEN, 
"green", _, "else"), "else")
+        self.assertEqual(match(1, Color.BLUE, "blue", Color.GREEN, "green", _, 
"else"), "else")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/tests/test_elaborate.py 
new/pampy-0.3.0/tests/test_elaborate.py
--- old/pampy-0.2.1/tests/test_elaborate.py     2018-12-24 12:16:30.000000000 
+0100
+++ new/pampy-0.3.0/tests/test_elaborate.py     2019-07-13 13:23:08.000000000 
+0200
@@ -1,4 +1,12 @@
 import unittest
+from datetime import datetime
+from typing import (
+    Union,
+    Optional,
+    Tuple,
+    List,
+    NewType,
+)
 
 from functools import reduce
 
@@ -116,3 +124,107 @@
             return sum(cutenesses) / len(cutenesses)
 
         self.assertEqual(avg_cuteness_pampy(), (4 + 3 + 4.6 + 7) / 4)
+
+    def test_advanced_lambda(self):
+        def either(pattern1, pattern2):
+            """Matches values satisfying pattern1 OR pattern2"""
+            def repack(*args):
+                return True, list(args)
+
+            def f(var):
+                return match(var,
+                     pattern1, repack,
+                     pattern2, repack,
+                     _,        (False, [])
+                )
+
+            return f
+
+        self.assertEqual(match('str', either(int, str), 'success'), 'success')
+
+        def datetime_p(year: int, month: int, day: int, hour: int = 0, minute: 
int = 0, second: int = 0):
+            """Matches a datetime with these values"""
+            def f(var: datetime):
+                if not isinstance(var, datetime):
+                    return False, []
+
+                args = []
+                for pattern, actual in [(year, var.year), (month, var.month), 
(day, var.day),
+                                        (hour, var.hour), (minute, 
var.minute), (second, var.second)]:
+                    if pattern is _:
+                        args.append(actual)
+                    elif pattern != actual:
+                        return False, []
+
+                return True, args
+
+            return f
+
+        def test(var):
+            return match(var,
+                datetime_p(2018, 12, 23), 'full match',
+                datetime_p(2018, _, _), lambda month, day: f'{month}/{day} in 
2018',
+                datetime_p(_, _, _, _, _, _), 'any datetime',
+                _, 'not a datetime'
+            )
+
+        self.assertEqual(test(datetime(2018, 12, 23)), 'full match')
+        self.assertEqual(test(datetime(2018, 1, 2)), '1/2 in 2018')
+        self.assertEqual(test(datetime(2017, 1, 2, 3, 4, 5)), 'any datetime')
+        self.assertEqual(test(11), 'not a datetime')
+
+    def test_typing_example(self):
+
+        timestamp = NewType("timestamp", Union[float, int])
+        year, month, day, hour, minute, second = int, int, int, int, int, int
+        day_tuple = Tuple[year, month, day]
+        dt_tuple = Tuple[year, month, day, hour, minute, second]
+
+        def datetime_p(patterns: List[str]):
+            def f(dt: str):
+                for pattern in patterns:
+                    try:
+                        return True, [datetime.strptime(dt, pattern)]
+                    except Exception:
+                        continue
+                else:
+                    return False, []
+            return f
+
+        def to_datetime(dt: Union[
+            timestamp,
+            day_tuple,
+            dt_tuple,
+            str,
+        ]) -> Optional[datetime]:
+            return match(dt,
+                timestamp, lambda x: datetime.fromtimestamp(x),
+                Union[day_tuple, dt_tuple], lambda *x: datetime(*x),
+                datetime_p(["%Y-%m-%d", "%Y-%m-%d %H:%M:%S"]), lambda x: x,
+                _, None
+            )
+
+        key_date_tuple = (2018, 1, 1)
+        detailed_key_date_tuple = (2018, 1, 1, 12, 5, 6)
+        key_date = datetime(*key_date_tuple)
+        detailed_key_date = datetime(*detailed_key_date_tuple)
+
+        self.assertEqual(to_datetime(key_date_tuple), key_date)
+        self.assertEqual(to_datetime(detailed_key_date_tuple), 
detailed_key_date)
+
+        key_date_ts = key_date.timestamp()
+        detailed_key_date_ts = int(detailed_key_date.timestamp())
+        self.assertEqual(to_datetime(key_date_ts), key_date)
+        self.assertEqual(to_datetime(detailed_key_date_ts), detailed_key_date)
+
+        key_date_ts_str_a = key_date.strftime("%Y-%m-%d")
+        key_date_ts_str_f = key_date.strftime("%Y-%m-%d %H:%M:%S")
+        key_date_ts_str_w = key_date.strftime("%m-%Y-%d")
+        self.assertEqual(to_datetime(key_date_ts_str_a), key_date)
+        self.assertEqual(to_datetime(key_date_ts_str_f), key_date)
+        self.assertEqual(to_datetime(key_date_ts_str_w), None)
+
+        detailed_key_date_ts_str = detailed_key_date.strftime("%Y-%m-%d 
%H:%M:%S")
+        self.assertEqual(to_datetime(detailed_key_date_ts_str), 
detailed_key_date)
+
+        self.assertEqual(to_datetime(set(key_date_tuple)), None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pampy-0.2.1/tests/test_typing.py 
new/pampy-0.3.0/tests/test_typing.py
--- old/pampy-0.2.1/tests/test_typing.py        1970-01-01 01:00:00.000000000 
+0100
+++ new/pampy-0.3.0/tests/test_typing.py        2019-07-13 13:23:08.000000000 
+0200
@@ -0,0 +1,211 @@
+import unittest
+from typing import (
+    Callable,
+    List,
+    Set,
+    FrozenSet,
+    Mapping,
+    Iterable,
+    Dict,
+    Any,
+    Union,
+    Tuple,
+    NewType,
+    Optional,
+    Type,
+)
+
+from pampy import (
+    match,
+    match_value,
+)
+
+
+class A:
+    pass
+
+
+class B(A):
+    pass
+
+
+class C(A):
+    pass
+
+
+class Z:
+    pass
+
+E = NewType("E", B)
+lol = NewType("lol", int)
+kek = NewType("kek", lol)
+cat = NewType("cat", str)
+double_kek = NewType('double_kek', Tuple[kek, kek])
+composite_kek = NewType("CKek", Union[cat, double_kek])
+
+
+def annotated(a: int) -> float:
+    pass
+
+
+def big_annotated(a: Tuple[int, float], b: str, c: E) -> double_kek:
+    pass
+
+
+def wrong_annotated(b: str) -> str:
+    pass
+
+
+def not_annotated(q):
+    pass
+
+
+class PampyTypingTests(unittest.TestCase):
+
+    def test_match_any(self):
+        self.assertEqual(match_value(Any, 3), (True, [3]))
+        self.assertEqual(match_value(Any, 'ok'), (True, ['ok']))
+
+    def test_match_union(self):
+        self.assertEqual(match_value(Union[int, str], 3), (True, [3]))
+        self.assertEqual(match_value(Union[int, str], 'ok'), (True, ['ok']))
+        self.assertEqual(match_value(Union[int, str, float], 5.25), (True, 
[5.25]))
+        self.assertEqual(match_value(Optional[int], None), (True, [None]))
+        self.assertEqual(match_value(Optional[int], 1), (True, [1]))
+        self.assertEqual(match_value(Optional[int], 1.0), (False, []))
+
+    def test_match_newtype(self):
+        self.assertEqual(match_value(lol, 3), (True, [3]))
+        self.assertEqual(match_value(kek, 3), (True, [3]))
+        self.assertEqual(match_value(double_kek, (13, 37)), (True, [13, 37]))
+        self.assertEqual(match_value(composite_kek, "Barsik"), (True, 
["Barsik"]))
+        self.assertEqual(match_value(composite_kek, (13, 37)), (True, [13, 
37]))
+
+    def test_match_type(self):
+        self.assertEqual(match_value(Type[int], int), (True, [int]))
+        self.assertEqual(match_value(Type[int], 1), (False, []))
+        self.assertEqual(match_value(Type[E], "cat"), (False, []))
+        self.assertEqual(match_value(Type[A], "cat"), (False, []))
+
+        self.assertEqual(match_value(Type[lol], int), (True, [int]))
+        self.assertEqual(match_value(Type[kek], int), (True, [int]))
+        self.assertEqual(match_value(Type[kek], str), (False, []))
+
+        self.assertEqual(match_value(Type[A], A), (True, [A]))
+        self.assertEqual(match_value(Type[A], B), (True, [B]))
+        self.assertEqual(match_value(Type[B], C), (False, []))
+        self.assertEqual(match_value(Type[E], B), (True, [B]))
+
+        self.assertEqual(match_value(Type[A], E), (True, [E]))
+
+        self.assertEqual(match_value(Type[E], C), (False, []))
+        self.assertEqual(match_value(Type[C], A), (False, []))
+
+        self.assertEqual(match_value(Type[Any], A), (True, [A]))
+        self.assertEqual(match_value(Type[Any], Z), (True, [Z]))
+        self.assertEqual(match_value(Type[Any], int), (True, [int]))
+
+    def test_match_callable(self):
+        self.assertEqual(match_value(Callable[[int], float], annotated), 
(True, [annotated]))
+        self.assertEqual(
+            match_value(Callable[[Tuple[int, float], str, E], double_kek], 
big_annotated), (True, [big_annotated])
+        )
+        self.assertEqual(match_value(Callable[[int], float], not_annotated), 
(False, []))
+        self.assertEqual(match_value(Callable[[Any], Any], not_annotated), 
(True, [not_annotated]))
+        self.assertEqual(match_value(Callable[[int], float], wrong_annotated), 
(False, []))
+
+    def test_match_tuple(self):
+        self.assertEqual(
+            match_value(Tuple[int, str], (1, "a")),
+            (True, [1, "a"])
+        )
+        self.assertEqual(
+            match_value(Tuple[int, str], (1, 1)),
+            (False, [])
+        )
+        self.assertEqual(
+            match_value(
+                Tuple[
+                    Union[int, float],
+                    Callable[[int], float],
+                    Tuple[str, Type[E]]
+                ],
+                (1.0, annotated, ("ololo", B))
+            ),
+            (True, [1.0, annotated, "ololo", B])
+        )
+        self.assertEqual(
+            match_value(
+                Tuple[
+                    Union[int, float],
+                    Callable[[int], float],
+                    Tuple[str, Type[E]]
+                ],
+                (1.0, False, ("ololo", B))
+            ),
+            (False, [])
+        )
+        self.assertEqual(
+            match_value(
+                Tuple[
+                    Union[int, float],
+                    Callable[[int], float],
+                    Tuple[str, Type[E]]
+                ],
+                ("kek", annotated, ("ololo", B))
+            ),
+            (False, [])
+        )
+        self.assertEqual(
+            match_value(
+                Tuple[
+                    Union[int, float],
+                    Callable[[int], float],
+                    Tuple[str, Type[E]]
+                ],
+                (1, annotated, ("ololo", B, 1488))
+            ),
+            (False, [])
+        )
+        self.assertEqual(
+            match_value(
+                Tuple[
+                    Union[int, float],
+                    Callable[[int], float],
+                    Tuple[str, Type[A]]
+                ],
+                (1, annotated, ("ololo", E))
+            ),
+            (True, [1, annotated, "ololo", E])
+        )
+
+    def test_match_mapping(self):
+        self.assertEqual(match_value(Dict[str, int], {"a": 1, "b": 2}), (True, 
[{"a": 1, "b": 2}]))
+        self.assertEqual(match_value(Mapping[str, lol], {"a": 1, "b": 2}), 
(True, [{"a": 1, "b": 2}]))
+        self.assertEqual(match_value(Dict[str, lol], {"a": 1, "b": 2}), (True, 
[{"a": 1, "b": 2}]))
+        self.assertEqual(match_value(Dict[str, int], {"a": 1.0, "b": 2.0}), 
(False, []))
+        self.assertEqual(match_value(Dict[str, int], {1: 1, 2: 2}), (False, 
[]))
+        self.assertEqual(match_value(Mapping[str, int], {1: 1, 2: 2}), (False, 
[]))
+        self.assertEqual(match_value(Dict[Union[str, int], int], {1: 1, 2: 
2}), (True, [{1: 1, 2: 2}]))
+        self.assertEqual(match_value(Dict[Union[str, lol], int], {1: 1, 2: 
2}), (True, [{1: 1, 2: 2}]))
+        self.assertEqual(
+            match_value(Dict[str, Callable[[int], float]], {"a": annotated, 
"b": annotated}),
+            (True, [{"a": annotated, "b": annotated}])
+        )
+
+    def test_match_iterable(self):
+        self.assertEqual(match_value(List[int], [1, 2, 3]), (True, [[1, 2, 
3]]))
+        self.assertEqual(match_value(List[int], range(10)), (False, []))
+        self.assertEqual(match_value(Iterable[int], [1, 2, 3]), (True, [[1, 2, 
3]]))
+        self.assertEqual(match_value(Iterable[int], range(10)), (True, 
[range(10)]))
+        self.assertEqual(match_value(List[lol], [1, 2, 3]), (True, [[1, 2, 
3]]))
+        self.assertEqual(match_value(List[str], [1, 2, 3]), (False, []))
+        self.assertEqual(match_value(Iterable[str], [1, 2, 3]), (False, []))
+        a_vals = [B(), C(), B()]
+        self.assertEqual(match_value(List[A], a_vals), (True, [a_vals]))
+        self.assertEqual(match_value(List[str], ["lol", "kek"]), (True, 
[["lol", "kek"]]))
+        self.assertEqual(match_value(List[Union[str, int]], ["lol", "kek"]), 
(True, [["lol", "kek"]]))
+        self.assertEqual(match_value(List[Union[str, int]], [1, 2, 3]), (True, 
[[1, 2, 3]]))
+        self.assertEqual(match_value(List[int], {1, 2, 3}), (False, []))
+        self.assertEqual(match_value(Set[int], {1, 2, 3}), (True, [{1, 2, 3}]))
+        self.assertEqual(match_value(FrozenSet[int], frozenset([1, 2, 3])), 
(True, [frozenset([1, 2, 3])]))


Reply via email to