Return an instance of PackageData built from a ``mapping`` native Python data. Known attributes that store a list of objects are also "rehydrated" (such as models.Party). Unknown attributes provided in ``mapping`` that do not exist as fields in the class are
(cls, mapping)
| 889 | |
| 890 | @classmethod |
| 891 | def from_dict(cls, mapping): |
| 892 | """ |
| 893 | Return an instance of PackageData built from a ``mapping`` native Python |
| 894 | data. Known attributes that store a list of objects are also |
| 895 | "rehydrated" (such as models.Party). |
| 896 | |
| 897 | Unknown attributes provided in ``mapping`` that do not exist as fields |
| 898 | in the class are kept as items in the extra_data mapping. An Exception |
| 899 | is raised if an "unknown attribute" name already exists as an extra_data |
| 900 | name. |
| 901 | """ |
| 902 | # TODO: consider using a proper library for this such as cattrs, |
| 903 | # marshmallow, etc. or use the field type that we declare. |
| 904 | |
| 905 | # Each of these are lists of class instances tracked here, which are stored |
| 906 | # as a list of mappings in scanc_data |
| 907 | |
| 908 | # these are computed attributes serialized on a package |
| 909 | # that should not be recreated when de-serializing |
| 910 | computed_attributes = set(['purl', ]) |
| 911 | |
| 912 | fields_by_name = attr.fields_dict(cls) |
| 913 | |
| 914 | extra_data = mapping.get('extra_data', {}) or {} |
| 915 | package_data = {} |
| 916 | |
| 917 | list_fields_by_item = { |
| 918 | 'parties': Party, |
| 919 | 'dependencies': DependentPackage, |
| 920 | 'file_references': FileReference, |
| 921 | } |
| 922 | |
| 923 | for name, value in mapping.items(): |
| 924 | if not value: |
| 925 | continue |
| 926 | |
| 927 | if name in computed_attributes: |
| 928 | continue |
| 929 | |
| 930 | field = fields_by_name.get(name) |
| 931 | if not field: |
| 932 | # keep unknown fields as extra data |
| 933 | if name not in extra_data: |
| 934 | extra_data[name] = value |
| 935 | continue |
| 936 | else: |
| 937 | raise Exception( |
| 938 | f'Invalid package "scan_data" with duplicated name: {name!r}={value!r} ' |
| 939 | f'present both as attribute AND as extra_data: {name!r}={extra_data[name]!r}' |
| 940 | ) |
| 941 | |
| 942 | # re-hydrate lists of typed objects |
| 943 | list_item_type = is_list_field = list_fields_by_item.get(name) |
| 944 | |
| 945 | if is_list_field: |
| 946 | items = list(_rehydrate_list(cls=list_item_type, values=value)) |
| 947 | package_data[name] = items |
| 948 | else: |
nothing calls this directly
no test coverage detected