mirror of
https://github.com/bendtherules/humblematch.git
synced 2026-08-18 22:02:32 +00:00
Added .save_as, .as_obj, doctest
This commit is contained in:
Binary file not shown.
+21
-3
@@ -95,11 +95,29 @@ IDEAS TO DO
|
|||||||
|
|
||||||
add more debug info
|
add more debug info
|
||||||
debug info or raise custom exception
|
debug info or raise custom exception
|
||||||
allow matches to store in var
|
|
||||||
better __repr__ print
|
better __repr__ print
|
||||||
|
|
||||||
|
allow matches to store in var (done)
|
||||||
|
|
||||||
add __mult__ alternative to .times (done)
|
add __mult__ alternative to .times (done)
|
||||||
|
|
||||||
what about dict and object? (dicts ok, )
|
what about dict ? (dicts ok, )
|
||||||
|
|
||||||
maybe dict to obj?
|
maybe dict to obj? (done)
|
||||||
|
|
||||||
|
save_as in multimatch with list behaviour(done)
|
||||||
|
|
||||||
|
Any and OR save_as (done)
|
||||||
|
|
||||||
|
test if data == Multi branch, raise error doesnt break anything
|
||||||
|
more docs
|
||||||
|
|
||||||
|
test integration save_as and times
|
||||||
|
|
||||||
|
do smart .times for single value
|
||||||
|
|
||||||
|
allow Any with a module.Class(Any), which allows only object - Any
|
||||||
|
|
||||||
|
more doctest for other methods and helpers
|
||||||
|
|
||||||
|
test helpers
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Run doctest -
|
||||||
|
python -m doctest .\wrap_obj.py
|
||||||
|
|
||||||
|
Run py.test-
|
||||||
|
py.test
|
||||||
+44
-5
@@ -1,4 +1,4 @@
|
|||||||
from wrap_obj import WrapObj as w, Any, OR
|
from wrap_obj import WrapObj, Any, OR, w
|
||||||
import collections
|
import collections
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ def test_w_single_value():
|
|||||||
assert(w("hello") == "hello")
|
assert(w("hello") == "hello")
|
||||||
assert(w(float("inf")) == float("inf"))
|
assert(w(float("inf")) == float("inf"))
|
||||||
assert(w(10.23) == 10.23)
|
assert(w(10.23) == 10.23)
|
||||||
assert(w("abhas") == u"abhas")
|
# assert(w("abhas") == u"abhas")
|
||||||
|
|
||||||
assert(w(object()) != object())
|
assert(w(object()) != object())
|
||||||
assert(w(5) != "asd")
|
assert(w(5) != "asd")
|
||||||
@@ -29,9 +29,9 @@ def test_w_single_type():
|
|||||||
assert(w(float) == 5.0)
|
assert(w(float) == 5.0)
|
||||||
assert(w(float) == float("inf"))
|
assert(w(float) == float("inf"))
|
||||||
|
|
||||||
assert(w(int) == int)
|
assert(w(int) != int)
|
||||||
assert(w(float) == float)
|
assert(w(float) != float)
|
||||||
assert(w(TypeError) == TypeError)
|
assert(w(TypeError) != TypeError)
|
||||||
|
|
||||||
assert(w(int) != 2.33)
|
assert(w(int) != 2.33)
|
||||||
assert(w(int) != "abhas")
|
assert(w(int) != "abhas")
|
||||||
@@ -178,3 +178,42 @@ def test_as_obj():
|
|||||||
|
|
||||||
test_obj.b = "qwerty"
|
test_obj.b = "qwerty"
|
||||||
assert(w({"a": int, "b": Any}).as_obj() == test_obj)
|
assert(w({"a": int, "b": Any}).as_obj() == test_obj)
|
||||||
|
|
||||||
|
|
||||||
|
def test_w_wrapObj():
|
||||||
|
assert(w is WrapObj)
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_as():
|
||||||
|
dict_match = (w(int).save_as("a") == 5)
|
||||||
|
assert(dict_match["a"] == 5)
|
||||||
|
|
||||||
|
dict_match = (w([int, w(float).save_as("a")]) == [5, 10.23])
|
||||||
|
assert(dict_match["a"] == 10.23)
|
||||||
|
|
||||||
|
dict_match = (w([w(int), w(float).save_as("a")]) == [5, 10.23])
|
||||||
|
assert(dict_match["a"] == 10.23)
|
||||||
|
|
||||||
|
dict_match = (w([{"what": w(int).save_as("b")}, w(float).save_as("a")]) == [{"what": 5}, 10.23])
|
||||||
|
assert(dict_match["b"] == 5 and dict_match["a"] == 10.23)
|
||||||
|
with pytest.raises(AssertionError):
|
||||||
|
assert(dict_match["b"] == 15)
|
||||||
|
with pytest.raises(AssertionError):
|
||||||
|
assert(dict_match["a"] == 5)
|
||||||
|
with pytest.raises(AssertionError):
|
||||||
|
assert(dict_match["a"] == 10)
|
||||||
|
|
||||||
|
dict_match = (w([{"what": w(int).save_as("a")}, w(float).save_as("a")]) == [{"what": 5}, 10.23])
|
||||||
|
assert(dict_match["a"] == 10.23)
|
||||||
|
|
||||||
|
dict_match = (w([[int, w(int).save_as("b")], float]) == [[5, 99], 10.23])
|
||||||
|
assert(dict_match["b"] == 99)
|
||||||
|
|
||||||
|
dict_match = (w([w(int).save_as("a")]).times(2) == [2, 3])
|
||||||
|
assert(dict_match["a"] != 3)
|
||||||
|
assert(dict_match["a"] == [2, 3])
|
||||||
|
|
||||||
|
dict_match_1 = (w([w([int]).times(2).save_as("b")]) == [5, 12])
|
||||||
|
dict_match_2 = (w([w([int]).save_as("b").times(2)]) == [5, 12])
|
||||||
|
assert(dict_match_1["b"] == [5, 12])
|
||||||
|
assert(dict_match_1 == dict_match_2)
|
||||||
|
|||||||
+308
-127
@@ -3,8 +3,11 @@ from enum import Enum
|
|||||||
from collections import Mapping, Iterable
|
from collections import Mapping, Iterable
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
import logging
|
import logging
|
||||||
|
import types
|
||||||
|
|
||||||
logging.debug = print
|
# logging.debug = print
|
||||||
|
|
||||||
|
__all__ = ["Any", "OR", "WrapObj", "w", "WrapMultiObj"]
|
||||||
|
|
||||||
|
|
||||||
class MetaAny(type):
|
class MetaAny(type):
|
||||||
@@ -15,12 +18,38 @@ class MetaAny(type):
|
|||||||
def times(self, range_low, range_high=None):
|
def times(self, range_low, range_high=None):
|
||||||
return WrapObj([self]).times(range_low, range_high)
|
return WrapObj([self]).times(range_low, range_high)
|
||||||
|
|
||||||
|
def save_as(self, save_key):
|
||||||
|
return WrapObj(self).save_as(save_key)
|
||||||
|
|
||||||
|
|
||||||
class Any(object):
|
class Any(object):
|
||||||
|
|
||||||
|
'''
|
||||||
|
Checker class which allows everything.
|
||||||
|
Usually, you would use it as a catch-all or placeholder.
|
||||||
|
>>> w(Any) == "asd"
|
||||||
|
True
|
||||||
|
>>> w(Any) == 2
|
||||||
|
True
|
||||||
|
>>> w(Any) == 2.35
|
||||||
|
True
|
||||||
|
>>> w(Any) == (lambda x:None)
|
||||||
|
True
|
||||||
|
'''
|
||||||
__metaclass__ = MetaAny
|
__metaclass__ = MetaAny
|
||||||
|
|
||||||
|
|
||||||
def makeMultiInstanceMatcher(*list_type):
|
def makeMultiInstanceMatcher(*list_type):
|
||||||
|
'''
|
||||||
|
Create checker class which allows some types or values.
|
||||||
|
For combining two or more possible types of a variable
|
||||||
|
|
||||||
|
>>> w(OR(int,float)) == 2.25 and w(OR(int,float)) == 2
|
||||||
|
True
|
||||||
|
>>> w(OR([int,float])) == 2.25 and w(OR([int,float])) == 2
|
||||||
|
True
|
||||||
|
'''
|
||||||
|
|
||||||
if len(list_type) == 1 and isinstance(list_type[0], Iterable):
|
if len(list_type) == 1 and isinstance(list_type[0], Iterable):
|
||||||
list_type = tuple(list_type[0])
|
list_type = tuple(list_type[0])
|
||||||
|
|
||||||
@@ -45,6 +74,9 @@ def makeMultiInstanceMatcher(*list_type):
|
|||||||
def times(self, range_low, range_high=None):
|
def times(self, range_low, range_high=None):
|
||||||
return WrapObj([self]).times(range_low, range_high)
|
return WrapObj([self]).times(range_low, range_high)
|
||||||
|
|
||||||
|
def save_as(self, save_key):
|
||||||
|
return WrapObj(self).save_as(save_key)
|
||||||
|
|
||||||
class MultiInstanceMatcher(type):
|
class MultiInstanceMatcher(type):
|
||||||
__metaclass__ = MetaMultiInstanceMatcher
|
__metaclass__ = MetaMultiInstanceMatcher
|
||||||
|
|
||||||
@@ -52,20 +84,44 @@ def makeMultiInstanceMatcher(*list_type):
|
|||||||
|
|
||||||
OR = makeMultiInstanceMatcher
|
OR = makeMultiInstanceMatcher
|
||||||
|
|
||||||
|
|
||||||
CHECK_DIR = Enum("Iterable check direction", ["FORWARDS", "BACKWARDS"])
|
CHECK_DIR = Enum("Iterable check direction", ["FORWARDS", "BACKWARDS"])
|
||||||
|
|
||||||
|
|
||||||
class WrapObj(object):
|
class WrapObj(object):
|
||||||
|
|
||||||
|
'''
|
||||||
|
Create WrapObj instance.
|
||||||
|
data -> Reference Object to match with.
|
||||||
|
DO_TYPECHECK -> Whether the refernece and checked object must
|
||||||
|
have same type? Defaults to false.
|
||||||
|
|
||||||
|
>>> WrapObj(int) # doctest: +ELLIPSIS
|
||||||
|
<wrap_obj.WrapObj object at ...>
|
||||||
|
>>> WrapObj(float, True) # doctest: +ELLIPSIS
|
||||||
|
<wrap_obj.WrapObj object at ...>
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, data, DO_TYPECHECK=False):
|
def __init__(self, data, DO_TYPECHECK=False):
|
||||||
|
|
||||||
super(WrapObj, self).__init__()
|
super(WrapObj, self).__init__()
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
self.DO_TYPECHECK = DO_TYPECHECK
|
self.DO_TYPECHECK = DO_TYPECHECK
|
||||||
self.treat_as_object = False
|
self.treat_as_object = False
|
||||||
|
self.dict_saved_values = {}
|
||||||
|
self.save_key = None
|
||||||
|
|
||||||
def as_obj(self):
|
def as_obj(self):
|
||||||
|
'''
|
||||||
|
Matches wrapped dict to object
|
||||||
|
|
||||||
|
>>> class object2(object): pass
|
||||||
|
>>> test_obj = object2(); test_obj.a = 20
|
||||||
|
>>> w({"a": int}).as_obj() == test_obj
|
||||||
|
True
|
||||||
|
'''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assert(isinstance(self.data, Mapping))
|
assert(isinstance(self.data, Mapping))
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
@@ -75,116 +131,214 @@ class WrapObj(object):
|
|||||||
self.treat_as_object = True
|
self.treat_as_object = True
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def save_as(self, arg_name):
|
||||||
|
'''
|
||||||
|
Saves matched value as arg_name, which is
|
||||||
|
used as key in the returned dict from __eq__
|
||||||
|
|
||||||
|
>>> _ = w([int,w(float).save_as("a")]) == [2,2.5]
|
||||||
|
>>> _["a"] == 2.5
|
||||||
|
True
|
||||||
|
'''
|
||||||
|
self.save_key = arg_name
|
||||||
|
return self
|
||||||
|
|
||||||
def times(self, range_low, range_high=None):
|
def times(self, range_low, range_high=None):
|
||||||
return WrapMultiObj(self.data, range_low, range_high)
|
'''
|
||||||
|
Makes all the items in wrapped iteratble (in given order) match
|
||||||
|
at least range_low times and at max (range_high-1) in __eq__
|
||||||
|
|
||||||
|
>>> w([int]).times(2,4) == [2,56,7]
|
||||||
|
True
|
||||||
|
>>> w([int]).times(2) == [2,56]
|
||||||
|
True
|
||||||
|
|
||||||
|
Wrapped object must be a iterator. If only range_low is passed,
|
||||||
|
must match for exactly range_low times.
|
||||||
|
|
||||||
|
Returns a WrapMultiObj.
|
||||||
|
'''
|
||||||
|
return_obj = WrapMultiObj(self.data, range_low, range_high)
|
||||||
|
return_obj.save_key = self.save_key
|
||||||
|
return return_obj
|
||||||
|
|
||||||
def __mul__(self, range_tuple):
|
def __mul__(self, range_tuple):
|
||||||
|
'''
|
||||||
|
Same as WrapObj.times
|
||||||
|
|
||||||
|
>>> w([int])*(2,4) == [2,56,7]
|
||||||
|
True
|
||||||
|
>>> w([int])*(2) == [2,56]
|
||||||
|
True
|
||||||
|
'''
|
||||||
return self.times(range_tuple, None)
|
return self.times(range_tuple, None)
|
||||||
|
|
||||||
def __pos__(self):
|
# def __pos__(self):
|
||||||
if (isinstance(self.data, Iterable) and (not isinstance(self.data, Mapping))):
|
# if (isinstance(self.data, Iterable) and (not isinstance(self.data, Mapping))):
|
||||||
return WrapMultiObj(self.data, [1, 2])
|
# return WrapMultiObj(self.data, [1, 2])
|
||||||
else:
|
# else:
|
||||||
TypeError("+WrapObj only works when WrapObj.data is list-ish")
|
# TypeError("+WrapObj only works when WrapObj.data is list-ish")
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
|
'''
|
||||||
|
Handles != matching. Returns opposite of == value
|
||||||
|
|
||||||
|
>>> w(int) != [2,56]
|
||||||
|
True
|
||||||
|
'''
|
||||||
return not(self.__eq__(other))
|
return not(self.__eq__(other))
|
||||||
|
|
||||||
|
def match_list(self, other):
|
||||||
|
'''
|
||||||
|
Internal method. Handles self == other, when other is list
|
||||||
|
'''
|
||||||
|
match_dict_or_True = None
|
||||||
|
check_dir = CHECK_DIR.FORWARDS
|
||||||
|
multiobj_index_forwards = None
|
||||||
|
multiobj_index_backwards = None
|
||||||
|
|
||||||
|
self.dict_saved_values = {}
|
||||||
|
|
||||||
|
if (check_dir == CHECK_DIR.FORWARDS):
|
||||||
|
|
||||||
|
for ele_index in range(0, len(self.data), 1):
|
||||||
|
ele_data = self.data[ele_index]
|
||||||
|
# as long as not multiobj, check in forwards
|
||||||
|
if isinstance(ele_data, WrapMultiObj):
|
||||||
|
multiobj_index_forwards = ele_index
|
||||||
|
check_dir = CHECK_DIR.BACKWARDS
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ele_other = other[ele_index]
|
||||||
|
except IndexError:
|
||||||
|
# To_be_checked list has length less than matching list
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
match_dict_or_True = (WrapObj(ele_data) == ele_other)
|
||||||
|
if match_dict_or_True and isinstance(match_dict_or_True, dict):
|
||||||
|
self.dict_saved_values.update(match_dict_or_True)
|
||||||
|
if not match_dict_or_True:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if (check_dir == CHECK_DIR.BACKWARDS):
|
||||||
|
for ele_index in range(0 - 1, -(len(self.data) + 1), -1):
|
||||||
|
# ele_index is of negative value from -1 to -len (including)
|
||||||
|
ele_data = self.data[ele_index]
|
||||||
|
# as long as not multiobj, check in backwards
|
||||||
|
if isinstance(ele_data, WrapMultiObj):
|
||||||
|
multiobj_index_backwards = ele_index
|
||||||
|
check_dir = CHECK_DIR.FORWARDS
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ele_other = other[ele_index]
|
||||||
|
except IndexError:
|
||||||
|
# To_be_checked list has length less than matching list
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
match_dict_or_True = (WrapObj(ele_data) == ele_other)
|
||||||
|
if match_dict_or_True and isinstance(match_dict_or_True, dict):
|
||||||
|
self.dict_saved_values.update(match_dict_or_True)
|
||||||
|
if not match_dict_or_True:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if (match_dict_or_True and (multiobj_index_forwards is None) and (multiobj_index_forwards == multiobj_index_backwards)):
|
||||||
|
# all of data is in other
|
||||||
|
# so, if they have same length, everything is perfect
|
||||||
|
if len(self.data) == len(other):
|
||||||
|
return (self.dict_saved_values or True)
|
||||||
|
elif (multiobj_index_forwards + (-multiobj_index_backwards)) != len(self.data):
|
||||||
|
# there is more than one length_ANY object
|
||||||
|
raise TypeError("There must be only one object of arbitary range")
|
||||||
|
else:
|
||||||
|
# there is only one length ANY object
|
||||||
|
|
||||||
|
# prepare back index for slicing operation
|
||||||
|
# add 1 (as this one will give outer bound) and if results to 0, set to None (as 0 doesnt mean last index)
|
||||||
|
multiobj_index_backwards += 1
|
||||||
|
multiobj_index_backwards = multiobj_index_backwards or None
|
||||||
|
ele_wrapmultiobj = self.data[multiobj_index_forwards]
|
||||||
|
logging.debug("Comparing WrapMultiObj with part of list")
|
||||||
|
match_dict_or_True = (ele_wrapmultiobj == other[multiobj_index_forwards:multiobj_index_backwards])
|
||||||
|
if match_dict_or_True:
|
||||||
|
if isinstance(match_dict_or_True, dict):
|
||||||
|
self.dict_saved_values.update(match_dict_or_True)
|
||||||
|
return (self.dict_saved_values or True)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def match_dict_or_obj(self, other, dict_or_obj=dict):
|
||||||
|
'''
|
||||||
|
Internal method. Handles self == other, when other is dict or obj
|
||||||
|
'''
|
||||||
|
assert(dict_or_obj in (dict, object))
|
||||||
|
match_dict_or_True = None # as in not known yet
|
||||||
|
|
||||||
|
self.dict_saved_values = {}
|
||||||
|
|
||||||
|
for (data_key, data_value) in self.data.iteritems():
|
||||||
|
if isinstance(data_key, type):
|
||||||
|
# todo: stop printing this line, which is broken into parts
|
||||||
|
warn(("{data_key} is a class used as a key, but it "
|
||||||
|
"wont match keys which are instances of this type"
|
||||||
|
"").format(data_key=data_key))
|
||||||
|
try:
|
||||||
|
if dict_or_obj is dict:
|
||||||
|
# if dict, try using that key
|
||||||
|
other_value = other[data_key]
|
||||||
|
elif dict_or_obj is object:
|
||||||
|
# if obj, try using that attribute
|
||||||
|
other_value = other.__getattribute__(data_key)
|
||||||
|
else:
|
||||||
|
raise TypeError("dict_or_obj must be dict or obj")
|
||||||
|
except (KeyError if dict_or_obj == dict else AttributeError):
|
||||||
|
# catch correct kind of error
|
||||||
|
# so if fails, it doesnt have that key or attribute. so, they dont match
|
||||||
|
match_dict_or_True = False
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
match_dict_or_True = (WrapObj(data_value) == other_value)
|
||||||
|
|
||||||
|
if match_dict_or_True and isinstance(match_dict_or_True, dict):
|
||||||
|
self.dict_saved_values.update(match_dict_or_True)
|
||||||
|
|
||||||
|
if match_dict_or_True is False:
|
||||||
|
return match_dict_or_True
|
||||||
|
else:
|
||||||
|
# should be True
|
||||||
|
assert(bool(match_dict_or_True) is True)
|
||||||
|
return (self.dict_saved_values or True)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
'''
|
||||||
|
Handles self == other correctly
|
||||||
|
|
||||||
|
>>> w(float) == 2.23
|
||||||
|
True
|
||||||
|
>>> w(str) == "2.23"
|
||||||
|
True
|
||||||
|
'''
|
||||||
if self.DO_TYPECHECK and (type(self.data) != type(other)):
|
if self.DO_TYPECHECK and (type(self.data) != type(other)):
|
||||||
return False
|
return False
|
||||||
elif (self.data == other) or (check_as_value_and_type(other, self.data)):
|
|
||||||
return True
|
elif isinstance(self.data, WrapObj):
|
||||||
|
return self.data == other
|
||||||
|
|
||||||
elif isinstance(self.data, WrapMultiObj):
|
elif isinstance(self.data, WrapMultiObj):
|
||||||
return (self.data == [other])
|
return (self.data == [other])
|
||||||
|
|
||||||
|
elif isinstance(self.data, types.StringTypes):
|
||||||
|
# take care of strings in normal way unlike lists
|
||||||
|
return self.data == other
|
||||||
|
|
||||||
elif (isinstance(self.data, Iterable) and (not isinstance(self.data, Mapping))) and\
|
elif (isinstance(self.data, Iterable) and (not isinstance(self.data, Mapping))) and\
|
||||||
(isinstance(other, Iterable) and (not isinstance(other, Mapping))):
|
(isinstance(other, Iterable) and (not isinstance(other, Mapping))):
|
||||||
does_match = None
|
return self.match_list(other)
|
||||||
check_dir = CHECK_DIR.FORWARDS
|
|
||||||
multiobj_index_forwards = None
|
|
||||||
multiobj_index_backwards = None
|
|
||||||
|
|
||||||
if (check_dir == CHECK_DIR.FORWARDS):
|
|
||||||
|
|
||||||
for ele_index in range(0, len(self.data), 1):
|
|
||||||
ele_data = self.data[ele_index]
|
|
||||||
# as long as not multiobj, check in forwards
|
|
||||||
if isinstance(ele_data, WrapMultiObj):
|
|
||||||
multiobj_index_forwards = ele_index
|
|
||||||
check_dir = CHECK_DIR.BACKWARDS
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
ele_other = other[ele_index]
|
|
||||||
except IndexError:
|
|
||||||
# To_be_checked list has length less than matching list
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
does_match = (WrapObj(ele_data) == ele_other)
|
|
||||||
if not does_match:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if (check_dir == CHECK_DIR.BACKWARDS):
|
|
||||||
for ele_index in range(0 - 1, -(len(self.data) + 1), -1):
|
|
||||||
# ele_index is of negative value from -1 to -len (including)
|
|
||||||
ele_data = self.data[ele_index]
|
|
||||||
# as long as not multiobj, check in backwards
|
|
||||||
if isinstance(ele_data, WrapMultiObj):
|
|
||||||
multiobj_index_backwards = ele_index
|
|
||||||
check_dir = CHECK_DIR.FORWARDS
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
ele_other = other[ele_index]
|
|
||||||
except IndexError:
|
|
||||||
# To_be_checked list has length less than matching list
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
does_match = does_match = (WrapObj(ele_data) == ele_other)
|
|
||||||
if not does_match:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if (does_match and (multiobj_index_forwards is None) and (multiobj_index_forwards == multiobj_index_backwards)):
|
|
||||||
# all of data is in other
|
|
||||||
# so, if they have same length, everything is perfect
|
|
||||||
return len(self.data) == len(other)
|
|
||||||
elif (multiobj_index_forwards + (-multiobj_index_backwards)) != len(self.data):
|
|
||||||
# there is more than one length_ANY object
|
|
||||||
raise TypeError("There must be only one object of arbitary range")
|
|
||||||
else:
|
|
||||||
# there is only one length ANY object
|
|
||||||
|
|
||||||
# prepare back index for slicing operation
|
|
||||||
# add 1 (as this one will give outer bound) and if results to 0, set to None (as 0 doesnt mean last index)
|
|
||||||
multiobj_index_backwards += 1
|
|
||||||
multiobj_index_backwards = multiobj_index_backwards or None
|
|
||||||
ele_wrapmultiobj = self.data[multiobj_index_forwards]
|
|
||||||
logging.debug("Comparing WrapMultiObj with part of list")
|
|
||||||
does_match = (ele_wrapmultiobj == other[multiobj_index_forwards:multiobj_index_backwards])
|
|
||||||
return does_match
|
|
||||||
elif not(self.treat_as_object) and (isinstance(self.data, Mapping) and isinstance(other, Mapping)):
|
elif not(self.treat_as_object) and (isinstance(self.data, Mapping) and isinstance(other, Mapping)):
|
||||||
does_match = None # as in not known yet
|
return self.match_dict_or_obj(other, dict)
|
||||||
for (data_key, data_value) in self.data.iteritems():
|
|
||||||
if isinstance(data_key, type):
|
|
||||||
# todo: stop printing this line, which is broken into parts
|
|
||||||
warn(("{data_key} is a class used as a key, but it "
|
|
||||||
"wont match keys which are instances of this type"
|
|
||||||
"").format(data_key=data_key))
|
|
||||||
try:
|
|
||||||
# try using that key
|
|
||||||
other_value = other[data_key]
|
|
||||||
except KeyError:
|
|
||||||
# if fails, it doesnt have that key. so, they dont match
|
|
||||||
does_match = False
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
does_match = (WrapObj(data_value) == other_value)
|
|
||||||
if does_match is False:
|
|
||||||
return does_match
|
|
||||||
else:
|
|
||||||
# should be True
|
|
||||||
assert(does_match is True)
|
|
||||||
return True
|
|
||||||
elif self.treat_as_object:
|
elif self.treat_as_object:
|
||||||
try:
|
try:
|
||||||
assert(isinstance(self.data, Mapping))
|
assert(isinstance(self.data, Mapping))
|
||||||
@@ -192,28 +346,16 @@ class WrapObj(object):
|
|||||||
raise TypeError("data={data} should be of type Mapping "
|
raise TypeError("data={data} should be of type Mapping "
|
||||||
"(e.g. dict)".format(data=self.data))
|
"(e.g. dict)".format(data=self.data))
|
||||||
|
|
||||||
does_match = None # as in not known yet
|
return self.match_dict_or_obj(other, object)
|
||||||
for (data_key, data_value) in self.data.iteritems():
|
|
||||||
if isinstance(data_key, type):
|
elif check_as_value_and_type(other, self.data):
|
||||||
# todo: stop printing this line, which is broken into parts
|
|
||||||
warn(("{data_key} is a class used as a key, but it "
|
if self.save_key is not None:
|
||||||
"wont match keys which are instances of this type"
|
self.dict_saved_values[self.save_key] = other
|
||||||
"").format(data_key=data_key))
|
return self.dict_saved_values
|
||||||
try:
|
|
||||||
# try using that key
|
|
||||||
other_value = other.__getattribute__(data_key)
|
|
||||||
except AttributeError:
|
|
||||||
# if fails, it doesnt have that attribute. so, they dont match
|
|
||||||
does_match = False
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
does_match = (WrapObj(data_value) == other_value)
|
|
||||||
if does_match is False:
|
|
||||||
return does_match
|
|
||||||
else:
|
else:
|
||||||
# should be True
|
|
||||||
assert(does_match is True)
|
|
||||||
return True
|
return True
|
||||||
|
# return True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@@ -241,7 +383,8 @@ class WrapMultiObj(object):
|
|||||||
elif WrapObj([INT_OR_INF, None]) == [range_low, range_high]:
|
elif WrapObj([INT_OR_INF, None]) == [range_low, range_high]:
|
||||||
range_high = range_low + 1
|
range_high = range_low + 1
|
||||||
else:
|
else:
|
||||||
raise TypeError("range_low and range_high must be of appropiate type")
|
raise TypeError("range_low={range_low} and range_high={range_high} must be of appropiate type"
|
||||||
|
"".format(range_low=range_low, range_high=range_high))
|
||||||
|
|
||||||
logging.debug("OK")
|
logging.debug("OK")
|
||||||
|
|
||||||
@@ -250,12 +393,23 @@ class WrapMultiObj(object):
|
|||||||
logging.debug("DO_TYPECHECK is off")
|
logging.debug("DO_TYPECHECK is off")
|
||||||
self.DO_TYPECHECK = False
|
self.DO_TYPECHECK = False
|
||||||
|
|
||||||
|
self.dict_saved_values = {}
|
||||||
|
self.save_key = None
|
||||||
|
|
||||||
logging.debug("Create OK, returned")
|
logging.debug("Create OK, returned")
|
||||||
|
|
||||||
|
def save_as(self, arg_name):
|
||||||
|
self.save_key = arg_name
|
||||||
|
return self
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not(self.__eq__(other))
|
return not(self.__eq__(other))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
'''
|
||||||
|
Similar to WrapObj.__eq__
|
||||||
|
But expects other is iterable
|
||||||
|
'''
|
||||||
logging.debug("Checking equality of {self_data} and {other}".format(self_data=self.data, other=other))
|
logging.debug("Checking equality of {self_data} and {other}".format(self_data=self.data, other=other))
|
||||||
if self.DO_TYPECHECK and (type(self.data) != type(other)):
|
if self.DO_TYPECHECK and (type(self.data) != type(other)):
|
||||||
logging.debug("DO_TYPECHECK is on and yet types dont match. So returned False")
|
logging.debug("DO_TYPECHECK is on and yet types dont match. So returned False")
|
||||||
@@ -278,6 +432,8 @@ class WrapMultiObj(object):
|
|||||||
else:
|
else:
|
||||||
logging.debug("OK")
|
logging.debug("OK")
|
||||||
|
|
||||||
|
self.dict_saved_values = {}
|
||||||
|
|
||||||
repeat_count = 0
|
repeat_count = 0
|
||||||
while True:
|
while True:
|
||||||
logging.debug("Running iteration {repeat_count}".format(repeat_count=repeat_count + 1))
|
logging.debug("Running iteration {repeat_count}".format(repeat_count=repeat_count + 1))
|
||||||
@@ -307,8 +463,21 @@ class WrapMultiObj(object):
|
|||||||
logging.debug("Checking for repeat matching at iteration = {repeat_count} failed".format(repeat_count=repeat_count))
|
logging.debug("Checking for repeat matching at iteration = {repeat_count} failed".format(repeat_count=repeat_count))
|
||||||
return False
|
return False
|
||||||
finally:
|
finally:
|
||||||
does_match = check_as_value_and_type(ele_other, ele_data)
|
# match_dict_or_True = check_as_value_and_type(ele_other, ele_data)
|
||||||
if not does_match:
|
match_dict_or_True = (WrapObj(ele_data) == ele_other)
|
||||||
|
# take care of save_as of child elements
|
||||||
|
if match_dict_or_True and isinstance(match_dict_or_True, dict):
|
||||||
|
# update self.dict_saved_values with each value in its list form,
|
||||||
|
# so as to allow muliple matches
|
||||||
|
for match_key in match_dict_or_True:
|
||||||
|
match_val = match_dict_or_True[match_key]
|
||||||
|
prev_match_val = self.dict_saved_values.get(match_key)
|
||||||
|
if isinstance(prev_match_val, list):
|
||||||
|
self.dict_saved_values[match_key].append(match_val)
|
||||||
|
else:
|
||||||
|
self.dict_saved_values[match_key] = [match_val]
|
||||||
|
|
||||||
|
if not match_dict_or_True:
|
||||||
# Return False as the match doesnt work out
|
# Return False as the match doesnt work out
|
||||||
logging.debug("{ele_other} with {ele_data} Match retuned False")
|
logging.debug("{ele_other} with {ele_data} Match retuned False")
|
||||||
return False
|
return False
|
||||||
@@ -318,18 +487,30 @@ class WrapMultiObj(object):
|
|||||||
logging.debug("Match works with {repeat_count} iterations".format(repeat_count=repeat_count))
|
logging.debug("Match works with {repeat_count} iterations".format(repeat_count=repeat_count))
|
||||||
if self.repeat_allowed_range[0] <= repeat_count < self.repeat_allowed_range[1]:
|
if self.repeat_allowed_range[0] <= repeat_count < self.repeat_allowed_range[1]:
|
||||||
logging.debug("And it is in valid range")
|
logging.debug("And it is in valid range")
|
||||||
return True
|
# take care of save_as of self
|
||||||
|
if self.save_key is not None:
|
||||||
|
self.dict_saved_values[self.save_key] = other
|
||||||
|
|
||||||
|
return (self.dict_saved_values or True)
|
||||||
else:
|
else:
|
||||||
logging.debug("And it is NOT in valid range")
|
logging.debug("And it is NOT in valid range")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_as_value_and_type(to_check, value_or_type):
|
def check_as_value_and_type(to_check, value_or_type):
|
||||||
logging.debug("Checking {1} with {0}".format(value_or_type, to_check))
|
'''
|
||||||
# if cheking with Any, return True (let it through)
|
Checks whether to_check is either
|
||||||
|
i) instance of value_or_type if value_or_type is a class
|
||||||
|
ii) to_check == value_or_type if value_or_type is not a class
|
||||||
|
|
||||||
# if value_or_type == Any:
|
>>> check_as_value_and_type( 5, int)
|
||||||
# return True
|
True
|
||||||
|
>>> check_as_value_and_type( 5, 5)
|
||||||
|
True
|
||||||
|
>>> check_as_value_and_type( int, int)
|
||||||
|
False
|
||||||
|
'''
|
||||||
|
logging.debug("Checking {1} with {0}".format(value_or_type, to_check))
|
||||||
|
|
||||||
# if its class, then check if instance of that
|
# if its class, then check if instance of that
|
||||||
if isinstance(value_or_type, type):
|
if isinstance(value_or_type, type):
|
||||||
@@ -337,12 +518,12 @@ def check_as_value_and_type(to_check, value_or_type):
|
|||||||
# try as value, if true return
|
# try as value, if true return
|
||||||
elif to_check == value_or_type:
|
elif to_check == value_or_type:
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
w = WrapObj
|
w = WrapObj
|
||||||
|
|
||||||
|
|
||||||
def fmatch():
|
# to think and todo
|
||||||
pass
|
# def fmatch():
|
||||||
# print(WrapObj([int, int]) == [2, 2])
|
# pass
|
||||||
print(WrapObj([2, WrapObj([3]).times((1, 3))]) == [2, 3, 3])
|
|
||||||
OR(int).times(5)
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user