Skip to content

definitions

xsdata.codegen.mappers.definitions

DefinitionsMapper

Map a definitions instance to message and service classes.

Currently, only SOAP 1.1 bindings with rpc/document style is supported.

Source code in xsdata/codegen/mappers/definitions.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
class DefinitionsMapper:
    """Map a definitions instance to message and service classes.

    Currently, only SOAP 1.1 bindings with rpc/document style is
    supported.
    """

    @classmethod
    def map(cls, definitions: Definitions) -> List[Class]:
        """Main entrypoint for this mapper.

        Iterates over their services and their ports and build
        the binding and service classes.

        Args:
            definitions: The definitions instance to map.

        Returns:
            The generated class instances
        """
        return [
            obj
            for service in definitions.services
            for port in service.ports
            for obj in cls.map_port(definitions, port)
        ]

    @classmethod
    def map_port(cls, definitions: Definitions, port: ServicePort) -> Iterator[Class]:
        """Map a service port into binding and service classes.

        Match a ServicePort to a Binding and PortType object and
        delegate the process to the next entry point.

        Args:
            definitions: The definitions instance
            port: The service port instance

        Yields:
            An iterator of class instances.
        """
        binding = definitions.find_binding(text.suffix(port.binding))
        port_type = definitions.find_port_type(text.suffix(binding.type))

        elements = itertools.chain(binding.extended_elements, port.extended_elements)
        config = cls.attributes(elements)

        yield from cls.map_binding(definitions, binding, port_type, config)

    @classmethod
    def map_binding(
        cls,
        definitions: Definitions,
        binding: Binding,
        port_type: PortType,
        config: Dict,
    ) -> Iterator[Class]:
        """Map binding operations into binding and service classes.

        Match every BindingOperation to a PortTypeOperation and
        delegate the process for each operation to the next entry point.

        Args:
            definitions: The definitions instance
            binding: The binding instance
            port_type: The port type instance
            config: Configuration dictionary

        Yields:
            An iterator of class instances.
        """
        for operation in binding.unique_operations():
            cfg = config.copy()
            cfg.update(cls.attributes(operation.extended_elements))
            port_operation = port_type.find_operation(operation.name)

            yield from cls.map_binding_operation(
                definitions, operation, port_operation, cfg, port_type.name
            )

    @classmethod
    def map_binding_operation(
        cls,
        definitions: Definitions,
        binding_operation: BindingOperation,
        port_type_operation: PortTypeOperation,
        config: Dict,
        name: str,
    ) -> Iterator[Class]:
        """Map a binding operation to a service and binding classes.

        Convert a BindingOperation to a service class and delegate
        the process of all the message classes to the next entry point.

        Args:
            definitions: The definitions instance
            binding_operation: The binding operation instance
            port_type_operation: The port type operation instance
            config: Configuration dictionary
            name: The operation name

        Yields:
            An iterator of class instances.
        """
        attrs = [
            cls.build_attr(key, str(DataType.STRING), native=True, default=config[key])
            for key in sorted(config.keys(), key=len)
            if config[key]
        ]

        style = config.get("style", "document")
        name = f"{name}_{binding_operation.name}"
        namespace = cls.operation_namespace(config)
        operation_messages = cls.map_binding_operation_messages(
            definitions,
            binding_operation,
            port_type_operation,
            name,
            style,
            namespace,
        )
        for message_class in operation_messages:
            yield message_class
            # Only Envelope classes need to be added in service input/output
            if message_class.meta_name:
                message_type = message_class.name.split("_")[-1]
                attrs.append(
                    cls.build_attr(
                        message_type, message_class.qname, reference=id(message_class)
                    )
                )

        assert binding_operation.location is not None

        yield Class(
            qname=namespaces.build_qname(definitions.target_namespace, name),
            status=Status.FLATTENED,
            tag=type(binding_operation).__name__,
            location=binding_operation.location,
            ns_map=binding_operation.ns_map,
            attrs=attrs,
        )

    @classmethod
    def map_binding_operation_messages(
        cls,
        definitions: Definitions,
        binding_operation: BindingOperation,
        port_type_operation: PortTypeOperation,
        name: str,
        style: str,
        namespace: Optional[str],
    ) -> Iterator[Class]:
        """Map the binding operation messages to binding classes.

        Args:
            definitions: The definitions instance
            binding_operation: The binding operation instance
            port_type_operation: The port type operation instance
            name: The operation name
            style: The operation style
            namespace: The operation namespace

        Yields:
            An iterator of class instances.
        """
        messages: List[Tuple[str, BindingMessage, PortTypeMessage, Optional[str]]] = []

        if binding_operation.input:
            messages.append(
                (
                    "input",
                    binding_operation.input,
                    port_type_operation.input,
                    binding_operation.name,
                )
            )

        if binding_operation.output:
            messages.append(
                ("output", binding_operation.output, port_type_operation.output, None)
            )

        for suffix, binding_message, port_type_message, operation_name in messages:
            if style == "rpc":
                yield cls.build_message_class(definitions, port_type_message)

            target = cls.build_envelope_class(
                definitions,
                binding_message,
                port_type_message,
                f"{name}_{suffix}",
                style,
                namespace,
                operation_name,
            )

            if suffix == "output":
                cls.build_envelope_fault(definitions, port_type_operation, target)

            yield target

    @classmethod
    def build_envelope_fault(
        cls,
        definitions: Definitions,
        port_type_operation: PortTypeOperation,
        target: Class,
    ):
        """Add an inner message fault class with default fields.

        Args:
            definitions: The definitions instance
            port_type_operation: The port type operation instance
            target: The target class instance
        """
        ns_map: Dict = {}
        body = next(inner for inner in target.inner if inner.name == "Body")
        fault_class = cls.build_inner_class(body, "Fault", target.namespace)

        detail_attrs: List[Attr] = []
        for fault in port_type_operation.faults:
            message = definitions.find_message(text.suffix(fault.message))
            detail_attrs.extend(cls.build_parts_attributes(message.parts, ns_map))

        default_fields = ["faultcode", "faultstring", "faultactor"]
        if detail_attrs:
            detail = cls.build_inner_class(fault_class, "detail", namespace="")
            detail.attrs.extend(detail_attrs)
        else:
            default_fields.append("detail")

        collections.prepend(
            fault_class.attrs,
            *(
                cls.build_attr(f, str(DataType.STRING), native=True, namespace="")
                for f in default_fields
            ),
        )

    @classmethod
    def build_envelope_class(
        cls,
        definitions: Definitions,
        binding_message: BindingMessage,
        port_type_message: PortTypeMessage,
        name: str,
        style: str,
        namespace: Optional[str],
        operation: Optional[str],
    ) -> Class:
        """Map the binding message to an envelope class.

        Args:
            definitions: The definitions instance
            binding_message: The port type message instance
            port_type_message: The port type message instance
            name: The class name
            style: The operation style e.g. rpc
            namespace: The operation namespace
            operation: The custom operation name, if it's empty
                the message name will be used instead

        Returns:
            The class instance.
        """
        assert binding_message.location is not None

        target = Class(
            qname=namespaces.build_qname(definitions.target_namespace, name),
            meta_name="Envelope",
            tag=Tag.BINDING_MESSAGE,
            location=binding_message.location,
            ns_map=binding_message.ns_map,
            namespace=namespace,
        )
        message = port_type_message.message

        for ext in binding_message.extended_elements:
            assert ext.qname is not None
            class_name = namespaces.local_name(ext.qname).title()
            inner = cls.build_inner_class(target, class_name)

            if style == "rpc" and class_name == "Body":
                namespace = ext.attributes.get("namespace")
                attrs = cls.map_port_type_message(
                    operation, port_type_message, namespace
                )
            else:
                attrs = cls.map_binding_message_parts(
                    definitions, message, ext, inner.ns_map
                )

            inner.attrs.extend(attrs)

        return target

    @classmethod
    def build_message_class(
        cls,
        definitions: Definitions,
        port_type_message: PortTypeMessage,
    ) -> Class:
        """Map the input/output message of a rpc style operation.

        Args:
            definitions: The definitions instance
            port_type_message: The port type message instance

        Returns:
            The class instance.
        """
        prefix, name = text.split(port_type_message.message)

        definition_message = definitions.find_message(name)
        ns_map = definition_message.ns_map.copy()
        source_namespace = ns_map.get(prefix)

        assert port_type_message.location is not None

        return Class(
            qname=namespaces.build_qname(source_namespace, name),
            namespace=source_namespace,
            status=Status.RAW,
            tag=Tag.ELEMENT,
            location=port_type_message.location,
            ns_map=ns_map,
            attrs=list(cls.build_parts_attributes(definition_message.parts, ns_map)),
        )

    @classmethod
    def build_inner_class(
        cls, target: Class, name: str, namespace: Optional[str] = None
    ) -> Class:
        """Build or retrieve an inner class.

        This helper will also create a forward reference attribute for
        the parent class.

        Args:
            target: The parent class instance
            name: The inner class name
            namespace: The inner class namespace

        Returns:
            The inner class instance.
        """
        inner = collections.first(inner for inner in target.inner if inner.name == name)
        if not inner:
            inner = Class(
                qname=namespaces.build_qname(target.target_namespace, name),
                tag=Tag.BINDING_MESSAGE,
                location=target.location,
                ns_map=target.ns_map.copy(),
            )
            attr = cls.build_attr(name, inner.qname, forward=True, namespace=namespace)

            inner.parent = target
            target.inner.append(inner)
            target.attrs.append(attr)

        return inner

    @classmethod
    def map_port_type_message(
        cls,
        operation: Optional[str],
        message: PortTypeMessage,
        namespace: Optional[str],
    ) -> Iterator[Attr]:
        """Build an attribute for the given port type message.

        Args:
            operation: The operation name, use the message name
                if it's empty
            message: The port type message instance
            namespace: The operation namespace

        Yields:
            An iterator of class attrs.
        """
        prefix, name = text.split(message.message)
        source_namespace = message.ns_map.get(prefix)

        if operation is None:
            operation = name

        yield cls.build_attr(
            operation,
            qname=namespaces.build_qname(source_namespace, name),
            namespace=namespace,
        )

    @classmethod
    def map_binding_message_parts(
        cls, definitions: Definitions, message: str, extended: AnyElement, ns_map: Dict
    ) -> Iterator[Attr]:
        """Find a Message instance and map its parts to attrs.

        Args:
            definitions: The definitions instance
            message: The message qualified name
            extended: The related extended element
            ns_map: The namespace prefix-URI map

        Yields:
            An iterator of class attrs.
        """
        parts = []
        if "part" in extended.attributes:
            parts.append(extended.attributes["part"])
        elif "parts" in extended.attributes:
            parts.extend(extended.attributes["parts"].split())

        if "message" in extended.attributes:
            message_name = namespaces.local_name(extended.attributes["message"])
        else:
            message_name = text.suffix(message)

        definition_message = definitions.find_message(message_name)
        message_parts = definition_message.parts

        if parts:
            message_parts = [part for part in message_parts if part.name in parts]

        yield from cls.build_parts_attributes(message_parts, ns_map)

    @classmethod
    def build_parts_attributes(cls, parts: List[Part], ns_map: Dict) -> Iterator[Attr]:
        """Build attributes for the given list of parts.

        Args:
            parts: A list of part instances
            ns_map: The namespace prefix-URI map

        Yields:
            An iterator of class attrs.
        """
        for part in parts:
            if part.element:
                prefix, type_name = text.split(part.element)
                name = type_name
            elif part.type:
                prefix, type_name = text.split(part.type)
                name = part.name
            else:
                logger.warning("Skip untyped message part %s", part.name)
                continue

            ns_map.update(part.ns_map)
            namespace = part.ns_map.get(prefix)
            type_qname = namespaces.build_qname(namespace, type_name)
            native = namespace == Namespace.XS.uri
            # If part has a type it could reference an element or a complex type or
            # a simple type, we can't make that detection yet, postpone it till the
            # classes processing.
            namespace = "##lazy" if part.type else namespace
            yield cls.build_attr(name, type_qname, namespace=namespace, native=native)

    @classmethod
    def operation_namespace(cls, config: Dict) -> Optional[str]:
        """Return the operation namespace by the operation transport.

        Args:
            config: The operation configuration

        Returns:
            The operation namespace string or None if transport is not soap.
        """
        transport = config.get("transport")
        namespace = None
        if transport == "http://schemas.xmlsoap.org/soap/http":
            namespace = "http://schemas.xmlsoap.org/soap/envelope/"

        return namespace

    @classmethod
    def attributes(cls, elements: Iterator[AnyElement]) -> Dict:
        """Return all attributes from all extended elements as a dictionary.

        Args:
            elements: An iterator of generic elements

        Returns:
            A key-value mapping of the xml attributes.
        """
        return {
            namespaces.local_name(qname): value
            for element in elements
            if isinstance(element, AnyElement)
            for qname, value in element.attributes.items()
        }

    @classmethod
    def build_attr(
        cls,
        name: str,
        qname: str,
        native: bool = False,
        forward: bool = False,
        namespace: Optional[str] = None,
        default: Optional[str] = None,
        reference: int = 0,
    ) -> Attr:
        """Helper method to build an attr instance.

        Args:
            name: The attr name
            qname: The attr qualified name
            native: Whether the type is native
            forward: Whether the type is a forward reference
            namespace: The attr namespace
            default: The attr default value
            reference: The class id reference, if any

        Returns:
            The new attr instance.
        """
        occurs = 1 if default is not None else None
        if native:
            namespace = ""

        return Attr(
            tag=Tag.ELEMENT,
            name=name,
            namespace=namespace,
            default=default,
            types=[
                AttrType(
                    qname=qname, forward=forward, native=native, reference=reference
                )
            ],
            restrictions=Restrictions(min_occurs=occurs, max_occurs=occurs),
        )

