1
1
from pynamodb .constants import (
2
- AND , ATTR_TYPE_MAP , BETWEEN , BINARY_SHORT , IN , NUMBER_SHORT , OR , SHORT_ATTR_TYPES , STRING_SHORT
2
+ AND , BETWEEN , BINARY_SHORT , IN , NUMBER_SHORT , OR , SHORT_ATTR_TYPES , STRING_SHORT
3
3
)
4
4
from pynamodb .expressions .util import get_value_placeholder , substitute_names
5
- from six import string_types
6
5
from six .moves import range
7
6
8
7
9
- class Operand (object ):
10
- """
11
- Operand is the base class for objects that support creating conditions from comparators.
12
- """
13
-
14
- def __eq__ (self , other ):
15
- return self ._compare ('=' , other )
16
-
17
- def __ne__ (self , other ):
18
- return self ._compare ('<>' , other )
19
-
20
- def __lt__ (self , other ):
21
- return self ._compare ('<' , other )
22
-
23
- def __le__ (self , other ):
24
- return self ._compare ('<=' , other )
25
-
26
- def __gt__ (self , other ):
27
- return self ._compare ('>' , other )
28
-
29
- def __ge__ (self , other ):
30
- return self ._compare ('>=' , other )
31
-
32
- def _compare (self , operator , other ):
33
- return Condition (self , operator , self ._serialize (other ))
34
-
35
- def between (self , lower , upper ):
36
- # This seemed preferable to other options such as merging value1 <= attribute & attribute <= value2
37
- # into one condition expression. DynamoDB only allows a single sort key comparison and having this
38
- # work but similar expressions like value1 <= attribute & attribute < value2 fail seems too brittle.
39
- return Between (self , self ._serialize (lower ), self ._serialize (upper ))
40
-
41
- def is_in (self , * values ):
42
- values = [self ._serialize (value ) for value in values ]
43
- return In (self , * values )
44
-
45
- def _serialize (self , value ):
46
- # Check to see if value is already serialized
47
- if isinstance (value , dict ) and len (value ) == 1 and list (value .keys ())[0 ] in SHORT_ATTR_TYPES :
48
- return value
49
- # Serialize value based on its type
50
- from pynamodb .attributes import _get_class_for_serialize
51
- attr_class = _get_class_for_serialize (value )
52
- return {ATTR_TYPE_MAP [attr_class .attr_type ]: attr_class .serialize (value )}
53
-
54
-
55
- class Size (Operand ):
56
- """
57
- Size is a special operand that represents the result of calling the 'size' function on a Path operand.
58
- """
59
-
60
- def __init__ (self , path ):
61
- # prevent circular import -- AttributePath imports Path
62
- from pynamodb .attributes import Attribute , AttributePath
63
- if isinstance (path , Path ):
64
- self .path = Path
65
- elif isinstance (path , Attribute ):
66
- self .path = AttributePath (path )
67
- else :
68
- self .path = Path (path )
69
-
70
- def _serialize (self , value ):
71
- if not isinstance (value , int ):
72
- raise TypeError ("size must be compared to an integer, not {0}" .format (type (value ).__name__ ))
73
- return {NUMBER_SHORT : str (value )}
74
-
75
- def __str__ (self ):
76
- return "size({0})" .format (self .path )
77
-
78
- def __repr__ (self ):
79
- return "Size({0})" .format (repr (self .path ))
80
-
81
-
82
8
# match dynamo function syntax: size(path)
83
9
def size (path ):
10
+ from pynamodb .expressions .operand import Size
84
11
return Size (path )
85
12
86
13
87
- class Path (Operand ):
88
- """
89
- Path is an operand that represents either an attribute name or document path.
90
- In addition to supporting comparisons, Path also supports creating conditions from functions.
91
- """
92
-
93
- def __init__ (self , path ):
94
- if not path :
95
- raise ValueError ("path cannot be empty" )
96
- self .path = path .split ('.' ) if isinstance (path , string_types ) else list (path )
97
-
98
- def __getitem__ (self , idx ):
99
- # list dereference operator
100
- if not isinstance (idx , int ):
101
- raise TypeError ("list indices must be integers, not {0}" .format (type (idx ).__name__ ))
102
- element_path = Path (self .path ) # copy the document path before indexing last element
103
- element_path .path [- 1 ] = '{0}[{1}]' .format (self .path [- 1 ], idx )
104
- return element_path
105
-
106
- def exists (self ):
107
- return Exists (self )
108
-
109
- def does_not_exist (self ):
110
- return NotExists (self )
111
-
112
- def is_type (self , attr_type ):
113
- return IsType (self , attr_type )
114
-
115
- def startswith (self , prefix ):
116
- # A 'pythonic' replacement for begins_with to match string behavior (e.g. "foo".startswith("f"))
117
- return BeginsWith (self , self ._serialize (prefix ))
118
-
119
- def contains (self , item ):
120
- return Contains (self , self ._serialize (item ))
121
-
122
- def __str__ (self ):
123
- # Quote the path to illustrate that any dot characters are not dereference operators.
124
- quoted_path = [self ._quote_path (segment ) if '.' in segment else segment for segment in self .path ]
125
- return '.' .join (quoted_path )
126
-
127
- def __repr__ (self ):
128
- return "Path({0})" .format (self .path )
129
-
130
- @staticmethod
131
- def _quote_path (path ):
132
- path , sep , rem = path .partition ('[' )
133
- return repr (path ) + sep + rem
134
-
135
-
136
14
class Condition (object ):
137
15
format_string = '{path} {operator} {0}'
138
16
@@ -151,6 +29,7 @@ def serialize(self, placeholder_names, expression_attribute_values):
151
29
return self .format_string .format (* values , path = path , operator = self .operator )
152
30
153
31
def _get_path (self , path , placeholder_names ):
32
+ from pynamodb .expressions .operand import Path , Size
154
33
if isinstance (path , Path ):
155
34
return substitute_names (path .path , placeholder_names )
156
35
elif isinstance (path , Size ):
0 commit comments