def test_add_default_attribute(self): xs_string = AttrTypeFactory.native(DataType.STRING) extension = ExtensionFactory.create(xs_string, Restrictions(required=True)) item = ClassFactory.elements(1, extensions=[extension]) ClassExtensionHandler.add_default_attribute(item, extension) expected = AttrFactory.create(name="@value", default=None, types=[xs_string], tag=Tag.EXTENSION) self.assertEqual(2, len(item.attrs)) self.assertEqual(0, len(item.extensions)) self.assertEqual(expected, item.attrs[0]) xs_int = AttrTypeFactory.native(DataType.INT) extension = ExtensionFactory.create(xs_int, Restrictions(tokens=True)) item.extensions.append(extension) ClassExtensionHandler.add_default_attribute(item, extension) expected.types.append(xs_int) expected_restrictions = Restrictions(tokens=True, required=True, min_occurs=1, max_occurs=1) self.assertEqual(2, len(item.attrs)) self.assertEqual(0, len(item.extensions)) self.assertEqual(expected, item.attrs[0]) self.assertEqual(expected_restrictions, item.attrs[0].restrictions)
def test_create_default_attribute_with_any_type(self): extension = ExtensionFactory.create( type=AttrTypeFactory.xs_any(), restrictions=Restrictions(min_occurs=1, max_occurs=1, required=True), ) item = ClassFactory.create(extensions=[extension]) ClassExtensionHandler.create_default_attribute(item, extension) expected = AttrFactory.create( name="any_element", index=0, default=None, types=[extension.type.clone()], tag=Tag.ANY, namespace="##any", restrictions=Restrictions(min_occurs=1, max_occurs=1, required=True), ) self.assertEqual(1, len(item.attrs)) self.assertEqual(0, len(item.extensions)) self.assertEqual(expected, item.attrs[0])
def test_element_children(self): sequence_one = Sequence(elements=[Element(), Element()]) sequence_two = Sequence(max_occurs=2, elements=[Element(), Element()]) restriction = Restriction( enumerations=[Enumeration(value=x) for x in "abc"], sequence=sequence_two, ) complex_type = ComplexType( attributes=[Attribute(), Attribute()], sequence=sequence_one, simple_content=SimpleContent(restriction=Restriction()), complex_content=ComplexContent(restriction=restriction,), ) restrictions = Restrictions.from_element(complex_type) children = self.builder.element_children(complex_type, restrictions) expected = [ (sequence_two.elements[0], Restrictions.from_element(sequence_two)), (sequence_two.elements[1], Restrictions.from_element(sequence_two)), (restriction.enumerations[0], Restrictions.from_element(restriction)), (restriction.enumerations[1], Restrictions.from_element(restriction)), (restriction.enumerations[2], Restrictions.from_element(restriction)), (sequence_one.elements[0], Restrictions.from_element(sequence_one)), (sequence_one.elements[1], Restrictions.from_element(sequence_one)), (complex_type.attributes[0], Restrictions.from_element(complex_type)), (complex_type.attributes[1], Restrictions.from_element(complex_type)), ] self.assertIsInstance(children, GeneratorType) self.assertEqual(expected, list(children))
def test_process_attribute_restrictions(self): restrictions = [ Restrictions(min_occurs=0, max_occurs=0, required=True), Restrictions(min_occurs=0, max_occurs=1, required=True), Restrictions(min_occurs=1, max_occurs=1, required=False), Restrictions(max_occurs=2, required=True), Restrictions(min_occurs=2, max_occurs=2, required=True), ] expected = [ {}, {}, { "required": True }, { "max_occurs": 2, "min_occurs": 0 }, { "max_occurs": 2, "min_occurs": 2 }, ] for idx, res in enumerate(restrictions): attr = AttrFactory.create(restrictions=res) self.sanitizer.process_attribute_restrictions(attr) self.assertEqual(expected[idx], res.asdict())
def test_sanitize_attribute_sequence(self): def len_sequential(target: Class): return len([ attr for attr in target.attrs if attr.restrictions.sequential ]) restrictions = Restrictions(max_occurs=2, sequential=True) target = ClassFactory.create(attrs=[ AttrFactory.create(restrictions=restrictions.clone()), AttrFactory.create(restrictions=restrictions.clone()), ]) attrs_clone = [attr.clone() for attr in target.attrs] self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(2, len_sequential(target)) target.attrs[0].restrictions.sequential = False self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(1, len_sequential(target)) self.sanitizer.process_attribute_sequence(target, target.attrs[1]) self.assertEqual(0, len_sequential(target)) target.attrs = attrs_clone target.attrs[1].restrictions.sequential = False self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(0, len_sequential(target)) target.attrs[0].restrictions.sequential = True target.attrs[0].restrictions.max_occurs = 0 target.attrs[1].restrictions.sequential = True self.sanitizer.process_attribute_sequence(target, target.attrs[0]) self.assertEqual(1, len_sequential(target))
def test_merge(self): source = Restrictions(min_length=2, max_length=10) target = Restrictions(min_length=1, pattern=r"[A-Z]") target.merge(source) self.assertEqual(2, target.min_length) self.assertEqual(10, target.max_length) self.assertEqual(r"[A-Z]", target.pattern)
def test_property_is_list(self): restrictions = Restrictions() self.assertFalse(restrictions.is_list) restrictions.max_occurs = 1 self.assertFalse(restrictions.is_list) restrictions.max_occurs = 2 self.assertTrue(restrictions.is_list)
def element_children( cls, obj: ElementBase, parent_restrictions: Restrictions ) -> Iterator[Tuple[ElementBase, Restrictions]]: """Recursively find and return all child elements that are qualified to be class attributes, with all their restrictions.""" for child in obj.children(): if child.is_attribute: yield child, parent_restrictions else: restrictions = parent_restrictions.clone() restrictions.merge(Restrictions.from_element(child)) yield from cls.element_children(child, restrictions)
def test_field_choices(self): attr = AttrFactory.create(choices=[ AttrFactory.element( namespace="foo", types=[type_float], restrictions=Restrictions(max_exclusive="10"), ), AttrFactory.element(namespace="bar"), AttrFactory.any(namespace="##other"), AttrFactory.element(name="bar", default="aa"), AttrFactory.element(name="tok", restrictions=Restrictions(tokens=True)), ]) actual = self.filters.field_choices(attr, "foo", ["a", "b"]) expected = ( { "name": "attr_B", "type": "Type[float]", "max_exclusive": 10.0 }, { "name": "attr_C", "namespace": "bar", "type": "Type[str]" }, { "namespace": "##other", "wildcard": True, "type": "Type[object]", }, { "default": '"aa"', "name": "bar", "type": "Type[str]" }, { "default_factory": "list", "name": "tok", "tokens": True, "type": "Type[List[str]]", }, ) self.assertEqual(expected, actual) self.filters.docstring_style = DocstringStyle.ACCESSIBLE attr.choices[0].help = "help" actual = self.filters.field_choices(attr, None, []) self.assertEqual(attr.choices[0].help, actual[0]["doc"]) self.assertNotIn("doc", actual[1])
def test_element_children_with_parents_restrictions(self): choice = Choice(elements=[Element(name="elem1")]) complex_type = ComplexType( sequence=Sequence(choices=[choice], min_occurs=0, max_occurs=3)) parent_restrictions = Restrictions.from_element(complex_type) children = SchemaMapper.element_children(complex_type, parent_restrictions) child, restrictions = next(children) expected = Restrictions(min_occurs=0, max_occurs=3, sequential=True, choice=str(id(choice))) self.assertEqual(expected, restrictions)
def test_clone_attribute(self): attr = AttrFactory.create( restrictions=Restrictions(length=1), types=[ AttrTypeFactory.create(qname="x"), AttrTypeFactory.create(qname="y"), AttrTypeFactory.xs_int(), ], ) restrictions = Restrictions(length=2) clone = ClassUtils.clone_attribute(attr, restrictions) self.assertEqual(2, clone.restrictions.length) self.assertIsNot(attr, clone)
def build_class_attribute( cls, target: Class, obj: ElementBase, parent_restrictions: Restrictions ): """Generate and append an attribute field to the target class.""" target.ns_map.update(obj.ns_map) types = cls.build_class_attribute_types(target, obj) restrictions = Restrictions.from_element(obj) if obj.class_name in (Tag.ELEMENT, Tag.ANY): restrictions.merge(parent_restrictions) if restrictions.prohibited: return name = obj.real_name target.attrs.append( Attr( index=obj.index, name=name, local_name=name, default=obj.default_value, fixed=obj.is_fixed, types=types, tag=obj.class_name, help=obj.display_help, namespace=cls.element_namespace(obj, target.qname.namespace), restrictions=restrictions, ) )
def test_copy_attribute_properties(self, mock_copy_inner_class): source = ClassFactory.elements(1, qname="Foobar") source.attrs[0].restrictions.max_length = 100 source.attrs[0].restrictions.min_length = 1 source.attrs[0].help = "foo" source.attrs[0].types = [ AttrTypeFactory.create(qname="first"), AttrTypeFactory.create(qname="second"), ] target = ClassFactory.elements(1) attr = target.attrs[0] attr.restrictions.min_length = 2 attr.types.clear() attr.types.append(AttrTypeFactory.create(qname=source.name)) self.assertEqual("Foobar", attr.types[0].name) self.processor.copy_attribute_properties(source, target, attr, attr.types[0]) self.assertEqual("first", attr.types[0].name) self.assertEqual("second", attr.types[1].name) self.assertEqual("foo", attr.help) self.assertEqual(Restrictions(min_length=2, max_length=100), attr.restrictions) mock_copy_inner_class.assert_has_calls([ mock.call(source, target, attr, source.attrs[0].types[0]), mock.call(source, target, attr, source.attrs[0].types[1]), ])
def test_group_fields(self): target = ClassFactory.create(attrs=AttrFactory.list(2)) target.attrs[0].restrictions.min_occurs = 10 target.attrs[0].restrictions.max_occurs = 15 target.attrs[1].restrictions.min_occurs = 5 target.attrs[1].restrictions.max_occurs = 20 expected = AttrFactory.create( name="attr_B_Or_attr_C", tag="Choice", index=0, types=[AttrTypeFactory.xs_any()], choices=[ AttrFactory.create( tag=target.attrs[0].tag, name="attr_B", types=target.attrs[0].types, ), AttrFactory.create( tag=target.attrs[1].tag, name="attr_C", types=target.attrs[1].types, ), ], ) expected_res = Restrictions(min_occurs=5, max_occurs=20) self.sanitizer.group_fields(target, list(target.attrs)) self.assertEqual(1, len(target.attrs)) self.assertEqual(expected, target.attrs[0]) self.assertEqual(expected_res, target.attrs[0].restrictions)
def create( cls, name=None, index=None, types=None, choices=None, tag=None, namespace=None, help=None, default=None, fixed=False, mixed=False, restrictions=None, ): name = name or f"attr_{cls.next_letter()}" return cls.model( name=name, index=cls.counter if index is None else index, types=types or [AttrTypeFactory.xs_string()], choices=choices or [], tag=tag or random.choice(cls.types).__name__, namespace=namespace or None, help=help or None, default=default or None, fixed=fixed, mixed=mixed, restrictions=restrictions or Restrictions(), )
def build_class_extension(cls, target: Class, name: str, restrictions: Dict) -> Extension: """Create an extension for the target class.""" return Extension( type=cls.build_data_type(target, name), restrictions=Restrictions(**restrictions), )
def process(cls, target: Class): """Add or update an existing an xs:anyType derived attribute if the target class supports mixed content.""" if not target.mixed: return wildcard = first(attr for attr in target.attrs if attr.tag == Tag.ANY) if wildcard: wildcard.mixed = True if not wildcard.is_list: wildcard.restrictions.min_occurs = 0 wildcard.restrictions.max_occurs = sys.maxsize else: attr = Attr( name="content", types=[AttrType(qname=str(DataType.ANY_TYPE), native=True)], tag=Tag.ANY, mixed=True, namespace=NamespaceType.ANY_NS, restrictions=Restrictions(min_occurs=0, max_occurs=sys.maxsize), ) target.attrs.insert(0, attr)
def create( cls, name: Optional[str] = None, index: Optional[int] = None, types: Optional[List[AttrType]] = None, choices: Optional[List[Attr]] = None, tag: Optional[str] = None, namespace: Optional[str] = None, default: Any = None, fixed: bool = False, mixed: bool = False, restrictions: Optional[Restrictions] = None, **kwargs: Any, ) -> Attr: name = name or f"attr_{cls.next_letter()}" return Attr( name=name, index=cls.counter if index is None else index, types=types or [AttrTypeFactory.native(DataType.STRING)], choices=choices or [], tag=tag or random.choice(cls.tags), namespace=namespace or None, default=default or None, fixed=fixed, mixed=mixed, restrictions=restrictions or Restrictions(), **kwargs, )
def group_fields(self, target: Class, attrs: List[Attr]): """Group attributes into a new compound field.""" pos = target.attrs.index(attrs[0]) names = [] choices = [] min_occurs = [] max_occurs = [] for attr in attrs: target.attrs.remove(attr) names.append(attr.local_name) min_occurs.append(attr.restrictions.min_occurs) max_occurs.append(attr.restrictions.max_occurs) choices.append(self.build_attr_choice(attr)) name = "choice" if len(names) > 3 else "_Or_".join(names) target.attrs.insert( pos, Attr( name=name, index=0, types=[AttrType(qname=str(DataType.ANY_TYPE), native=True)], tag=Tag.CHOICE, restrictions=Restrictions( min_occurs=min((x for x in min_occurs if x is not None), default=0), max_occurs=max((x for x in max_occurs if x is not None), default=0), ), choices=choices, ), )
def build_class_extension( self, target: Class, name: str, index: int, restrictions: Dict ) -> Extension: """Create an extension for the target class.""" return Extension( type=self.build_data_type(target, name, index=index), restrictions=Restrictions(**restrictions), )
def test_merge_ignore_nillable(self): parent = Restrictions(nillable=True) child = Restrictions() child.merge(parent) self.assertIsNone(child.nillable) child.nillable = False child.merge(parent) self.assertFalse(child.nillable)
def test_build_class_attribute( self, mock_real_name, mock_display_help, mock_prefix, mock_default_value, mock_is_fixed, mock_get_restrictions, mock_element_namespace, mock_build_class_attribute_types, ): item = ClassFactory.create(ns_map={"bar": "foo"}) mock_build_class_attribute_types.return_value = AttrTypeFactory.list( 1, qname="int") mock_real_name.return_value = item.name mock_display_help.return_value = "sos" mock_prefix.return_value = "com" mock_default_value.return_value = "default" mock_is_fixed.return_value = True mock_element_namespace.return_value = "http://something/common" mock_get_restrictions.return_value = {"required": True} attribute = Attribute(default="false") attribute.index = 66 attribute.ns_map["foo"] = "bar" SchemaMapper.build_class_attribute(item, attribute, Restrictions()) expected = AttrFactory.create( name=mock_real_name.return_value, types=mock_build_class_attribute_types.return_value, tag=Tag.ATTRIBUTE, namespace=mock_element_namespace.return_value, help=mock_display_help.return_value, default=mock_default_value.return_value, fixed=mock_is_fixed.return_value, index=66, restrictions=Restrictions(required=True), ) self.assertEqual(expected, item.attrs[0]) self.assertEqual({"bar": "foo", "foo": "bar"}, item.ns_map) mock_build_class_attribute_types.assert_called_once_with( item, attribute) mock_element_namespace.assert_called_once_with(attribute, item.target_namespace)
def build_class_attributes(cls, obj: ElementBase, target: Class): """Build the target class attributes from the given ElementBase children.""" restrictions = Restrictions.from_element(obj) for child, restrictions in cls.element_children(obj, restrictions): cls.build_class_attribute(target, child, restrictions) target.attrs.sort(key=lambda x: x.index)
def test_field_type_with_token_attr(self): attr = AttrFactory.create( types=AttrTypeFactory.list(1, qname="foo_bar"), restrictions=Restrictions(tokens=True), ) self.assertEqual("List[FooBar]", self.filters.field_type(attr, [])) attr.restrictions.max_occurs = 2 self.assertEqual("List[List[FooBar]]", self.filters.field_type(attr, []))
def create( cls, attr_type: Optional[AttrType] = None, restrictions: Optional[Restrictions] = None, **kwargs: Any, ) -> Extension: return Extension( type=attr_type or AttrTypeFactory.create(), restrictions=restrictions or Restrictions(), )
def setUp(self) -> None: self.restrictions = Restrictions( required=True, prohibited=None, min_occurs=1, max_occurs=1, min_exclusive="1.1", min_inclusive="1", min_length=1, max_exclusive="1", max_inclusive="1.1", max_length=10, total_digits=333, fraction_digits=2, length=5, white_space="collapse", pattern=r"[A-Z]", explicit_timezone="+1", nillable=True, )
def test_asdict_with_types(self): restrictions = Restrictions( required=True, prohibited=None, min_occurs=1, max_occurs=1, min_exclusive="1.1", min_inclusive="1", min_length=1, max_exclusive="1", max_inclusive="1.1", max_length=10, total_digits=333, fraction_digits=2, length=5, white_space="collapse", pattern=r"[A-Z]", explicit_timezone="+1", nillable=True, ) expected = { "explicit_timezone": "+1", "fraction_digits": 2, "length": 5, "max_exclusive": 1.0, # str -> float "max_inclusive": 1.1, # str -> float "max_length": 10, "max_occurs": 1, "min_exclusive": 1.1, # str -> float "min_inclusive": 1.0, # str -> float "min_length": 1, "min_occurs": 1, "nillable": True, "pattern": "[A-Z]", "required": True, "total_digits": 333, "white_space": "collapse", } self.assertEqual(expected, restrictions.asdict(types=[float]))
def element_children( self, obj: ElementBase, restrictions: Restrictions ) -> Iterator[Tuple[ElementBase, Restrictions]]: """Recursively find and return all child elements that are qualified to be class attributes.""" for child in obj.children(): if child.is_attribute: yield child, restrictions else: yield from self.element_children( child, restrictions=Restrictions.from_element(child) )
def test_process(self): one = AttrFactory.attribute(fixed=True) one_clone = one.clone() restrictions = Restrictions(min_occurs=10, max_occurs=15) two = AttrFactory.element(restrictions=restrictions, fixed=True) two_clone = two.clone() two_clone.restrictions.min_occurs = 5 two_clone.restrictions.max_occurs = 5 two_clone_two = two.clone() two_clone_two.restrictions.min_occurs = 4 two_clone_two.restrictions.max_occurs = 4 three = AttrFactory.element() four = AttrFactory.enumeration() four_clone = four.clone() five = AttrFactory.element() five_clone = five.clone() five_clone_two = five.clone() target = ClassFactory.create( attrs=[ one, one_clone, two, two_clone, two_clone_two, three, four, four_clone, five, five_clone, five_clone_two, ] ) winners = [one, two, three, four, five] self.processor.process(target) self.assertEqual(winners, target.attrs) self.assertTrue(one.fixed) self.assertIsNone(one.restrictions.min_occurs) self.assertIsNone(one.restrictions.max_occurs) self.assertFalse(two.fixed) self.assertEqual(4, two.restrictions.min_occurs) self.assertEqual(24, two.restrictions.max_occurs) self.assertIsNone(three.restrictions.min_occurs) self.assertIsNone(three.restrictions.max_occurs) self.assertIsNone(four.restrictions.min_occurs) self.assertIsNone(four.restrictions.max_occurs) self.assertEqual(0, five.restrictions.min_occurs) self.assertEqual(3, five.restrictions.max_occurs)
def test_asdict_with_implied_types(self): restrictions = Restrictions(min_occurs=1, max_occurs=4) self.assertEqual({ "max_occurs": 4, "min_occurs": 1 }, restrictions.asdict()) restrictions.min_occurs = 0 self.assertEqual({"max_occurs": 4}, restrictions.asdict()) restrictions.max_occurs = sys.maxsize self.assertEqual({}, restrictions.asdict())