map(definitions) classmethod

Main entrypoint for this mapper.

Iterates over their services and their ports and build the binding and service classes.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance to map.

required

Returns:

Type Description
List[Class]

The generated class instances

Source code in xsdata/codegen/mappers/definitions.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@classmethod
def map(cls, definitions: Definitions) -> List[Class]:
    """Main entrypoint for this mapper.

    Iterates over their services and their ports and build
    the binding and service classes.

    Args:
        definitions: The definitions instance to map.

    Returns:
        The generated class instances
    """
    return [
        obj
        for service in definitions.services
        for port in service.ports
        for obj in cls.map_port(definitions, port)
    ]

map_port(definitions, port) classmethod

Map a service port into binding and service classes.

Match a ServicePort to a Binding and PortType object and delegate the process to the next entry point.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
port ServicePort

The service port instance

required

Yields:

Type Description
Class

An iterator of class instances.

Source code in xsdata/codegen/mappers/definitions.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@classmethod
def map_port(cls, definitions: Definitions, port: ServicePort) -> Iterator[Class]:
    """Map a service port into binding and service classes.

    Match a ServicePort to a Binding and PortType object and
    delegate the process to the next entry point.

    Args:
        definitions: The definitions instance
        port: The service port instance

    Yields:
        An iterator of class instances.
    """
    binding = definitions.find_binding(text.suffix(port.binding))
    port_type = definitions.find_port_type(text.suffix(binding.type))

    elements = itertools.chain(binding.extended_elements, port.extended_elements)
    config = cls.attributes(elements)

    yield from cls.map_binding(definitions, binding, port_type, config)

