Create a new `Schema` by merging this and the provided `schema`. Neither this `Schema` nor the provided `schema` are modified. The resulting `Schema` inherits the `required` and `extra` parameters of this, unless overridden. Both schemas must be dictionary-based.
(
self,
schema: Schemable,
required: typing.Optional[bool] = None,
extra: typing.Optional[int] = None,
)
| 703 | return validate_set |
| 704 | |
| 705 | def extend( |
| 706 | self, |
| 707 | schema: Schemable, |
| 708 | required: typing.Optional[bool] = None, |
| 709 | extra: typing.Optional[int] = None, |
| 710 | ) -> Schema: |
| 711 | """Create a new `Schema` by merging this and the provided `schema`. |
| 712 | |
| 713 | Neither this `Schema` nor the provided `schema` are modified. The |
| 714 | resulting `Schema` inherits the `required` and `extra` parameters of |
| 715 | this, unless overridden. |
| 716 | |
| 717 | Both schemas must be dictionary-based. |
| 718 | |
| 719 | :param schema: dictionary to extend this `Schema` with |
| 720 | :param required: if set, overrides `required` of this `Schema` |
| 721 | :param extra: if set, overrides `extra` of this `Schema` |
| 722 | """ |
| 723 | |
| 724 | assert isinstance(self.schema, dict) and isinstance( |
| 725 | schema, dict |
| 726 | ), 'Both schemas must be dictionary-based' |
| 727 | |
| 728 | result = self.schema.copy() |
| 729 | |
| 730 | # returns the key that may have been passed as an argument to Marker constructor |
| 731 | def key_literal(key): |
| 732 | return key.schema if isinstance(key, Marker) else key |
| 733 | |
| 734 | # build a map that takes the key literals to the needed objects |
| 735 | # literal -> Required|Optional|literal |
| 736 | result_key_map = dict((key_literal(key), key) for key in result) |
| 737 | |
| 738 | # for each item in the extension schema, replace duplicates |
| 739 | # or add new keys |
| 740 | for key, value in schema.items(): |
| 741 | # if the key is already in the dictionary, we need to replace it |
| 742 | # transform key to literal before checking presence |
| 743 | if key_literal(key) in result_key_map: |
| 744 | result_key = result_key_map[key_literal(key)] |
| 745 | result_value = result[result_key] |
| 746 | |
| 747 | # if both are dictionaries, we need to extend recursively |
| 748 | # create the new extended sub schema, then remove the old key and add the new one |
| 749 | if isinstance(result_value, dict) and isinstance(value, dict): |
| 750 | new_value = Schema(result_value).extend(value).schema |
| 751 | del result[result_key] |
| 752 | result[key] = new_value |
| 753 | # one or the other or both are not sub-schemas, simple replacement is fine |
| 754 | # remove old key and add new one |
| 755 | else: |
| 756 | del result[result_key] |
| 757 | result[key] = value |
| 758 | |
| 759 | # key is new and can simply be added |
| 760 | else: |
| 761 | result[key] = value |
| 762 |