/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/load.py
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/Thuban/Model/load.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2375 - (hide annotations)
Sun Oct 3 21:05:30 2004 UTC (20 years, 5 months ago) by jan
Original Path: trunk/thuban/Thuban/Model/load.py
File MIME type: text/x-python
File size: 25237 byte(s)
(SessionLoader.start_cldata): Also parse the size attribute.

1 bh 2104 # Copyright (C) 2001, 2002, 2003, 2004 by Intevation GmbH
2 bh 6 # Authors:
3     # Jan-Oliver Wagner <[email protected]>
4 bh 267 # Bernhard Herzog <[email protected]>
5 jonathan 413 # Jonathan Coles <[email protected]>
6 bh 6 #
7     # This program is free software under the GPL (>=v2)
8     # Read the file COPYING coming with GRASS for details.
9    
10     """
11     Parser for thuban session files.
12     """
13    
14     __version__ = "$Revision$"
15    
16 bh 723 import string, os
17 bh 267
18     import xml.sax
19     import xml.sax.handler
20 jonathan 526 from xml.sax import make_parser, ErrorHandler, SAXNotRecognizedException
21 bh 267
22 jan 374 from Thuban import _
23 jonathan 413
24 jonathan 473 from Thuban.Model.table import FIELDTYPE_INT, FIELDTYPE_DOUBLE, \
25     FIELDTYPE_STRING
26    
27 jonathan 1339 from Thuban.Model.color import Color, Transparent
28    
29 bh 6 from Thuban.Model.session import Session
30     from Thuban.Model.map import Map
31 jonathan 930 from Thuban.Model.layer import Layer, RasterLayer
32 bh 6 from Thuban.Model.proj import Projection
33 jonathan 874 from Thuban.Model.range import Range
34 jonathan 413 from Thuban.Model.classification import Classification, \
35 jonathan 439 ClassGroupDefault, ClassGroupSingleton, ClassGroupRange, ClassGroupMap, \
36     ClassGroupProperties
37 bh 1268 from Thuban.Model.data import DerivedShapeStore, ShapefileStore
38     from Thuban.Model.table import DBFTable
39     from Thuban.Model.transientdb import TransientJoinedTable
40 bh 6
41 bh 1646 from Thuban.Model.xmlreader import XMLReader
42     import resource
43    
44     import postgisdb
45    
46 bh 1268 class LoadError(Exception):
47    
48 bh 1646 """Exception raised when the thuban file is corrupted
49 bh 6
50 bh 1646 Not all cases of corrupted thuban files will lead to this exception
51     but those that are found by checks in the loading code itself are.
52     """
53    
54    
55     class LoadCancelled(Exception):
56    
57     """Exception raised to indicate that loading was interrupted by the user"""
58    
59    
60 bh 267 def parse_color(color):
61     """Return the color object for the string color.
62 bh 6
63 bh 267 Color may be either 'None' or of the form '#RRGGBB' in the usual
64     HTML color notation
65 bh 6 """
66     color = string.strip(color)
67     if color == "None":
68 jonathan 1339 result = Transparent
69 bh 6 elif color[0] == '#':
70     if len(color) == 7:
71     r = string.atoi(color[1:3], 16) / 255.0
72     g = string.atoi(color[3:5], 16) / 255.0
73     b = string.atoi(color[5:7], 16) / 255.0
74     result = Color(r, g, b)
75     else:
76 jan 374 raise ValueError(_("Invalid hexadecimal color specification %s")
77 bh 6 % color)
78     else:
79 jan 374 raise ValueError(_("Invalid color specification %s") % color)
80 bh 6 return result
81    
82 bh 1268 class AttrDesc:
83    
84     def __init__(self, name, required = False, default = "",
85     conversion = None):
86     if not isinstance(name, tuple):
87     fullname = (None, name)
88     else:
89     fullname = name
90     name = name[1]
91     self.name = name
92     self.fullname = fullname
93     self.required = required
94     self.default = default
95     self.conversion = conversion
96    
97     # set by the SessionLoader's check_attrs method
98     self.value = None
99    
100    
101 jonathan 706 class SessionLoader(XMLReader):
102 jonathan 694
103 bh 1646 def __init__(self, db_connection_callback = None):
104 jonathan 694 """Inititialize the Sax handler."""
105 jonathan 706 XMLReader.__init__(self)
106 jonathan 694
107 bh 1646 self.db_connection_callback = db_connection_callback
108    
109 jonathan 694 self.theSession = None
110     self.aMap = None
111     self.aLayer = None
112    
113 bh 1268 # Map ids used in the thuban file to the corresponding objects
114     # in the session
115     self.idmap = {}
116 jonathan 706
117 bh 1268 dispatchers = {
118     'session' : ("start_session", "end_session"),
119 bh 1646
120     'dbconnection': ("start_dbconnection", None),
121    
122     'dbshapesource': ("start_dbshapesource", None),
123 bh 1268 'fileshapesource': ("start_fileshapesource", None),
124     'derivedshapesource': ("start_derivedshapesource", None),
125     'filetable': ("start_filetable", None),
126     'jointable': ("start_jointable", None),
127    
128     'map' : ("start_map", "end_map"),
129     'projection' : ("start_projection", "end_projection"),
130     'parameter' : ("start_parameter", None),
131     'layer' : ("start_layer", "end_layer"),
132     'rasterlayer' : ("start_rasterlayer", "end_rasterlayer"),
133     'classification': ("start_classification", "end_classification"),
134     'clnull' : ("start_clnull", "end_clnull"),
135     'clpoint' : ("start_clpoint", "end_clpoint"),
136     'clrange' : ("start_clrange", "end_clrange"),
137     'cldata' : ("start_cldata", "end_cldata"),
138     'table' : ("start_table", "end_table"),
139     'labellayer' : ("start_labellayer", None),
140     'label' : ("start_label", None)}
141    
142 bh 1375 # all dispatchers should be used for the 0.8 and 0.9 namespaces too
143     for xmlns in ("http://thuban.intevation.org/dtds/thuban-0.8.dtd",
144 bh 1664 "http://thuban.intevation.org/dtds/thuban-0.9-dev.dtd",
145 bh 1844 "http://thuban.intevation.org/dtds/thuban-0.9.dtd",
146 bh 2004 "http://thuban.intevation.org/dtds/thuban-1.0-dev.dtd",
147 bh 2036 "http://thuban.intevation.org/dtds/thuban-1.0rc1.dtd",
148 bh 2104 "http://thuban.intevation.org/dtds/thuban-1.0.0.dtd",
149     "http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd"):
150 bh 1375 for key, value in dispatchers.items():
151     dispatchers[(xmlns, key)] = value
152 bh 1268
153     XMLReader.AddDispatchers(self, dispatchers)
154    
155 bh 1930 def Destroy(self):
156     """Clear all instance variables to cut cyclic references.
157    
158     The GC would have collected the loader eventually but it can
159     happen that it doesn't run at all until Thuban is closed (2.3
160     but not 2.2 tries a bit harder and forces a collection when the
161     interpreter terminates)
162     """
163     self.__dict__.clear()
164    
165 bh 267 def start_session(self, name, qname, attrs):
166 bh 1268 self.theSession = Session(self.encode(attrs.get((None, 'title'),
167     None)))
168 bh 6
169 bh 267 def end_session(self, name, qname):
170     pass
171 bh 6
172 bh 1268 def check_attrs(self, element, attrs, descr):
173     """Check and convert some of the attributes of an element
174    
175     Parameters:
176     element -- The element name
177     attrs -- The attrs mapping as passed to the start_* methods
178     descr -- Sequence of attribute descriptions (AttrDesc instances)
179    
180     Return a dictionary containig normalized versions of the
181     attributes described in descr. The keys of that dictionary are
182     the name attributes of the attribute descriptions. The attrs
183     dictionary will not be modified.
184    
185     If the attribute is required, i.e. the 'required' attribute of
186     the descrtiption is true, but it is not in attrs, raise a
187     LoadError.
188    
189     If the attribute has a default value and it is not present in
190     attrs, use that default value as the value in the returned dict.
191    
192 bh 1970 The value is converted before putting it into the returned dict.
193     The following conversions are available:
194 bh 1268
195     'filename' -- The attribute is a filename.
196    
197     If the filename is a relative name, interpret
198     it relative to the directory containing the
199     .thuban file and make it an absolute name
200    
201     'shapestore' -- The attribute is the ID of a shapestore
202     defined earlier in the .thuban file. Look it
203     up self.idmap
204    
205     'table' -- The attribute is the ID of a table or shapestore
206     defined earlier in the .thuban file. Look it up
207     self.idmap. If it's the ID of a shapestore the
208     value will be the table of the shapestore.
209 bh 1646
210     'idref' -- The attribute is the id of an object defined
211     earlier in the .thuban file. Look it up self.idmap
212    
213     'ascii' -- The attribute is converted to a bytestring with
214     ascii encoding.
215 bh 1844
216     a callable -- The attribute value is passed to the callable
217 bh 1970 and the return value is used as the converted
218 bh 1844 value
219 bh 1970
220     If no conversion is specified for an attribute it is converted
221     with self.encode.
222 bh 1268 """
223     normalized = {}
224    
225     for d in descr:
226     if d.required and not attrs.has_key(d.fullname):
227 bh 1642 raise LoadError("Element %s requires an attribute %r"
228     % (element, d.name))
229 bh 1268 value = attrs.get(d.fullname, d.default)
230    
231 bh 1646 if d.conversion in ("idref", "shapesource"):
232 bh 1268 if value in self.idmap:
233     value = self.idmap[value]
234     else:
235     raise LoadError("Element %s requires an already defined ID"
236     " in attribute %r"
237     % (element, d.name))
238     elif d.conversion == "table":
239     if value in self.idmap:
240     value = self.idmap[value]
241     if isinstance(value, ShapefileStore):
242     value = value.Table()
243     else:
244     raise LoadError("Element %s requires an already defined ID"
245     " in attribute %r"
246     % (element, d.name))
247     elif d.conversion == "filename":
248     value = os.path.abspath(os.path.join(self.GetDirectory(),
249     value))
250 bh 1646 elif d.conversion == "ascii":
251     value = value.encode("ascii")
252 bh 1844 elif d.conversion:
253     # Assume it's a callable
254     value = d.conversion(value)
255 bh 1970 else:
256     value = self.encode(value)
257 bh 1268
258     normalized[d.name] = value
259     return normalized
260    
261 bh 1646 def start_dbconnection(self, name, qname, attrs):
262     attrs = self.check_attrs(name, attrs,
263     [AttrDesc("id", True),
264     AttrDesc("dbtype", True),
265     AttrDesc("host", False, ""),
266     AttrDesc("port", False, ""),
267     AttrDesc("user", False, ""),
268     AttrDesc("dbname", True)])
269     ID = attrs["id"]
270     dbtype = attrs["dbtype"]
271     if dbtype != "postgis":
272     raise LoadError("dbtype %r not supported" % filetype)
273    
274     del attrs["id"]
275     del attrs["dbtype"]
276    
277     # Try to open the connection and if it fails ask the user for
278     # the correct parameters repeatedly.
279     # FIXME: it would be better not to insist on getting a
280     # connection here. We should handle this more like the raster
281     # images where the layers etc still are created but are not
282     # drawn in case Thuban can't use the data for various reasons
283     while 1:
284     try:
285     conn = postgisdb.PostGISConnection(**attrs)
286     break
287     except postgisdb.ConnectionError, val:
288     if self.db_connection_callback is not None:
289     attrs = self.db_connection_callback(attrs, str(val))
290     if attrs is None:
291     raise LoadCancelled
292     else:
293     raise
294    
295     self.idmap[ID] = conn
296     self.theSession.AddDBConnection(conn)
297    
298     def start_dbshapesource(self, name, qname, attrs):
299     attrs = self.check_attrs(name, attrs,
300     [AttrDesc("id", True),
301     AttrDesc("dbconn", True,
302     conversion = "idref"),
303     AttrDesc("tablename", True,
304 bh 2104 conversion = "ascii"),
305     # id_column and geometry_column were
306     # newly introduced with thuban-1.1.dtd
307     # where they're required. Since we
308     # support the older formats too we
309     # have them optional here.
310     AttrDesc("id_column", False, "gid",
311     conversion = "ascii"),
312     AttrDesc("geometry_column", False,
313 bh 1646 conversion = "ascii")])
314 bh 2104 # The default value of geometry_column to use when instantiating
315     # the db shapestore is None which we currently can't easily use
316     # in check_attrs
317     geometry_column = attrs["geometry_column"]
318     if not geometry_column:
319     geometry_column = None
320     dbopen = self.theSession.OpenDBShapeStore
321     self.idmap[attrs["id"]] = dbopen(attrs["dbconn"], attrs["tablename"],
322     id_column = attrs["id_column"],
323     geometry_column=geometry_column)
324 bh 1646
325 bh 1268 def start_fileshapesource(self, name, qname, attrs):
326     attrs = self.check_attrs(name, attrs,
327     [AttrDesc("id", True),
328     AttrDesc("filename", True,
329     conversion = "filename"),
330     AttrDesc("filetype", True)])
331     ID = attrs["id"]
332     filename = attrs["filename"]
333     filetype = attrs["filetype"]
334     if filetype != "shapefile":
335     raise LoadError("shapesource filetype %r not supported" % filetype)
336     self.idmap[ID] = self.theSession.OpenShapefile(filename)
337    
338     def start_derivedshapesource(self, name, qname, attrs):
339     attrs = self.check_attrs(name, attrs,
340     [AttrDesc("id", True),
341     AttrDesc("shapesource", True,
342     conversion = "shapesource"),
343     AttrDesc("table", True, conversion="table")])
344 bh 1282 store = DerivedShapeStore(attrs["shapesource"], attrs["table"])
345     self.theSession.AddShapeStore(store)
346     self.idmap[attrs["id"]] = store
347 bh 1268
348     def start_filetable(self, name, qname, attrs):
349     attrs = self.check_attrs(name, attrs,
350     [AttrDesc("id", True),
351     AttrDesc("title", True),
352     AttrDesc("filename", True,
353     conversion = "filename"),
354     AttrDesc("filetype")])
355     filetype = attrs["filetype"]
356     if filetype != "DBF":
357     raise LoadError("shapesource filetype %r not supported" % filetype)
358     table = DBFTable(attrs["filename"])
359     table.SetTitle(attrs["title"])
360     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
361    
362     def start_jointable(self, name, qname, attrs):
363     attrs = self.check_attrs(name, attrs,
364     [AttrDesc("id", True),
365     AttrDesc("title", True),
366     AttrDesc("left", True, conversion="table"),
367     AttrDesc("leftcolumn", True),
368     AttrDesc("right", True, conversion="table"),
369 bh 1375 AttrDesc("rightcolumn", True),
370    
371     # jointype is required for file
372     # version 0.9 but this attribute
373     # wasn't in the 0.8 version because of
374     # an oversight so we assume it's
375     # optional since we want to handle
376     # both file format versions here.
377     AttrDesc("jointype", False,
378     default="INNER")])
379    
380     jointype = attrs["jointype"]
381     if jointype == "LEFT OUTER":
382     outer_join = True
383     elif jointype == "INNER":
384     outer_join = False
385     else:
386     raise LoadError("jointype %r not supported" % jointype )
387 bh 1268 table = TransientJoinedTable(self.theSession.TransientDB(),
388     attrs["left"], attrs["leftcolumn"],
389 bh 1375 attrs["right"], attrs["rightcolumn"],
390     outer_join = outer_join)
391 bh 1268 table.SetTitle(attrs["title"])
392     self.idmap[attrs["id"]] = self.theSession.AddTable(table)
393    
394 bh 267 def start_map(self, name, qname, attrs):
395     """Start a map."""
396 frank 1408 self.aMap = Map(self.encode(attrs.get((None, 'title'), None)))
397 bh 267
398     def end_map(self, name, qname):
399     self.theSession.AddMap(self.aMap)
400 jonathan 874 self.aMap = None
401 bh 267
402     def start_projection(self, name, qname, attrs):
403 bh 1844 attrs = self.check_attrs(name, attrs,
404     [AttrDesc("name", conversion=self.encode),
405     AttrDesc("epsg", default=None,
406     conversion=self.encode)])
407     self.projection_name = attrs["name"]
408     self.projection_epsg = attrs["epsg"]
409     self.projection_params = [ ]
410 bh 267
411     def end_projection(self, name, qname):
412 jonathan 874 if self.aLayer is not None:
413     obj = self.aLayer
414     elif self.aMap is not None:
415     obj = self.aMap
416     else:
417     assert False, "projection tag out of context"
418     pass
419    
420 bh 1844 obj.SetProjection(Projection(self.projection_params,
421     self.projection_name,
422     epsg = self.projection_epsg))
423 bh 267
424     def start_parameter(self, name, qname, attrs):
425     s = attrs.get((None, 'value'))
426     s = str(s) # we can't handle unicode in proj
427 bh 1844 self.projection_params.append(s)
428 bh 267
429     def start_layer(self, name, qname, attrs, layer_class = Layer):
430     """Start a layer
431    
432     Instantiate a layer of class layer_class from the attributes in
433     attrs which may be a dictionary as well as the normal SAX attrs
434     object and bind it to self.aLayer.
435     """
436 jonathan 874 title = self.encode(attrs.get((None, 'title'), ""))
437 bh 267 filename = attrs.get((None, 'filename'), "")
438 jonathan 694 filename = os.path.join(self.GetDirectory(), filename)
439 jonathan 930 filename = self.encode(filename)
440     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
441 bh 267 fill = parse_color(attrs.get((None, 'fill'), "None"))
442     stroke = parse_color(attrs.get((None, 'stroke'), "#000000"))
443     stroke_width = int(attrs.get((None, 'stroke_width'), "1"))
444 bh 1268 if attrs.has_key((None, "shapestore")):
445     store = self.idmap[attrs[(None, "shapestore")]]
446     else:
447     store = self.theSession.OpenShapefile(filename)
448     self.aLayer = layer_class(title, store,
449 bh 723 fill = fill, stroke = stroke,
450 jonathan 772 lineWidth = stroke_width,
451 jonathan 930 visible = visible)
452 bh 267
453     def end_layer(self, name, qname):
454     self.aMap.AddLayer(self.aLayer)
455 jonathan 874 self.aLayer = None
456 bh 267
457 jonathan 930 def start_rasterlayer(self, name, qname, attrs, layer_class = RasterLayer):
458     title = self.encode(attrs.get((None, 'title'), ""))
459     filename = attrs.get((None, 'filename'), "")
460     filename = os.path.join(self.GetDirectory(), filename)
461     filename = self.encode(filename)
462     visible = self.encode(attrs.get((None, 'visible'), "true")) != "false"
463    
464     self.aLayer = layer_class(title, filename, visible = visible)
465    
466     def end_rasterlayer(self, name, qname):
467     self.aMap.AddLayer(self.aLayer)
468     self.aLayer = None
469    
470 jonathan 365 def start_classification(self, name, qname, attrs):
471 bh 1970 attrs = self.check_attrs(name, attrs,
472     [AttrDesc("field", True),
473     AttrDesc("field_type", True)])
474     field = attrs["field"]
475     fieldType = attrs["field_type"]
476 jonathan 465
477     dbFieldType = self.aLayer.GetFieldType(field)
478    
479     if fieldType != dbFieldType:
480     raise ValueError(_("xml field type differs from database!"))
481    
482     # setup conversion routines depending on the kind of data
483     # we will be seeing later on
484     if fieldType == FIELDTYPE_STRING:
485     self.conv = str
486     elif fieldType == FIELDTYPE_INT:
487     self.conv = lambda p: int(float(p))
488     elif fieldType == FIELDTYPE_DOUBLE:
489     self.conv = float
490    
491 bh 1452 self.aLayer.SetClassificationColumn(field)
492 jonathan 465
493 jonathan 365 def end_classification(self, name, qname):
494     pass
495    
496     def start_clnull(self, name, qname, attrs):
497 jonathan 439 self.cl_group = ClassGroupDefault()
498 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
499 jonathan 439 self.cl_prop = ClassGroupProperties()
500 jonathan 365
501     def end_clnull(self, name, qname):
502 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
503     self.aLayer.GetClassification().SetDefaultGroup(self.cl_group)
504     del self.cl_group, self.cl_prop
505 jonathan 365
506     def start_clpoint(self, name, qname, attrs):
507     attrib_value = attrs.get((None, 'value'), "0")
508    
509 bh 1452 field = self.aLayer.GetClassificationColumn()
510 jonathan 1428 if self.aLayer.GetFieldType(field) == FIELDTYPE_STRING:
511 bh 1417 value = self.encode(attrib_value)
512     else:
513     value = self.conv(attrib_value)
514 jonathan 439 self.cl_group = ClassGroupSingleton(value)
515 jonathan 874 self.cl_group.SetLabel(self.encode(attrs.get((None, 'label'), "")))
516 jonathan 439 self.cl_prop = ClassGroupProperties()
517 jonathan 413
518 jonathan 365
519     def end_clpoint(self, name, qname):
520 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
521 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
522 jonathan 439 del self.cl_group, self.cl_prop
523 jonathan 365
524     def start_clrange(self, name, qname, attrs):
525    
526 jonathan 874 range = attrs.get((None, 'range'), None)
527     # for backward compatibility (min/max are not saved)
528     min = attrs.get((None, 'min'), None)
529     max = attrs.get((None, 'max'), None)
530    
531 jonathan 365 try:
532 jonathan 874 if range is not None:
533     self.cl_group = ClassGroupRange(Range(range))
534     elif min is not None and max is not None:
535 jonathan 1354 self.cl_group = ClassGroupRange((self.conv(min),
536     self.conv(max)))
537 jonathan 874 else:
538     self.cl_group = ClassGroupRange(Range(None))
539    
540 jonathan 365 except ValueError:
541 jan 374 raise ValueError(_("Classification range is not a number!"))
542 jonathan 365
543 jonathan 439 self.cl_group.SetLabel(attrs.get((None, 'label'), ""))
544     self.cl_prop = ClassGroupProperties()
545 jonathan 413
546 jonathan 365
547     def end_clrange(self, name, qname):
548 jonathan 439 self.cl_group.SetProperties(self.cl_prop)
549 jonathan 614 self.aLayer.GetClassification().AppendGroup(self.cl_group)
550 jonathan 439 del self.cl_group, self.cl_prop
551 jonathan 365
552     def start_cldata(self, name, qname, attrs):
553 jonathan 465 self.cl_prop.SetLineColor(
554     parse_color(attrs.get((None, 'stroke'), "None")))
555     self.cl_prop.SetLineWidth(
556 jonathan 390 int(attrs.get((None, 'stroke_width'), "0")))
557 jan 2375 self.cl_prop.SetSize(int(attrs.get((None, 'size'), "5")))
558 jonathan 439 self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
559 jonathan 365
560     def end_cldata(self, name, qname):
561     pass
562    
563 bh 267 def start_labellayer(self, name, qname, attrs):
564     self.aLayer = self.aMap.LabelLayer()
565    
566     def start_label(self, name, qname, attrs):
567 bh 2034 attrs = self.check_attrs(name, attrs,
568     [AttrDesc("x", True, conversion = float),
569     AttrDesc("y", True, conversion = float),
570     AttrDesc("text", True),
571     AttrDesc("halign", True,
572     conversion = "ascii"),
573     AttrDesc("valign", True,
574     conversion = "ascii")])
575     x = attrs['x']
576     y = attrs['y']
577     text = attrs['text']
578     halign = attrs['halign']
579     valign = attrs['valign']
580     if halign not in ("left", "center", "right"):
581     raise LoadError("Unsupported halign value %r" % halign)
582     if valign not in ("top", "center", "bottom"):
583     raise LoadError("Unsupported valign value %r" % valign)
584 bh 267 self.aLayer.AddLabel(x, y, text, halign = halign, valign = valign)
585    
586     def characters(self, chars):
587     pass
588    
589    
590 bh 1646 def load_session(filename, db_connection_callback = None):
591     """Load a Thuban session from the file object file
592 jonathan 694
593 bh 1646 The db_connection_callback, if given should be a callable object
594     that can be called like this:
595     db_connection_callback(params, message)
596    
597     where params is a dictionary containing the known connection
598     parameters and message is a string with a message why the connection
599     failed. db_connection_callback should return a new dictionary with
600     corrected and perhaps additional parameters like a password or None
601     to indicate that the user cancelled.
602     """
603     handler = SessionLoader(db_connection_callback)
604 jonathan 706 handler.read(filename)
605 jonathan 694
606 bh 6 session = handler.theSession
607     # Newly loaded session aren't modified
608     session.UnsetModified()
609    
610 bh 1930 handler.Destroy()
611    
612 bh 6 return session
613    

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26