map_binding(definitions, binding, port_type, config) classmethod

Map binding operations into binding and service classes.

Match every BindingOperation to a PortTypeOperation and delegate the process for each operation to the next entry point.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
binding Binding

The binding instance

required
port_type PortType

The port type instance

required
config Dict

Configuration dictionary

required

Yields:

Type Description
Class

An iterator of class instances.

Source code in xsdata/codegen/mappers/definitions.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@classmethod
def map_binding(
    cls,
    definitions: Definitions,
    binding: Binding,
    port_type: PortType,
    config: Dict,
) -> Iterator[Class]:
    """Map binding operations into binding and service classes.

    Match every BindingOperation to a PortTypeOperation and
    delegate the process for each operation to the next entry point.

    Args:
        definitions: The definitions instance
        binding: The binding instance
        port_type: The port type instance
        config: Configuration dictionary

    Yields:
        An iterator of class instances.
    """
    for operation in binding.unique_operations():
        cfg = config.copy()
        cfg.update(cls.attributes(operation.extended_elements))
        port_operation = port_type.find_operation(operation.name)

        yield from cls.map_binding_operation(
            definitions, operation, port_operation, cfg, port_type.name
        )

map_binding_operation(definitions, binding_operation, port_type_operation, config, name) classmethod

Map a binding operation to a service and binding classes.

