se implementa un esquema para modelar el xml
FossilOrigin-Name: e4de658f60fe8fcbb330923e14958a5d8f8e0e6395db4f992ec7da45062fa193
This commit is contained in:
		
							
								
								
									
										62
									
								
								facho/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								facho/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  |  | ||||||
|  | class ModelMeta(type): | ||||||
|  |     def __new__(cls, name, bases, ns): | ||||||
|  |         new = type.__new__(cls, name, bases, ns) | ||||||
|  |         if '__name__' in ns: | ||||||
|  |             new.__name__ = ns['__name__'] | ||||||
|  |         if '__namespace__' in ns: | ||||||
|  |             new.__namespace__ = ns['__namespace__'] | ||||||
|  |         else: | ||||||
|  |             new.__namespace__ = {} | ||||||
|  |              | ||||||
|  |         return new | ||||||
|  |  | ||||||
|  | class ModelBase(object, metaclass=ModelMeta): | ||||||
|  |  | ||||||
|  |     def __new__(cls, *args, **kwargs): | ||||||
|  |         obj = super().__new__(cls, *args, **kwargs) | ||||||
|  |         obj._xml_attributes = {} | ||||||
|  |         obj._fields = {} | ||||||
|  |         obj._text = "" | ||||||
|  |         obj._namespace_prefix = None | ||||||
|  |          | ||||||
|  |         return obj | ||||||
|  |  | ||||||
|  |     def __setitem__(self, key, val): | ||||||
|  |         self._xml_attributes[key] = val | ||||||
|  |  | ||||||
|  |     def __getitem__(self, key): | ||||||
|  |         return self._xml_attributes[key] | ||||||
|  |  | ||||||
|  |     def to_xml(self): | ||||||
|  |         tag = self.__name__ | ||||||
|  |         ns = '' | ||||||
|  |         if self._namespace_prefix is not None: | ||||||
|  |             ns = "%s:" % (self._namespace_prefix) | ||||||
|  |  | ||||||
|  |         pair_attributes = ["%s=\"%s\"" % (k, v) for (k, v) in self._xml_attributes.values()] | ||||||
|  |  | ||||||
|  |         for (prefix, url) in self.__namespace__.items(): | ||||||
|  |             pair_attributes.append("xmlns:%s=\"%s\"" % (prefix, url)) | ||||||
|  |         attributes = "" | ||||||
|  |         if pair_attributes: | ||||||
|  |             attributes = " " + " ".join(pair_attributes) | ||||||
|  |  | ||||||
|  |         content = "" | ||||||
|  |  | ||||||
|  |         for name, value in self._fields.items(): | ||||||
|  |             if hasattr(value, 'to_xml'): | ||||||
|  |                 print(self._fields) | ||||||
|  |                 content += value.to_xml() | ||||||
|  |             elif isinstance(value, str): | ||||||
|  |                 content += value | ||||||
|  |         content += self._text | ||||||
|  |  | ||||||
|  |         if content == "": | ||||||
|  |             return "<%s%s%s/>" % (ns, tag, attributes) | ||||||
|  |         else: | ||||||
|  |             return "<%s%s%s>%s</%s%s>" % (ns, tag, attributes, content, ns, tag) | ||||||
|  |  | ||||||
|  | class Model(ModelBase): | ||||||
|  |     pass | ||||||
|  |          | ||||||
							
								
								
									
										5
									
								
								facho/model/fields/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								facho/model/fields/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | from .attribute import Attribute | ||||||
|  | from .many2one import Many2One | ||||||
|  | from .model import Model | ||||||
|  |  | ||||||
|  | __all__ = [Attribute, Many2One, Model] | ||||||
							
								
								
									
										17
									
								
								facho/model/fields/attribute.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								facho/model/fields/attribute.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | from .field import Field | ||||||
|  |  | ||||||
|  | class Attribute(Field): | ||||||
|  |     def __init__(self, tag): | ||||||
|  |         self.tag = tag | ||||||
|  |    | ||||||
|  |     def __get__(self, inst, cls): | ||||||
|  |         if inst is None: | ||||||
|  |             return self | ||||||
|  |  | ||||||
|  |         assert self.name is not None | ||||||
|  |         (tag, value) =  inst._xml_attributes[self.name] | ||||||
|  |         return value | ||||||
|  |  | ||||||
|  |     def __set__(self, inst, value): | ||||||
|  |         assert self.name is not None | ||||||
|  |         inst._xml_attributes[self.name] = (self.tag, value) | ||||||
							
								
								
									
										21
									
								
								facho/model/fields/field.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								facho/model/fields/field.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | class Field: | ||||||
