From 3f2febb2a3af27e899b680ab3d2fba3f149da596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20=C3=96zkural?= Date: Wed, 21 Sep 2005 22:46:55 +0000 Subject: [PATCH] * birtakim duzeltmeler (saka canim) * autoxml'de muhtelif duzeltmeler * toplam code size'ini azaltip, list type'ini encode disinda implement et. sira class type'inda * run.py'de pisi.api'i init et, ne bileyim iste. * code'u yazan biziz calistiran da biz olacagiz --- pisi/xmlfile.py | 242 ++++++++++++++++++++++++------------------ tests/run.py | 4 + tests/xmlfiletests.py | 6 +- 3 files changed, 144 insertions(+), 108 deletions(-) diff --git a/pisi/xmlfile.py b/pisi/xmlfile.py index 75382b65..2fe3c3cb 100644 --- a/pisi/xmlfile.py +++ b/pisi/xmlfile.py @@ -143,7 +143,7 @@ class autoxml(type): class Company: __metaclass__ = autoxml - t_Employees = [ Employee, xmlfile.mandatory] + t_Employees = [ [Employee], xmlfile.mandatory] Logically, inside the Company tag, we will have several Employee tags, which are inserted to the Employees instance variable of @@ -154,7 +154,7 @@ class autoxml(type): It is also possible to change the XML path we expect the tag in, just like with any other tag. - t_Employees = [ Employee, xmlfile.mandatory, 'Employees/Employee'] + t_Employees = [ [Employee], xmlfile.mandatory, 'Employees/Employee'] You see, it works like magic, when it works of course. All of it @@ -175,12 +175,11 @@ class autoxml(type): #TODO: initialize class attribute __xml_tags #setattr(cls, 'xml_variables', []) - if not dict.has_key('tag'): - setattr(cls, 'tag', name) + # default class tag is class name + if not dict.has_key('tag'): + cls.tag = name - root_tag = cls.tag - - # generate helper routines + # generate helper routines, for each XML component inits = [] decoders = [] encoders = [] @@ -188,177 +187,208 @@ class autoxml(type): for var in dict: if var.startswith('t_') or var.startswith('a_'): name = var[2:] - #print 'name', name - if var.startswith('t_'): - x = autoxml.gen_tag(cls, name) - elif var.startswith('a_'): - x = autoxml.gen_attr(cls, name) - ctx.ui.debug(x) + print 'generating member', name + if var.startswith('a_'): + x = autoxml.gen_attr_member(cls, name) + elif var.startswith('t_'): + x = autoxml.gen_tag_member(cls, name) (init, decoder, encoder, formatter) = x inits.append(init) decoders.append(decoder) encoders.append(encoder) formatters.append(formatter) - varname = cls.mixed_case(name) - - setattr(cls, 'initializers', inits) + # generate top-level helper functions + cls.initializers = inits def initialize(self): for init in self.__class__.initializers: init(self) - setattr(cls, '__init__', initialize) + cls.__init__ = initialize - setattr(cls, 'decoders', decoders) + cls.decoders = decoders def decode(self, node): for decoder in self.__class__.decoders: - setattr(self, varname, decoder(self, node)) - setattr(cls, 'decode', decode) + decoder(self, node) + cls.decode = decode - setattr(cls, 'encoders', encoders) + cls.encoders = encoders def encode(self, xml): for encoder in self.__class__.encoders: - encoder(self, xml, getattr(self, varname)) - setattr(cls, 'encode', encode) + encoder(self, xml) + cls.encode = encode - setattr(cls, 'formatters', formatters) + cls.formatters = formatters def format(self): string = '' for formatter in self.__class__.formatters: - string = string + formatter(self) + string += formatter(self) return string - setattr(cls, 'format', format) + cls.format = format if not dict.has_key('__str__'): - def string(self): - return self.format() - setattr(cls, '__str__', string) + cls.__str__ = format - def gen_tag(cls, tag): - # generate readers and writers for the tag - val = getattr(cls, 't_' + tag) - tag_type = val[0] - return autoxml.gen_tag_aux(cls, tag, tag_type, val) - - def gen_tag_aux(cls, tag, tag_type, val): - if type(tag_type) == types.TypeType: - # basic types - return autoxml.gen_basic_tag(cls, tag, val) - elif type(tag_type) == types.ClassType: - return autoxml.gen_class_tag(cls, tag, val) - elif type(tag_type) == types.ListType: - return autoxml.gen_list_tag(cls, tag, val) - - def gen_attr(cls, tag): - "generate readers and writers for an attribute" - #print 'attr:', tag - val = getattr(cls, 'a_' + tag) - tag_type = val[0] + def gen_attr_member(cls, attr): + """generate readers and writers for an attribute member""" + print 'attr:', attr + spec = getattr(cls, 'a_' + attr) + tag_type = spec[0] assert type(tag_type) == type(type) - return autoxml.gen_basic_attr(cls, tag, val) + def readtext(node, attr): + return getNodeAttribute(node, attr) + def writetext(xml, node, attr, value): + node.setAttribute(attr, value) + anonfuns = cls.gen_anon_basic(attr, spec, readtext, writetext) + return cls.gen_named_comp(attr, spec, anonfuns) + + def gen_tag_member(cls, tag): + """generate helper funs for tag member of class""" + spec = getattr(cls, 't_' + tag) + def readtext(node, tag): + return getNodeText(getNode(node, tag)) + def writetext(xml, node, tag, value): + xml.addTextNodeUnder(node, tag, value) + anonfuns = cls.gen_tag(tag, spec, readtext, writetext) + return cls.gen_named_comp(tag, spec, anonfuns) + + def gen_named_comp(cls, token, spec, anonfuns): + """generate a named component tag/attr. a decoration of + anonymous functions that do not bind to variable names""" + name = cls.mixed_case(token) + token_type = spec[0] + req = spec[1] + (init_a, decode_a, encode_a, format_a) = anonfuns + + def init(self): + setattr(self, name, init_a()) + + def decode(self, node): + setattr(self, name, decode_a(node)) + + def encode(self, xml): + if hasattr(self, name): + value = getattr(self, name) + else: + value = None + encode_a(xml, value) + + def format(self): + if hasattr(self, name): + value = getattr(self,name) + return '%s: %s\n' % (token, format_a(value)) + else: + if req == mandatory: + raise Error('Mandatory variable %s not available' % name) + return '' + + return (init, decode, encode, format) + + def gen_tag(cls, tag, spec, readtext, writetext): + """generate readers and writers for the tag""" + tag_type = spec[0] + if type(tag_type) == types.TypeType: + return cls.gen_anon_basic(tag, spec, readtext, writetext) + elif type(tag_type) == types.ListType: + return cls.gen_list_tag(tag, spec, readtext, writetext) + elif type(tag_type) == types.ClassType: + return cls.gen_class_tag(tag, spec, readtext, writetext) def mixed_case(cls, identifier): + """helper function to turn token name into mixed case""" identifier_p = identifier[0].lower() + identifier[1:] return identifier_p - def gen_basic(cls, token, val, readtext, writetext): - """Generate a basic tag or attribute. This has got - to be pretty generic so we can invoke it from the complex - types such as Class and List""" + def gen_anon_basic(cls, token, spec, readtext, writetext): + """Generate a tag or attribute with one of the basic + types like integer. This has got to be pretty generic + so we can invoke it from the complex types such as Class + and List. The readtext and writetext arguments achieve + the text input I/O for this datatype.""" + name = cls.mixed_case(token) - token_type = val[0] - req = val[1] + token_type = spec[0] + req = spec[1] - def initialize(self): - #print 'init:', name - setattr(self, name, None) + def initialize(): + """default value for all basic types is None""" + return None - def decode(self, node): + def decode(node): + """decode from DOM node, the value, watching the spec""" text = readtext(node, token) + print 'read text ', text if text: try: value = autoxml.basic_cons_map[token_type](text) except Error: - raise Error('Type mismatch') - setattr(self, name, value) + raise Error('Type mismatch: read text cannot be decoded') + return value else: if req == mandatory: raise Error('Mandatory argument not available') else: return None - def encode(self, xml, value): + def encode(xml, value): + """encode given value into xml file""" node = xml.newNode(cls.tag) - if hasattr(self, name): + if value: writetext(xml, node, token, str(value)) else: if req == mandatory: raise Error('Mandatory argument not available') - def format(self): - if hasattr(self, name): - return '%s: %s\n' % (token, str(getattr(self, name))) - else: - if req == mandatory: - raise Error('Mandatory variable %s not available' % name) - return "" + def format(value): + return str(value) return initialize, decode, encode, format - def gen_basic_attr(cls, attr, spec): - """generate an attribute with a basic datatype""" - def readtext(node, attr): - return getNodeAttribute(node, attr) - def writetext(xml, node, attr, value): - node.setAttribute(attr, value) - return autoxml.gen_basic(cls, attr, spec, readtext, writetext) - - def gen_basic_tag(cls, tag, spec): - """generate a tag with a basic datatype""" - def readtext(node, tag): - return getNodeText(getNode(node, tag)) - def writetext(xml, node, tag, value): - xml.addTextNodeUnder(node, tag, value) - return autoxml.gen_basic(cls, tag, spec, readtext, writetext) - - def gen_class_tag(cls, tag, val): + def gen_class_tag(cls, tag, spec, readtext, writetext): pass - def gen_list_tag(cls, tag, val): + def gen_list_tag(cls, tag, spec, readtext, writetext): + """ generate a list member of a class. we do not support + list members that are embedded in lists. """ name = cls.mixed_case(tag) - tag_type = val[0] - req = val[1] + tag_type = spec[0] + req = spec[1] - if len(val)>=3: - path = val[2] # an alternative path specified + if len(spec)>=3: + path = spec[2] # an alternative path specified else: path = tag # otherwise it's the same name as # the tag if len(tag_type) != 1: raise Error('List type must contain only one element') - x = autoxml.gen_tag_aux(cls, tag, tag_type[0], val) + def readtext(node, tag): + return getNodeText(node) + def writetext(xml, node, tag, value): + xml.addTextNode(node, value) + x = cls.gen_tag(tag, [tag_type[0], mandatory], readtext, writetext) (init_item, decode_item, encode_item, format_item) = x - def init(self): - setattr(self, name, []) + def init(): + return [] - def decode(self, node): + def decode(node): l = [] - self.nodes = getAllNodes(node, path) - print 'F U', self.nodes - if len(self.nodes) is 0 and req is mandatory: + nodes = getAllNodes(node, path) + print 'F U', nodes + if len(nodes) is 0 and req is mandatory: raise Error('Mandatory list empty') - for node in self.nodes: - l.append(decode_item(self, node)) + for node in nodes: + l.append(decode_item(node)) return l - def encode(self, xml, value): + def encode(xml, l): pass - def format(self): + def format(l): #print 'format:', name s = '' - l = getattr(self, name) for ix in range(len(l)): - s += str(ix) + format_item(l[ix]) + s += str(ix+1) + ': ' + format_item(l[ix]) + if ix != len(l)-1: + s += ', ' return s return (init, decode, encode, format) diff --git a/tests/run.py b/tests/run.py index 1cca4e1c..809b7a1c 100755 --- a/tests/run.py +++ b/tests/run.py @@ -17,6 +17,9 @@ import os sys.path.append('.') sys.path.append('..') +import pisi +import pisi.api + runTestSuite = lambda(x): unittest.TextTestRunner(verbosity=2).run(x) def run_all(): @@ -59,6 +62,7 @@ def run_all(): runTestSuite(alltests) if __name__ == "__main__": + pisi.api.init(False) args = sys.argv if len(args) > 1: # run modules given from the command line tests = sys.argv[1:] diff --git a/tests/xmlfiletests.py b/tests/xmlfiletests.py index c563d2b8..0d023c67 100644 --- a/tests/xmlfiletests.py +++ b/tests/xmlfiletests.py @@ -32,7 +32,7 @@ class XmlFileTestCase(unittest.TestCase): t_Number = [types.IntType, xmlfile.optional] t_Email = [types.StringType, xmlfile.optional] a_href = [types.StringType, xmlfile.mandatory] - #t_Projects = [ [types.StringType], xmlfile.mandatory] + t_Projects = [ [types.StringType], xmlfile.mandatory, 'Projects/Project'] a = A() self.assertEqual(a.href, None) dom = mdom.parse('tests/a.xml') @@ -43,13 +43,15 @@ class XmlFileTestCase(unittest.TestCase): self.assertEqual(a.number, 868) self.assertEqual(a.name, 'Eray Ozkural') string = a.format() - #print '*', string + print '*', string #self.assert_(string.startswith('Name')) outfn = '/tmp/a2.xml' xml = xmlfile.XmlFile('A') a2 = A() a2.name = "Baris Metin" a2.email = "baris@uludag.org.tr" + a2.href = 'http://cekirdek.uludag.org.tr/~baris' + a2.projects = [ 'pisi', 'tasma', 'plasma' ] elt = a2.encode(xml) string = a2.format()