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 cmp((
101 bytype(self.constraint),
102 self.options is not None,
103 self.constraint.name,
104 ), (
105 bytype(other.constraint),
106 other.options is not None,
107 other.constraint.name,
108 ))
109
110 - def repr(self, symbol, args, keywords=(), short=False):
111 """
112 Base repr for all constraints
113
114 :Parameters:
115 `args` : iterable
116 Positional arguments
117
118 :Return: The constraint repr
119 :Rtype: ``str``
120 """
121
122
123
124 params = []
125 if self.constraint.name is not None:
126 params.append('name=%r' % (self.constraint.name,))
127 if self.constraint.deferrable is not None:
128 params.append('deferrable=%r' % (self.constraint.deferrable,))
129 if self.constraint.initially is not None:
130 params.append('initially=%r' % (self.constraint.initially,))
131 for keyword in keywords:
132 if getattr(self.constraint, keyword) is not None:
133 params.append("%s=%r" % (
134 keyword, getattr(self.constraint, keyword)
135 ))
136 if short and len(params) > 1:
137 short = False
138 if args:
139 if short:
140 args = ', '.join(args)
141 else:
142 args = '\n ' + ',\n '.join(args) + ','
143 else:
144 args = ''
145
146 if short:
147 params = ', '.join(params)
148 if args and params:
149 params = ', ' + params
150 else:
151 params = ',\n '.join(params)
152 if params:
153 params = '\n ' + params + ','
154 if args or params:
155 params += '\n'
156
157 return "%s(%s%s)" % (self._symbols[symbol], args, params)
158
159
161 """
162 Generate column access string (either as attribute or via dict access)
163
164 :Parameters:
165 `col` : SA Column
166 Column
167
168 :Return: Access string
169 :Rtype: ``str``
170 """
171 try:
172 name = col.name
173 except AttributeError:
174 name = col
175 try:
176 if _util.py2 and isinstance(name, _util.bytes):
177 name.decode('ascii')
178 else:
179 name.encode('ascii')
180 except UnicodeError:
181 is_ascii = False
182 else:
183 is_ascii = True
184 if is_ascii and not _keyword.iskeyword(name) and \
185 _re.match(_tokenize.Name + '$', name):
186 return ".c.%s" % name
187 return ".c[%r]" % name
188
189
191 """ Unique constraint """
192 _SYMBOL = 'uk'
193 _IMPORT = 'from %(constraints)s import Unique as %(uk)s'
194
196 """
197 Make string representation
198
199 :Return: The string representation
200 :Rtype: ``str``
201 """
202 empty = len(self.constraint.columns) == 0
203 short = len(self.constraint.columns) <= 1
204 result = self.repr(self._SYMBOL, [
205 "%s%s" % (self.table, access_col(col))
206 for col in self.constraint.columns
207 ], short=short)
208 if empty:
209 result = "# %s" % result
210 return result
211
212
214 """ Primary Key constraint """
215 _SYMBOL = 'pk'
216 _IMPORT = 'from %(constraints)s import PrimaryKey as %(pk)s'
217
218
220 """ ForeignKey constraint """
221 _SYMBOL = 'fk'
222 _IMPORT = 'from %(constraints)s import ForeignKey as %(fk)s'
223
225 """
226 Make string representation
227
228 :Return: The string representation
229 :Rtype: ``str``
230 """
231 columns = "[%s]" % ',\n '.join([
232 "%s%s" % (self.table, access_col(col))
233 for col in self.constraint.columns
234 ])
235 refcolumns = "[%s]" % ',\n '.join(["%s%s" % (
236 self._symbols[u'table_%s' % key.column.table.name],
237 access_col(key.column),
238 ) for key in self.constraint.elements])
239 keywords = ['onupdate', 'ondelete']
240 if self.constraint.use_alter:
241 keywords.append('use_alter')
242 result = self.repr('fk', [columns, refcolumns], keywords)
243
244 if self.options:
245 cyclic = self.constraint.use_alter
246 if self.options.startswith('seen:'):
247 table = self.options.split(None, 1)[1]
248 if cyclic:
249 result = '\n# Cyclic foreign key:\n' + result
250 else:
251 result = '\n# Foreign key belongs to %r:\n%s' % (
252 table, result
253 )
254 elif self.options.startswith('unseen:'):
255 table = self.options.split(None, 1)[1]
256 result = result.splitlines(True)
257 if cyclic:
258 result.insert(
259 0,
260 'Cyclic foreign key, defined at table %r:\n' % table
261 )
262 else:
263 result.insert(0, 'Defined at table %r:\n' % table)
264 result = '\n' + ''.join(['# %s' % item for item in result])
265
266 return result
267