Convert a BindingOperation to a service class and delegate the process of all the message classes to the next entry point.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
binding_operation BindingOperation

The binding operation instance

required
port_type_operation PortTypeOperation

The port type operation instance

required
config Dict

Configuration dictionary

required
name str

The operation name

required

Yields:

Type Description
Class

An iterator of class instances.

Source code in xsdata/codegen/mappers/definitions.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@classmethod
def map_binding_operation(
    cls,
    definitions: Definitions,
    binding_operation: BindingOperation,
    port_type_operation: PortTypeOperation,
    config: Dict,
    name: str,
) -> Iterator[Class]:
    """Map a binding operation to a service and binding classes.

    Convert a BindingOperation to a service class and delegate
    the process of all the message classes to the next entry point.

    Args:
        definitions: The definitions instance
        binding_operation: The binding operation instance
        port_type_operation: The port type operation instance
        config: Configuration dictionary
        name: The operation name

    Yields:
        An iterator of class instances.
    """
    attrs = [
        cls.build_attr(key, str(DataType.STRING), native=True, default=config[key])
        for key in sorted(config.keys(), key=len)
        if config[key]
    ]

    style = config.get("style", "document")
    name = f"{name}_{binding_operation.name}"
    namespace = cls.operation_namespace(config)
    operation_messages = cls.map_binding_operation_messages(
        definitions,
        binding_operation,
        port_type_operation,
        name,
        style,
        namespace,
    )
    for message_class in operation_messages:
        yield message_class
        # Only Envelope classes need to be added in service input/output
        if message_class.meta_name:
            message_type = message_class.name.split("_")[-1]
            attrs.append(
                cls.build_attr(
                    message_type, message_class.qname, reference=id(message_class)
                )
            )

    assert binding_operation.location is not None

    yield Class(
        qname=namespaces.build_qname(definitions.target_namespace, name),
        status=Status.FLATTENED,
        tag=type(binding_operation).__name__,
        location=binding_operation.location,
        ns_map=binding_operation.ns_map,
        attrs=attrs,
    )

