text: from inspect import getmro
  
  from marshmallow.schema import Schema, SchemaMeta, SchemaOpts
  
  from .converter import BaseConverter
  from .registry import registry
  from typing import Dict, Any
  
  
  class AnnotationSchemaOpts(SchemaOpts):
      """
      marshmallow-annotations specific SchemaOpts implementation, provides:
  
      - converter_factory
      - registry
      - register_as_scheme
      - target
      - field_configs
      - converter
      """
  
      def __init__(self, meta, schema=None, ordered=False):
          super().__init__(meta)
          self.__sentinel = object()
          self.field_configs: Dict[str, Dict[str, Any]] = {}
  
          self._process(meta, schema)
          self._finalize()
          self.converter = self.converter_factory(registry=self.registry)
  
          if schema is not None and self.register_as_scheme and hasattr(self, "target"):
              self.converter.registry.register_scheme_factory(self.target, schema)
  
          del self.__sentinel
  
      def _process(self, meta, schema):
          self._extract_from_parents(schema, self._extract_from)
          self._extract_from(meta)
          self._gather_field_configs(schema, meta)
  
      def _extract_from_parents(self, schema, f):
          # can't look at just next parent
          # they may or may be an AnnotationSchema
          # so we need to walk backwards from object to get the correct
          # combination of settings
          for parent in reversed(getmro(schema)):
              opts = getattr(parent, "opts", self.__sentinel)
              if opts is self.__sentinel:
                  continue
  
              f(opts)
  
      def _extract_from(self, source):
          if hasattr(source, "converter_factory"):
              self.converter_factory = source.converter_factory
          if hasattr(source, "register_as_scheme"):
              self.register_as_scheme = source.register_as_scheme
          if hasattr(source, "target"):
              self.target = source.target
          if hasattr(source, "registry"):
              self.registry = source.registry
  
      def _gather_field_configs(self, schema, meta):
          def merge_field_configs(opts):
              field_configs = getattr(opts, "field_configs", self.__sentinel)
  
              if field_configs is self.__sentinel:
                  return
  
              for k, v in field_configs.items():
                  config = self.field_configs.setdefault(k, {})
                  config.update(dict(v))
  
          self._extract_from_parents(schema, merge_field_configs)
  
          defaults = getattr(meta, "Fields", self.__sentinel)
          if defaults is self.__sentinel:
              return
  
          for k, v in defaults.__dict__.items():
              if k.startswith("_"):
                  continue
  
              self.field_configs.setdefault(k, {}).update(v)
  
      def _finalize(self):
          self.converter_factory = getattr(self, "converter_factory", BaseConverter)
          self.register_as_scheme = getattr(self, "register_as_scheme", False)
          self.registry = getattr(self, "registry", registry)
  
  
  class AnnotationSchemaMeta(SchemaMeta):
      @classmethod
      def get_declared_fields(mcls, klass, cls_fields, inherited_fields, dict_cls):
          fields = super().get_declared_fields(
              klass, cls_fields, inherited_fields, dict_cls
          )
  
          target = getattr(klass.opts, "target", None)
  
          if target is None:
              return fields
  
          converter = klass.opts.converter
  
          # ignore anything explicitly declared on this scheme
          # or any parent scheme, also ignore anything explicitly
          # passed into exclude
          ignore = set(fields) | set(klass.opts.exclude)
          fields.update(converter.convert_all(target, ignore, klass.opts.field_configs))
  
          return fields
  
  
  class AnnotationSchema(Schema, metaclass=AnnotationSchemaMeta):
      """
      Base class for creating annotation schema with::
  
          from marshmallow_annotations import AnnotationSchema
          from my.app.entities import Artist
  
          class ArtistScheme(AnnotationSchema):
              class Meta:
                  target = Artist
                  register_as_scheme = True
  
      """
  
      OPTIONS_CLASS_TYPE = AnnotationSchemaOpts
  
      @classmethod
      def OPTIONS_CLASS(cls, meta, **kwargs):
          return cls.OPTIONS_CLASS_TYPE(meta, cls, **kwargs)
  