|  |     def __set_name__(self, owner, name): | ||||||
|  |         self.name = name | ||||||
|  |  | ||||||
|  |     def __get__(self, inst, cls): | ||||||
|  |         if inst is None: | ||||||
|  |             return self | ||||||
|  |         assert self.name is not None | ||||||
|  |         return inst._fields[self.name] | ||||||
|  |  | ||||||
|  |     def __set__(self, inst, value): | ||||||
|  |         assert self.name is not None | ||||||
|  |         inst._fields[self.name] = value | ||||||
|  |  | ||||||
|  |     def _set_namespace(self, inst, name, namespaces): | ||||||
|  |         if name is None: | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         if name not in namespaces: | ||||||
|  |             raise KeyError("namespace %s not found" % (name)) | ||||||
|  |         inst._namespace_prefix = name | ||||||
							
								
								
									
										35
									
								
								facho/model/fields/many2one.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								facho/model/fields/many2one.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | from .field import Field | ||||||
|  |  | ||||||
|  | class Many2One(Field): | ||||||
|  |     def __init__(self, model, setter=None, namespace=None): | ||||||
|  |         self.model = model | ||||||
|  |         self.setter = setter | ||||||
|  |         self.namespace = namespace | ||||||
|  |  | ||||||
|  |     def __set_name__(self, owner, name): | ||||||
|  |         self.name = name | ||||||
|  |  | ||||||
|  |     def __get__(self, inst, cls): | ||||||
|  |         if inst is None: | ||||||
|  |             return self | ||||||
|  |         assert self.name is not None | ||||||
|  |         return inst._fields[self.name] | ||||||
|  |          | ||||||
|  |     def __set__(self, inst, value): | ||||||
|  |         assert self.name is not None | ||||||
|  |         class_model = self.model | ||||||
|  |         inst_model = class_model() | ||||||
|  |  | ||||||
|  |         self._set_namespace(inst_model, self.namespace,  inst.__namespace__) | ||||||
|  |         inst._fields[self.name] = inst_model | ||||||
|  |  | ||||||
|  |         # si hay setter manual se ejecuta | ||||||
|  |         # de lo contrario se asigna como texto del elemento | ||||||
|  |         setter = getattr(inst, self.setter or '', None) | ||||||
|  |         if callable(setter): | ||||||
|  |             setter(inst_model, value) | ||||||
|  |         else: | ||||||
|  |             inst_model._text = str(value) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										26
									
								
								facho/model/fields/model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								facho/model/fields/model.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | from .field import Field | ||||||
|  |  | ||||||
|  | class Model(Field): | ||||||
|  |     def __init__(self, model, namespace=None): | ||||||
|  |         self.model = model | ||||||
|  |         self.namespace = namespace | ||||||
|  |  | ||||||
|  |     def __get__(self, inst, cls): | ||||||
|  |         if inst is None: | ||||||
|  |             return self | ||||||
|  |         assert self.name is not None | ||||||
|  |         return self._create_model(inst) | ||||||
|  |  | ||||||
|  |     def __set__(self, inst, value): | ||||||
|  |         obj = self._create_model(inst) | ||||||
|  |         obj._text = str(value) | ||||||
|  |  | ||||||
|  |     def _create_model(self, inst): | ||||||
|  |         try: | ||||||
|  |             return inst._fields[self.name] | ||||||
|  |         except KeyError: | ||||||
|  |             obj = self.model() | ||||||
|  |             self._set_namespace(obj, self.namespace, inst.__namespace__) | ||||||
|  |             inst._fields[self.name] = obj | ||||||
|  |             return obj | ||||||
|  |              | ||||||
							
								
								
									
										167
									
								
								tests/test_model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								tests/test_model.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # This file is part of facho.  The COPYRIGHT file at the top level of | ||||||
