Changeset 19b628f in sasview


Ignore:
Timestamp:
Dec 27, 2013 12:34:16 PM (10 years ago)
Author:
Jeff Krzywon <jeffery.krzywon@…>
Branches:
master, ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc, costrafo411, magnetic_scatt, release-4.1.1, release-4.1.2, release-4.2.2, release_4.0.1, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
Children:
17a25d4
Parents:
c614c02
Message:

The last update was not complete. For some reason a single file was missed.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/sans/dataloader/readers/cansas_reader.py

    rffbe487 r19b628f  
    11""" 
    2     CanSAS data reader 
     2    CanSAS data reader - new recursive cansasVersion. 
    33""" 
    44############################################################################ 
     
    1313############################################################################# 
    1414 
    15 # Known issue: reader not compatible with multiple SASdata entries 
    16 # within a single SASentry. Will raise a runtime error. 
    17  
    18 #TODO: check that all vectors are written only if they have at  
    19 #    least one non-empty value 
    20 #TODO: Writing only allows one SASentry per file. 
    21 #     Would be best to allow multiple entries. 
    22 #TODO: Store error list 
    23 #TODO: Allow for additional meta data for each section 
    24 #TODO: Notes need to be implemented. They can be any XML  
    25 #    structure in version 1.0 
    26 #      Process notes have the same problem. 
    27 #TODO: Unit conversion is not complete (temperature units are missing) 
    28  
    2915import logging 
    3016import numpy 
     
    3622from sans.dataloader.data_info import Process 
    3723from sans.dataloader.data_info import Aperture 
    38 from lxml import etree 
     24import xml_reader 
    3925import xml.dom.minidom 
     26from cansas_constants import cansasConstants 
     27 
    4028_ZERO = 1e-16 
    4129HAS_CONVERTER = True 
     
    4432except: 
    4533    HAS_CONVERTER = False 
    46  
    47 CANSAS_NS = "cansas1d/1.0" 
     34     
     35CANSAS_FORMAT = cansasConstants.CANSAS_FORMAT 
     36CANSAS_NS = cansasConstants.CANSAS_NS 
    4837ALLOW_ALL = True 
    4938 
     
    6756        return True 
    6857    return False 
    69  
     58                 
    7059 
    7160def get_content(location, node): 
     
    10998    return value, attr 
    11099 
    111              
    112 class Reader: 
     100 
     101 
     102class CANSASError(Exception): 
     103    """Base class all CANSAS reader exceptions are derived""" 
     104    pass 
     105 
     106class NotCANSASFileError(CANSASError): 
     107    def __init__(self): 
     108        self.value = "This is not a proper CanSAS file." 
     109    def __str__(self): 
     110        return repr(self.value) 
     111 
     112class Reader(): 
    113113    """ 
    114114    Class to load cansas 1D XML files 
    115115     
    116116    :Dependencies: 
    117         The CanSas reader requires PyXML 0.8.4 or later. 
     117        The CanSAS reader requires PyXML 0.8.4 or later. 
    118118    """ 
    119     ## CanSAS version 
    120     version = '1.0' 
    121     ## File type 
    122     type_name = "CanSAS 1D" 
     119    ##CanSAS version - defaults to version 1.0 
     120    cansasVersion = "1.0" 
     121    ##Data reader 
     122    reader = xml_reader.XMLreader() 
     123    errors = [] 
     124     
     125    type_name = "canSAS" 
     126     
    123127    ## Wildcards 
    124     type = ["CanSAS 1D files (*.xml)|*.xml", 
    125                         "CanSAS 1D AVE files (*.AVEx)|*.AVEx", 
    126                          "CanSAS 1D AVE files (*.ABSx)|*.ABSx"] 
    127  
     128    type = ["XML files (*.xml)|*.xml"] 
    128129    ## List of allowed extensions 
    129     ext = ['.xml', '.XML', '.avex', '.AVEx', '.absx', 'ABSx'] 
     130    ext = ['.xml', '.XML'] 
     131     
     132    ## Flag to bypass extension check 
     133    allow_all = True 
    130134     
    131135    def __init__(self): 
    132136        ## List of errors 
    133137        self.errors = [] 
    134      
    135     def read(self, path): 
    136         """ 
    137         Load data file 
    138          
    139         :param path: file path 
    140          
    141         :return: Data1D object if a single SASentry was found,  
    142                     or a list of Data1D objects if multiple entries were found, 
    143                     or None of nothing was found 
     138         
     139    def isCansas(self): 
     140        """ 
     141        Checks to see if the xml file is a CanSAS file 
     142        """ 
     143        if self.reader.validateXML(): 
     144            xmlns = self.reader.xmlroot.keys() 
     145            if (CANSAS_NS.get(self.cansasVersion).get("ns") == self.reader.xmlroot.get(xmlns[1]).rsplit(" ")[0]): 
     146                return True 
     147        return False 
     148     
     149    def read(self, xml): 
     150        """ 
     151        Validate and read in an xml file in the canSAS format. 
     152         
     153        :param xml: A canSAS file path in proper XML format 
     154        """ 
     155        # X - Q value; Y - Intensity (Abs) 
     156        x = numpy.empty(0) 
     157        y = numpy.empty(0) 
     158        dx = numpy.empty(0) 
     159        dy = numpy.empty(0) 
     160        dxl = numpy.empty(0) 
     161        dxw = numpy.empty(0) 
     162         
     163        # output - Final list of Data1D objects 
     164        output = [] 
     165        # ns - Namespace hierarchy for current xml object 
     166        ns = [] 
     167         
     168        # Check that the file exists 
     169        if os.path.isfile(xml): 
     170            basename = os.path.basename(xml) 
     171            _, extension = os.path.splitext(basename) 
     172            # If the fiel type is not allowed, return nothing 
     173            if extension in self.ext or self.allow_all: 
     174                base_name = xml_reader.__file__ 
     175                base = base_name.split("\\sans\\")[0] 
     176                 
     177                # Load in the xml file and get the cansas version from the header 
     178                self.reader.setXMLFile(xml) 
     179                root = self.reader.xmlroot 
     180                if root is None: 
     181                    root = {} 
     182                self.cansasVersion = root.get("version", "1.0") 
     183                 
     184                # Generic values for the cansas file based on the version 
     185                cansas_defaults = CANSAS_NS.get(self.cansasVersion, "1.0") 
     186                schema_path = "{0}\\sans\\dataloader\\readers\\schema\\{1}".format(base, cansas_defaults.get("schema")).replace("\\", "/") 
     187                 
     188                # Link a schema to the XML file. 
     189                self.reader.setSchema(schema_path) 
     190             
     191                # Try to load the file, but raise an error if unable to. 
     192                # Check the file matches the XML schema 
     193                try: 
     194                    if self.isCansas(): 
     195                        # Get each SASentry from the XML file and add it to a list. 
     196                        entry_list = root.xpath('/ns:SASroot/ns:SASentry', 
     197                                                     namespaces={'ns': cansas_defaults.get("ns")}) 
     198                        ns.append("SASentry") 
     199                         
     200                        # If there are multiple files, modify the name for each is unique 
     201                        multipleFiles = len(entry_list) - 1 
     202                        n = 0 
     203                        name = basename 
     204                        # Parse each SASentry item 
     205                        for entry in entry_list: 
     206                             
     207                            # Define a new Data1D object with zeroes for x and y 
     208                            data1D = Data1D(x,y,dx,dy) 
     209                            data1D.dxl = dxl 
     210                            data1D.dxw = dxw 
     211                             
     212                            # If more than one SASentry, number each in order 
     213                            if multipleFiles: 
     214                                name += "_{0}".format(n) 
     215                                n += 1 
     216                             
     217                            # Set the Data1D name and then parse the entry. The entry is appended to a list of entry values 
     218                            data1D.filename = name 
     219                            data1D.meta_data["loader"] = "CanSAS 1D" 
     220                            return_value, extras = self._parse_entry(entry, ns, data1D) 
     221                            del extras[:] 
     222                             
     223                            #Final cleanup - Remove empty nodes, verify array sizes are correct 
     224                            for error in self.errors: 
     225                                return_value.errors.append(error) 
     226                            del self.errors[:] 
     227                            numpy.trim_zeros(return_value.x) 
     228                            numpy.trim_zeros(return_value.y) 
     229                            numpy.trim_zeros(return_value.dy) 
     230                            size_dx = return_value.dx.size 
     231                            size_dxl = return_value.dxl.size 
     232                            size_dxw = return_value.dxw.size 
     233                            if size_dxl == 0 and size_dxw == 0: 
     234                                return_value.dxl = None 
     235                                return_value.dxw = None 
     236                                numpy.trim_zeros(return_value.dx) 
     237                            elif size_dx == 0: 
     238                                return_value.dx = None 
     239                                size_dx = size_dxl 
     240                                numpy.trim_zeros(return_value.dxl) 
     241                                numpy.trim_zeros(return_value.dxw) 
     242                             
     243                            output.append(return_value) 
     244                    else: 
     245                        value = self.reader.findInvalidXML() 
     246                        output.append("Invalid XML at: {0}".format(value)) 
     247                except: 
     248                    # If the file does not match the schema, raise this error 
     249                    raise RuntimeError, "%s cannot be read \n" % xml 
     250                return output 
     251        # Return a list of parsed entries that dataloader can manage 
     252        return None 
     253     
     254    def _create_unique_key(self, dictionary, name, i): 
     255        if dictionary.get(name) is not None: 
     256            i += 1 
     257            name = name.split("_")[0] 
     258            name += "_{0}".format(i) 
     259            name = self._create_unique_key(dictionary, name, i) 
     260        return name 
     261     
     262    def _iterate_namespace(self, ns): 
     263        # The current level to look through in cansas_constants. 
     264        current_level = CANSAS_FORMAT.get("SASentry") 
     265        # Defaults for variable and datatype 
     266        ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\"" 
     267        ns_datatype = "content" 
     268        ns_optional = True 
     269        for name in ns: 
     270            if name != "SASentry": 
     271                current_level = current_level.get("children").get(name, "") 
     272                if current_level == "": 
     273                    current_level = current_level.get("<any>", "") 
     274                cl_variable = current_level.get("variable", "") 
     275                cl_datatype = current_level.get("storeas", "") 
     276                cl_units_optional = current_level.get("units_required", "") 
     277                # Where are how to store the variable for the given namespace 
     278                # The CANSAS_CONSTANTS tree is hierarchical, so is no value, inherit 
     279                ns_variable = cl_variable if cl_variable != "" else ns_variable 
     280                ns_datatype = cl_datatype if cl_datatype != "" else ns_datatype 
     281                ns_optional = cl_units_optional if cl_units_optional != ns_optional else ns_optional 
     282        return current_level, ns_variable, ns_datatype, ns_optional 
     283     
     284    def _unit_conversion(self, new_current_level, attr, data1D, node_value, optional = True): 
     285        value_unit = '' 
     286        if 'unit' in attr and new_current_level.get('unit') is not None: 
     287            try: 
     288                if isinstance(node_value, float) is False: 
     289                    exec("node_value = float({0})".format(node_value)) 
     290                default_unit = None 
     291                unitname = new_current_level.get("unit") 
     292                exec "default_unit = data1D.{0}".format(unitname) 
     293                local_unit = attr['unit'] 
     294                if local_unit.lower() != default_unit.lower() and local_unit is not None\ 
     295                    and local_unit.lower() != "none" and default_unit is not None: 
     296                    if HAS_CONVERTER == True: 
     297                        try: 
     298                            data_conv_q = Converter(attr['unit']) 
     299                            value_unit = default_unit 
     300                            exec "node_value = data_conv_q(node_value, units=data1D.{0})".format(unitname) 
     301                        except: 
     302                            err_msg = "CanSAS reader: could not convert " 
     303                            err_msg += "Q unit {0}; ".format(local_unit) 
     304                            intermediate = "err_msg += \"expecting [{1}]  {2}\".format(data1D.{0}, sys.exc_info()[1])".format(unitname, "{0}", "{1}") 
     305                            exec intermediate 
     306                            self.errors.append(err_msg) 
     307                            if optional: 
     308                                logging.info(err_msg) 
     309                            else: 
     310                                raise ValueError, err_msg 
     311                    else: 
     312                        value_unit = local_unit 
     313                        err_msg = "CanSAS reader: unrecognized %s unit [%s];"\ 
     314                        % (node_value, default_unit) 
     315                        err_msg += " expecting [%s]" % local_unit 
     316                        self.errors.append(err_msg) 
     317                        if optional: 
     318                            logging.info(err_msg) 
     319                        else: 
     320                            raise ValueError, err_msg 
     321                else: 
     322                    value_unit = local_unit 
     323            except: 
     324                err_msg = "CanSAS reader: could not convert " 
     325                err_msg += "Q unit [%s]; " % attr['unit'], 
     326                exec "err_msg += \"expecting [%s]\n  %s\" % (data1D.{0}, sys.exc_info()[1])".format(unitname) 
     327                self.errors.append(err_msg) 
     328                if optional: 
     329                    logging.info(err_msg) 
     330                else: 
     331                    raise ValueError, err_msg 
     332        elif 'unit' in attr: 
     333            value_unit = attr['unit'] 
     334        node_value = "float({0})".format(node_value) 
     335        return node_value, value_unit 
     336     
     337    def _parse_entry(self, dom, ns, data1D, extras = []): 
     338        """ 
     339        Parse a SASEntry - new recursive method for parsing the dom of 
     340            the CanSAS data format. This will allow multiple data files 
     341            and extra nodes to be read in simultaneously. 
     342         
     343        :param dom: dom object with a namespace base of ns 
     344        :param ns: A list of element names that lead up to the dom object 
     345        :param data1D: The data1D object that will be modified 
     346        """ 
     347          
     348        # A portion of every namespace entry 
     349        base_ns = "{0}{1}{2}".format("{", CANSAS_NS.get(self.cansasVersion).get("ns"), "}") 
     350        unit = '' 
     351         
     352        # Go through each child in the parent element 
     353        for node in dom: 
     354            try: 
     355                # Get the element name and set the current ns level 
     356                tagname = node.tag.replace(base_ns, "") 
     357                tagname_original = tagname 
     358                ns.append(tagname) 
     359                attr = node.attrib 
     360                 
     361                # Look for special cases 
     362                save_data1D = data1D 
     363                if tagname == "SASdetector": 
     364                    data1D = Detector() 
     365                elif tagname == "SAScollimation": 
     366                    data1D = Collimation() 
     367                elif tagname == "SASprocess": 
     368                    data1D = Process() 
     369                    for child in node: 
     370                        if child.tag.replace(base_ns, "") == "term": 
     371                            term_attr = {} 
     372                            for attr in child.keys(): 
     373                                term_attr[attr] = ' '.join(child.get(attr).split()) 
     374                            if child.text is not None: 
     375                                term_attr['value'] = ' '.join(child.text.split()) 
     376                            data1D.term.append(term_attr) 
     377                elif tagname == "aperture": 
     378                    data1D = Aperture() 
     379                 
     380                # Get where to store content 
     381                new_current_level, ns_variable, ns_datatype, optional = self._iterate_namespace(ns) 
     382                # If the element is a child element, recurse 
     383                if node.getchildren() is not None: 
     384                    # Returned value is new Data1D object with all previous and new values in it. 
     385                    data1D, extras = self._parse_entry(node, ns, data1D, extras) 
    144386                     
    145         :raise RuntimeError: when the file can't be opened 
    146         :raise ValueError: when the length of the data vectors are inconsistent 
    147         """ 
    148         output = [] 
    149         if os.path.isfile(path): 
    150             basename = os.path.basename(path) 
    151             root, extension = os.path.splitext(basename) 
    152             if ALLOW_ALL or extension.lower() in self.ext: 
    153                 try: 
    154                     tree = etree.parse(path, parser=etree.ETCompatXMLParser()) 
    155                     # Check the format version number 
    156                     # Specifying the namespace will take care of the file 
    157                     # format version 
    158                     root = tree.getroot() 
     387                #Get the information from the node 
     388                node_value = node.text 
     389                if node_value == "": 
     390                    node_value = None 
     391                if node_value is not None: 
     392                    node_value = ' '.join(node_value.split()) 
     393                 
     394                # If the value is a float, compile with units. 
     395                if ns_datatype == "float": 
     396                    # If an empty value is given, store as zero. 
     397                    if node_value is None or node_value.isspace() or node_value.lower() == "nan": 
     398                        node_value = "0.0" 
     399                    node_value, unit = self._unit_conversion(new_current_level, attr, data1D, node_value, optional) 
    159400                     
    160                     entry_list = root.xpath('/ns:SASroot/ns:SASentry', 
    161                                              namespaces={'ns': CANSAS_NS}) 
    162                      
    163                     for entry in entry_list: 
    164                         self.errors = [] 
    165                         sas_entry = self._parse_entry(entry) 
    166                         sas_entry.filename = basename 
    167                          
    168                         # Store loading process information 
    169                         sas_entry.errors = self.errors 
    170                         sas_entry.meta_data['loader'] = self.type_name 
    171                         output.append(sas_entry) 
    172                 except: 
    173                     raise RuntimeError, "%s cannot be read \n" % path 
    174         else: 
    175             raise RuntimeError, "%s is not a file" % path 
    176         # Return output consistent with the loader's api 
    177         if len(output) == 0: 
    178             #cannot return none when it cannot read 
    179             #return None 
    180             raise RuntimeError, "%s cannot be read \n" % path 
    181         elif len(output) == 1: 
    182             return output[0] 
    183         else: 
    184             return output 
    185                  
    186     def _parse_entry(self, dom): 
    187         """ 
    188         Parse a SASentry 
    189          
    190         :param node: SASentry node 
    191          
    192         :return: Data1D object 
    193         """ 
    194         x = numpy.zeros(0) 
    195         y = numpy.zeros(0) 
    196          
    197         data_info = Data1D(x, y) 
    198          
    199         # Look up title 
    200         self._store_content('ns:Title', dom, 'title', data_info) 
    201          
    202         # Look up run number 
    203         nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS}) 
    204         for item in nodes: 
    205             if item.text is not None: 
    206                 value = item.text.strip() 
    207                 if len(value) > 0: 
    208                     data_info.run.append(value) 
    209                     if item.get('name') is not None: 
    210                         data_info.run_name[value] = item.get('name') 
    211                             
    212         # Look up instrument name 
    213         self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', 
    214                              data_info) 
    215  
    216         # Notes 
    217         note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS}) 
    218         for note in note_list: 
    219             try: 
    220                 if note.text is not None: 
    221                     note_value = note.text.strip() 
    222                     if len(note_value) > 0: 
    223                         data_info.notes.append(note_value) 
    224             except: 
    225                 err_mess = "cansas_reader.read: error processing" 
    226                 err_mess += " entry notes\n  %s" % sys.exc_value 
    227                 self.errors.append(err_mess) 
    228                 logging.error(err_mess) 
    229          
    230         # Sample info ################### 
    231         entry = get_content('ns:SASsample', dom) 
    232         if entry is not None: 
    233             data_info.sample.name = entry.get('name') 
    234              
    235         self._store_content('ns:SASsample/ns:ID', 
    236                      dom, 'ID', data_info.sample) 
    237         self._store_float('ns:SASsample/ns:thickness', 
    238                      dom, 'thickness', data_info.sample) 
    239         self._store_float('ns:SASsample/ns:transmission', 
    240                      dom, 'transmission', data_info.sample) 
    241         self._store_float('ns:SASsample/ns:temperature', 
    242                      dom, 'temperature', data_info.sample) 
    243          
    244         nodes = dom.xpath('ns:SASsample/ns:details', 
    245                           namespaces={'ns': CANSAS_NS}) 
    246         for item in nodes: 
    247             try: 
    248                 if item.text is not None: 
    249                     detail_value = item.text.strip() 
    250                     if len(detail_value) > 0: 
    251                         data_info.sample.details.append(detail_value) 
    252             except: 
    253                 err_mess = "cansas_reader.read: error processing " 
    254                 err_mess += " sample details\n  %s" % sys.exc_value 
    255                 self.errors.append(err_mess) 
    256                 logging.error(err_mess) 
    257          
    258         # Position (as a vector) 
    259         self._store_float('ns:SASsample/ns:position/ns:x', 
    260                      dom, 'position.x', data_info.sample) 
    261         self._store_float('ns:SASsample/ns:position/ns:y', 
    262                      dom, 'position.y', data_info.sample) 
    263         self._store_float('ns:SASsample/ns:position/ns:z', 
    264                      dom, 'position.z', data_info.sample) 
    265          
    266         # Orientation (as a vector) 
    267         self._store_float('ns:SASsample/ns:orientation/ns:roll', 
    268                      dom, 'orientation.x', data_info.sample) 
    269         self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
    270                      dom, 'orientation.y', data_info.sample) 
    271         self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
    272                      dom, 'orientation.z', data_info.sample) 
    273         
    274         # Source info ################### 
    275         entry = get_content('ns:SASinstrument/ns:SASsource', dom) 
    276         if entry is not None: 
    277             data_info.source.name = entry.get('name') 
    278          
    279         self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
    280                      dom, 'radiation', data_info.source) 
    281         self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
    282                      dom, 'beam_shape', data_info.source) 
    283         self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
    284                      dom, 'wavelength', data_info.source) 
    285         self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
    286                      dom, 'wavelength_min', data_info.source) 
    287         self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
    288                      dom, 'wavelength_max', data_info.source) 
    289         self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
    290                      dom, 'wavelength_spread', data_info.source) 
    291          
    292         # Beam size (as a vector)    
    293         entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom) 
    294         if entry is not None: 
    295             data_info.source.beam_size_name = entry.get('name') 
    296              
    297         self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
    298                      dom, 'beam_size.x', data_info.source) 
    299         self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
    300                      dom, 'beam_size.y', data_info.source) 
    301         self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
    302                      dom, 'beam_size.z', data_info.source) 
    303          
    304         # Collimation info ################### 
    305         nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', 
    306                           namespaces={'ns': CANSAS_NS}) 
    307         for item in nodes: 
    308             collim = Collimation() 
    309             if item.get('name') is not None: 
    310                 collim.name = item.get('name') 
    311             self._store_float('ns:length', item, 'length', collim) 
    312              
    313             # Look for apertures 
    314             apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS}) 
    315             for apert in apert_list: 
    316                 aperture = Aperture() 
    317                  
    318                 # Get the name and type of the aperture 
    319                 aperture.name = apert.get('name') 
    320                 aperture.type = apert.get('type') 
    321                      
    322                 self._store_float('ns:distance', apert, 'distance', aperture) 
    323                  
    324                 entry = get_content('ns:size', apert) 
    325                 if entry is not None: 
    326                     aperture.size_name = entry.get('name') 
    327                  
    328                 self._store_float('ns:size/ns:x', apert, 'size.x', aperture) 
    329                 self._store_float('ns:size/ns:y', apert, 'size.y', aperture) 
    330                 self._store_float('ns:size/ns:z', apert, 'size.z', aperture) 
    331                  
    332                 collim.aperture.append(aperture) 
    333                  
    334             data_info.collimation.append(collim) 
    335          
    336         # Detector info ###################### 
    337         nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', 
    338                            namespaces={'ns': CANSAS_NS}) 
    339         for item in nodes: 
    340              
    341             detector = Detector() 
    342              
    343             self._store_content('ns:name', item, 'name', detector) 
    344             self._store_float('ns:SDD', item, 'distance', detector) 
    345              
    346             # Detector offset (as a vector) 
    347             self._store_float('ns:offset/ns:x', item, 'offset.x', detector) 
    348             self._store_float('ns:offset/ns:y', item, 'offset.y', detector) 
    349             self._store_float('ns:offset/ns:z', item, 'offset.z', detector) 
    350              
    351             # Detector orientation (as a vector) 
    352             self._store_float('ns:orientation/ns:roll', item, 'orientation.x', 
    353                                detector) 
    354             self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', 
    355                                detector) 
    356             self._store_float('ns:orientation/ns:yaw', item, 'orientation.z', 
    357                                detector) 
    358              
    359             # Beam center (as a vector) 
    360             self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', 
    361                                detector) 
    362             self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', 
    363                               detector) 
    364             self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', 
    365                                detector) 
    366              
    367             # Pixel size (as a vector) 
    368             self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', 
    369                                detector) 
    370             self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', 
    371                                detector) 
    372             self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', 
    373                                detector) 
    374              
    375             self._store_float('ns:slit_length', item, 'slit_length', detector) 
    376              
    377             data_info.detector.append(detector) 
    378  
    379         # Processes info ###################### 
    380         nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS}) 
    381         for item in nodes: 
    382             process = Process() 
    383             self._store_content('ns:name', item, 'name', process) 
    384             self._store_content('ns:date', item, 'date', process) 
    385             self._store_content('ns:description', item, 'description', process) 
    386              
    387             term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS}) 
    388             for term in term_list: 
    389                 try: 
    390                     term_attr = {} 
    391                     for attr in term.keys(): 
    392                         term_attr[attr] = term.get(attr).strip() 
    393                     if term.text is not None: 
    394                         term_attr['value'] = term.text.strip() 
    395                         process.term.append(term_attr) 
    396                 except: 
    397                     err_mess = "cansas_reader.read: error processing " 
    398                     err_mess += " process term\n  %s" % sys.exc_value 
    399                     self.errors.append(err_mess) 
    400                     logging.error(err_mess) 
    401              
    402             note_list = item.xpath('ns:SASprocessnote', 
    403                                    namespaces={'ns': CANSAS_NS}) 
    404             for note in note_list: 
    405                 if note.text is not None: 
    406                     process.notes.append(note.text.strip()) 
    407              
    408             data_info.process.append(process) 
    409              
    410         # Data info ###################### 
    411         nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS}) 
    412         if len(nodes) > 1: 
    413             msg = "CanSAS reader is not compatible with multiple" 
    414             msg += " SASdata entries" 
    415             raise RuntimeError, msg 
    416          
    417         nodes = dom.xpath('ns:SASdata/ns:Idata', namespaces={'ns': CANSAS_NS}) 
    418  
    419         x = numpy.zeros(0) 
    420         y = numpy.zeros(0) 
    421         dx = numpy.zeros(0) 
    422         dy = numpy.zeros(0) 
    423         dxw = numpy.zeros(0) 
    424         dxl = numpy.zeros(0) 
    425          
    426         for item in nodes: 
    427             _x, attr = get_float('ns:Q', item) 
    428             _dx, attr_d = get_float('ns:Qdev', item) 
    429             _dxl, attr_l = get_float('ns:dQl', item) 
    430             _dxw, attr_w = get_float('ns:dQw', item) 
    431             if _dx == None: 
    432                 _dx = 0.0 
    433             if _dxl == None: 
    434                 _dxl = 0.0 
    435             if _dxw == None: 
    436                 _dxw = 0.0 
    437                  
    438             if 'unit' in attr and \ 
    439                 attr['unit'].lower() != data_info.x_unit.lower(): 
    440                 if HAS_CONVERTER == True: 
    441                     try: 
    442                         data_conv_q = Converter(attr['unit']) 
    443                         _x = data_conv_q(_x, units=data_info.x_unit) 
    444                     except: 
    445                         msg = "CanSAS reader: could not convert " 
    446                         msg += "Q unit [%s]; " % attr['unit'], 
    447                         msg += "expecting [%s]\n  %s" % (data_info.x_unit, 
    448                                                          sys.exc_value) 
    449                         raise ValueError, msg 
    450                          
     401                # If appending to a dictionary (meta_data | run_name), name sure the key is unique 
     402                if ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"": 
     403                    # If we are within a Process, Detector, Collimation or Aperture instance, pull out old data1D 
     404                    tagname = self._create_unique_key(data1D.meta_data, tagname, 0) 
     405                    if isinstance(data1D, Data1D) == False: 
     406                        store_me = ns_variable.format("data1D", node_value, tagname) 
     407                        extras.append(store_me) 
     408                        ns_variable = None 
     409                if ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"": 
     410                    tagname = self._create_unique_key(data1D.run_name, tagname, 0) 
     411                 
     412                # Check for Data1D object and any extra commands to save 
     413                if isinstance(data1D, Data1D): 
     414                    for item in extras: 
     415                        exec item 
     416                # Don't bother saving empty information unless it is a float 
     417                if ns_variable is not None and node_value is not None and node_value.isspace() == False: 
     418                    # Format a string and then execute it. 
     419                    store_me = ns_variable.format("data1D", node_value, tagname) 
     420                    exec store_me 
     421                # Get attributes and process them 
     422                if attr is not None: 
     423                    for key in node.keys(): 
     424                        try: 
     425                            cansas_attrib = new_current_level.get("attributes").get(key) 
     426                            attrib_variable = cansas_attrib.get("variable") 
     427                            if key == 'unit' and unit != '': 
     428                                attrib_value = unit 
     429                            else: 
     430                                attrib_value = node.attrib[key] 
     431                            store_attr = attrib_variable.format("data1D", attrib_value, key) 
     432                            exec store_attr 
     433                        except AttributeError as e: 
     434                            pass 
     435                             
     436                      
     437            except Exception as e: 
     438                exc_type, exc_obj, exc_tb = sys.exc_info() 
     439                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 
     440                print(e, exc_type, fname, exc_tb.tb_lineno, tagname, exc_obj) 
     441            finally: 
     442                # Save special cases in original data1D object and then restore the data1D 
     443                if tagname_original == "SASdetector": 
     444                    save_data1D.detector.append(data1D) 
     445                elif tagname_original == "SAScollimation": 
     446                    save_data1D.collimation.append(data1D) 
     447                elif tagname_original == "SASprocess": 
     448                    save_data1D.process.append(data1D) 
     449                elif tagname_original == "aperture": 
     450                    save_data1D.aperture.append(data1D) 
    451451                else: 
    452                     msg = "CanSAS reader: unrecognized Q unit [%s]; "\ 
    453                     % attr['unit'] 
    454                     msg += "expecting [%s]" % data_info.x_unit 
    455                     raise ValueError, msg 
    456                          
    457             # Error in Q 
    458             if 'unit' in attr_d and \ 
    459                 attr_d['unit'].lower() != data_info.x_unit.lower(): 
    460                 if HAS_CONVERTER == True: 
    461                     try: 
    462                         data_conv_q = Converter(attr_d['unit']) 
    463                         _dx = data_conv_q(_dx, units=data_info.x_unit) 
    464                     except: 
    465                         msg = "CanSAS reader: could not convert dQ unit [%s]; "\ 
    466                         % attr['unit'] 
    467                         msg += " expecting " 
    468                         msg += "[%s]\n  %s" % (data_info.x_unit, sys.exc_value) 
    469                         raise ValueError, msg 
    470                          
    471                 else: 
    472                     msg = "CanSAS reader: unrecognized dQ unit [%s]; "\ 
    473                     % attr['unit'] 
    474                     msg += "expecting [%s]" % data_info.x_unit 
    475                     raise ValueError, msg 
    476                          
    477             # Slit length 
    478             if 'unit' in attr_l and \ 
    479                 attr_l['unit'].lower() != data_info.x_unit.lower(): 
    480                 if HAS_CONVERTER == True: 
    481                     try: 
    482                         data_conv_q = Converter(attr_l['unit']) 
    483                         _dxl = data_conv_q(_dxl, units=data_info.x_unit) 
    484                     except: 
    485                         msg = "CanSAS reader: could not convert dQl unit [%s];"\ 
    486                         % attr['unit'] 
    487                         msg += " expecting [%s]\n  %s" % (data_info.x_unit, 
    488                                                           sys.exc_value) 
    489                         raise ValueError, msg  
    490                 else: 
    491                     msg = "CanSAS reader: unrecognized dQl unit [%s];"\ 
    492                     % attr['unit'] 
    493                     msg += " expecting [%s]" % data_info.x_unit 
    494                     raise ValueError, msg 
    495                          
    496             # Slit width 
    497             if 'unit' in attr_w and \ 
    498             attr_w['unit'].lower() != data_info.x_unit.lower(): 
    499                 if HAS_CONVERTER == True: 
    500                     try: 
    501                         data_conv_q = Converter(attr_w['unit']) 
    502                         _dxw = data_conv_q(_dxw, units=data_info.x_unit) 
    503                     except: 
    504                         msg = "CanSAS reader: could not convert dQw unit [%s];"\ 
    505                         % attr['unit'] 
    506                         msg += " expecting [%s]\n  %s" % (data_info.x_unit, 
    507                                                           sys.exc_value) 
    508                         raise ValueError, msg 
    509                          
    510                 else: 
    511                     msg = "CanSAS reader: unrecognized dQw unit [%s];"\ 
    512                     % attr['unit'] 
    513                     msg += " expecting [%s]" % data_info.x_unit 
    514                     raise ValueError, msg 
    515             _y, attr = get_float('ns:I', item) 
    516             _dy, attr_d = get_float('ns:Idev', item) 
    517             if _dy == None: 
    518                 _dy = 0.0 
    519             if 'unit' in attr and \ 
    520             attr['unit'].lower() != data_info.y_unit.lower(): 
    521                 if HAS_CONVERTER == True: 
    522                     try: 
    523                         data_conv_i = Converter(attr['unit']) 
    524                         _y = data_conv_i(_y, units=data_info.y_unit) 
    525                     except: 
    526                         if attr['unit'].lower() == 'count': 
    527                             pass 
    528                         else: 
    529                             msg = "CanSAS reader: could not" 
    530                             msg += " convert I(q) unit [%s];" % str(attr['unit']) 
    531                             msg += " expecting [%s]\n" % str(data_info.y_unit) 
    532                             msg += "  %s" % str(sys.exc_value) 
    533                             raise ValueError, msg 
    534                 else: 
    535                     msg = "CanSAS reader: unrecognized I(q) unit [%s];"\ 
    536                     % attr['unit'] 
    537                     msg += " expecting [%s]" % data_info.y_unit 
    538                     raise ValueError, msg  
    539                          
    540             if 'unit' in attr_d and \ 
    541             attr_d['unit'].lower() != data_info.y_unit.lower(): 
    542                 if HAS_CONVERTER == True: 
    543                     try: 
    544                         data_conv_i = Converter(attr_d['unit']) 
    545                         _dy = data_conv_i(_dy, units=data_info.y_unit) 
    546                     except: 
    547                         if attr_d['unit'].lower() == 'count': 
    548                             pass 
    549                         else: 
    550                             msg = "CanSAS reader: could not convert dI(q) unit " 
    551                             msg += "[%s]; expecting [%s]\n  %s" % (attr_d['unit'], 
    552                                                  data_info.y_unit, sys.exc_value) 
    553                             raise ValueError, msg 
    554                 else: 
    555                     msg = "CanSAS reader: unrecognized dI(q) unit [%s]; "\ 
    556                     % attr_d['unit'] 
    557                     msg += "expecting [%s]" % data_info.y_unit 
    558                     raise ValueError, msg 
    559                  
    560             if _x is not None and _y is not None: 
    561                 x = numpy.append(x, _x) 
    562                 y = numpy.append(y, _y) 
    563                 dx = numpy.append(dx, _dx) 
    564                 dy = numpy.append(dy, _dy) 
    565                 dxl = numpy.append(dxl, _dxl) 
    566                 dxw = numpy.append(dxw, _dxw) 
    567         # Zeros in dx, dy 
    568         if not numpy.all(dx == 0): 
    569             dx[dx == 0] = _ZERO 
    570         if not numpy.all(dy == 0): 
    571             dy[dy == 0] = _ZERO 
    572         
    573         data_info.x = x[x != 0] 
    574         data_info.y = y[x != 0] 
    575         data_info.dx = dx[x != 0] 
    576          
    577         data_info.dy = dy[x != 0] 
    578         data_info.dxl = dxl[x != 0] 
    579         data_info.dxw = dxw[x != 0] 
    580          
    581         data_conv_q = None 
    582         data_conv_i = None 
    583          
    584         if HAS_CONVERTER == True and data_info.x_unit != '1/A': 
    585             data_conv_q = Converter('1/A') 
    586             # Test it 
    587             data_conv_q(1.0, data_info.x_unit) 
    588              
    589         if HAS_CONVERTER == True and data_info.y_unit != '1/cm': 
    590             data_conv_i = Converter('1/cm') 
    591             # Test it 
    592             data_conv_i(1.0, data_info.y_unit) 
    593                  
    594         if data_conv_q is not None: 
    595             data_info.xaxis("\\rm{Q}", data_info.x_unit) 
    596         else: 
    597             data_info.xaxis("\\rm{Q}", 'A^{-1}') 
    598         if data_conv_i is not None: 
    599             data_info.yaxis("\\rm{Intensity}", data_info.y_unit) 
    600         else: 
    601             data_info.yaxis("\\rm{Intensity}", "cm^{-1}") 
    602  
    603         return data_info 
    604  
     452                    save_data1D = data1D 
     453                data1D = save_data1D 
     454                # Remove tagname from ns to restore original base 
     455                ns.remove(tagname_original) 
     456         
     457        return data1D, extras 
     458         
    605459    def _to_xml_doc(self, datainfo): 
    606460        """ 
     
    613467            raise RuntimeError, "The cansas writer expects a Data1D instance" 
    614468         
     469        ns = CANSAS_NS.get(self.cansasVersion).get("ns") 
    615470        doc = xml.dom.minidom.Document() 
    616471        main_node = doc.createElement("SASroot") 
    617         main_node.setAttribute("version", self.version) 
    618         main_node.setAttribute("xmlns", "cansas1d/%s" % self.version) 
     472        main_node.setAttribute("version", self.cansasVersion) 
     473        main_node.setAttribute("xmlns", ns) 
    619474        main_node.setAttribute("xmlns:xsi", 
    620475                               "http://www.w3.org/2001/XMLSchema-instance") 
    621476        main_node.setAttribute("xsi:schemaLocation", 
    622                                "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version) 
     477                               "{0} http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd".format(ns)) 
    623478         
    624479        doc.appendChild(main_node) 
     
    646501                write_node(doc, pt, "I", datainfo.y[i], 
    647502                            {'unit': datainfo.y_unit}) 
     503            if datainfo.dy != None and len(datainfo.dy) >= i: 
     504                write_node(doc, pt, "Idev", datainfo.dy[i], 
     505                            {'unit': datainfo.y_unit}) 
    648506            if datainfo.dx != None and len(datainfo.dx) >= i: 
    649507                write_node(doc, pt, "Qdev", datainfo.dx[i], 
     508                            {'unit': datainfo.x_unit}) 
     509            if datainfo.dxw != None and len(datainfo.dxw) >= i: 
     510                write_node(doc, pt, "dQw", datainfo.dxw[i], 
    650511                            {'unit': datainfo.x_unit}) 
    651512            if datainfo.dxl != None and len(datainfo.dxl) >= i: 
    652513                write_node(doc, pt, "dQl", datainfo.dxl[i], 
    653514                            {'unit': datainfo.x_unit}) 
    654             if datainfo.dxw != None and len(datainfo.dxw) >= i: 
    655                 write_node(doc, pt, "dQw", datainfo.dxw[i], 
    656                             {'unit': datainfo.x_unit}) 
    657             if datainfo.dy != None and len(datainfo.dy) >= i: 
    658                 write_node(doc, pt, "Idev", datainfo.dy[i], 
    659                             {'unit': datainfo.y_unit}) 
     515 
     516        # Transmission Spectrum Info 
     517        if len(datainfo.trans_spectrum.wavelength) > 0: 
     518            node = doc.createElement("SAStransmission_spectrum") 
     519            entry_node.appendChild(node) 
     520            for i in range(len(datainfo.trans_spectrum.wavelength)): 
     521                pt = doc.createElement("Tdata") 
     522                node.appendChild(pt) 
     523                write_node(doc, pt, "Lambda", datainfo.trans_spectrum.wavelength[i],  
     524                           {'unit': datainfo.trans_spectrum.wavelength_unit}) 
     525                write_node(doc, pt, "T", datainfo.trans_spectrum.transmission[i],  
     526                           {'unit': datainfo.trans_spectrum.transmission_unit}) 
     527                if datainfo.trans_spectrum.transmission_deviation != None \ 
     528                and len(datainfo.trans_spectrum.transmission_deviation) >= i: 
     529                    write_node(doc, pt, "Tdev", datainfo.trans_spectrum.transmission_deviation[i],  
     530                               {'unit': datainfo.trans_spectrum.transmission_deviation_unit}) 
    660531 
    661532        # Sample info 
     
    670541        write_node(doc, sample, "temperature", datainfo.sample.temperature, 
    671542                   {"unit": datainfo.sample.temperature_unit}) 
    672          
    673         for item in datainfo.sample.details: 
    674             write_node(doc, sample, "details", item) 
    675543         
    676544        pos = doc.createElement("position") 
     
    699567            sample.appendChild(ori) 
    700568         
     569        for item in datainfo.sample.details: 
     570            write_node(doc, sample, "details", item) 
     571         
    701572        # Instrument info 
    702573        instr = doc.createElement("SASinstrument") 
     
    710581            source.setAttribute("name", str(datainfo.source.name)) 
    711582        instr.appendChild(source) 
    712          
    713583        write_node(doc, source, "radiation", datainfo.source.radiation) 
    714         write_node(doc, source, "beam_shape", datainfo.source.beam_shape) 
     584         
    715585        size = doc.createElement("beam_size") 
    716586        if datainfo.source.beam_size_name is not None: 
     
    727597            source.appendChild(size) 
    728598             
     599        write_node(doc, source, "beam_shape", datainfo.source.beam_shape) 
    729600        write_node(doc, source, "wavelength", 
    730601                   datainfo.source.wavelength, 
     
    758629                coll.appendChild(ap) 
    759630                 
    760                 write_node(doc, ap, "distance", apert.distance, 
    761                            {"unit": apert.distance_unit}) 
    762                  
    763631                size = doc.createElement("size") 
    764632                if apert.size_name is not None: 
     
    772640                if written == True: 
    773641                    ap.appendChild(size) 
     642                 
     643                write_node(doc, ap, "distance", apert.distance, 
     644                           {"unit": apert.distance_unit}) 
    774645 
    775646        #   Detectors 
     
    779650            written = written | write_node(doc, det, "SDD", item.distance, 
    780651                                           {"unit": item.distance_unit}) 
    781             written = written | write_node(doc, det, "slit_length", 
    782                                            item.slit_length, 
    783                                            {"unit": item.slit_length_unit}) 
    784652            if written == True: 
    785653                instr.appendChild(det) 
     
    794662            if written == True: 
    795663                det.appendChild(off) 
     664                 
     665            ori = doc.createElement("orientation") 
     666            written = write_node(doc, ori, "roll", item.orientation.x, 
     667                                 {"unit": item.orientation_unit}) 
     668            written = written | write_node(doc, ori, "pitch", 
     669                                           item.orientation.y, 
     670                                           {"unit": item.orientation_unit}) 
     671            written = written | write_node(doc, ori, "yaw", 
     672                                           item.orientation.z, 
     673                                           {"unit": item.orientation_unit}) 
     674            if written == True: 
     675                det.appendChild(ori) 
    796676             
    797677            center = doc.createElement("beam_center") 
     
    816696            if written == True: 
    817697                det.appendChild(pix) 
    818                  
    819             ori = doc.createElement("orientation") 
    820             written = write_node(doc, ori, "roll", item.orientation.x, 
    821                                  {"unit": item.orientation_unit}) 
    822             written = written | write_node(doc, ori, "pitch", 
    823                                            item.orientation.y, 
    824                                            {"unit": item.orientation_unit}) 
    825             written = written | write_node(doc, ori, "yaw", 
    826                                            item.orientation.z, 
    827                                            {"unit": item.orientation_unit}) 
    828             if written == True: 
    829                 det.appendChild(ori) 
    830                  
     698            written = written | write_node(doc, det, "slit_length", 
     699                                           item.slit_length, 
     700                                           {"unit": item.slit_length_unit}) 
     701             
    831702        # Processes info 
    832703        for item in datainfo.process: 
     
    843714            for note in item.notes: 
    844715                write_node(doc, node, "SASprocessnote", note) 
    845          
     716            if len(item.notes) == 0: 
     717                write_node(doc, node, "SASprocessnote", "") 
     718                 
     719        # Note info 
     720        if len(datainfo.notes) == 0: 
     721            node = doc.createElement("SASnote") 
     722            entry_node.appendChild(node) 
     723            if node.hasChildNodes(): 
     724                for child in node.childNodes: 
     725                    node.removeChild(child) 
     726        else: 
     727            for item in datainfo.notes: 
     728                node = doc.createElement("SASnote") 
     729                entry_node.appendChild(node) 
     730                node.appendChild(doc.createTextNode(item)) 
     731                 
    846732        # Return the document, and the SASentry node associated with 
    847733        # the data we just wrote 
     
    861747        fd.write(doc.toprettyxml()) 
    862748        fd.close() 
    863          
    864     def _store_float(self, location, node, variable, storage, optional=True): 
    865         """ 
    866         Get the content of a xpath location and store 
    867         the result. Check that the units are compatible 
    868         with the destination. The value is expected to 
    869         be a float. 
    870          
    871         The xpath location might or might not exist. 
    872         If it does not exist, nothing is done 
    873          
    874         :param location: xpath location to fetch 
    875         :param node: node to read the data from 
    876         :param variable: name of the data member to store it in [string] 
    877         :param storage: data object that has the 'variable' data member 
    878         :param optional: if True, no exception will be raised 
    879             if unit conversion can't be done 
    880  
    881         :raise ValueError: raised when the units are not recognized 
    882         """ 
    883         entry = get_content(location, node) 
    884         try: 
    885             value = float(entry.text) 
    886         except: 
    887             value = None 
    888              
    889         if value is not None: 
    890             # If the entry has units, check to see that they are 
    891             # compatible with what we currently have in the data object 
    892             units = entry.get('unit') 
    893             if units is not None: 
    894                 toks = variable.split('.') 
    895                 local_unit = None 
    896                 exec "local_unit = storage.%s_unit" % toks[0] 
    897                 if local_unit != None and units.lower() != local_unit.lower(): 
    898                     if HAS_CONVERTER == True: 
    899                         try: 
    900                             conv = Converter(units) 
    901                             exec "storage.%s = %g" % (variable, 
    902                                             conv(value, units=local_unit)) 
    903                         except: 
    904                             err_mess = "CanSAS reader: could not convert" 
    905                             err_mess += " %s unit [%s]; expecting [%s]\n  %s" \ 
    906                                 % (variable, units, local_unit, sys.exc_value) 
    907                             self.errors.append(err_mess) 
    908                             if optional: 
    909                                 logging.info(err_mess) 
    910                             else: 
    911                                 raise ValueError, err_mess 
    912                     else: 
    913                         err_mess = "CanSAS reader: unrecognized %s unit [%s];"\ 
    914                         % (variable, units) 
    915                         err_mess += " expecting [%s]" % local_unit 
    916                         self.errors.append(err_mess) 
    917                         if optional: 
    918                             logging.info(err_mess) 
    919                         else: 
    920                             raise ValueError, err_mess 
    921                 else: 
    922                     exec "storage.%s = value" % variable 
    923             else: 
    924                 exec "storage.%s = value" % variable 
    925                  
    926     def _store_content(self, location, node, variable, storage): 
    927         """ 
    928         Get the content of a xpath location and store 
    929         the result. The value is treated as a string. 
    930          
    931         The xpath location might or might not exist. 
    932         If it does not exist, nothing is done 
    933          
    934         :param location: xpath location to fetch 
    935         :param node: node to read the data from 
    936         :param variable: name of the data member to store it in [string] 
    937         :param storage: data object that has the 'variable' data member 
    938          
    939         :return: return a list of errors 
    940         """ 
    941         entry = get_content(location, node) 
    942         if entry is not None and entry.text is not None: 
    943             exec "storage.%s = entry.text.strip()" % variable 
Note: See TracChangeset for help on using the changeset viewer.