forked from linode/linode_api4-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfiltering.py
More file actions
123 lines (93 loc) · 4.09 KB
/
Copy pathfiltering.py
File metadata and controls
123 lines (93 loc) · 4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
def or_(a, b):
if not isinstance(a, Filter) or not isinstance(b, Filter):
raise TypeError
return a.__or__(b)
def and_(a, b):
return a.__and__(b)
class Filter:
def __init__(self, dct):
self.dct = dct
def __or__(self, other):
if not isinstance(other, Filter):
raise TypeError("You can only or Filter types!")
if '+or' in self.dct:
return Filter({ '+or': self.dct['+or'] + [ other.dct ] })
else:
return Filter({ '+or': [self.dct, other.dct] })
def __and__(self, other):
if not isinstance(other, Filter):
raise TypeError("You can only and Filter types!")
if '+and' in self.dct:
return Filter({ '+and': self.dct['+and'] + [ other.dct ] })
else:
return Filter({ '+and': [self.dct, other.dct] })
def order_by(self, field, desc=False):
# we can't include two order_bys
if '+order_by' in self.dct:
raise AssertionError("You may only order by once!")
if not isinstance(field, FilterableAttribute):
raise TypeError("Can only order by filterable attributes!")
self.dct['+order_by'] = field.name
if desc:
self.dct['+order'] = 'desc'
return self
def limit(self, limit):
# we can't limit twice
if '+limit' in self.dct:
raise AssertionError("You may only limit once!")
if not type(limit) == int:
raise TypeError("Limit must be an int!")
self.dct['+limit'] = limit
return self
class FilterableAttribute:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return Filter({ self.name: other })
def __ne__(self, other):
return Filter({ self.name: { "+ne": other } })
# "in" evaluates the return value - have to use
# type.contains instead
def contains(self, other):
return Filter({ self.name: { "+contains": other } })
def __gt__(self, other):
return Filter({ self.name: { "+gt": other } })
def __lt__(self, other):
return Filter({ self.name: { "+lt": other } })
def __ge__(self, other):
return Filter({ self.name: { "+gte": other } })
def __le__(self, other):
return Filter({ self.name: { "+lte": other } })
class NonFilterableAttribute:
"""This class is used to provide better error messages should a user attempt
to filter an object on an attribute that is defined in properties, but is
not filterable (otherwise they'd see "{} has no attribute {}" which is less
obvious
"""
def __init__(self, clsname, atrname):
self.clsname = clsname
self.atrname = atrname
def __eq__(self, other):
raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))
def __ne__(self, other):
raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))
def contains(self, other):
raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))
def __gt__(self, other):
raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))
def __lt__(self, other):
raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))
def __ge__(self, other):
raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))
def __le__(self, other):
raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))
class FilterableMetaclass(type):
def __init__(cls, name, bases, dct):
if hasattr(cls, 'properties'):
for key in cls.properties.keys():
# TODO - either add NonFilterableAttribute or remove commented code
#if cls.properties[key].filterable:
setattr(cls, key, FilterableAttribute(key)) # pylint: disable=bad-indentation
#else:
# setattr(cls, key, NonFilterableAttribute(cls.__name__, key))
super(FilterableMetaclass, cls).__init__(name, bases, dct)