|  | # this repository contains the full copyright notices and license terms. | ||||||
|  |  | ||||||
|  | """Tests for `facho` package.""" | ||||||
|  |  | ||||||
|  | import pytest | ||||||
|  |  | ||||||
|  | import facho.model | ||||||
|  | import facho.model.fields as fields | ||||||
|  |  | ||||||
|  | def test_model_to_element(): | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |  | ||||||
|  |     person = Person() | ||||||
|  |  | ||||||
|  |     assert "<Person/>" == person.to_xml() | ||||||
|  |  | ||||||
|  | def test_model_to_element_with_attribute(): | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |         id = fields.Attribute('id') | ||||||
|  |          | ||||||
|  |     person = Person() | ||||||
|  |     person.id =  33 | ||||||
|  |  | ||||||
|  |     personb = Person() | ||||||
|  |     personb.id = 44 | ||||||
|  |  | ||||||
|  |     assert "<Person id=\"33\"/>" == person.to_xml() | ||||||
|  |     assert "<Person id=\"44\"/>" == personb.to_xml() | ||||||
|  |  | ||||||
|  | def test_model_to_element_with_attribute_as_element(): | ||||||
|  |     class ID(facho.model.Model): | ||||||
|  |         __name__ = 'ID' | ||||||
|  |  | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |          | ||||||
|  |         id = fields.Many2One(ID) | ||||||
|  |  | ||||||
|  |     person = Person() | ||||||
|  |     person.id = 33 | ||||||
|  |     assert "<Person><ID>33</ID></Person>" == person.to_xml() | ||||||
|  |  | ||||||
|  | def test_many2one_with_custom_attributes(): | ||||||
|  |     class TaxAmount(facho.model.Model): | ||||||
|  |         __name__ = 'TaxAmount' | ||||||
|  |  | ||||||
|  |         currencyID = fields.Attribute('currencyID') | ||||||
|  |          | ||||||
|  |     class TaxTotal(facho.model.Model): | ||||||
|  |         __name__ = 'TaxTotal' | ||||||
|  |  | ||||||
|  |         amount = fields.Many2One(TaxAmount) | ||||||
|  |  | ||||||
|  |     tax_total = TaxTotal() | ||||||
|  |     tax_total.amount = 3333 | ||||||
|  |     tax_total.amount.currencyID = 'COP' | ||||||
|  |     assert '<TaxTotal><TaxAmount currencyID="COP">3333</TaxAmount></TaxTotal>' == tax_total.to_xml() | ||||||
|  |  | ||||||
|  | def test_many2one_with_custom_setter(): | ||||||
|  |  | ||||||
|  |     class PhysicalLocation(facho.model.Model): | ||||||
|  |         __name__ = 'PhysicalLocation' | ||||||
|  |  | ||||||
|  |         id = fields.Attribute('ID') | ||||||
|  |          | ||||||
|  |     class Party(facho.model.Model): | ||||||
|  |         __name__ = 'Party' | ||||||
|  |  | ||||||
|  |         location = fields.Many2One(PhysicalLocation, setter='location_setter') | ||||||
|  |  | ||||||
|  |         def location_setter(self, field, value): | ||||||
|  |             field.id = value | ||||||
|  |              | ||||||
|  |     party = Party() | ||||||
|  |     party.location = 99 | ||||||
|  |     assert '<Party><PhysicalLocation ID="99"/></Party>' == party.to_xml() | ||||||
|  |  | ||||||
|  | def test_field_model(): | ||||||
|  |     class ID(facho.model.Model): | ||||||
|  |         __name__ = 'ID' | ||||||
|  |          | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |  | ||||||
|  |         id = fields.Model(ID) | ||||||
|  |  | ||||||
|  |     person = Person() | ||||||
|  |     person.id = ID() | ||||||
|  |     person.id = 33 | ||||||
|  |     assert "<Person><ID>33</ID></Person>" == person.to_xml() | ||||||
|  |  | ||||||
|  | def test_field_model_failed_initialization(): | ||||||
|  |     class ID(facho.model.Model): | ||||||
|  |         __name__ = 'ID' | ||||||
|  |          | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |  | ||||||
|  |         id = fields.Model(ID) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     person = Person() | ||||||
|  |     person.id = 33 | ||||||
|  |     assert "<Person><ID>33</ID></Person>" == person.to_xml() | ||||||
|  |  | ||||||
|  | def test_field_model_default_initialization_with_attributes(): | ||||||
|  |     class ID(facho.model.Model): | ||||||
|  |         __name__ = 'ID' | ||||||
|  |  | ||||||
|  |         reference = fields.Attribute('REFERENCE') | ||||||
|  |          | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |  | ||||||
|  |         id = fields.Model(ID) | ||||||
|  |  | ||||||
|  |     person = Person() | ||||||
|  |     person.id = 33 | ||||||
|  |     person.id.reference = 'haber' | ||||||
|  |     assert '<Person><ID REFERENCE="haber">33</ID></Person>' == person.to_xml() | ||||||
|  |  | ||||||
|  | def test_model_with_xml_namespace(): | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |         __namespace__ = { | ||||||
|  |             'facho': 'http://lib.facho.cyou' | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     person = Person() | ||||||
|  |     assert '<Person xmlns:facho="http://lib.facho.cyou"/>' | ||||||
|  |  | ||||||
|  | def test_model_with_xml_namespace_nested(): | ||||||
|  |     class ID(facho.model.Model): | ||||||
|  |         __name__ = 'ID' | ||||||
|  |          | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |         __namespace__ = { | ||||||
|  |             'facho': 'http://lib.facho.cyou' | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         id = fields.Many2One(ID, namespace='facho') | ||||||
|  |          | ||||||
|  |     person = Person() | ||||||
|  |     person.id = 33 | ||||||
|  |     assert '<Person xmlns:facho="http://lib.facho.cyou"><facho:ID>33</facho:ID></Person>' == person.to_xml() | ||||||
|  |  | ||||||
|  | def test_field_model_with_namespace(): | ||||||
|  |     class ID(facho.model.Model): | ||||||
|  |         __name__ = 'ID' | ||||||
|  |          | ||||||
|  |     class Person(facho.model.Model): | ||||||
|  |         __name__ = 'Person' | ||||||
|  |         __namespace__ = { | ||||||
|  |             "facho": "http://lib.facho.cyou"  | ||||||
|  |         } | ||||||
|  |         id = fields.Model(ID, namespace="facho") | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     person = Person() | ||||||
|  |     person.id = 33 | ||||||
|  |     assert '<Person xmlns:facho="http://lib.facho.cyou"><facho:ID>33</facho:ID></Person>' == person.to_xml() | ||||||
		Reference in New Issue
	
	Block a user