Package glue :: Package ligolw :: Module param
[hide private]
[frames] | no frames]

Source Code for Module glue.ligolw.param

  1  # Copyright (C) 2006--2009,2012--2016  Kipp Cannon 
  2  # 
  3  # This program is free software; you can redistribute it and/or modify it 
  4  # under the terms of the GNU General Public License as published by the 
  5  # Free Software Foundation; either version 3 of the License, or (at your 
  6  # option) any later version. 
  7  # 
  8  # This program is distributed in the hope that it will be useful, but 
  9  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General 
 11  # Public License for more details. 
 12  # 
 13  # You should have received a copy of the GNU General Public License along 
 14  # with this program; if not, write to the Free Software Foundation, Inc., 
 15  # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
 16   
 17   
 18  # 
 19  # ============================================================================= 
 20  # 
 21  #                                   Preamble 
 22  # 
 23  # ============================================================================= 
 24  # 
 25   
 26   
 27  """ 
 28  High-level support for Param elements. 
 29  """ 
 30   
 31   
 32  import re 
 33  import sys 
 34  from xml.sax.saxutils import escape as xmlescape 
 35  from xml.sax.xmlreader import AttributesImpl as Attributes 
 36  try: 
 37          import yaml 
 38  except ImportError: 
 39          # yaml serialization is optional 
 40          pass 
 41   
 42   
 43  from glue import git_version 
 44  from . import ligolw 
 45  from . import types as ligolwtypes 
 46   
 47   
 48  __author__ = "Kipp Cannon <kipp.cannon@ligo.org>" 
 49  __version__ = "git id %s" % git_version.id 
 50  __date__ = git_version.date 
