running silent, running deep

This commit is contained in:
Eray Özkural
2005-09-01 17:48:29 +00:00
parent 7cfdcb06c0
commit 4063513ce5
5 changed files with 353 additions and 39 deletions
+144
View File
@@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC)
# files are specification files for PISI source packages. This module
# provides read and write access to PSPEC files.
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr>
# standard python modules
from os.path import basename
# pisi modules
from pisi.xmlext import *
import pisi.xmlfile as xmlfile
from pisi.xmlfile import XmlFile
from pisi.ui import ui
from pisi.dependency import DepInfo
from pisi.util import Checks
class Packager:
__metaclass__ = xmlfile.autoxml
t_Name = [types.StringType, xmlfile.mandatory]
t_Email = [types.StringType, xmlfile.mandatory]
def __str__(self):
s = "%s <%s>" % (self.name, self.email)
return s
class AdditionalFileInfo:
__metaclass__ = xmlfile.autoxml
s_Filename = xmlfile.mandatory
a_Target = [types.StringType, xmlfile.mandatory]
a_Permission = [types.StringType, xmlfile.optional]
def __str__(self):
s = "%s -> %s " % (self.filename, self.target)
if self.permission:
s += '(%s)' % self.permission
return s
class Patch:
__metaclass__ = xmlfile.autoxml
s_Filename = xmlfile.mandatory
a_compressionType = [types.StringType, xmlfile.optional]
a_level = [types.StringType, xmlfile.optional]
a_target = [types.StringType, xmlfile.optional]
def __str__(self):
s = self.filename
if self.compressionType:
s += ' (' + self.compressionType + ')'
if self.level:
s += ' level:' + self.level
if self.target:
s += ' target:' + self.target
return s
class Update:
__metaclass__ = xmlfile.autoxml
t_Date = [types.StringType, xmlfile.mandatory]
t_Version = [types.StringType, xmlfile.mandatory]
t_Release = [types.StringType, xmlfile.mandatory]
t_Type = [types.StringType, xmlfile.optional]
def __str__(self):
s = self.date
s += ", ver=" + self.version
s += ", rel=" + self.release
if self.type:
s += ", type=" + self.type
return s
class Path:
__metaclass__ = xmlfile.autoxml
s_Path = xmlfile.mandatory
a_fileType = [types.StringType, xmlfile.optional]
def __str__(self):
s = self.pathname
s += ", type=" + self.fileType
return s
class ComarProvide:
__metaclass__ = xmlfile.autoxml
s_om = [types.StringType, xmlfile.mandatory]
a_script = [types.StringType, xmlfile.mandatory]
def __str__(self):
# FIXME: descriptive enough?
s = self.script
s += ' (' + self.om + ')'
return s
class Archive:
__metaclass__ = xmlfile.autoxml
s_uri = [ types.StringType, xmlfile.mandatory ]
a_type =[ types.StringType, xmlfile.mandatory ]
a_sha1sum =[ types.StringType, xmlfile.mandatory ]
def decode_post(self):
self.name = basename(self.uri)
class Source:
__metaclass__ = xmlfile.autoxml
t_Name = [types.StringType, xmlfile.mandatory]
t_HomePage = [types.StringType, xmlfile.mandatory]
t_Packager = [Packager, xmlfile.mandatory]
t_Summary = [types.StringType, xmlfile.mandatory]
t_Description = [types.StringType, xmlfile.mandatory]
t_License = [ [types.StringType], xmlfile.mandatory]
t_IsA = [ [types.StringType], xmlfile.mandatory]
t_PartOf = [types.StringType, xmlfile.mandatory]
t_Archive = [Archive, xmlfile.mandatory ]
t_Patch = [ [Patch], xmlfile.mandatory, "Patches/Patch"]
t_BuildDep = [ [Dependency], xmlfile.mandatory, "BuildDependencies/Dependency"]
t_History = [ [Update], xmlfile.mandatory, "History/Update"]
class Package:
__metaclass__ = xmlfile.autoxml
t_Name = [ types.StringType, xmlfile.mandatory ]
t_Summary = [ types.StringType, xmlfile.mandatory ]
t_Description = [ types.StringType, xmlfile.mandatory ]
t_IsA = [ [types.StringType], xmlfile.mandatory]
t_PartOf = [types.StringType, xmlfile.mandatory]
t_History = [ [Update], xmlfile.mandatory, "History/Update"]
t_Conflicts = [ [types.StringType], xmlfile.mandatory, "Conflicts/Package"]
t_ProvidesComar = [ [ComarProvide], xmlfile.mandatory, "Provides/COMAR"]
t_RequriesComar = [ [types.StringType], xmlfile.mandatory, "Requires/COMAR"]
t_AdditionalFiles = [ [AdditionalFile], xmlfile.mandatory, "AdditionalFiles/AdditionalFile"]
+7 -6
View File
@@ -9,17 +9,19 @@
#
# Please read the COPYING file.
#
# some helper functions for using minidom
# works well enough for now... :/
# function names are mixedCase for compatibility with minidom,
# an old library
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
"""
some helper functions for using minidom
function names are mixedCase for compatibility with minidom,
an old library
"""
import pisi
class XmlError(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
def getNodeAttribute(node, attrname):
@@ -105,7 +107,6 @@ def getAllNodes(node, tagPath):
return nodeList
def createTagPath(dom, node, tags):
"""create new child at the end of a tag chain starting from node
no matter what"""
+167 -31
View File
@@ -9,35 +9,47 @@
#
# Please read the COPYING file.
#
# XmlFile is a halper module for accessing XML files using
# xml.dom.minidom.
#
# XmlFile class that further abstracts a dom object using the
# high-level dom functions provided in xml module (and sorely lacking
# in xml.dom :( )
#
# method names are mixedCase for compatibility with minidom,
# an old library
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
"""
xmlfile is a helper module for accessing XML files using
xml.dom.minidom.
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module (and sorely lacking
in xml.dom :( )
autoxml is a metaclass for automatic XML translation, using
a miniature type system
Method names are mixedCase for compatibility with minidom,
an old library
"""
# System
import xml.dom.minidom as mdom
from xml.parsers.expat import ExpatError
import codecs
from xmlext import *
# PiSi
import pisi
from pisi.xmlext import *
class Error(pisi.Error):
pass
import types
mandatory, optional = range(2)
class autoxml(type):
"high-level automatic XML transformation interface for xmlfile"
def __init__(cls, name, bases, dict):
@@ -50,36 +62,160 @@ class autoxml(type):
super(autoxml, cls).__init__(name, bases, dict)
# initialize class attribute __xml_tags
setattr(cls, '__xml_tags', [])
#setattr(cls, 'xml_variables', [])
# find variables that start with x_
if not dict.has_key('tag'):
setattr(cls, 'tag', name)
root_tag = cls.tag
# generate helper routines
initializers = []
decoders = []
encoders = []
formatters = []
for var in dict:
if var.startswith('t_'):
print 'xml tag', var
autoxml.gen_tag(cls, var[2:])
if var.startswith('t_') or var.startswith('a_'):
name = var[2:]
#print 'name', name
# possible python bug, binds var only to t_Email here!
## def initializer(self):
## print 'got', name
## setattr(self, name, None)
## initializers.append(initializer)
initializers.append(autoxml.gen_init(cls, name))
if var.startswith('t_'):
x = autoxml.gen_tag(cls, name)
elif var.startswith('a_'):
x = autoxml.gen_attr(cls, name)
(decoder, encoder, formatter) = x
decoders.append(decoder)
encoders.append(encoder)
formatters.append(formatter)
#print initializers
setattr(cls, 'initializers', initializers)
def initialize(self):
for initializer in self.__class__.initializers:
initializer(self)
setattr(cls, '__init__', initialize)
setattr(cls, 'decoders', decoders)
def decode(self, node):
for decoder in self.__class__.decoders:
decoder(self, node)
setattr(cls, 'decode', decode)
setattr(cls, 'encoders', encoders)
def encode(self, xml):
for encoder in self.__class__.encoders:
encoder(self, xml)
setattr(cls, 'encode', encode)
setattr(cls, 'formatters', formatters)
def format(self):
string = ''
for formatter in self.__class__.formatters:
string = string + formatter(self)
return string
setattr(cls, 'format', format)
if not dict.has_key('__str__'):
def string(self):
return self.format()
setattr(cls, '__str__', string)
def gen_init(cls, name):
def initializer(self):
varname = cls.mixed_case(name)
#print 'init:', varname
setattr(self, varname, None)
return initializer
def gen_tag(cls, tag):
# generate readers and writers for the tag
val = getattr(cls, 't_' + tag)
tag_type = val[0]
if type(tag_type) == type(type):
if val[0] == types.IntType:
autoxml.gen_int_tag(cls, tag, val)
#gen_tag = staticmethod(gen_tag)
# basic types
return autoxml.gen_basic_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]
assert type(tag_type) == type(type)
return autoxml.gen_basic_attr(cls, tag, val)
def mixed_case(cls, identifier):
identifier_p = identifier[0].lower() + identifier[1:]
return identifier_p
def gen_int_tag(cls, tag, val):
print tag, val
name = cls.mixed_case(tag)
def read_int(self):
self.name = getNodeText(getNode(node, tag))
setattr(cls, 'decode' + tag, read_int)
def gen_basic(cls, token, val, readtext, writetext):
"generate a basic tag or attribute"
name = cls.mixed_case(token)
token_type = val[0]
req = val[1]
def decode(self, node):
text = readtext(node, token)
if text:
try:
value = autoxml.basic_cons_map[token_type](text)
except Error:
return ['Type mismatch']
setattr(self, name, value)
else:
if req == mandatory:
return ['Mandatory argument not available']
else:
setattr(self, name, None)
return None
def encode(self, xml):
node = xml.newNode(cls.tag)
if hasattr(self, name):
writetext(xml, node, token, str(getattr(self, name)))
else:
if req == mandatory:
return ['Mandatory argument not available']
def formatter(self):
#print 'format:', name
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 ""
return decode, encode, formatter
def gen_basic_attr(cls, attr, val):
"""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, val, readtext, writetext)
def gen_basic_tag(cls, tag, val):
"""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, val, readtext, writetext)
basic_cons_map = {
types.StringType : str,
types.IntType : int
}
class XmlFile(object):
"""A class for retrieving information from an XML file"""
"""A class to help reading and writing an XML file"""
def __init__(self, rootTag):
self.rootTag = rootTag
@@ -98,7 +234,8 @@ class XmlFile(object):
try:
self.dom = mdom.parse(fileName)
except ExpatError, inst:
raise XmlError("File '%s' has invalid XML: %s\n" % (fileName, str(inst)))
raise Error("File '%s' has invalid XML: %s\n" % (fileName,
str(inst)))
def writexml(self, fileName):
f = codecs.open(fileName,'w', "utf-8")
@@ -107,7 +244,7 @@ class XmlFile(object):
def verifyRootTag(self):
if self.dom.documentElement.tagName != self.rootTag:
raise XmlError("Root tagname not " + self.rootTag + " as expected")
raise Error("Root tagname not " + self.rootTag + " as expected")
# construction helpers
@@ -156,7 +293,8 @@ class XmlFile(object):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
try:
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
node.childNodes)
except AttributeError:
return None
@@ -188,5 +326,3 @@ class XmlFile(object):
def addTextNodeUnder(self, node, tagPath, text):
"add a text node under given node with tag path (phew)"
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<A href="http://www.cs.bilkent.edu.tr/~erayo">
<Name>Eray Ozkural</Name>
<Number>868</Number>
</A>
+30 -2
View File
@@ -10,19 +10,47 @@
import unittest
import os
import xml.dom.minidom as mdom
from xml.parsers.expat import ExpatError
import types
from pisi import xmlfile
from pisi.config import config
import pisi.util as util
import types
from pisi.xmlext import *
class XmlFileTestCase(unittest.TestCase):
def setUp(self):
# impl = mdom.getDOMImplementation()
pass
def testMetaClass(self):
class A:
__metaclass__ = xmlfile.autoxml
t_Number = [types.IntType, xmlfile.mandatory]
t_Name = [types.StringType, xmlfile.mandatory]
t_Number = [types.IntType, xmlfile.optional]
t_Email = [types.StringType, xmlfile.optional]
a_href = [types.StringType, xmlfile.mandatory]
#t_Projects = [ [types.StringType], xmlfile.mandatory]
a = A()
#print a
self.assertEqual(a.href, None)
dom = mdom.parse('tests/sandbox/a.xml')
node = getNode(dom, 'A')
self.assert_(len(A.decoders)>0)
a.decode(node)
self.assert_(a.href.startswith('http'))
self.assertEqual(a.number, 868)
self.assertEqual(a.name, 'Eray Ozkural')
string = a.format()
#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"
elt = a2.encode(xml)
string = a2.format()
suite = unittest.makeSuite(XmlFileTestCase)