map_binding_operation_messages(definitions, binding_operation, port_type_operation, name, style, namespace) classmethod

Map the binding operation messages to binding classes.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
binding_operation BindingOperation

The binding operation instance

required
port_type_operation PortTypeOperation

The port type operation instance

required
name str

The operation name

required
style str

The operation style

required
namespace Optional[str]

The operation namespace

required

Yields:

Type Description
Class

An iterator of class instances.

Source code in xsdata/codegen/mappers/definitions.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@classmethod
def map_binding_operation_messages(
    cls,
    definitions: Definitions,
    binding_operation: BindingOperation,
    port_type_operation: PortTypeOperation,
    name: str,
    style: str,
    namespace: Optional[str],
) -> Iterator[Class]:
    """Map the binding operation messages to binding classes.

    Args:
        definitions: The definitions instance
        binding_operation: The binding operation instance
        port_type_operation: The port type operation instance
        name: The operation name
        style: The operation style
        namespace: The operation namespace

    Yields:
        An iterator of class instances.
    """
    messages: List[Tuple[str, BindingMessage, PortTypeMessage, Optional[str]]] = []

    if binding_operation.input:
        messages.append(
            (
                "input",
                binding_operation.input,
                port_type_operation.input,
                binding_operation.name,
            )
        )

    if binding_operation.output:
        messages.append(
            ("output", binding_operation.output, port_type_operation.output, None)
        )

    for suffix, binding_message, port_type_message, operation_name in messages:
        if style == "rpc":
            yield cls.build_message_class(definitions, port_type_message)

        target = cls.build_envelope_class(
            definitions,
            binding_message,
            port_type_message,
            f"{name}_{suffix}",
            style,
            namespace,
            operation_name,
        )

        if suffix == "output":
            cls.build_envelope_fault(definitions, port_type_operation, target)

        yield target

