1
2 r"""
3 ===================
4 Symbol management
5 ===================
6
7 Symbol management.
8
9 :Copyright:
10
11 Copyright 2010 - 2017
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 operator as _op
37 import weakref as _weakref
38
39 from . import _util
40
41
44
45
47 """
48 Symbol table
49
50 :IVariables:
51 `_symbols` : ``dict``
52 Symbols
53
54 `imports` : `_Imports`
55 Import container
56
57 `types` : `_Types`
58 Type container
59 """
60
61 - def __init__(self, symbols=None, imports=None):
62 """
63 Initialization
64
65 :Parameters:
66 `symbols` : ``dict``
67 Initial symbols
68 """
69 self._symbols = {}
70 defaults = dict(
71 sa="_sa",
72 meta="m",
73 table="T",
74 type="t",
75 column="C",
76 default="D",
77 pk="PrimaryKey",
78 fk="ForeignKey",
79 uk="Unique",
80 constraints=(
81 __name__.rsplit('.', 1)[0] + '.constraints'
82 ),
83 )
84 self.imports = _Imports(imports=imports)
85 self.types = _Types(_weakref.proxy(self))
86 if symbols is None:
87 symbols = {}
88 else:
89 symbols = dict(symbols)
90 for key, value in defaults.items():
91 symbols.setdefault(key, value)
92 for name, symbol in dict(symbols).items():
93 self[name] = symbol
94
96 """ Remove symbol entry if available """
97 try:
98 del self._symbols[name]
99 except KeyError:
100 pass
101
103 """
104 Set symbol table entry
105
106 :Parameters:
107 `name` : ``str``
108 Symbol identifier
109
110 `symbol` : ``str``
111 Symbol
112
113 :Exceptions:
114 - `SymbolException` : Symbol could not be set because of some
115 conflict
116 """
117 if _util.py2 and not isinstance(name, unicode):
118 name = str(name).decode('ascii')
119 if _keyword.iskeyword(symbol):
120 raise SymbolException(
121 "Cannot use keyword %r as symbol" % (symbol,)
122 )
123 elif symbol in list(self._symbols.values()):
124 if name in self._symbols and self._symbols[name] != symbol:
125 raise SymbolException("Symbol conflict: %r" % (symbol,))
126 elif name in self._symbols and self._symbols[name] != symbol:
127 raise SymbolException("Symbol identifier conflict: %r" % (name,))
128 self._symbols[name] = symbol
129
131 """
132 Get symbol table entry
133
134 :Parameters:
135 `name` : ``str``
136 Symbol identifier
137
138 :Return: The symbol
139 :Rtype: ``str``
140
141 :Exceptions:
142 - `KeyError` : Symbol not found
143 """
144 if _util.py2 and not isinstance(name, unicode):
145 name = str(name).decode('ascii')
146 return self._symbols[name]
147
149 """
150 Check symbol table entry
151
152 :Parameters:
153 `name` : ``str``
154 Symbol identifier
155
156 :Return: The symbol
157 :Rtype: ``str``
158
159 :Exceptions:
160 - `KeyError` : Symbol not found
161 """
162 if _util.py2 and not isinstance(name, unicode):
163 name = str(name).decode('ascii')
164 return name in self._symbols
165
167 """
168 Make item iterator
169
170 :Return: The iterator
171 :Rtype: iterable
172 """
173 return iter(list(self._symbols.items()))
174
175
177 """
178 Type container
179
180 :IVariables:
181 `_types` : ``dict``
182 Type map
183
184 `_symbols` : `Symbols`
185 Symbol table
186 """
187
189 """
190 Initialization
191
192 :Parameters:
193 `symbols` : `Symbols`
194 Symbol table
195 """
196 self._types = {}
197 self._symbols = symbols
198 self.instance_repr = {}
199 self.defines = []
200
202 """
203 Set type
204
205 :Parameters:
206 `class_` : ``type``
207 Type to match
208
209 `symbol` : ``str``
210 Type module symbol
211
212 :Exceptions:
213 - `SymbolException` : Type conflict
214 """
215 if class_ in self._types:
216 if self._types[class_] != symbol:
217 raise SymbolException("Type conflict: %r" % (symbol,))
218 else:
219 self._types[class_] = symbol
220
221 - def resolve(self, type_, dialect):
222 """
223 Resolve type to module symbol
224
225 :Parameters:
226 `type_` : ``object``
227 Type to resolve
228
229 `dialect` : ``str``
230 Dialect name
231
232 :Return: Resolved symbol
233 :Rtype: ``str``
234
235 :Exceptions:
236 - `SymbolException` : Type could not be resolved
237 """
238 if type_.__class__ in self._types:
239 return self._symbols[self._types[type_.__class__]]
240 for class_, symbol in self._types.items():
241 if isinstance(type_, class_):
242 return self._symbols[symbol]
243
244 mod = type_.__module__
245 if mod.startswith('sqlalchemy.'):
246 mod = '.'.join(mod.split('.')[:3])
247 if mod == 'sqlalchemy.dialects.%s' % dialect:
248 return self._symbols['type']
249 else:
250 try:
251 _load_dotted('sqlalchemy.dialects.%s.%s' % (
252 dialect, type_.__class__.__name__
253 ))
254 return self._symbols['type']
255 except ImportError:
256 try:
257 _load_dotted('sqlalchemy.types.%s' % (
258 type_.__class__.__name__,
259 ))
260 return "%s.types" % (self._symbols['sa'],)
261 except ImportError:
262 pass
263 raise SymbolException(
264 "Don't know how to address type %r" % (type_,)
265 )
266
267
269 """
270 Import table
271
272 :IVariables:
273 `_imports` : ``list``
274 Import list
275 """
276
278 """ Initialization """
279 self._imports = list(imports or ())
280
282 """ Check if name is in imports """
283 for key, _ in self._imports:
284 if key == name:
285 return True
286 return False
287
289 """
290 Set import
291
292 :Parameters:
293 `name` : ``str``
294 Symbolic name (to support import uniqueness)
295
296 `import_` : ``str``
297 Import statement
298
299 :Exceptions:
300 - `SymbolException` : Import conflict
301 """
302 if _util.py2 and not isinstance(name, unicode):
303 name = str(name).decode('ascii')
304 imports = dict(self._imports)
305 if name in imports:
306 if imports[name] != import_:
307 raise SymbolException("Import conflict: %r: %r vs. %r" % (
308 name, imports[name], import_
309 ))
310 else:
311 self._imports.append((name, import_))
312
314 """
315 Make iterator over the import statements
316
317 :Return: The iterator
318 :Rtype: iterable
319 """
320 return iter(map(_op.itemgetter(1), self._imports))
321
322
324 """
325 Load a dotted name
326
327 (Stolen from wtf-server)
328
329 The dotted name can be anything, which is passively resolvable
330 (i.e. without the invocation of a class to get their attributes or
331 the like).
332
333 :Parameters:
334 `name` : ``str``
335 The dotted name to load
336
337 :Return: The loaded object
338 :Rtype: any
339
340 :Exceptions:
341 - `ImportError` : A module in the path could not be loaded
342 """
343 components = name.split('.')
344 path = [components.pop(0)]
345 obj = __import__(path[0])
346 while components:
347 comp = components.pop(0)
348 path.append(comp)
349 try:
350 obj = getattr(obj, comp)
351 except AttributeError:
352 __import__('.'.join(path))
353 try:
354 obj = getattr(obj, comp)
355 except AttributeError:
356 raise ImportError('.'.join(path))
357 return obj
358