51 52 53 # 54 # ============================================================================= 55 # 56 # Utilities 57 # 58 # ============================================================================= 59 # 60 61 62 -def get_param(xmldoc, name):
63 """ 64 Scan xmldoc for a param named name. Raises ValueError if not 65 exactly 1 such param is found. 66 """ 67 params = Param.getParamsByName(xmldoc, name) 68 if len(params) != 1: 69 raise ValueError("document must contain exactly one %s param" % Param.ParamName(name)) 70 return params[0]
71
72 73 -def get_pyvalue(xml, name):
74 """ 75 Convenience wrapper for get_param() that recovers an instance of a 76 Python builtin type from a Param element. 77 """ 78 # Note: the Param is automatically parsed into the correct Python 79 # type, so this function is mostly a no-op. 80 return get_param(xml, name).pcdata
81
82 83 # 84 # ============================================================================= 85 # 86 # Element Classes 87 # 88 # ============================================================================= 89 # 90 91 92 # 93 # FIXME: params of type string should be quoted in order to correctly 94 # delimit their extent. If that were done, then the pcdata in a Param 95 # element could be parsed using the Stream tokenizer (i.e., as though it 96 # were a single-token stream), which would guarantee that Stream data and 97 # Param data is parsed using the exact same rules. Unfortunately, common 98 # practice is to not quote Param string values, so we parse things 99 # differently here. In particular, we strip whitespace from the start and 100 # stop of all Param pcdata. If this causes your string Param values to be 101 # corrupted (because you need leading and trailing white space preserved), 102 # then you need to make everyone switch to quoting their string Param 103 # values, and once that is done then this code will be changed. Perhaps a 104 # warning should be emitted for non-quoted strings to encourage a 105 # transition? 106 # 107 108 109 -class Param(ligolw.Param):
110 """ 111 High-level Param element. The value is stored in the pcdata 112 attribute as the native Python type rather than as a string. 113 """
114 - class ParamName(ligolw.LLWNameAttr):
115 dec_pattern = re.compile(r"(?P<Name>[a-z0-9_:]+):param\Z") 116 enc_pattern = u"%s:param"
117 118 Name = ligolw.attributeproxy(u"Name", enc = ParamName.enc, dec = ParamName) 119 Scale = ligolw.attributeproxy(u"Scale", enc = ligolwtypes.FormatFunc[u"real_8"], dec = ligolwtypes.ToPyType[u"real_8"]) 120 Type = ligolw.attributeproxy(u"Type", default = u"lstring") 121
122 - def endElement(self):
123 if self.pcdata is not None: 124 # convert pcdata from string to native Python type 125 if self.Type == u"yaml": 126 try: 127 yaml 128 except NameError: 129 raise NotImplementedError("yaml support not installed") 130 self.pcdata = yaml.load(self.pcdata) 131 else: 132 self.pcdata = ligolwtypes.ToPyType[self.Type](self.pcdata.strip())
133
134 - def write(self, fileobj = sys.stdout, indent = u""):
135 fileobj.write(self.start_tag(indent)) 136 for c in self.childNodes: 137 if c.tagName not in self.validchildren: 138 raise ligolw.ElementError("invalid child %s for %s" % (c.tagName, self.tagName)) 139 c.write(fileobj, indent + ligolw.Indent) 140 if self.pcdata is not None: 141 if self.Type == u"yaml": 142 try: 143 yaml 144 except NameError: 145 raise NotImplementedError("yaml support not installed") 146 fileobj.write(xmlescape(yaml.dump(self.pcdata).strip())) 147 else: 148 # we have to strip quote characters from 149 # string formats (see comment above). if 150 # the result is a zero-length string it 151 # will get parsed as None when the document 152 # is loaded, but on this code path we know 153 # that .pcdata is not None, so as a hack 154 # until something better comes along we 155 # replace zero-length strings here with a 156 # bit of whitespace. whitespace is 157 # stripped from strings during parsing so 158 # this will turn .pcdata back into a 159 # zero-length string. NOTE: if .pcdata is 160 # None, then it will become a zero-length 161 # string, which will be turned back into 162 # None on parsing, so this mechanism is how 163 # None is encoded (a zero-length Param is 164 # None) 165 fileobj.write(xmlescape(ligolwtypes.FormatFunc[self.Type](self.pcdata).strip(u"\"") or u" ")) 166 fileobj.write(self.end_tag(u"") + u"\n")
167 168 @classmethod
169 - def build(cls, name, Type, value, start = None, scale = None, unit = None, dataunit = None, comment = None):
170 """ 171 Construct a LIGO Light Weight XML Param document subtree. 172 FIXME: document keyword arguments. 173 """ 174 elem = cls() 175 elem.Name = name 176 elem.Type = Type 177 elem.pcdata = value 178 # FIXME: I have no idea how most of the attributes should be 179 # encoded, I don't even know what they're supposed to be. 180 if dataunit is not None: 181 elem.DataUnit = dataunit 182 if scale is not None: 183 elem.Scale = scale 184 if start is not None: 185 elem.Start = start 186 if unit is not None: 187 elem.Unit = unit 188 if comment is not None: 189 elem.appendChild(ligolw.Comment()).pcdata = comment 190 return elem
191 192 @classmethod
193 - def from_pyvalue(cls, name, value, **kwargs):
194 """ 195 Convenience wrapper for .build() that constructs a Param 196 element from an instance of a Python builtin type. See 197 .build() for a description of the valid keyword arguments. 198 """ 199 return cls.build(name, ligolwtypes.FromPyType[type(value)], value, **kwargs)
200 201 @classmethod
202 - def getParamsByName(cls, elem, name):
203 """ 204 Return a list of params with name name under elem. 205 """ 206 name = cls.ParamName(name) 207 return elem.getElements(lambda e: (e.tagName == cls.tagName) and (e.Name == name))
208
209 210 # 211 # ============================================================================= 212 # 213 # Content Handler 214 # 215 # ============================================================================= 216 # 217 218 219 # 220 # Override portions of a ligolw.LIGOLWContentHandler class 221 # 222 223 224 -def use_in(ContentHandler):
225 """ 226 Modify ContentHandler, a sub-class of 227 glue.ligolw.LIGOLWContentHandler, to cause it to use the Param 228 class defined in this module when parsing XML documents. 229 230 Example: 231 232 >>> from glue.ligolw import ligolw 233 >>> def MyContentHandler(ligolw.LIGOLWContentHandler): 234 ... pass 235 ... 236 >>> use_in(MyContentHandler) 237 """ 238 def startParam(self, parent, attrs): 239 return Param(attrs)
240 241 ContentHandler.startParam = startParam 242 243 return ContentHandler 244