build_envelope_fault(definitions, port_type_operation, target) classmethod

Add an inner message fault class with default fields.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
port_type_operation PortTypeOperation

The port type operation instance

required
target Class

The target class instance

required
Source code in xsdata/codegen/mappers/definitions.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
@classmethod
def build_envelope_fault(
    cls,
    definitions: Definitions,
    port_type_operation: PortTypeOperation,
    target: Class,
):
    """Add an inner message fault class with default fields.

    Args:
        definitions: The definitions instance
        port_type_operation: The port type operation instance
        target: The target class instance
    """
    ns_map: Dict = {}
    body = next(inner for inner in target.inner if inner.name == "Body")
    fault_class = cls.build_inner_class(body, "Fault", target.namespace)

    detail_attrs: List[Attr] = []
    for fault in port_type_operation.faults:
        message = definitions.find_message(text.suffix(fault.message))
        detail_attrs.extend(cls.build_parts_attributes(message.parts, ns_map))

    default_fields = ["faultcode", "faultstring", "faultactor"]
    if detail_attrs:
        detail = cls.build_inner_class(fault_class, "detail", namespace="")
        detail.attrs.extend(detail_attrs)
    else:
        default_fields.append("detail")

    collections.prepend(
        fault_class.attrs,
        *(
            cls.build_attr(f, str(DataType.STRING), native=True, namespace="")
            for f in default_fields
        ),
    )

build_envelope_class(definitions, binding_message, port_type_message, name, style, namespace, operation) classmethod

Map the binding message to an envelope class.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
binding_message BindingMessage

The port type message instance

required
port_type_message PortTypeMessage

The port type message instance

required
name str

The class name

required
style str

The operation style e.g. rpc

required
namespace Optional[str]

The operation namespace

required
operation Optional[str]

The custom operation name, if it's empty the message name will be used instead

required

Returns:

Type Description
Class

The class instance.

Source code in xsdata/codegen/mappers/definitions.py
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
@classmethod
def build_envelope_class(
    cls,
    definitions: Definitions,
    binding_message: BindingMessage,
    port_type_message: PortTypeMessage,
    name: str,
    style: str,
    namespace: Optional[str],
    operation: Optional[str],
) -> Class:
    """Map the binding message to an envelope class.

    Args:
        definitions: The definitions instance
        binding_message: The port type message instance
        port_type_message: The port type message instance
        name: The class name
        style: The operation style e.g. rpc
        namespace: The operation namespace
        operation: The custom operation name, if it's empty
            the message name will be used instead

    Returns:
        The class instance.
    """
    assert binding_message.location is not None

    target = Class(
        qname=namespaces.build_qname(definitions.target_namespace, name),
        meta_name="Envelope",
        tag=Tag.BINDING_MESSAGE,
        location=binding_message.location,
        ns_map=binding_message.ns_map,
        namespace=namespace,
    )
    message = port_type_message.message

    for ext in binding_message.extended_elements:
        assert ext.qname is not None
        class_name = namespaces.local_name(ext.qname).title()
        inner = cls.build_inner_class(target, class_name)

        if style == "rpc" and class_name == "Body":
            namespace = ext.attributes.get("namespace")
            attrs = cls.map_port_type_message(
                operation, port_type_message, namespace
            )
        else:
            attrs = cls.map_binding_message_parts(
                definitions, message, ext, inner.ns_map
            )

        inner.attrs.extend(attrs)

    return target

build_message_class(definitions, port_type_message) classmethod

Map the input/output message of a rpc style operation.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
port_type_message PortTypeMessage

The port type message instance

required

Returns:

Type Description
Class

The class instance.

Source code in xsdata/codegen/mappers/definitions.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
@classmethod
def build_message_class(
    cls,
    definitions: Definitions,
    port_type_message: PortTypeMessage,
) -> Class:
    """Map the input/output message of a rpc style operation.

    Args:
        definitions: The definitions instance
        port_type_message: The port type message instance

    Returns:
        The class instance.
    """
    prefix, name = text.split(port_type_message.message)

    definition_message = definitions.find_message(name)
    ns_map = definition_message.ns_map.copy()
    source_namespace = ns_map.get(prefix)

    assert port_type_message.location is not None

    return Class(
        qname=namespaces.build_qname(source_namespace, name),
        namespace=source_namespace,
        status=Status.RAW,
        tag=Tag.ELEMENT,
        location=port_type_message.location,
        ns_map=ns_map,
        attrs=list(cls.build_parts_attributes(definition_message.parts, ns_map)),
    )

