configurationengine/source/plugins/common/ConeTemplatePlugin/templatemlplugin/templatemlplugin.py
changeset 3 e7e0ae78773e
parent 0 2e8eeb919028
child 4 0951727b8815
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
    17 Template plugin for ConE that handles templateml files. Utilizes Jinja template engine.
    17 Template plugin for ConE that handles templateml files. Utilizes Jinja template engine.
    18 '''
    18 '''
    19 
    19 
    20 import re
    20 import re
    21 import os
    21 import os
    22 import sys
       
    23 import logging
    22 import logging
    24 import codecs
    23 import codecs
    25 import xml.parsers.expat
    24 import pkg_resources
    26 from jinja2 import Environment, PackageLoader, FileSystemLoader, Template, DictLoader
    25 from jinja2 import Environment, DictLoader
    27 import traceback
    26 import traceback
    28 try:
    27 try:
    29     from cElementTree import ElementTree
    28     from cElementTree import ElementTree
    30 except ImportError:
    29 except ImportError:
    31     try:    
    30     try:    
    45         try:
    44         try:
    46             from xml.etree import cElementInclude as ElementInclude
    45             from xml.etree import cElementInclude as ElementInclude
    47         except ImportError:
    46         except ImportError:
    48             from xml.etree import ElementInclude
    47             from xml.etree import ElementInclude
    49 
    48 
    50 import __init__
    49 
    51 
    50 from cone.public import exceptions,plugin,utils 
    52 from cone.public import exceptions,plugin,utils,api 
       
    53 from cone.confml import persistentconfml
       
    54 
    51 
    55 ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
    52 ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
    56 
    53 
    57 class TemplatemlImpl(plugin.ImplBase):
    54 class TemplatemlImpl(plugin.ImplBase):
    58     
    55     
    60     
    57     
    61     """
    58     """
    62     Implementation class of template plugin.
    59     Implementation class of template plugin.
    63     """
    60     """
    64     
    61     
    65     IMPL_TYPE_ID = "templateml" 
    62     IMPL_TYPE_ID = "templateml"
    66     
       
    67     
    63     
    68     def __init__(self,ref,configuration, reader=None):
    64     def __init__(self,ref,configuration, reader=None):
    69         """
    65         """
    70         Overloading the default constructor
    66         Overloading the default constructor
    71         """
    67         """
    73         self.logger = logging.getLogger('cone.templateml(%s)' % self.ref)
    69         self.logger = logging.getLogger('cone.templateml(%s)' % self.ref)
    74         self.errors = False
    70         self.errors = False
    75         self.reader = reader
    71         self.reader = reader
    76         if self.reader and self.reader.tags:
    72         if self.reader and self.reader.tags:
    77             self.set_tags(self.reader.tags)
    73             self.set_tags(self.reader.tags)
    78 
    74     
    79     def get_context(self):
    75     def __getstate__(self):
    80         if TemplatemlImpl.context == None:
    76         state = super(TemplatemlImpl, self).__getstate__()
    81             TemplatemlImpl.context = self.create_dict()
    77         state['reader'] = self.reader
    82         
    78         return state
    83         return TemplatemlImpl.context
    79 
       
    80     def get_context(self, generation_context):
       
    81         ddict = generation_context.impl_data_dict 
       
    82         if ddict.get('templateml_context', None) is None:
       
    83             ddict['templateml_context'] = self.create_dict()
       
    84         return ddict['templateml_context']
    84     
    85     
    85     def generate(self, context=None):
    86     def generate(self, context=None):
    86         """
    87         """
    87         Generate the given implementation.
    88         Generate the given implementation.
    88         """
    89         """
    89 
    90         self.context = context
    90         self.create_output()
    91         self.logger.debug('Generating from %s:%s' % (self.ref, self.lineno))
       
    92         self.create_output(context)
    91         return 
    93         return 
    92     
    94     
    93     def create_output(self, layers=None):
    95     def create_output(self, generation_context):
    94         generator = Generator(self.reader.outputs, self.reader.filters, self.get_context(), self.configuration)
    96         templateml_context = self.get_context(generation_context)
    95         generator.generate(self.output, self.ref)
    97         templateml_context['gen_context'] = generation_context
       
    98         if not generation_context.configuration:
       
    99             generation_context.configuration = self.configuration
       
   100         self.reader.expand_output_refs_by_default_view()
       
   101         generator = Generator(self.reader.outputs, self.reader.filters, templateml_context, self)
       
   102         generator.generate(generation_context, self.ref)
    96         return
   103         return
    97     
   104     
    98     def get_refs(self):
   105     def get_refs(self):
    99         refs = []
   106         refs = []
   100         for output in self.reader.outputs:
   107         for output in self.reader.outputs:
   101             template = output.template.template
   108             template = output.template.template
   102             refs.extend(self._extract_refs_from_template(template))
   109             refs.extend(self._extract_refs_from_template(template))
       
   110             refs_oa = self._extract_refs_from_output_attribs(output)
       
   111             for r in refs_oa:
       
   112                 if refs.count(r) < 1:
       
   113                     refs.append(r)
       
   114         return refs
       
   115     
       
   116     def _extract_refs_from_output_attribs(self, output):
       
   117         refs = [] 
       
   118         pattern = re.compile(r'\$\{(.*)\}', re.UNICODE)
       
   119         for key, value in vars(output).iteritems():
       
   120             m = pattern.search(str(value))
       
   121             if m:
       
   122                 ref = m.group(1)
       
   123                 refs.append(ref)
       
   124             if key == 'ref':
       
   125                 refs.append(value)
   103         return refs
   126         return refs
   104     
   127     
   105     @classmethod
   128     @classmethod
   106     def _extract_refs_from_template(cls, template_text):
   129     def _extract_refs_from_template(cls, template_text):
   107         refs = []
   130         refs = []
   130             refs = [refs] 
   153             refs = [refs] 
   131         
   154         
   132         for output in self.reader.outputs:
   155         for output in self.reader.outputs:
   133             if re.search("feat_list.*", output.template.template) != None:
   156             if re.search("feat_list.*", output.template.template) != None:
   134                 return True
   157                 return True
   135         
   158         return plugin.uses_ref(refs, self.get_refs())
   136         refs_in_templates = self.get_refs()
       
   137             
       
   138         for ref in refs:
       
   139             if ref in refs_in_templates:
       
   140                 return True
       
   141         return False
       
   142     
   159     
   143     
   160     
   144     def list_output_files(self):
   161     def list_output_files(self):
   145         """ Return a list of output files as an array. """
   162         """ Return a list of output files as an array. """
   146         result = []
   163         result = []
   147         for output in self.reader.outputs:
   164         for output in self.reader.outputs:
   148             result.append(os.path.normpath(os.path.join(self.output, output.path, output.filename)))
   165             filename = ""
       
   166             if output.fearef != None:
       
   167                 filename = self.configuration.get_default_view().get_feature(output.fearef).value
       
   168             else:
       
   169                 filename = output.filename
       
   170             result.append(os.path.normpath(os.path.join(self.output, output.path, filename)))
   149         return result
   171         return result
   150     
   172     
   151     def create_dict(self):
   173     def create_dict(self):
   152         """
   174         """
   153         Creates dict from configuration that can be passed to template engine.
   175         Creates dict from configuration that can be passed to template engine.
   156         context_dict = {}
   178         context_dict = {}
   157         
   179         
   158         if self.configuration:
   180         if self.configuration:
   159             dview = self.configuration.get_default_view()
   181             dview = self.configuration.get_default_view()
   160             feat_list = []
   182             feat_list = []
   161             feat_tree = {}
   183             feat_tree = FeatureDictProxy(None)
   162             
   184             
   163             def add_feature(feature, feature_dict):
   185             def add_feature(feature, feature_dict):
   164                 fea_dict = FeatureDictProxy(feature)
   186                 fea_dict = FeatureDictProxy(feature)
   165                 feat_list.append(fea_dict)
   187                 feat_list.append(fea_dict)
   166                 feature_dict[feature.ref] = fea_dict
   188                 feature_dict[feature.ref] = fea_dict
   200 class TemplatemlImplReader(plugin.ReaderBase):
   222 class TemplatemlImplReader(plugin.ReaderBase):
   201     """
   223     """
   202     Parses a single templateml file
   224     Parses a single templateml file
   203     """ 
   225     """ 
   204     NAMESPACE = 'http://www.s60.com/xml/templateml/1'
   226     NAMESPACE = 'http://www.s60.com/xml/templateml/1'
       
   227     NAMESPACE_ID = 'templateml'
       
   228     ROOT_ELEMENT_NAME = 'templateml'
   205     FILE_EXTENSIONS = ['templateml']
   229     FILE_EXTENSIONS = ['templateml']
       
   230     NEWLINE_WIN_PARSE_OPTIONS = ['win', 'windows', 'dos', 'symbian', 'symbianos', 'cr+lf', 'crlf']
   206     
   231     
   207     def __init__(self, resource_ref=None, configuration=None):
   232     def __init__(self, resource_ref=None, configuration=None):
   208         self.desc = None
   233         self.desc = None
   209         self.namespaces = [self.NAMESPACE]
   234         self.namespaces = [self.NAMESPACE]
   210         self.outputs = None
   235         self.outputs = None
   217     @classmethod
   242     @classmethod
   218     def read_impl(cls, resource_ref, configuration, etree):
   243     def read_impl(cls, resource_ref, configuration, etree):
   219         reader = TemplatemlImplReader(resource_ref, configuration)
   244         reader = TemplatemlImplReader(resource_ref, configuration)
   220         reader.from_elementtree(etree)
   245         reader.from_elementtree(etree)
   221         return TemplatemlImpl(resource_ref, configuration, reader)
   246         return TemplatemlImpl(resource_ref, configuration, reader)
       
   247     
       
   248     @classmethod
       
   249     def get_schema_data(cls):
       
   250         return pkg_resources.resource_string('templatemlplugin', 'xsd/templateml.xsd')
   222     
   251     
   223     def fromstring(self, xml_string):
   252     def fromstring(self, xml_string):
   224         etree = ElementTree.fromstring(xml_string)
   253         etree = ElementTree.fromstring(xml_string)
   225         self.from_elementtree(etree)
   254         self.from_elementtree(etree)
   226     
   255     
   287                 file = template_elem.get('file')
   316                 file = template_elem.get('file')
   288                 if template_elem.text != None: 
   317                 if template_elem.text != None: 
   289                     logging.getLogger('cone.templateml').warning("In template element file attribute and text defined. Using template found from file attribute.")
   318                     logging.getLogger('cone.templateml').warning("In template element file attribute and text defined. Using template found from file attribute.")
   290                 template_text = _read_relative_file(self.configuration, file, self.resource_ref)
   319                 template_text = _read_relative_file(self.configuration, file, self.resource_ref)
   291                 tempfile.set_template(template_text)
   320                 tempfile.set_template(template_text)
       
   321         
   292         return tempfile
   322         return tempfile
   293     
   323     
   294     def parse_outputs(self, etree):
   324     def parse_outputs(self, etree):
   295         outputs = []
   325         outputs = []
   296         output_elems = etree.findall("{%s}output" % self.namespaces[0])
   326         output_elems = etree.findall("{%s}output" % self.namespaces[0])
   297         for output_elem in output_elems:
   327         for output_elem in output_elems:
   298             if output_elem != None:
   328             if output_elem != None:
   299                 outputfile = OutputFile()
   329                 outputfile = OutputFile()
       
   330                 outputfile.set_output_elem(output_elem)
   300                 if output_elem.get('encoding') != None:
   331                 if output_elem.get('encoding') != None:
   301                     encoding = output_elem.get('encoding')
   332                     encoding = output_elem.get('encoding')
   302                     # Check the encoding
       
   303                     try:
       
   304                         codecs.lookup(encoding)
       
   305                     except LookupError:
       
   306                         raise exceptions.ParseError("Invalid output encoding: %s" % encoding)
       
   307                     
       
   308                     if self.configuration != None:
       
   309                         encoding = utils.expand_refs_by_default_view(encoding, self.configuration.get_default_view())
       
   310                     outputfile.set_encoding(encoding)
   333                     outputfile.set_encoding(encoding)
   311                 if output_elem.get('file') != None:
   334                 if output_elem.get('file') != None:
   312                     file = output_elem.get('file')
   335                     file = output_elem.get('file')
   313                     
       
   314                     if self.configuration != None:
       
   315                         file = utils.expand_refs_by_default_view(file, self.configuration.get_default_view())
       
   316                     outputfile.set_filename(file)
   336                     outputfile.set_filename(file)
   317                 if output_elem.get('dir') != None:
   337                 if output_elem.get('dir') != None:
   318                     dir = output_elem.get('dir')
   338                     dir = output_elem.get('dir')
   319                     if self.configuration != None:
       
   320                         dir = utils.expand_refs_by_default_view(dir, self.configuration.get_default_view())
       
   321                     outputfile.set_path(dir)
   339                     outputfile.set_path(dir)
   322                 if output_elem.get('ref'):
   340                 if output_elem.get('ref'):
   323                     # Fetch the output value from a configuration reference
   341                     # Fetch the output value from a configuration reference
   324                     fea = self.configuration.get_default_view().get_feature(output_elem.get('ref'))
   342                     outputfile.set_fearef(output_elem.get('ref'))
   325                     outputfile.set_filename(fea.value) 
       
   326                 if output_elem.get('bom'):
   343                 if output_elem.get('bom'):
   327                     outputfile.bom = output_elem.get('bom').lower() in ('1', 'true', 't', 'yes', 'y')
   344                     outputfile.set_bom(output_elem.get('bom'))
       
   345                 if output_elem.get('newline', ''):
       
   346                     outputfile.set_newline(output_elem.get('newline', ''))
       
   347                 
   328                 outputfile.set_template(self.parse_template(output_elem))
   348                 outputfile.set_template(self.parse_template(output_elem))
   329                 outputfile.set_filters(self.parse_filters(output_elem))
   349                 outputfile.set_filters(self.parse_filters(output_elem))
   330                 outputs.append(outputfile)
   350                 outputs.append(outputfile)
       
   351                             
   331         return outputs
   352         return outputs
   332 
   353 
       
   354     def expand_output_refs_by_default_view(self):
       
   355         for output in self.outputs:     
       
   356             if output.encoding:
       
   357                 if self.configuration != None:
       
   358                     output.set_encoding(utils.expand_refs_by_default_view(output.encoding, self.configuration.get_default_view()))
       
   359                 try:
       
   360                     codecs.lookup(output.encoding)
       
   361                 except LookupError:
       
   362                     raise exceptions.ParseError("Invalid output encoding: %s" % output.encoding)
       
   363             if output.filename:
       
   364                 if self.configuration != None:
       
   365                     output.set_filename(utils.expand_refs_by_default_view(output.filename, self.configuration.get_default_view()))
       
   366             if output.path:
       
   367                 if self.configuration != None:
       
   368                     output.set_path(utils.expand_refs_by_default_view(output.path, self.configuration.get_default_view()))
       
   369             if output.newline:
       
   370                 newline = output.newline
       
   371                 if self.configuration != None:
       
   372                     newline = utils.expand_refs_by_default_view(output.newline, self.configuration.get_default_view())
       
   373                 if newline.lower() in self.NEWLINE_WIN_PARSE_OPTIONS:
       
   374                     output.set_newline(OutputFile.NEWLINE_WIN)
       
   375             if output.bom:
       
   376                 bom = output.bom
       
   377                 if self.configuration != None:
       
   378                     bom = utils.expand_refs_by_default_view(output.bom, self.configuration.get_default_view())
       
   379                 output.bom = bom.lower() in ('1', 'true', 't', 'yes', 'y')
       
   380             if output.fearef:
       
   381                 if self.configuration != None:
       
   382                     fea = self.configuration.get_default_view().get_feature(output.fearef)
       
   383                     output.set_filename(fea.value)
       
   384 
   333 class Generator(object):
   385 class Generator(object):
   334     """
   386     """
   335     Class that generates
   387     Class that generates
   336     """
   388     """
   337     
   389     
   338     def __init__(self, outputs, filters, context, configuration=None):
   390     def __init__(self, outputs, filters, context, implementation=None):
   339         self.outputs = outputs
   391         self.outputs = outputs
   340         self.filters = filters
   392         self.filters = filters
   341         self.context = context
   393         self.context = context
   342         self.configuration = configuration
   394         self.implementation = implementation
   343     
   395     
   344     def generate(self, output_path, ref):
   396     def generate(self, generation_context, ref):
   345         """ 
   397         """ 
   346         Generates output based on templates 
   398         Generates output based on templates 
   347         """
   399         """
   348         if self.outputs != None:
   400         if self.outputs != None:
   349         
   401         
   350             for output in self.outputs:
   402             for output in self.outputs:
   351                 try:
   403                 try:
   352                     logging.getLogger('cone.templateml').debug(output)
   404                     out_path = output.path
   353                     out_path = os.path.abspath(os.path.join(output_path, output.path))
   405                     out_filepath = os.path.join(out_path, output.filename)
   354                     if out_path != '':
   406                     logging.getLogger('cone.templateml').debug("Output file '%s', encoding '%s'" % (out_filepath, output.encoding))
   355                         if not os.path.exists(out_path):
   407                     
   356                             os.makedirs(out_path)
   408                     out_file = generation_context.create_file(out_filepath, implementation=self.implementation)
   357                     
       
   358                     out_file = open(os.path.join(out_path, output.filename), 'wb')
       
   359                     
   409                     
   360                     if output.template.path:
   410                     if output.template.path:
   361                         output.template.template = _read_relative_file(self.configuration, output.template.path, ref)
   411                         output.template.template = _read_relative_file(generation_context.configuration, output.template.path, ref)
   362                     
   412                     
   363                     dict_loader = DictLoader({'template': output.template.template})
   413                     dict_loader = DictLoader({'template': output.template.template})
   364                     env = Environment(loader=dict_loader)
   414                     
       
   415                     if output.newline == OutputFile.NEWLINE_WIN:
       
   416                         env = Environment(loader=dict_loader, newline_sequence='\r\n')
       
   417                     else:
       
   418                         env = Environment(loader=dict_loader)
   365 
   419 
   366                     # Common filters
   420                     # Common filters
   367                     for filter in self.filters:
   421                     for filter in self.filters:
   368                         
   422                         
   369                         if filter.path:
   423                         if filter.path:
   370                             filter.code = _read_relative_file(self.configuration, filter.path, ref)
   424                             filter.code = _read_relative_file(generation_context.configuration, filter.path, ref)
       
   425                         
       
   426                         if not filter.code:
       
   427                             logging.getLogger('cone.templateml').warning("Skipping empty filter definition.")
       
   428                         else:
       
   429                             env.filters[str(filter.name)] = eval(filter.code.replace('\r', ''))
       
   430                     
       
   431                     # Output file specific filters
       
   432                     for filter in output.filters:
       
   433                         if filter.path:
       
   434                             filter.code = _read_relative_file(generation_context.configuration, filter.path, ref)
   371                         
   435                         
   372                         if not filter.code:
   436                         if not filter.code:
   373                             logging.getLogger('cone.templateml').warning("Skipping empty filter definition.")
   437                             logging.getLogger('cone.templateml').warning("Skipping empty filter definition.")
   374                         else:
   438                         else:
   375                             env.filters[str(filter.name)] = eval(filter.code)
   439                             env.filters[str(filter.name)] = eval(filter.code)
   376                     
   440                     
   377                     # Output file specific filters
       
   378                     for filter in output.filters:
       
   379                         if filter.path:
       
   380                            filter.code = _read_relative_file(self.configuration, filter.path, ref)
       
   381                         
       
   382                         if not filter.code:
       
   383                             logging.getLogger('cone.templateml').warning("Skipping empty filter definition.")
       
   384                         else:
       
   385                             env.filters[str(filter.name)] = eval(filter.code)
       
   386                     
       
   387                     template = env.get_template('template')
   441                     template = env.get_template('template')
   388                     
   442                     
   389                     file_string = template.render(self.context)
   443                     file_string = template.render(self.context)
   390                     out_file.write(self._encode_data(file_string, output.encoding, output.bom))
   444                     out_file.write(self._encode_data(file_string, output.encoding, output.bom))
   391                     out_file.close()
   445                     out_file.close()
   392                     
   446                     
   393                 except Exception, e:
   447                 except Exception, e:
   394                     logging.getLogger('cone.templateml').error('Failed to generate template: %s %s\n%s' % (type(e), e, traceback.format_exc()) )
   448                     utils.log_exception(
       
   449                         logging.getLogger('cone.templateml'),
       
   450                         '%r: Failed to generate output: %s: %s' % (self.implementation, type(e).__name__, e))
   395         else:
   451         else:
   396             logging.getLogger('cone.templateml').info('No (valid) templates found.')
   452             logging.getLogger('cone.templateml').info('No (valid) templates found.')
   397     
   453     
   398     def _encode_data(self, data, encoding, write_bom):
   454     def _encode_data(self, data, encoding, write_bom):
   399         """
   455         """
   424                     data = data[len(BOM):]
   480                     data = data[len(BOM):]
   425         return data
   481         return data
   426         
   482         
   427 
   483 
   428 class OutputFile(object):
   484 class OutputFile(object):
       
   485     NEWLINE_UNIX = "unix"
       
   486     NEWLINE_WIN = "win" 
       
   487     
   429     def __init__(self):
   488     def __init__(self):
   430         self.filename = ''
   489         self.filename = ''
   431         self.path = ''
   490         self.path = ''
   432         self.encoding = "utf-8"
   491         self.encoding = "utf-8"
   433         self.template = TempFile()
   492         self.template = TempFile()
   434         self.filters = []
   493         self.filters = []
   435         self.bom = None
   494         self.bom = None
       
   495         self.newline = self.NEWLINE_UNIX
       
   496         self.fearef = None
       
   497         self.output_elem = None
       
   498 
       
   499     def set_newline(self, newline):
       
   500         self.newline = newline
   436 
   501 
   437     def set_filename(self, filename):
   502     def set_filename(self, filename):
   438         self.filename = filename
   503         self.filename = filename
   439     
   504     
   440     def set_path(self, path):
   505     def set_path(self, path):
   445         
   510         
   446     def set_template(self, template):
   511     def set_template(self, template):
   447         self.template = template
   512         self.template = template
   448 
   513 
   449     def add_filter(self, filter):
   514     def add_filter(self, filter):
   450         self.filters.append(filters)
   515         self.filters.append(filter)
   451     
   516     
   452     def set_filters(self, filters):
   517     def set_filters(self, filters):
   453         self.filters = filters
   518         self.filters = filters
   454     
   519     
       
   520     def set_bom(self, bom):
       
   521         self.bom = bom
       
   522         
       
   523     def set_fearef(self, ref):
       
   524         self.fearef = ref
       
   525         
       
   526     def set_output_elem(self, output_elem):
       
   527         self.output_elem = output_elem
       
   528     
   455     def __eq__(self, other):
   529     def __eq__(self, other):
   456         if (self.template == other.template and self.encoding == other.encoding and self.path == other.path and self.filename == other.filename and self.filters == other.filters):
   530         if other:
   457             return True
   531             if (self.template == other.template and self.newline == other.newline and self.encoding == other.encoding and self.path == other.path and self.filename == other.filename and self.filters == other.filters):
       
   532                 return True
   458         return False
   533         return False
   459     
   534     
   460     def __repr__(self):
   535     def __repr__(self):
   461         return "OutputFile(filename=%r, path=%r, encoding=%r, template=%r, filters=%r" % (self.filename, self.path, self.encoding, self.template, self.filters)
   536         return "OutputFile(filename=%r, path=%r, encoding=%r, template=%r, filters=%r" % (self.filename, self.path, self.encoding, self.template, self.filters)
   462 
   537 
   481     
   556     
   482     def add_filter2(self, name, code):
   557     def add_filter2(self, name, code):
   483         self.filters.append(Filter(name, code))
   558         self.filters.append(Filter(name, code))
   484         
   559         
   485     def __eq__(self, other):
   560     def __eq__(self, other):
   486         if self.template == other.template and self.filters == other.filters and self.extensions == other.extensions and self.path == other.path:
   561         if other:
   487             return True
   562             if self.template == other.template and self.filters == other.filters and self.extensions == other.extensions and self.path == other.path:
       
   563                 return True
   488         return False
   564         return False
       
   565     
   489         
   566         
   490 class Filter(object):
   567 class Filter(object):
   491     def __init__(self, name, code):
       
   492         self.name = name
       
   493         self.code = code
       
   494         self.path = None
       
   495     
       
   496     def __init__(self):
   568     def __init__(self):
   497         self.name = None
   569         self.name = None
   498         self.code = None
   570         self.code = None
   499         self.path = None
   571         self.path = None
   500     
   572     
   520     def __init__(self, feature):
   592     def __init__(self, feature):
   521         self._feature = feature
   593         self._feature = feature
   522         self._children = {}
   594         self._children = {}
   523     
   595     
   524     def _get_dict(self):
   596     def _get_dict(self):
   525         result = {
   597         result = {}
   526             '_name'        : self._feature.name,
   598         if self._feature is not None:
   527             '_namespace'   : self._feature.namespace,
   599             result.update({
   528             '_value'       : self._feature.get_value(),
   600                 '_name'        : self._feature.name,
   529             '_fqr'         : self._feature.fqr,
   601                 '_namespace'   : self._feature.namespace,
   530             '_type'        : self._feature.type}
   602                 '_value'       : self._feature.get_value(),
   531         for ref, obj in self._children.iteritems():
   603                 '_fqr'         : self._feature.fqr,
   532             result[ref] = obj
   604                 '_type'        : self._feature.type})
       
   605         result.update(self._children)
   533         return result
   606         return result
   534     
   607     
   535     def items(self):
   608     def items(self):
   536         return self._get_dict().items()
   609         return self._get_dict().items()
   537     
   610     
   538     def iteritems(self):
   611     def iteritems(self):
   539         return self._get_dict().iteritems()
   612         return self._get_dict().iteritems()
   540     
   613     
   541     def __getitem__(self, name):
   614     def __getitem__(self, name):
   542         if name == '_name':         return self._feature.name        
   615         if self._feature is not None:
   543         elif name == '_namespace':  return self._feature.namespace
   616             if name == '_name':         return self._feature.name
   544         elif name == '_value':      return self._feature.get_value()
   617             elif name == '_namespace':  return self._feature.namespace
   545         elif name == '_fqr':        return self._feature.fqr
   618             elif name == '_value':      return self._feature.get_value()
   546         elif name == '_type':       return self._feature.type
   619             elif name == '_fqr':        return self._feature.fqr
   547         else:                       return self._children[name]
   620             elif name == '_type':       return self._feature.type
       
   621         
       
   622         try:
       
   623             return self._children[name]
       
   624         except KeyError:
       
   625             if self._feature:
       
   626                 msg = "Feature '%s.%s' not found" % (self._feature.fqr, name)
       
   627             else:
       
   628                 msg = "Feature '%s' not found" % name
       
   629             raise exceptions.NotFound(msg)
   548     
   630     
   549     def __setitem__(self, name, value):
   631     def __setitem__(self, name, value):
   550         self._children[name] = value
   632         self._children[name] = value
   551     
   633     
   552     def __len__(self):
   634     def __len__(self):