justanr / marshmallow-annotations

Allows declaring marshmallow schema through type annotations
MIT License
48 stars 12 forks source link

Snag with AttrsSchema and a list attribute #23

Closed Midnighter closed 5 years ago

Midnighter commented 5 years ago

So far the attrs extension looks very promising. I've run into the following error, though, with a list attribute:

import typing

import attr
from marshmallow_annotations.ext.attrs import AttrsSchema

@attr.s(auto_attribs=True)
class Car:
    passengers: typing.List[str] = attr.ib(factory=list)

class CarSchema(AttrsSchema):

    class Meta:
        target = Car

The error I get is:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-21-7366b4031858> in <module>()
----> 1 class CarSchema(AttrsSchema):
      2 
      3     class Meta:
      4         target = Car

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow/schema.py in __new__(mcs, name, bases, attrs)
    115             cls_fields=cls_fields,
    116             inherited_fields=inherited_fields,
--> 117             dict_cls=dict_cls
    118         )
    119         return klass

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/scheme.py in get_declared_fields(mcls, klass, cls_fields, inherited_fields, dict_cls)
    108         # passed into exclude
    109         ignore = set(fields) | set(klass.opts.exclude)
--> 110         fields.update(converter.convert_all(target, ignore, klass.opts.field_configs))
    111 
    112         return fields

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/ext/attrs.py in convert_all(self, target, ignore, configs)
     36     ) -> GeneratedFields:
     37         self._ensure_all_hints_are_attribs(target, ignore)
---> 38         return super().convert_all(target, ignore, configs)
     39 
     40     def _get_field_defaults(self, target):

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/converter.py in convert_all(self, target, ignore, configs)
     78         return {
     79             k: self.convert(v, configs.get(k, {}), field_name=k, target=target)
---> 80             for k, v in self._get_type_hints(target, ignore)
     81         }
     82 

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/converter.py in <dictcomp>(.0)
     78         return {
     79             k: self.convert(v, configs.get(k, {}), field_name=k, target=target)
---> 80             for k, v in self._get_type_hints(target, ignore)
     81         }
     82 

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/converter.py in convert(self, typehint, opts, field_name, target)
     64         opts = opts if opts is not None else {}
     65         return self._field_from_typehint(
---> 66             typehint, opts, field_name=field_name, target=target
     67         )
     68 

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/converter.py in _field_from_typehint(self, typehint, kwargs, field_name, target)
    115         self._postprocess_typehint(typehint, kwargs, field_name, target)
    116         field_constructor = self.registry.get(typehint)
--> 117         return field_constructor(self, subtypes, kwargs)
    118 
    119     def _get_type_hints(self, item, ignore):

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/registry.py in _list_converter(converter, subtypes, opts)
     55         return converter.convert(subtypes[0], opts)
     56     sub_opts = opts.pop("_interior", {})
---> 57     return fields.List(converter.convert(subtypes[0], sub_opts), **opts)
     58 
     59 

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/converter.py in convert(self, typehint, opts, field_name, target)
     64         opts = opts if opts is not None else {}
     65         return self._field_from_typehint(
---> 66             typehint, opts, field_name=field_name, target=target
     67         )
     68 

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/converter.py in _field_from_typehint(self, typehint, kwargs, field_name, target)
     90         # need that immutable dict in the stdlib pls
     91         kwargs = kwargs if kwargs is not None else {}
---> 92         self._preprocess_typehint(typehint, kwargs, field_name, target)
     93 
     94         # sane defaults

~/.virtualenvs/struct/lib/python3.6/site-packages/marshmallow_annotations/ext/attrs.py in _preprocess_typehint(self, typehing, kwargs, field_name, target)
     46 
     47     def _preprocess_typehint(self, typehing, kwargs, field_name, target):
---> 48         attr = _get_attr_from_attrs(target.__attrs_attrs__, field_name)
     49 
     50         if attr.default != NOTHING:

AttributeError: 'NoneType' object has no attribute '__attrs_attrs__'
justanr commented 5 years ago

Oh that's nasty. I'll see if I can dig into it this evening but it might be until the weekend before I can get to it but I have a passing hunch on how to handle it. Got get back into the code to be sure though

justanr commented 5 years ago

Putting together a fix PR right now. Hopefully get it merged + released tomorrow or Wednesday.