build_inner_class(target, name, namespace=None) classmethod

Build or retrieve an inner class.

This helper will also create a forward reference attribute for the parent class.

Parameters:

Name Type Description Default
target Class

The parent class instance

required
name str

The inner class name

required
namespace Optional[str]

The inner class namespace

None

Returns:

Type Description
Class

The inner class instance.

Source code in xsdata/codegen/mappers/definitions.py
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
@classmethod
def build_inner_class(
    cls, target: Class, name: str, namespace: Optional[str] = None
) -> Class:
    """Build or retrieve an inner class.

    This helper will also create a forward reference attribute for
    the parent class.

    Args:
        target: The parent class instance
        name: The inner class name
        namespace: The inner class namespace

    Returns:
        The inner class instance.
    """
    inner = collections.first(inner for inner in target.inner if inner.name == name)
    if not inner:
        inner = Class(
            qname=namespaces.build_qname(target.target_namespace, name),
            tag=Tag.BINDING_MESSAGE,
            location=target.location,
            ns_map=target.ns_map.copy(),
        )
        attr = cls.build_attr(name, inner.qname, forward=True, namespace=namespace)

        inner.parent = target
        target.inner.append(inner)
        target.attrs.append(attr)

    return inner

map_port_type_message(operation, message, namespace) classmethod

Build an attribute for the given port type message.

Parameters:

Name Type Description Default
operation Optional[str]

The operation name, use the message name if it's empty

required
message PortTypeMessage

The port type message instance

required
namespace Optional[str]

The operation namespace

required

Yields:

Type Description
Attr

An iterator of class attrs.

Source code in xsdata/codegen/mappers/definitions.py
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
@classmethod
def map_port_type_message(
    cls,
    operation: Optional[str],
    message: PortTypeMessage,
    namespace: Optional[str],
) -> Iterator[Attr]:
    """Build an attribute for the given port type message.

    Args:
        operation: The operation name, use the message name
            if it's empty
        message: The port type message instance
        namespace: The operation namespace

    Yields:
        An iterator of class attrs.
    """
    prefix, name = text.split(message.message)
    source_namespace = message.ns_map.get(prefix)

    if operation is None:
        operation = name

    yield cls.build_attr(
        operation,
        qname=namespaces.build_qname(source_namespace, name),
        namespace=namespace,
    )

map_binding_message_parts(definitions, message, extended, ns_map) classmethod

Find a Message instance and map its parts to attrs.

Parameters:

Name Type Description Default
definitions Definitions

The definitions instance

required
message str

The message qualified name

required
extended AnyElement

The related extended element

required
ns_map Dict

The namespace prefix-URI map

required

Yields:

Type Description
Attr

An iterator of class attrs.

Source code in xsdata/codegen/mappers/definitions.py
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
@classmethod
def map_binding_message_parts(
    cls, definitions: Definitions, message: str, extended: AnyElement, ns_map: Dict
) -> Iterator[Attr]:
    """Find a Message instance and map its parts to attrs.

    Args:
        definitions: The definitions instance
        message: The message qualified name
        extended: The related extended element
        ns_map: The namespace prefix-URI map

    Yields:
        An iterator of class attrs.
    """
    parts = []
    if "part" in extended.attributes:
        parts.append(extended.attributes["part"])
    elif "parts" in extended.attributes:
        parts.extend(extended.attributes["parts"].split())

    if "message" in extended.attributes:
        message_name = namespaces.local_name(extended.attributes["message"])
    else:
        message_name = text.suffix(message)

    definition_message = definitions.find_message(message_name)
    message_parts = definition_message.parts

    if parts:
        message_parts = [part for part in message_parts if part.name in parts]

    yield from cls.build_parts_attributes(message_parts, ns_map)

build_parts_attributes(parts, ns_map) classmethod

Build attributes for the given list of parts.

Parameters:

Name Type Description Default
parts List[Part]

