From: Alina Quereilhac Date: Wed, 16 Feb 2011 09:43:33 +0000 (+0100) Subject: xml serialization of the experiment description X-Git-Tag: nepi_v2~203 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=0f01b517b571d08381f4fd38fa8346ca08ff083e;p=nepi.git xml serialization of the experiment description --- diff --git a/examples/design1.py b/examples/design1.py index 7f56ace3..1e980794 100644 --- a/examples/design1.py +++ b/examples/design1.py @@ -1,41 +1,54 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from nepi.core.experiment import ExperimentDescription +from nepi.core.description import ExperimentDescription +from nepi.testbeds import netns -testbed_id = "netns" +exp_desc = ExperimentDescription() testbed_version = "01" -experiment = ExperimentDescription() -netns = experiment.add_testbed_description(testbed_id, testbed_version) -node1 = netns.create("Node") -node2 = netns.create("Node") -iface1 = netns.create("NodeInterface") +netns_provider = netns.TestbedFactoriesProvider(testbed_version) +netns_desc = exp_desc.add_testbed_description(netns_provider) + +node1 = netns_desc.create("Node") +node2 = netns_desc.create("Node") +iface1 = netns_desc.create("NodeInterface") iface1.set_attribute_value("up", True) node1.connector("devs").connect(iface1.connector("node")) ip1 = iface1.add_address() ip1.set_attribute_value("Address", "10.0.0.1") -iface2 = netns.create("NodeInterface") +iface2 = netns_desc.create("NodeInterface") iface2.set_attribute_value("up", True) node2.connector("devs").connect(iface2.connector("node")) ip2 = iface2.add_address() ip2.set_attribute_value("Address", "10.0.0.2") -switch = netns.create("Switch") +switch = netns_desc.create("Switch") switch.set_attribute_value("up", True) iface1.connector("switch").connect(switch.connector("devs")) iface2.connector("switch").connect(switch.connector("devs")) -app = netns.create("Application") +app = netns_desc.create("Application") app.set_attribute_value("command", "ping -qc10 10.0.0.2") app.connector("node").connect(node1.connector("apps")) -#from nepi.util.parser.base import Parser -#p = Parser() -#data = p.to_data(experiment) -#print data -#e2 = p.from_data(data) -#data2 = p.to_data(e2) -#print data2 +from nepi.util.parser.base import ExperimentParser +p = ExperimentParser() +data = p.to_data(exp_desc) +print data +exp_desc2 = ExperimentDescription() +p.from_data(exp_desc2, data) +data2 = p.to_data(exp_desc2) +print data2 +print data == data2 + +from nepi.util.parser._xml import XmlExperimentParser +p = XmlExperimentParser() +xml = p.to_xml(exp_desc) +print xml +exp_desc2 = ExperimentDescription() +p.from_xml(exp_desc2, xml) +xml2 = p.to_xml(exp_desc2) +print xml2 +print xml == xml2 -#print data == data2 #print experiment.xml_description diff --git a/src/nepi/core/description.py b/src/nepi/core/description.py index 650aca8a..cc3cb076 100644 --- a/src/nepi/core/description.py +++ b/src/nepi/core/description.py @@ -3,6 +3,8 @@ from nepi.core.attributes import AttributesMap, Attribute from nepi.util import validation +from nepi.util.guid import GuidGenerator +from nepi.util.parser._xml import XmlExperimentParser import sys AF_INET = 0 @@ -411,11 +413,21 @@ class RoutingTableBoxFactory(BoxFactory): def create(self, guid, testbed_description): return RoutingTableBox(guid, self) -class FactoriesProvider(object): - def __init__(self): - super(FactoriesProvider, self).__init__() +class TestbedFactoriesProvider(object): + def __init__(self, testbed_id, testbed_version): + super(TestbedFactoriesProvider, self).__init__() + self._testbed_id = testbed_id + self._testbed_version = testbed_version self._factories = dict() + @property + def testbed_id(self): + return self._testbed_id + + @property + def testbed_version(self): + return self._testbed_version + def factory(self, factory_id): return self._factories[factory_id] @@ -429,12 +441,10 @@ class FactoriesProvider(object): return self._factories.keys() class TestbedDescription(AttributesMap): - def __init__(self, guid_generator, testbed_id, testbed_version, provider): + def __init__(self, guid_generator, provider): super(TestbedDescription, self).__init__() self._guid_generator = guid_generator self._guid = guid_generator.next() - self._testbed_id = testbed_id - self._testbed_version = testbed_version self._provider = provider self._boxes = dict() @@ -442,17 +452,9 @@ class TestbedDescription(AttributesMap): def guid(self): return self._guid - @property - def testbed_id(self): - return self._testbed_id - - @property - def testbed_version(self): - return self._testbed_version - @property def provider(self): - return provider + return self._provider @property def boxes(self): @@ -479,3 +481,42 @@ class TestbedDescription(AttributesMap): box.destroy() self._boxes = None +class ExperimentDescription(object): + def __init__(self, guid = 0): + self._guid_generator = GuidGenerator(guid) + # testbed design instances + self._testbed_descriptions = dict() + + @property + def testbed_descriptions(self): + return self._testbed_descriptions.values() + + def to_xml(self): + parser = XmlExperimentParser() + return parser.to_xml(self) + + def from_xml(self, xml): + parser = XmlExperimentParser() + parser.from_xml(self, xml) + + def testbed_description(self, guid): + return self._testbed_descriptions[guid] \ + if guid in self._testbed_descriptions else None + + def box(self, guid): + for testbed_description in self._testbed_descriptions.values(): + box = testbed_description.box(guid) + if box: return box + return None + + def add_testbed_description(self, provider): + testbed_description = TestbedDescription(self._guid_generator, + provider) + guid = testbed_description.guid + self._testbed_descriptions[guid] = testbed_description + return testbed_description + + def remove_testbed_description(self, testbed_description): + guid = testbed_description.guid + del self._testbed_descriptions[guid] + diff --git a/src/nepi/core/experiment.py b/src/nepi/core/experiment.py deleted file mode 100644 index a0db3293..00000000 --- a/src/nepi/core/experiment.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from nepi.util.guid import GuidGenerator -from nepi.util.parser.xml import XmlParser -import sys - -class ExperimentDescription(object): - def __init__(self, guid = 0): - self._guid_generator = GuidGenerator(guid) - # testbed design instances - self._testbed_descriptions = dict() - self._testbed_providers = dict() - - @property - def testbed_descriptions(self): - return self._testbed_descriptions.values() - - @property - def xml_description(self): - parser = XmlParser() - return parser.to_xml(self) - - def testbed_description(self, guid): - return self._testbed_descriptions[guid] \ - if guid in self._testbed_descriptions else None - - def box(self, guid): - for testbed_description in self._testbed_descriptions.values(): - box = testbed_description.box(guid) - if box: return box - return None - - def add_testbed_description(self, testbed_id, testbed_version): - testbed_module = self._testbed_module(testbed_id) - testbed_provider = self._testbed_provider(testbed_id, testbed_version) - testbed_description = testbed_module.create_description_instance( - self._guid_generator, testbed_version, testbed_provider) - guid = testbed_description.guid - self._testbed_descriptions[guid] = testbed_description - return testbed_description - - def remove_testbed_description(self, testbed_description): - guid = testbed_description.guid - del self._testbed_descriptions[guid] - - def _testbed_module(self, testbed_id): - mod_name = 'nepi.testbeds.%s' % testbed_id - if not mod_name in sys.modules: - __import__(mod_name) - return sys.modules[mod_name] - - def _testbed_provider(self, testbed_id, testbed_version): - key = "%s_%s" % (testbed_id, testbed_version) - if key not in self._testbed_providers: - testbed_module = self._testbed_module(testbed_id) - testbed_provider = testbed_module.create_provider(testbed_version) - self._testbed_providers[key] = testbed_provider - return self._testbed_providers[key] - diff --git a/src/nepi/testbeds/netns/__init__.py b/src/nepi/testbeds/netns/__init__.py index d6fb0e09..7ae173cf 100644 --- a/src/nepi/testbeds/netns/__init__.py +++ b/src/nepi/testbeds/netns/__init__.py @@ -64,13 +64,10 @@ def create_factories(version): factories.append(factory) return factories -def create_provider(version): - provider = description.FactoriesProvider() - for factory in create_factories(version): - provider.add_factory(factory) - return provider - -def create_description_instance(guid_generator, version, provider): - return description.TestbedDescription(guid_generator, TESTBED_ID, version, - provider) +class TestbedFactoriesProvider(description.TestbedFactoriesProvider): + def __init__(self, testbed_version): + super(TestbedFactoriesProvider, self).__init__(TESTBED_ID, + testbed_version) + for factory in create_factories(testbed_version): + self.add_factory(factory) diff --git a/src/nepi/util/parser/_xml.py b/src/nepi/util/parser/_xml.py new file mode 100644 index 00000000..db4c0d95 --- /dev/null +++ b/src/nepi/util/parser/_xml.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from nepi.core.attributes import Attribute +from nepi.util.parser.base import ExperimentParser +from xml.dom import minidom + +class XmlExperimentParser(ExperimentParser): + def to_xml(self, experiment_description): + data = self.to_data(experiment_description) + doc = minidom.Document() + exp_tag = doc.createElement("experiment") + testbeds_tag = doc.createElement("testbeds") + exp_tag.appendChild(testbeds_tag) + + elements_tags = dict() + for guid, elem_data in data.iteritems(): + if "testbed_id" in elem_data: + elements_tag = self.testbed_data_to_xml(doc, testbeds_tag, guid, + elem_data) + elements_tags[guid] = elements_tag + else: + testbed_guid = elem_data["testbed_guid"] + elements_tag = elements_tags[testbed_guid] + self.box_data_to_xml(doc, elements_tag, guid, elem_data) + + doc.appendChild(exp_tag) + xml = doc.toprettyxml(indent=" ", encoding="UTF-8") + return xml + + def testbed_data_to_xml(self, doc, parent_tag, guid, testbed_data): + testbed_tag = doc.createElement("testbed") + testbed_tag.setAttribute("guid", str(guid)) + testbed_tag.setAttribute("testbed_id", str(testbed_data["testbed_id"])) + testbed_tag.setAttribute("testbed_version", + str(testbed_data["testbed_version"])) + parent_tag.appendChild(testbed_tag) + elements_tag = doc.createElement("elements") + testbed_tag.appendChild(elements_tag) + return elements_tag + + def box_data_to_xml(self, doc, parent_tag, guid, box_data): + element_tag = doc.createElement("element") + parent_tag.appendChild(element_tag) + element_tag.setAttribute("factory_id", str(box_data["factory_id"])) + element_tag.setAttribute("guid", str(guid)) + if "factory_attributes" in box_data: + self.factory_attributes_data_to_xml(doc, element_tag, + box_data["factory_attributes"]) + if "attributes" in box_data: + self.attributes_data_to_xml(doc, element_tag, + box_data["attributes"]) + if "traces" in box_data: + self.traces_data_to_xml(doc, element_tag, box_data["traces"]) + if "addresses" in box_data: + self.addresses_data_to_xml(doc, element_tag, + box_data["addresses"]) + if "routes" in box_data: + self.routes_data_to_xml(doc, element_tag, box_data["routes"]) + if "connections" in box_data: + self.connections_data_to_xml(doc, element_tag, + box_data["connections"]) + + def factory_attributes_data_to_xml(self, doc, parent_tag, data): + factory_attributes_tag = doc.createElement("factory_attributes") + parent_tag.appendChild(factory_attributes_tag) + for name, value in data.iteritems(): + factory_attribute_tag = doc.createElement("factory_attribute") + factory_attributes_tag.appendChild(factory_attribute_tag) + factory_attribute_tag.setAttribute("name", name) + factory_attribute_tag.setAttribute("value", str(value)) + factory_attribute_tag.setAttribute("type", self.type_to_standard(value)) + + def attributes_data_to_xml(self, doc, parent_tag, data): + attributes_tag = doc.createElement("attributes") + parent_tag.appendChild(attributes_tag) + for name, value in data.iteritems(): + attribute_tag = doc.createElement("attribute") + attributes_tag.appendChild(attribute_tag) + attribute_tag.setAttribute("name", name) + attribute_tag.setAttribute("value", str(value)) + attribute_tag.setAttribute("type", self.type_to_standard(value)) + + def traces_data_to_xml(self, doc, parent_tag, data): + traces_tag = doc.createElement("traces") + parent_tag.appendChild(traces_tag) + for name in data: + trace_tag = doc.createElement("trace") + traces_tag.appendChild(trace_tag) + trace_tag.setAttribute("name", name) + + def addresses_data_to_xml(self, doc, parent_tag, data): + addresses_tag = doc.createElement("addresses") + parent_tag.appendChild(addresses_tag) + for address in data: + address_tag = doc.createElement("address") + addresses_tag.appendChild(address_tag) + for name, value in address.iteritems(): + address_tag.setAttribute(name, str(value)) + + def routes_data_to_xml(self, doc, parent_tag, data): + routes_tag = doc.createElement("routes") + parent_tag.appendChild(routes_tag) + for route in data: + route_tag = doc.createElement("route") + routes_tag.appendChild(route_tag) + for name, value in route.iteritems(): + route_tag.setAttribute(name, str(value)) + + def connections_data_to_xml(self, doc, parent_tag, data): + connections_tag = doc.createElement("connections") + parent_tag.appendChild(connections_tag) + for connector_type_id, connections in data.iteritems(): + for other_guid, other_connector_type_id in connections.iteritems(): + connection_tag = doc.createElement("connection") + connections_tag.appendChild(connection_tag) + connection_tag.setAttribute("connector", connector_type_id) + connection_tag.setAttribute("other_guid", str(other_guid)) + connection_tag.setAttribute("other_connector", + other_connector_type_id) + + def from_xml(self, experiment_description, xml): + data = dict() + doc = minidom.parseString(xml) + testbeds_tag = doc.getElementsByTagName("testbeds")[0] + testbed_tag_list = testbeds_tag.getElementsByTagName("testbed") + for testbed_tag in testbed_tag_list: + if testbed_tag.nodeType == doc.ELEMENT_NODE: + testbed_data = self.testbed_data_from_xml(testbed_tag) + testbed_guid = testbed_tag.getAttribute("guid") + data[int(testbed_guid)] = testbed_data + elements_tag = testbed_tag.getElementsByTagName("elements")[0] + element_tag_list = elements_tag.getElementsByTagName("element") + for element_tag in element_tag_list: + if element_tag.nodeType == doc.ELEMENT_NODE: + box_data = self.box_data_from_xml(testbed_guid, element_tag) + guid = element_tag.getAttribute("guid") + data[int(guid)] = box_data + print data + self.from_data(experiment_description, data) + + def testbed_data_from_xml(self, tag): + testbed_id = tag.getAttribute("testbed_id") + testbed_version = tag.getAttribute("testbed_version") + return dict({ + "testbed_id": str(testbed_id), + "testbed_version": str(testbed_version), + }) + + def box_data_from_xml(self, testbed_guid, tag): + factory_id = tag.getAttribute("factory_id") + data = dict({ + "testbed_guid": int(testbed_guid), + "factory_id": str(factory_id) + }) + self.factory_attributes_data_from_xml(data, tag) + self.attributes_data_from_xml(data, tag) + self.traces_data_from_xml(data, tag) + self.addresses_data_from_xml(data, tag) + self.routes_data_from_xml(data, tag) + self.connections_data_from_xml(data, tag) + return data + + def factory_attributes_data_from_xml(self, data, tag): + factory_attributes_tag_list = tag.getElementsByTagName( + "factory_attributes") + if len(factory_attributes_tag_list) == 0: + return + + factory_attribute_tag_list = factory_attributes_tag_list[0].\ + getElementsByTagName("factory_attribute") + factory_attributes_data = dict() + for factory_attribute_tag in factory_attribute_tag_list: + if factory_attribute_tag.nodeType == tag.ELEMENT_NODE: + name = factory_attribute_tag.getAttribute("name") + value = factory_attribute_tag.getAttribute("value") + std_type = factory_attribute_tag.getAttribute("type") + value = self.type_from_standard(std_type, value) + factory_attributes_data[str(name)] = value + data["factory_attributes"] = factory_attributes_data + + def attributes_data_from_xml(self, data, tag): + attributes_tag_list= tag.getElementsByTagName("attributes") + if len(attributes_tag_list) == 0: + return + + attribute_tag_list = attributes_tag_list[0].\ + getElementsByTagName("attribute") + attributes_data = dict() + for attribute_tag in attribute_tag_list: + if attribute_tag.nodeType == tag.ELEMENT_NODE: + name = attribute_tag.getAttribute("name") + value = attribute_tag.getAttribute("value") + std_type = attribute_tag.getAttribute("type") + value = self.type_from_standard(std_type, value) + attributes_data[str(name)] = value + data["attributes"] = attributes_data + + def traces_data_from_xml(self, data, tag): + traces_tag_list = tag.getElementsByTagName("traces") + if len(traces_tag_list) == 0: + return + + trace_tag_list = traces_tag_list[0].getElementsByTagName( + "trace") + traces_data = list() + for trace_tag in trace_tag_list: + if trace_tag.nodeType == tag.ELEMENT_NODE: + name = trace_tag.getAttribute("name") + traces_data.append(name) + data["traces"] = traces_data + + def addresses_data_from_xml(self, data, tag): + addresses_tag_list = tag.getElementsByTagName("addresses") + if len(addresses_tag_list) == 0: + return + + address_tag_list = addresses_tag_list[0].\ + getElementsByTagName("address") + addresses_data = list() + address_attributes = dict({"AutoConfigure": Attribute.BOOL, + "Address": Attribute.STRING, + "Family": Attribute.INTEGER, + "NetPrefix": Attribute.INTEGER, + "Broadcast": Attribute.STRING + }) + for address_tag in address_tag_list: + if address_tag.nodeType == tag.ELEMENT_NODE: + address_data = dict() + for attr_name in address_attributes.keys(): + if address_tag.hasAttribute(attr_name): + value = address_tag.getAttribute(attr_name) + type = address_attributes[attr_name] + address_data[attr_name] = self.type_from_standard( + type, value) + addresses_data.append(address_data) + data["addresses"] = addresses_data + + def routes_data_from_xml(self, data, tag): + routes_tag_list = tag.getElementsByTagName("routes") + if len(routes_tag_list) == 0: + return + + route_tag_list = routes_tag_list[0].getElementsByTagName("route") + routes_data = list() + route_attributes = dict({"Family": Attribute.INTEGER, + "Destination": Attribute.STRING, + "NetPrefix": Attribute.INTEGER, + "NextHop": Attribute.STRING, + "Interface": Attribute.STRING, + }) + for route_tag in route_tag_list: + if address_tag.nodeType == tag.ELEMENT_NODE: + route_data = dict() + for attr_name in route_attributes.keys(): + if route_tag.hasAttribute(attr_name): + value = route_tag.getAttribute(attr_name) + type = route_attributes[attr_name] + route_data[attr_name] = self.type_from_standard( + type, value) + routes_data.append(route_data) + data["routes"] = routes_data + + def connections_data_from_xml(self, data, tag): + connections_tag_list = tag.getElementsByTagName("connections") + if len(connections_tag_list) == 0: + return + + connection_tag_list = connections_tag_list[0].getElementsByTagName( + "connection") + connections_data = dict() + for connection_tag in connection_tag_list: + if connection_tag.nodeType == tag.ELEMENT_NODE: + connector = connection_tag.getAttribute("connector") + other_connector = connection_tag.getAttribute( + "other_connector") + other_guid = connection_tag.getAttribute("other_guid") + if not connector in connections_data: + connections_data[str(connector)] = dict() + connection_data = connections_data[str(connector)] + connection_data[int(other_guid)] = str(other_connector) + data["connections"] = connections_data + + def type_to_standard(self, value): + if type(value) == str: + return Attribute.STRING + if type(value) == bool: + return Attribute.BOOL + if type(value) == int: + return Attribute.INTEGER + if type(value) == float: + return Attribute.DOUBLE + + def type_from_standard(self, type, value): + if type == Attribute.STRING: + return str(value) + if type == Attribute.BOOL: + return bool(value) + if type == Attribute.INTEGER: + return int(value) + if type == Attribute.DOUBLE: + return float(value) + diff --git a/src/nepi/util/parser/base.py b/src/nepi/util/parser/base.py index 2f6b67a1..06a03100 100644 --- a/src/nepi/util/parser/base.py +++ b/src/nepi/util/parser/base.py @@ -1,22 +1,22 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from nepi.core.experiment import ExperimentDescription +import sys -class Parser(object): +class ExperimentParser(object): def to_data(self, experiment_description): - exp = dict() + data = dict() for testbed_description in experiment_description.testbed_descriptions: guid = testbed_description.guid - exp[guid] = self.testbed_to_data(testbed_description) + data[guid] = self.testbed_to_data(testbed_description) for box in testbed_description.boxes: - exp[box.guid] = self.box_to_data(guid, box) - return exp + data[box.guid] = self.box_to_data(guid, box) + return data def testbed_to_data(self, testbed_description): elem = dict() - elem["testbed_id"] = testbed_description.testbed_id - elem["testbed_version"] = testbed_description.testbed_version + elem["testbed_id"] = testbed_description.provider.testbed_id + elem["testbed_version"] = testbed_description.provider.testbed_version return elem def box_to_data(self, testbed_guid, box): @@ -95,28 +95,33 @@ class Parser(object): rts.append(rt) return rts if len(rts) > 0 else None - def from_data(self, data): - experiment = ExperimentDescription() + def from_data(self, experiment_description, data): connections_data = dict() for guid, elem_data in data.iteritems(): if "testbed_id" in elem_data: - self.testbed_from_data(experiment, elem_data) + self.testbed_from_data(experiment_description, elem_data) else: - self.box_from_data(experiment, elem_data) + self.box_from_data(experiment_description, elem_data) if "connections" in elem_data: connections_data[guid] = elem_data["connections"] # Connections need all boxes to be created - self.connections_from_data(experiment, connections_data) - return experiment + self.connections_from_data(experiment_description, connections_data) + return experiment_description - def testbed_from_data(self, experiment, data): + def testbed_from_data(self, experiment_description, data): testbed_id = data["testbed_id"] testbed_version = data["testbed_version"] - experiment.add_testbed_description(testbed_id, testbed_version) - - def box_from_data(self, experiment, data): + mod_name = 'nepi.testbeds.%s' % testbed_id + if not mod_name in sys.modules: + __import__(mod_name) + testbed_mod = sys.modules[mod_name] + provider = testbed_mod.TestbedFactoriesProvider(testbed_version) + experiment_description.add_testbed_description(provider) + + def box_from_data(self, experiment_description, data): testbed_guid = data["testbed_guid"] - testbed_description = experiment.testbed_description(testbed_guid) + testbed_description = experiment_description.testbed_description( + testbed_guid) factory_id = data["factory_id"] if "factory_attributes" in data: self.factory_attributes_from_data(factory_id, testbed_description, @@ -129,7 +134,7 @@ class Parser(object): if "addresses" in data: self.addresses_from_data(box, data["addresses"]) if "routes" in data: - self.routes_from_data(box, experiment, data["routes"]) + self.routes_from_data(box, experiment_description, data["routes"]) def factory_attributes_from_data(self, factory_id, testbed_description, data): @@ -145,12 +150,12 @@ class Parser(object): for name in data: box.trace(name).enable() - def connections_from_data(self, experiment, data): + def connections_from_data(self, experiment_description, data): for guid, connector_data in data.iteritems(): - box = experiment.box(guid) + box = experiment_description.box(guid) for connector_type_name, connections in connector_data.iteritems(): for guid, other_connector_type_name in connections.iteritems(): - other_box = experiment.box(guid) + other_box = experiment_description.box(guid) connector = box.connector(connector_type_name) other_connector = other_box.connector( other_connector_type_name) @@ -163,7 +168,7 @@ class Parser(object): for name, value in address_attrs.iteritems(): addr.set_attribute_value(name, value) - def routes_from_data(self, box, experiment, data): + def routes_from_data(self, box, experiment_description, data): for route_attrs in data: route = box.add_route() for name, value in route_attrs.iteritems(): diff --git a/src/nepi/util/parser/xml.py b/src/nepi/util/parser/xml.py deleted file mode 100644 index 92da570f..00000000 --- a/src/nepi/util/parser/xml.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -class XmlParser(object): - pass - -description = """ - - - - - - - - - - - - - - - - - - - - - - -""" -