1
2 r"""
3 ==========================================
4 Constraint inspection and representation
5 ==========================================
6
7 Constraint inspection and representation.
8
9 :Copyright:
10
11 Copyright 2010 - 2016
12 Andr\xe9 Malo or his licensors, as applicable
13
14 :License:
15
16 Licensed under the Apache License, Version 2.0 (the "License");
17 you may not use this file except in compliance with the License.
18 You may obtain a copy of the License at
19
20 http://www.apache.org/licenses/LICENSE-2.0
21
22 Unless required by applicable law or agreed to in writing, software
23 distributed under the License is distributed on an "AS IS" BASIS,
24 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 See the License for the specific language governing permissions and
26 limitations under the License.
27
28 """
29 if __doc__:
30
31 __doc__ = __doc__.encode('ascii').decode('unicode_escape')
32 __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
33 __docformat__ = "restructuredtext en"
34
35 import keyword as _keyword
36 import re as _re
37 import tokenize as _tokenize
38
39 from . import _util
40
41
43 """
44 Reflected Constraint
45
46 :IVariables:
47 `constraint` : SA Constraint
48 Constraint
49 """
50 _SYMBOL, _IMPORT = None, None
51
52 - def __new__(cls, constraint, table, symbols, options=None):
53 """ Constraint factory """
54 if cls == Constraint:
55 name = constraint.__class__.__name__
56 if name == 'CheckConstraint':
57 return None
58 return globals()[name](
59 constraint, table, symbols, options=options
60 )
61 return object.__new__(cls)
62
63 - def __init__(self, constraint, table, symbols, options=None):
64 """
65 Initialization
66
67 :Parameters:
68 `constraint` : SA Constraint
69 Constraint
70
71 `table` : ``str``
72 Table varname
73 """
74 self.constraint = constraint
75 self.table = table
76 self._symbols = symbols
77 self._symbols.imports[self._SYMBOL] = self._IMPORT
78 self.options = options
79
81 """ Create shallow copy """
82 return self.__class__(
83 self.constraint, self.table, self._symbols, self.options
84 )
85
87 """ Compare """
88 names = [
89 'PrimaryKeyConstraint', 'UniqueConstraint',
90 'ForeignKeyConstraint', 'CheckConstraint'
91 ]
92
93 def bytype(const):
94 """ Sort by type """
95 try:
96 return names.index(const.__class__.__name__)
97 except IndexError:
98 return -1
99
100 return _util.cmp((
101 bytype(self.constraint),
102 self.options is not None,
103 self.constraint.name,
104 repr(self),
105 ), (
106 bytype(other.constraint),
107 other.options is not None,
108 other.constraint.name,
109 repr(other),
110 ))
111
113 """ Check for '<' """
114 return _cmp(self, other) < 0
115
116 - def repr(self, symbol, args, keywords=(), short=False):
117 """
118 Base repr for all constraints
119
120 :Parameters:
121 `args` : iterable
122 Positional arguments
123
124 :Return: The constraint repr
125 :Rtype: ``str``
126 """
127
128
129
130 params = []
131 if self.constraint.name is not None:
132 params.append('name=%r' % (self.constraint.name,))
133 if self.constraint.deferrable is not None:
134 params.append('deferrable=%r' % (self.constraint.deferrable,))
135 if self.constraint.initially is not None:
136 params.append('initially=%r' % (self.constraint.initially,))
137 for keyword in keywords:
138 if getattr(self.constraint, keyword) is not None:
139 params.append("%s=%r" % (
140 keyword, getattr(self.constraint, keyword)
141 ))
142 if short and len(params) > 1:
143 short = False
144 if args:
145 if short:
146 args = ', '.join(args)
147 else:
148 args = '\n ' + ',\n '.join(args) + ','
149 else:
150 args = ''
151
152 if short:
153 params = ', '.join(params)
154 if args and params:
155 params = ', ' + params
156 else:
157 params = ',\n '.join(params)
158 if params:
159 params = '\n ' + params + ','
160 if args or params:
161 params += '\n'
162
163 return "%s(%s%s)" % (self._symbols[symbol], args, params)
164
165
167 """
168 Generate column access string (either as attribute or via dict access)
169
170 :Parameters:
171 `col` : SA Column
172 Column
173
174 :Return: Access string
175 :Rtype: ``str``
176 """
177 try:
178 name = col.name
179 except AttributeError:
180 name = col
181 try:
182 if _util.py2 and isinstance(name, _util.bytes):
183 name.decode('ascii')
184 else:
185 name.encode('ascii')
186 except UnicodeError:
187 is_ascii = False
188 else:
189 is_ascii = True
190 if is_ascii and not _keyword.iskeyword(name) and \
191 _re.match(_tokenize.Name + '$', name):
192 return ".c.%s" % name
193 return ".c[%r]" % name
194
195
197 """ Unique constraint """
198 _SYMBOL = 'uk'
199 _IMPORT = 'from %(constraints)s import Unique as %(uk)s'
200
202 """
203 Make string representation
204
205 :Return: The string representation
206 :Rtype: ``str``
207 """
208 empty = len(self.constraint.columns) == 0
209 short = len(self.constraint.columns) <= 1
210 result = self.repr(self._SYMBOL, [
211 "%s%s" % (self.table, access_col(col))
212 for col in self.constraint.columns
213 ], short=short)
214 if empty:
215 result = "# %s" % result
216 return result
217
218
220 """ Primary Key constraint """
221 _SYMBOL = 'pk'
222 _IMPORT = 'from %(constraints)s import PrimaryKey as %(pk)s'
223
224
226 """ ForeignKey constraint """
227 _SYMBOL = 'fk'
228 _IMPORT = 'from %(constraints)s import ForeignKey as %(fk)s'
229
231 """
232 Make string representation
233
234 :Return: The string representation
235 :Rtype: ``str``
236 """
237 columns = "[%s]" % ',\n '.join([
238 "%s%s" % (self.table, access_col(col))
239 for col in self.constraint.columns
240 ])
241 refcolumns = "[%s]" % ',\n '.join(["%s%s" % (
242 self._symbols[u'table_%s' % key.column.table.name],
243 access_col(key.column),
244 ) for key in self.constraint.elements])
245 keywords = ['onupdate', 'ondelete']
246 if self.constraint.use_alter:
247 keywords.append('use_alter')
248 result = self.repr('fk', [columns, refcolumns], keywords)
249
250 if self.options:
251 cyclic = self.constraint.use_alter
252 if self.options.startswith('seen:'):
253 table = self.options.split(None, 1)[1]
254 if cyclic:
255 result = '\n# Cyclic foreign key:\n' + result
256 else:
257 result = '\n# Foreign key belongs to %r:\n%s' % (
258 table, result
259 )
260 elif self.options.startswith('unseen:'):
261 table = self.options.split(None, 1)[1]
262 result = result.splitlines(True)
263 if cyclic:
264 result.insert(
265 0,
266 'Cyclic foreign key, defined at table %r:\n' % table
267 )
268 else:
269 result.insert(0, 'Defined at table %r:\n' % table)
270 result = '\n' + ''.join(['# %s' % item for item in result])
271
272 return result
273