A list of part instances

required
ns_map Dict

The namespace prefix-URI map

required

Yields:

Type Description
Attr

An iterator of class attrs.

Source code in xsdata/codegen/mappers/definitions.py
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
@classmethod
def build_parts_attributes(cls, parts: List[Part], ns_map: Dict) -> Iterator[Attr]:
    """Build attributes for the given list of parts.

    Args:
        parts: A list of part instances
        ns_map: The namespace prefix-URI map

    Yields:
        An iterator of class attrs.
    """
    for part in parts:
        if part.element:
            prefix, type_name = text.split(part.element)
            name = type_name
        elif part.type:
            prefix, type_name = text.split(part.type)
            name = part.name
        else:
            logger.warning("Skip untyped message part %s", part.name)
            continue

        ns_map.update(part.ns_map)
        namespace = part.ns_map.get(prefix)
        type_qname = namespaces.build_qname(namespace, type_name)
        native = namespace == Namespace.XS.uri
        # If part has a type it could reference an element or a complex type or
        # a simple type, we can't make that detection yet, postpone it till the
        # classes processing.
        namespace = "##lazy" if part.type else namespace
        yield cls.build_attr(name, type_qname, namespace=namespace, native=native)

operation_namespace(config) classmethod

Return the operation namespace by the operation transport.

Parameters:

Name Type Description Default
config Dict

The operation configuration

required

Returns:

Type Description
Optional[str]

The operation namespace string or None if transport is not soap.

Source code in xsdata/codegen/mappers/definitions.py
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@classmethod
def operation_namespace(cls, config: Dict) -> Optional[str]:
    """Return the operation namespace by the operation transport.

    Args:
        config: The operation configuration

    Returns:
        The operation namespace string or None if transport is not soap.
    """
    transport = config.get("transport")
    namespace = None
    if transport == "http://schemas.xmlsoap.org/soap/http":
        namespace = "http://schemas.xmlsoap.org/soap/envelope/"

    return namespace

attributes(elements) classmethod

Return all attributes from all extended elements as a dictionary.

Parameters:

Name Type Description Default
elements Iterator[AnyElement]

An iterator of generic elements

required

Returns:

Type Description
Dict

A key-value mapping of the xml attributes.

Source code in xsdata/codegen/mappers/definitions.py
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
@classmethod
def attributes(cls, elements: Iterator[AnyElement]) -> Dict:
    """Return all attributes from all extended elements as a dictionary.

    Args:
        elements: An iterator of generic elements

    Returns:
        A key-value mapping of the xml attributes.
    """
    return {
        namespaces.local_name(qname): value
        for element in elements
        if isinstance(element, AnyElement)
        for qname, value in element.attributes.items()
    }

build_attr(name, qname, native=False, forward=False, namespace=None, default=None, reference=0) classmethod

Helper method to build an attr instance.

Parameters:

Name Type Description Default
name str

The attr name

required
qname str

The attr qualified name

required
native bool

Whether the type is native

False
forward bool

Whether the type is a forward reference

False
namespace Optional[str]

The attr namespace

None
default Optional[str]

The attr default value

None
reference int

The class id reference, if any

0

Returns:

Type Description
Attr

The new attr instance.

Source code in xsdata/codegen/mappers/definitions.py
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
@classmethod
def build_attr(
    cls,
    name: str,
    qname: str,
    native: bool = False,
    forward: bool = False,
    namespace: Optional[str] = None,
    default: Optional[str] = None,
    reference: int = 0,
) -> Attr:
    """Helper method to build an attr instance.

    Args:
        name: The attr name
        qname: The attr qualified name
        native: Whether the type is native
        forward: Whether the type is a forward reference
        namespace: The attr namespace
        default: The attr default value
        reference: The class id reference, if any

    Returns:
        The new attr instance.
    """
    occurs = 1 if default is not None else None
    if native:
        namespace = ""

    return Attr(
        tag=Tag.ELEMENT,
        name=name,
        namespace=namespace,
        default=default,
        types=[
            AttrType(
                qname=qname, forward=forward, native=native, reference=reference
            )
        ],
        restrictions=Restrictions(min_occurs=occurs, max_occurs=occurs),
    )