Added: incubator/oodt/agility/agile-oodt/trunk/oodt/tests/profileTest.py URL: http://svn.apache.org/viewvc/incubator/oodt/agility/agile-oodt/trunk/oodt/tests/profileTest.py?rev=925913&view=auto ============================================================================== --- incubator/oodt/agility/agile-oodt/trunk/oodt/tests/profileTest.py (added) +++ incubator/oodt/agility/agile-oodt/trunk/oodt/tests/profileTest.py Sun Mar 21 22:08:01 2010 @@ -0,0 +1,239 @@ +# encoding: utf-8 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE.txt file distributed with +# this work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +'''Unit tests for the `profile` module. +''' + +__docformat__ = 'restructuredtext' + +import unittest, xml.dom.minidom +from oodt.profile import ProfileAttributes, ResourceAttributes, Profile, UnspecifiedProfileElement, RangedProfileElement,\ + EnumeratedProfileElement + +class ProfileAttributesTest(unittest.TestCase): + '''Unit test for the ProfileAttributes class. + ''' + def testDefaults(self): + '''Test to see if default values are reasonable. + ''' + pa = ProfileAttributes() + self.assertEquals('UNKNOWN', pa.id) + self.assertEquals('1.0.0', pa.version) + self.assertEquals('profile', pa.type) + self.assertEquals('active', pa.statusID) + self.assertEquals('unclassified', pa.securityType) + self.assertEquals('UNKNOWN', pa.parentID) + self.assertEquals(0, len(pa.childIDs)) + self.assertEquals('UNKNOWN', pa.regAuthority) + self.assertEquals(0, len(pa.revNotes)) + + def testCmp(self): + '''Test comparison operators. + ''' + a = ProfileAttributes('1') + b = ProfileAttributes('1') + c = ProfileAttributes('2') + self.assertEquals(a, a) + self.assertEquals(a, b) + self.assertNotEquals(a, c) + self.assert_(a <= a) + self.assert_(a <= b) + self.assert_(a <= c) + self.assert_(a < c) + + def testXML(self): + '''Test XML serialization and re-composition from XML. + ''' + a = ProfileAttributes('1.3.1.9', '2.0.0', 'profile', 'inactive', 'classified', '1.3.1', ['1.3.1.9.1', '1.3.1.9.2'], + 'NASA', ['Updated', 'Created']) + doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None) + node = a.toXML(doc) + b = ProfileAttributes(node=node) + self.assertEquals(a, b) + + def testXMLValidity(self): + '''Test to see if all required XML elements are in there. + ''' + a = ProfileAttributes('1.3.1.9', '2.0.0', 'profile', 'inactive', 'classified', '1.3.1', ['1.3.1.9.1', '1.3.1.9.2'], + 'NASA', ['Updated', 'Created']) + doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None) + node = a.toXML(doc) + self.assertEquals('profAttributes', node.nodeName) + childElements = [n.nodeName for n in node.childNodes] + self.assertEquals([u'profId', u'profVersion', u'profType', u'profStatusId', u'profSecurityType', u'profParentId', + u'profChildId', u'profChildId', u'profRegAuthority', u'profRevisionNote', u'profRevisionNote'], + childElements) + + def testInstances(self): + '''Test to ensure instances don't share instance data. + ''' + a = ProfileAttributes(id='1') + b = ProfileAttributes(id='2') + self.assertNotEquals(a, b) + a.childIDs.append('3') + self.assertNotEquals(a.childIDs, b.childIDs) + a.revNotes.append('Uhhhhh, spam?') + self.assertNotEquals(a.revNotes, b.revNotes) + + +class ResourceAttributesTest(unittest.TestCase): + '''Unit test for the ResourceAttributes class. + ''' + def testDefaults(self): + '''Test if default values are reasonable. + ''' + ra = ResourceAttributes() + self.assertEquals('UNKNOWN', ra.identifier) + self.assertEquals('UNKNOWN', ra.title) + self.assertEquals(0, len(ra.formats)) + self.assertEquals('UNKNOWN', ra.description) + self.assertEquals(0, len(ra.creators)) + self.assertEquals(0, len(ra.subjects)) + self.assertEquals(0, len(ra.publishers)) + self.assertEquals(0, len(ra.contributors)) + self.assertEquals(0, len(ra.dates)) + self.assertEquals(0, len(ra.types)) + self.assertEquals(0, len(ra.sources)) + self.assertEquals(0, len(ra.languages)) + self.assertEquals(0, len(ra.relations)) + self.assertEquals(0, len(ra.coverages)) + self.assertEquals(0, len(ra.rights)) + self.assertEquals(0, len(ra.contexts)) + self.assertEquals('UNKNOWN', ra.aggregation) + self.assertEquals('UNKNOWN', ra.resClass) + self.assertEquals(0, len(ra.locations)) + + def testCmp(self): + '''Test comparison operations. + ''' + a = ResourceAttributes('uri:fish', 'Fish', ['text/html'], 'A book about fish.') + b = ResourceAttributes('uri:fish', 'Fish', ['text/html'], 'A book about fish.') + c = ResourceAttributes('uri:clams', 'Clams', ['text/html'], 'A book about clams.') + self.assertEquals(a, a) + self.assertEquals(a, b) + self.assertNotEquals(a, c) + self.assert_(a <= b) + self.assert_(a >= c) + self.assert_(a > c) + self.assert_(a <= a) + + def testXML(self): + '''Test XML serialization and recomposition from XML. + ''' + a = ResourceAttributes('uri:fish', 'Fish', ['text/html'], 'A book about fish.') + doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None) + node = a.toXML(doc) + b = ResourceAttributes(node=node) + self.assertEquals(a, b) + + def testXMLValidity(self): + '''Test to see if all required XML elements are in there. + ''' + a = ResourceAttributes('uri:anus', 'Anus', ['text/html'], 'The anus, rectum, and other parts of the bum.', + ['Buttman'], ['butts', 'henies', 'booties'], ['Butts and Co Publishing'], ['Dr Eugene Bottomman, III'], + [], ['reference'], ['The Big Book of Booty'], ['en'], ['Buttholes and other oddities'], + ['anatomy'], ['Cannot touch this'], ['system.buttServer'], 'granule', 'system.buttServer', + ['http://butt.info/butt']) + doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None) + node = a.toXML(doc) + childElements = [n.nodeName for n in node.childNodes] + self.assertEquals([u'Identifier', u'Title', u'Format', u'Description', u'Creator', u'Subject', u'Subject', + u'Subject', u'Publisher', u'Contributor', u'Type', u'Source', u'Language', u'Relation', u'Coverage', + u'Rights', u'resContext', u'resAggregation', u'resClass', u'resLocation'], childElements) + + def testInstances(self): + '''Test to ensure instances don't share instance data. + ''' + a = ResourceAttributes() + b = ResourceAttributes() + a.formats.append('text/xml') + a.creators.append('Dennis Moore') + a.subjects.append('Silliness') + a.publishers.append('BBC') + a.contributors.append('Nigel') + a.types.append('Video') + a.languages.append('en') + a.relations.append('Fawlty Towers') + a.coverages.append('1970') + a.rights.append('Abused') + a.contexts.append('humor') + a.locations.append('http://bbc.co.uk/') + self.assertNotEquals(a.formats, b.formats) + self.assertNotEquals(a.creators, b.creators) + self.assertNotEquals(a.subjects, b.subjects) + self.assertNotEquals(a.publishers, b.publishers) + self.assertNotEquals(a.contributors, b.contributors) + self.assertNotEquals(a.types, b.types) + self.assertNotEquals(a.languages, b.languages) + self.assertNotEquals(a.relations, b.relations) + self.assertNotEquals(a.coverages, b.coverages) + self.assertNotEquals(a.rights, b.rights) + self.assertNotEquals(a.contexts, b.contexts) + self.assertNotEquals(a.locations, b.locations) + + +class ProfileTest(unittest.TestCase): + '''Unit test for class Profile. + ''' + def testCmp(self): + a = Profile() + b = Profile() + self.assertEquals(a, b) + + def testXML(self): + '''Test XML serialization and recomposition from XML. + ''' + a = Profile() + doc = xml.dom.minidom.getDOMImplementation().createDocument(None, None, None) + node = a.toXML(doc) + b = Profile(node=node) + self.assertEquals(a, b) + + x = UnspecifiedProfileElement('tastiness', 'How tasty it was', 'char', 'subjective', ['yumminess'], + 'This is highly subjective.') + y = EnumeratedProfileElement('meal', 'What meal was eaten', 'char', 'meal', ['serving'], 'Typical values', + ['Breakfast', 'Brunch', 'Lunch', 'Dinner']) + z = RangedProfileElement('spicyness', 'How spicy it was', 'float', 'scovilles', ['piquancy'], + 'Hotter the better, I say', 0.0, 1000000.0) + a.profElements['tastiness'], a.profElements['meal'], a.profElements['spicyness'] = x, y, z + node = a.toXML(doc) + b = Profile(node=node) + self.assertEquals(a, b) + + def testInstances(self): + '''Test to ensure isntances don't share isntance data. + ''' + a = Profile() + b = Profile() + self.assertEquals(a, b) + a.profElements['a'] = 'b' + self.assertNotEquals(a, b) + + +def test_suite(): + '''Make the test suite. + ''' + import doctest + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ProfileAttributesTest)) + suite.addTest(unittest.makeSuite(ResourceAttributesTest)) + suite.addTest(unittest.makeSuite(ProfileTest)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite')
Added: incubator/oodt/agility/agile-oodt/trunk/oodt/tests/queryTest.py URL: http://svn.apache.org/viewvc/incubator/oodt/agility/agile-oodt/trunk/oodt/tests/queryTest.py?rev=925913&view=auto ============================================================================== --- incubator/oodt/agility/agile-oodt/trunk/oodt/tests/queryTest.py (added) +++ incubator/oodt/agility/agile-oodt/trunk/oodt/tests/queryTest.py Sun Mar 21 22:08:01 2010 @@ -0,0 +1,341 @@ +# encoding: utf-8 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE.txt file distributed with +# this work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +'''Unit tests for the query module. + +This module tests various query classes and the query expression parser. +''' + +__docformat__ = 'restructuredtext' + +import unittest, xml.dom, oodt.xmlutils +from oodt.query import QueryElement, QueryHeader, QueryResult, Query, _parseQuery, ExpressionParseError + +class QueryElementTest(unittest.TestCase): + '''Test the `QueryElement` class. + ''' + def testDefaults(self): + '''Test to see if defaults are reasonable. + ''' + qe = QueryElement() + self.assertEqual('UNKNOWN', qe.role) + self.assertEqual('UNKNOWN', qe.value) + + def testArgs(self): + '''Test to see if initializer arguments are used. + ''' + qe = QueryElement(role='role', value='value') + self.assertEqual('role', qe.role) + self.assertEqual('value', qe.value) + + def testComparisons(self): + '''Test comparison operators. + ''' + a = QueryElement('a', '1') + b = QueryElement('a', '1') + c = QueryElement('b', '1') + d = QueryElement('a', '2') + self.assertEqual(a, a) + self.assertEqual(a, b) + self.assertEqual(b, a) + self.assertNotEqual(a, c) + self.assertNotEqual(c, a) + self.assertNotEqual(a, d) + self.assertNotEqual(d, a) + self.assertNotEqual(c, d) + self.assert_(a <= a) + self.assert_(a <= c) + self.assert_(a < c) + self.assert_(a <= d) + self.assert_(a < d) + + def testBadArgs(self): + '''Test reactions to bad arugments. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype + self.assertRaises(ValueError, QueryElement, node=doc.createElement('notAQueryElement')) + + def testXML(self): + '''Test XML serialization. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype + q1 = QueryElement(role='a', value='1') + root = q1.toXML(doc) + self.assertEqual('queryElement', root.nodeName) + for child in root.childNodes: + if 'tokenRole' == child.nodeName: + self.assertEquals('a', oodt.xmlutils.text(child)) + elif 'tokenValue' == child.nodeName: + self.assertEquals('1', oodt.xmlutils.text(child)) + else: + self.fail('Unknown node "' + child.nodeName + '" in XML result') + + q2 = QueryElement(node=root) + self.assertEqual(q1, q2) + + root = doc.createElement('queryElement') + doc.appendChild(root) + elem = doc.createElement('tokenRole') + root.appendChild(elem) + elem.appendChild(doc.createTextNode('a')) + elem = doc.createElement('tokenValue') + root.appendChild(elem) + elem.appendChild(doc.createTextNode('2')) + q3 = QueryElement(node=root) + self.assertNotEqual(q1, q3) + + +_QUERY_HEADER_ATTRS = { + 'queryId': 'id', + 'queryTitle': 'title', + 'queryDesc': 'desc', + 'queryType': 'type', + 'queryStatusId': 'status', + 'querySecurityType': 'security', + 'queryRevisionNote': 'rev', + 'queryDataDictId': 'dataDict' +} + +class QueryHeaderTest(unittest.TestCase): + '''Unit test for the `QueryHeader` class. + ''' + def testDefaults(self): + '''Test if defaults are reasonable. + ''' + qh = QueryHeader() + self.assertEqual('UNKNOWN', qh.id) + self.assertEqual('UNKNOWN', qh.title) + self.assertEqual('UNKNOWN', qh.desc) + self.assertEqual('QUERY', qh.type) + self.assertEqual('ACTIVE', qh.status) + self.assertEqual('UNKNOWN', qh.security) + self.assertEqual('2005-10-01 SCK v0.0.0 Under Development', qh.rev) + self.assertEqual('UNKNOWN', qh.dataDict) + + def testArgs(self): + '''Test if initializer arguments are used. + ''' + qh = QueryHeader('id', 'title', 'desc', 'type', 'status', 'security', 'rev', 'dataDict') + self.assertEqual(qh.id, 'id') + self.assertEqual(qh.title, 'title') + self.assertEqual(qh.desc, 'desc') + self.assertEqual(qh.type, 'type') + self.assertEqual(qh.status, 'status') + self.assertEqual(qh.security, 'security') + self.assertEqual(qh.rev, 'rev') + self.assertEqual(qh.dataDict, 'dataDict') + + def testBadArgs(self): + '''Test reaction to bad arguments. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype + self.assertRaises(ValueError, QueryHeader, node=doc.createElement('notAQueryHeader')) + + def testXML(self): + '''Test XML serialization. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype + q1 = QueryHeader('id', 'title', 'desc', 'type', 'status', 'security', 'rev', 'dataDict') + root = q1.toXML(doc) + self.assertEqual('queryAttributes', root.nodeName) + for child in root.childNodes: + self.check(child.nodeName, oodt.xmlutils.text(child)) + q2 = QueryHeader(node=root) + self.assertEqual(q1, q2) + + def check(self, name, value): + '''Check if the given tag name is valid. + ''' + if name in _QUERY_HEADER_ATTRS: + self.assertEqual(value, _QUERY_HEADER_ATTRS[name]) + else: + fail('Unknown element ' + name + ' in query header') + + +class QueryResultTest(unittest.TestCase): + '''Unit test for the `QueryResults` class. + ''' + def testDefaults(self): + '''Test if defaults are reasonable. + ''' + qr = QueryResult() + self.assertEquals(0, len(qr.results)) + self.assertEquals(0, len(qr)) + self.assertRaises(IndexError, qr.__getitem__, 0) + + def testBadArgs(self): + '''Test reaction to bad arguments. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype + self.assertRaises(ValueError, QueryResult, node=doc.createElement('notAQueryResult')) + + def testXML(self): + '''Test XML serialization. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype + q1 = QueryResult() + root = q1.toXML(doc) + self.assertEqual('queryResultSet', root.nodeName) + q2 = QueryResult(node=root) + self.assertEqual(q1, q2) + + +class QueryTest(unittest.TestCase): + '''Unit test for the `Query` class. + ''' + def testDefaults(self): + '''Test if defaults are reasonable. + ''' + q = Query() + self.assertEqual(QueryHeader(), q.header) + self.assertEqual('ATTRIBUTE', q.resultModeID) + self.assertEqual('BROADCAST', q.propType) + self.assertEqual('N/A', q.propLevels) + self.assertEqual(1, q.maxResults) + self.assertEqual(0, len(q.mimeAccept)) + + def testParser(self): + '''Test the query expresion parser. + ''' + # Empty + self.assertEqual(([], []), _parseQuery('')) + + # Simple + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'), + QueryElement(role='RELOP', value='EQ') ], []), _parseQuery('x = 1')) + + # Logical or + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'), + QueryElement(role='RELOP', value='LT'), QueryElement(role='elemName', value='y'), + QueryElement(role='LITERAL', value='-2'), QueryElement(role='RELOP', value='GT'), + QueryElement(role='LOGOP', value='OR') ], []), _parseQuery('x < 1 or y > -2')) + + # Logical and has higher precendence + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'), + QueryElement(role='RELOP', value='LE'), QueryElement(role='elemName', value='y'), + QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GE'), + QueryElement(role='elemName', value='z'), QueryElement(role='LITERAL', value='-3.96'), + QueryElement(role='RELOP', value='NE'), QueryElement(role='LOGOP', value='AND'), + QueryElement(role='LOGOP', value='OR') ], []), _parseQuery('x <= 1 | y >= 2 and z != -3.96')) + + # Logical or has lower precedence + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'), + QueryElement(role='RELOP', value='LT'), QueryElement(role='elemName', value='y'), + QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GT'), + QueryElement(role='LOGOP', value='AND'), QueryElement(role='elemName', value='z'), + QueryElement(role='LITERAL', value='3'), QueryElement(role='RELOP', value='NE'), + QueryElement(role='LOGOP', value='OR') ], []), _parseQuery('x LT 1 & y GT 2 or z NE 3')) + + # Parenthesis + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'), + QueryElement(role='RELOP', value='LE'), QueryElement(role='elemName', value='y'), \ + QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GE'), + QueryElement(role='LOGOP', value='OR'), QueryElement(role='elemName', value='z'), + QueryElement(role='LITERAL', value='3'), QueryElement(role='RELOP', value='EQ'), + QueryElement(role='LOGOP', value='AND') ], []), _parseQuery('(x LE 1 or y GE 2) and z EQ 3')) + + # Logical not + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='1'), + QueryElement(role='RELOP', value='LE'), QueryElement(role='elemName', value='y'), + QueryElement(role='LITERAL', value='2'), QueryElement(role='RELOP', value='GE'), + QueryElement(role='LOGOP', value='OR'), QueryElement(role='LOGOP', value='NOT'), + QueryElement(role='elemName', value='z'), QueryElement(role='LITERAL', value='3'), + QueryElement(role='RELOP', value='EQ'), QueryElement(role='LOGOP', value='NOT'), + QueryElement(role='LOGOP', value='AND') ], []), _parseQuery('not (x LE 1 or y GE 2) and ! z EQ 3')) + + # Quoted strings + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='Fish Poop'), + QueryElement(role='RELOP', value='EQ'), QueryElement(role='elemName', value='y'), + QueryElement(role='LITERAL', value='Monkey Poop'), QueryElement(role='RELOP', value='EQ'), + QueryElement(role='LOGOP', value='AND') ], []), + _parseQuery("""x = "Fish Poop" and y = 'Monkey Poop'""")) + + # RETURN elements + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='Fish Poop'), + QueryElement(role='RELOP', value='EQ'), QueryElement(role='elemName', value='y'), + QueryElement(role='LITERAL', value='Monkey Poop'), QueryElement(role='RELOP', value='EQ'), + QueryElement(role='LOGOP', value='AND') ], [QueryElement(role='elemName', value='fish')]), + _parseQuery("""x = "Fish Poop" and RETURN > fish and y = 'Monkey Poop'""")) + + # RETURN at beginning and middle + self.assertEqual(([ QueryElement(role='elemName', value='x'), QueryElement(role='LITERAL', value='Fish Poop'), + QueryElement(role='RELOP', value='EQ'), QueryElement(role='elemName', value='y'), + QueryElement(role='LITERAL', value='Monkey Poop'), QueryElement(role='RELOP', value='EQ'), + QueryElement(role='LOGOP', value='AND') ], [QueryElement(role='elemName', value='fish'), + QueryElement(role='elemName', value='poop')]), + _parseQuery("""RETURN < fish and x = "Fish Poop" and RETURN > poop and y = 'Monkey Poop'""")) + + # Bad expressions + for expr in ('heya', 'RETURN =', 'AND', 'LAT ~ fish', 'NOT', '(', ')'): + self.assertRaises(ExpressionParseError, _parseQuery, expr) + + # Unusual symbols + self.assertEqual(([QueryElement('elemName', 'DATA_SET_ID'), QueryElement('LITERAL', 'ARCB-L-RTLS-3-70CM-V1.0'), + QueryElement('RELOP', 'EQ')], []), _parseQuery('''DATA_SET_ID = ARCB-L-RTLS-3-70CM-V1.0''')) + self.assertEqual(([QueryElement('elemName', 'file'), QueryElement('LITERAL', '/usr/local/bin/poop'), + QueryElement('RELOP', 'EQ')], []), _parseQuery('''file = /usr/local/bin/poop''')) + self.assertEqual(([QueryElement('elemName', 'start_time'), QueryElement('LITERAL', '2006-02-06T13:12:13'), + QueryElement('RELOP', 'EQ')], []), _parseQuery('''start_time = 2006-02-06T13:12:13''')) + + def testCmp(self): + '''Test comparison operators. + ''' + a = Query('lat > 3 and lon < -92.6') + b = Query('lat > 3 and lon < -92.6') + c = Query('lat < 3 and lon > -92.6') + self.assert_(a == a) + self.assert_(a == b) + self.assert_(a < c) + self.assert_(c > a) + self.assert_(a <= b) + self.assert_(a >= b) + self.assert_(a <= c) + self.assert_(c >= a) + self.assert_(c != a) + + def testXML(self): + '''Test XML serialization. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # namespace URI, qualified Name, doctype + q1 = Query('lat > 3 and lon < -92.6') + root = q1.toXML(doc) + self.assertEqual('query', root.nodeName) + q2 = Query(node=root) + self.assertEqual(q1, q2) + + +def test_suite(): + '''Create the suite of tests. + ''' + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(QueryElementTest)) + suite.addTest(unittest.makeSuite(QueryHeaderTest)) + suite.addTest(unittest.makeSuite(QueryResultTest)) + suite.addTest(unittest.makeSuite(QueryTest)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + Added: incubator/oodt/agility/agile-oodt/trunk/oodt/tests/xmlutilsTest.py URL: http://svn.apache.org/viewvc/incubator/oodt/agility/agile-oodt/trunk/oodt/tests/xmlutilsTest.py?rev=925913&view=auto ============================================================================== --- incubator/oodt/agility/agile-oodt/trunk/oodt/tests/xmlutilsTest.py (added) +++ incubator/oodt/agility/agile-oodt/trunk/oodt/tests/xmlutilsTest.py Sun Mar 21 22:08:01 2010 @@ -0,0 +1,109 @@ +# encoding: utf-8 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE.txt file distributed with +# this work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +'''Unit tests for the `xmlutils` module. +''' + +__docformat__ = 'restructuredtext' + +import unittest, xml.dom, oodt.xmlutils +from oodt.xmlutils import DocumentableField + +class XMLTest(unittest.TestCase): + '''Unit test the XML utilities. + ''' + def testText(self): + '''Test getting text from nodes. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # ns URI, qual name, doctype + root = doc.createElement('root') + root.appendChild(doc.createTextNode('Hello')) + child = doc.createElement('child') + root.appendChild(child) + root.appendChild(doc.createCDATASection('world')) + child.appendChild(doc.createTextNode('cruel')) + empty = doc.createElement('empty') + root.appendChild(empty) + + self.assertEquals('cruel', oodt.xmlutils.text(child)) + self.assertEquals(0, len(oodt.xmlutils.text(empty))) + self.assertEquals('Hellocruelworld', oodt.xmlutils.text(root)) + + def testAdds(self): + '''Test adding things to nodes. + ''' + domImpl = xml.dom.getDOMImplementation() + doc = domImpl.createDocument(None, None, None) # ns URI, qual name, doctype + root = doc.createElement('root') + + oodt.xmlutils.add(root, 'first') + self.assertEquals(0, len(oodt.xmlutils.text(root))) + oodt.xmlutils.add(root, 'second', 'with text') + self.assertEquals('with text', oodt.xmlutils.text(root)) + + oodt.xmlutils.add(root, 'number', [str(i) for i in range(1, 4)]) + self.assertEquals('with text123', oodt.xmlutils.text(root)) + + +class DocumentableTest(unittest.TestCase): + '''Unit test the `Documentable` and `DocumentableField` classes. + ''' + def testIt(self): + '''Test the `Documentable` and `DocumentableField` classes. + ''' + class X(oodt.xmlutils.Documentable): + def __init__(self, mission='UNK', target='UNK', measurements=[], node=None): + self.mission, self.target, self.measurements = mission, target, measurements + if node is not None: + self.parse(node) + def computeValueFromDocument(self, attrName, text): + if attrName == 'measurements': + return int(text) + else: + return text + def getDocumentElementName(self): + return 'X' + def getDocumentableFields(self): + return (DocumentableField('mission', u'MISSION', DocumentableField.SINGLE_VALUE_KIND), + DocumentableField('target', u'TARGET', DocumentableField.SINGLE_VALUE_KIND), + DocumentableField('measurements', u'MZR', DocumentableField.MULTI_VALUED_KIND)) + def __eq__(self, other): + return (self.mission == other.mission and self.target == other.target + and cmp(self.measurements, other.measurements) == 0) + def __str__(self): + return '%s,%s:%s' % (self.mission, self.target, self.measurements) + a = X('Explorer', 'Moon', [1, 2, 3]) + doc = xml.dom.getDOMImplementation().createDocument(None, None, None) + node = a.toXML(doc) + b = X(node=node) + self.assertEquals(a, b) + + +def test_suite(): + '''Create the suite of tests. + ''' + import doctest + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(XMLTest)) + suite.addTest(unittest.makeSuite(DocumentableTest)) + suite.addTest(doctest.DocTestSuite(oodt.xmlutils)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') Added: incubator/oodt/agility/agile-oodt/trunk/oodt/webgrid.py URL: http://svn.apache.org/viewvc/incubator/oodt/agility/agile-oodt/trunk/oodt/webgrid.py?rev=925913&view=auto ============================================================================== --- incubator/oodt/agility/agile-oodt/trunk/oodt/webgrid.py (added) +++ incubator/oodt/agility/agile-oodt/trunk/oodt/webgrid.py Sun Mar 21 22:08:01 2010 @@ -0,0 +1,226 @@ +# encoding: utf-8 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE.txt file distributed with +# this work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +'''Agile Web Grid. HTTP-based profile and product servers. +''' + +__docformat__ = 'restructuredtext' + +from oodt.query import Query +from xml.dom.minidom import parseString, getDOMImplementation +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +import cgi, os, shutil, stat + +_validKinds = ('profile', 'product') +_doc = getDOMImplementation().createDocument(None, None, None) # qname, nsuri, doctype + +class WebGridRequestHandler(BaseHTTPRequestHandler): + '''HTTP request handler for Web-Grid requests. This request handler accepts GET + or POST requests and directs them to profile or product handlers. Additionally, + requests to install new handlers, to list currently installed handlers, and to + remove handlers by ID are supported. + ''' + def do_POST(self): + '''Handle a POST. + ''' + try: + length = int(self.headers['content-length']) + kind = self.headers['content-type'] + if kind.endswith('www-form-urlencoded'): + if length > 0: + params = cgi.parse_qs(self.rfile.read(length), True, True) # Keep blanks, strict parse + else: + params = {} + self.__execute(self.path, params) + else: + raise ValueError('Unknown encoding "%s"' % kind) + except Exception, e: + self.send_error(500, str(e)) + + def do_GET(self): + '''Handle a GET. + ''' + try: + index = self.path.find('?') + if index >= 0: + params = cgi.parse_qs(self.path[index+1:], True, True) # Keep blanks, strict parse + path = self.path[0:index] + else: + params, path = {}, self.path + self.__execute(path, params) + except Exception, e: + self.send_error(500, str(e)) + + def __execute(self, path, params): + '''Execute an HTTP request. + ''' + components = path.split('/') + if len(components) == 3: + context, command = components[1], components[2] + if context != self.server.serverID: + raise ValueError('Unknown server ID "%s"' % context) + func = getattr(self, command) + if callable(func): + func(params) + return + raise KeyError('Unknown command') + + def echo(self, params): + '''Debugging method that echoes back the request parameters. + ''' + u = unicode(params) + self.send_response(200) + self.send_header('Content-type', 'text/plain;charset=utf-8') + self.send_header('Content-length', str(len(u))) + self.end_headers() + self.wfile.write(u) + + def sendEmptyResponse(self): + '''Send an empty response to the HTTP client. + ''' + self.send_response(200) + self.send_header('Content-type', 'text/plain;charset=utf-8') + self.send_header('Content-length', '0') + self.end_headers() + + def install(self, params): + '''Install a new handler. This will overwrite existing handlers with the + same ID. + ''' + handlers = self.server.getHandlers(params['kind'][0]) + globs = dict(globals()) + del globs['__name__'] + # TODO: use rexec or otherwise limit the code than can be uploaded. + exec params['code'][0] in globs, globs + handlers[params['id'][0]] = globs['handler'] + self.sendEmptyResponse() + + def remove(self, params): + '''Remove an existing handler. + ''' + handlers = self.server.getHandlers(params['kind'][0]) + del handlers[params['id'][0]] + self.sendEmptyResponse() + + def list(self, params): + '''List installed handlers. + ''' + handlers = {} + for kind in _validKinds: + handlers[kind] = self.server.getHandlers(kind).keys() + handlers = unicode(handlers) + self.send_response(200) + self.send_header('Content-type', 'text/plain;charset=utf-8') + self.send_header('Content-length', str(len(handlers))) + self.end_headers() + self.wfile.write(handlers) + + def __createQuery(self, params): + '''Create a Query from the request parameters. This method prefers the + xmlq parameter and parses it as an XML document and into a Query object. + However, if it's not provided, or fails to parse, it'll use the q parameter, + which is expected to be just a query expression. + ''' + try: + doc = parseString(params['xmlq'][0]) + return Query(node=doc.documentElement) + except KeyError: + return Query(params['q'][0]) + + def sendProduct(self, match): + '''Send a matching product. + ''' + self.send_response(200) + self.send_header('Content-type', match.contentType) + self.send_header('Content-length', str(match.length)) + self.end_headers() + shutil.copyfileobj(match.data, self.wfile) + self.log_request(200, match.length) + + def prod(self, params): + '''Handle a product query. + ''' + query = self.__createQuery(params) + for handler in self.server.getHandlers('product'): + matches = handler.query(query) + if len(matches) > 0: + self.sendProduct(matches[0]) + self.send_error(404, 'No matching products') + + def prof(self, params): + '''Handle a profile query. + ''' + query = self.__createQuery(params) + tmp = os.tmpfile() + tmp.writelines((u'<?xml version="1.0" encoding="UTF-8"?>\n', + u'<!DOCTYPE profiles PUBLIC "-//JPL//DTD Profile 1.1//EN"\n', + u' "http://oodt.jpl.nasa.gov/grid-profile/dtd/prof.dtd">\n', + u'<profiles>\n')) + for handler in self.server.getHandlers('profile').itervalues(): + for profile in handler.query(query): + node = profile.toXML(_doc) + tmp.write(node.toxml()) + tmp.write(u'</profiles>') + tmp.flush() + tmp.seek(0L) + self.send_response(200) + self.send_header('Content-type', 'text/xml;charset=utf-8') + size = os.fstat(tmp.fileno())[stat.ST_SIZE] + self.send_header('Content-length', str(size)) + self.end_headers() + shutil.copyfileobj(tmp, self.wfile) + self.log_request(200, size) + + +class WebGridServer(HTTPServer): + '''Web grid HTTP server. This server handles incoming HTTP requests and directs them to a + WebGridRequestHandler. It also contains the server's ID, and the sequences of profile and + product handlers. + ''' + def __init__(self, addr, serverID): + '''Initialize by saving the server ID and creating empty sequences of profile + and product handlers. + ''' + HTTPServer.__init__(self, addr, WebGridRequestHandler) + self.serverID = serverID + self.__handlers = {} + for kind in _validKinds: + self.__handlers[kind] = {} + + def getHandlers(self, kind): + '''Get the map of handlers for the given kind, which is either "product" or "profile". + ''' + if kind not in _validKinds: + raise ValueError('Invalid handler kind "%s"' % kind) + return self.__handlers[kind] + + +def _main(): + '''Run the web grid server. + ''' + import sys + try: + serverID = sys.argv[1] + except IndexError: + serverID = 'oodt' + listenAddr = ('', 7576) + httpd = WebGridServer(listenAddr, serverID) + httpd.serve_forever() + + +if __name__ == '__main__': + _main() Added: incubator/oodt/agility/agile-oodt/trunk/oodt/xmlutils.py URL: http://svn.apache.org/viewvc/incubator/oodt/agility/agile-oodt/trunk/oodt/xmlutils.py?rev=925913&view=auto ============================================================================== --- incubator/oodt/agility/agile-oodt/trunk/oodt/xmlutils.py (added) +++ incubator/oodt/agility/agile-oodt/trunk/oodt/xmlutils.py Sun Mar 21 22:08:01 2010 @@ -0,0 +1,209 @@ +# encoding: utf-8 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE.txt file distributed with +# this work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +'''Object Oriented Data Technology XML utilities. These are some simple utilities +that make working with rather obtuse DOM API less painful. +''' + +__docformat__ = 'restructuredtext' + +import string +import xml.dom + +def text(node): + '''Return the text under the given node. Text and CDATA nodes simply return + their content. Otherwise, text is gathered in a depth-first left-to-right + traversal of the node, building up text as we go along. + + >>> import xml.dom + >>> domImpl = xml.dom.getDOMImplementation() + >>> doc = domImpl.createDocument(None, None, None) + >>> root = doc.createElement('root') + >>> root.appendChild(doc.createTextNode('alpha')) # doctest: +ELLIPSIS + <DOM Text node...> + >>> text(root) + 'alpha' + >>> child = root.appendChild(doc.createElement('child')) + >>> text(child.appendChild(doc.createCDATASection('beta'))) + 'beta' + >>> text(root) + 'alphabeta' + ''' + strings = [] + _text0(node, strings) + return string.join(strings, '') # no separator + + +def _text0(node, strings): + '''Add the text under the given node to the list of strings. + ''' + if node.nodeType == xml.dom.Node.CDATA_SECTION_NODE: + strings.append(node.nodeValue) + elif node.nodeType == xml.dom.Node.TEXT_NODE: + strings.append(node.nodeValue) + for child in node.childNodes: + _text0(child, strings) + + +def add(node, elementName, value=None): + '''Add an element under the given node. Optionally, the value text is added to the element. + Returns the original node, handy for chaining calls together. If the value is a sequence, + then the element is added multiple times for each item in the sequence, using the item + as the text. + + >>> from xml.dom.minidom import getDOMImplementation + >>> doc = getDOMImplementation().createDocument(None, None, None) + >>> root = doc.createElement('root') + >>> root.toxml() + '<root/>' + >>> add(root, 'empty').toxml() + '<root><empty/></root>' + >>> add(root, 'full', 'of text').toxml() + '<root><empty/><full>of text</full></root>' + >>> add(root, 'item', [str(i) for i in range(1, 4)]).toxml() + '<root><empty/><full>of text</full><item>1</item><item>2</item><item>3</item></root>' + ''' + owner = node.ownerDocument + if isinstance(value, basestring): + elem = owner.createElement(elementName) + node.appendChild(elem) + elem.appendChild(owner.createTextNode(value)) + elif value is not None: + for i in value: + elem = owner.createElement(elementName) + node.appendChild(elem) + elem.appendChild(owner.createTextNode(i)) + else: + node.appendChild(owner.createElement(elementName)) + return node + + +class DocumentableField(object): + '''A documentable field is an attribute of an object that we can serialize into XML. + ''' + SINGLE_VALUE_KIND = 1 + MULTI_VALUED_KIND = 2 + DOCUMENTABLE_KIND = 3 + + def __init__(self, attrName, elemName, kind): + '''Initialize a documentable field with attrName (name of attribute in object), + elemName (name to use for XML element) and kind, which is one of the DOCUMENTABLE_* + constants. + ''' + self.attrName = attrName + self.elemName = elemName + if kind not in (DocumentableField.SINGLE_VALUE_KIND, DocumentableField.MULTI_VALUED_KIND, + DocumentableField.DOCUMENTABLE_KIND): + raise ValueError('Invalid kind %d' % kind) + self.kind = kind + + def __cmp__(self, other): + attrName = cmp(self.attrName, other.attrName) + if attrName < 0: + return -1 + elif attrName == 0: + elemName = cmp(self.elemName, other.elemName) + if elemName < 0: + return -1 + elif elemName == 0: + return cmp(self.kind, other.kind) + return 1 + + def __hash__(self): + return hash(self.attrName) ^ hash(self.elemName) ^ self.kind + + +class Documentable(object): + '''An object that can be documented into XML and parsed from an XML document. + ''' + def getDocumentElementName(self): + '''Get the XML tag name. Subclasses must override this + otherwise they get the tag name `UNKNOWN`. + ''' + return 'UNKNOWN' + + def getDocumentableFields(self): + '''Get the sequence of documentable attributes. Subclasses should override this + with a sequence of `DocumentableField` objects. By default, we return an empty + sequence. + ''' + return [] + + def toXML(self, owner): + '''Convert this object into XML owned by the given `owner` document. + ''' + root = owner.createElement(self.getDocumentElementName()) + for df in self.getDocumentableFields(): + if df.kind == DocumentableField.SINGLE_VALUE_KIND: + add(root, df.elemName, str(getattr(self, df.attrName))) + elif df.kind == DocumentableField.MULTI_VALUED_KIND: + add(root, df.elemName, [str(value) for value in getattr(self, df.attrName)]) + else: + root.appendChild(getattr(self, df.attrName).toXML(owner)) + return root + + def computeValueFromDocument(self, attrName, text): + '''Compute a value for the attribute named `attrName` from the + XML text representation `text`. Subclasses may wish to + override this in order to provide custom typing for certain + attributes, such as returning an integer or datetime value by + parsing the text. By default, the `text` is returned + unmodified as a string. + ''' + return text + + def parse(self, node): + '''Initialize this object from the given XML DOM `node`. + ''' + if node.nodeName != self.getDocumentElementName(): + raise ValueError('Expected %s element but got %s' % (self.getDocumentElementName(), node.nodeName)) + fieldMap = dict(zip([i.elemName for i in self.getDocumentableFields()], self.getDocumentableFields())) + for child in filter(lambda n: n.nodeType == xml.dom.Node.ELEMENT_NODE, node.childNodes): + name = child.nodeName + if name in fieldMap: + field = fieldMap[name] + if field.kind == DocumentableField.SINGLE_VALUE_KIND: + setattr(self, field.attrName, self.computeValueFromDocument(field.attrName, text(child))) + elif field.kind == DocumentableField.MULTI_VALUED_KIND: + if not hasattr(self, field.attrName): + setattr(self, field.attrName, []) + getattr(self, field.attrName).append(self.computeValueFromDocument(field.attrName, + text(child))) + else: + getattr(self, field.attrName).parse(child) + + def __cmp__(self, other): + '''The documentable fields provide a bonus: we can use them to do comparisons. + ''' + for field in self.getDocumentableFields(): + attrName = field.attrName + mine = getattr(self, attrName) + others = getattr(other, attrName) + rc = cmp(mine, others) + if rc < 0: + return -1 + elif rc > 0: + return 1 + return 0 + + def __hash__(self): + '''The documentable fields provide another bonus: we can use them + to do hashing. + ''' + return reduce(lambda x, y: hash(x) ^ hash(y), [getattr(self, i.attrName) for i in self.getDocumentableFields()], + 0x55555555) + \ No newline at end of file Added: incubator/oodt/agility/agile-oodt/trunk/setup.cfg URL: http://svn.apache.org/viewvc/incubator/oodt/agility/agile-oodt/trunk/setup.cfg?rev=925913&view=auto ============================================================================== --- incubator/oodt/agility/agile-oodt/trunk/setup.cfg (added) +++ incubator/oodt/agility/agile-oodt/trunk/setup.cfg Sun Mar 21 22:08:01 2010 @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE.txt file distributed with +# this work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +[egg_info] +tag_build = .dev +tag_svn_revision = 1 Added: incubator/oodt/agility/agile-oodt/trunk/setup.py URL: http://svn.apache.org/viewvc/incubator/oodt/agility/agile-oodt/trunk/setup.py?rev=925913&view=auto ============================================================================== --- incubator/oodt/agility/agile-oodt/trunk/setup.py (added) +++ incubator/oodt/agility/agile-oodt/trunk/setup.py Sun Mar 21 22:08:01 2010 @@ -0,0 +1,98 @@ +# encoding: utf-8 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE.txt file distributed with +# this work for additional information regarding copyright ownership. The ASF +# licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os.path +from distribute_setup import use_setuptools +use_setuptools() +from setuptools import find_packages, setup + + +# Package data +# ------------ + +_name = 'oodt' +_version = '1.0.0' +_description = 'Agile OODT' +_url = 'http://incubator.apache.org/projects/oodt.html' +_downloadURL = 'http://pypi.python.org/pypi/oodt/' +_author = 'Sean Kelly' +_authorEmail = '[email protected]' +_license = 'Apache 2.0' +_namespaces = [] +_testSuite = 'oodt.tests.test_suite' +_zipSafe = True +_keywords = 'data grid discovery query optimization object middleware archive catalog index' +_requirements = [] +_entryPoints = { + 'console_scripts': ['webgrid = oodt.webgrid:_main'], +} +_classifiers = [ + 'Environment :: Console', + 'Environment :: No Input/Output (Daemon)', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'Intended Audience :: Science/Research', + 'Topic :: Database :: Front-Ends', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', + 'Topic :: Internet :: Z39.50', + 'Topic :: Scientific/Engineering', + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries :: Python Modules', +] + + +# Setup Metadata +# -------------- + +def _read(*rnames): + return open(os.path.join(os.path.dirname(__file__), *rnames)).read() + +_header = '*' * len(_name) + '\n' + _name + '\n' + '*' * len(_name) +_longDescription = '\n\n'.join([ + _header, + _read('README.txt'), + _read('docs', 'INSTALL.txt'), + _read('docs', 'HISTORY.txt') +]) +open('doc.txt', 'w').write(_longDescription) + +setup( + author=_author, + author_email=_authorEmail, + classifiers=_classifiers, + description=_description, + download_url=_downloadURL, + entry_points=_entryPoints, + include_package_data=True, + install_requires=_requirements, + keywords=_keywords, + license=_license, + long_description=_longDescription, + name=_name, + namespace_packages=_namespaces, + packages=find_packages(), + test_suite=_testSuite, + url=_url, + version=_version, + zip_safe=_zipSafe, +)
