[PATCH 17 of 24 yams V2] [mypy] type schema.py

Laurent Peuch cortex at worlddomination.be
Wed Mar 4 15:17:53 CET 2020


# HG changeset patch
# User Laurent Peuch <cortex at worlddomination.be>
# Date 1580312272 -3600
#      Wed Jan 29 16:37:52 2020 +0100
# Node ID 499c2e99508417aaf627758c77a1fb445c452f50
# Parent  7c2414f5ab7e8c1ee2ab5bb99de8b74c79b8e343
# Available At https://hg.logilab.org/users/lpeuch/yams
#              hg pull https://hg.logilab.org/users/lpeuch/yams -r 499c2e995084
# EXP-Topic type_annotations
[mypy] type schema.py

diff --git a/yams/buildobjs.py b/yams/buildobjs.py
--- a/yams/buildobjs.py
+++ b/yams/buildobjs.py
@@ -575,7 +575,7 @@ class EntityType(Definition, metaclass=m
         _check_kwargs(kwargs, ETYPE_PROPERTIES)
 
         self.__dict__.update(kwargs)
-        self.specialized_type = self.__class__.__dict__.get('__specializes__')
+        self.specialized_type: Optional[str] = self.__class__.__dict__.get('__specializes__')
 
     def __str__(self) -> str:
         return 'entity type %r' % self.name
diff --git a/yams/constraints.py b/yams/constraints.py
--- a/yams/constraints.py
+++ b/yams/constraints.py
@@ -31,7 +31,8 @@ from logilab.common.deprecation import c
 import yams
 from yams import BadSchemaDefinition
 from yams.interfaces import IConstraint, IVocabularyConstraint
-from yams.types import _jsonSerializableType, _RdefType, _SubjObjSchema, _RtypeType
+from yams.types import (_jsonSerializableType, _RdefType, _SubjObjSchema,
+                        _RtypeType, _CheckersType, _ConvertersType)
 
 __docformat__: str = "restructuredtext en"
 
@@ -721,7 +722,7 @@ def yes(*args, **kwargs) -> bool:
     return True
 
 
-BASE_CHECKERS: Dict[str, Callable[[Any, Any], bool]] = {
+BASE_CHECKERS: _CheckersType = {
     'Date': yes,
     'Time': yes,
     'Datetime': yes,
@@ -738,8 +739,7 @@ BASE_CHECKERS: Dict[str, Callable[[Any, 
     'Bytes': check_file,
 }
 
-BASE_CONVERTERS: Dict[str, Callable[[Any],
-                                    Union[str, bytes, int, float, bool, decimal.Decimal]]] = {
+BASE_CONVERTERS: _ConvertersType = {
     'String': str,
     'Password': bytes,
     'Int': int,
diff --git a/yams/diff.py b/yams/diff.py
--- a/yams/diff.py
+++ b/yams/diff.py
@@ -287,7 +287,8 @@ def schema2descr(schema: _SchemaType,
 
     # Handle multi-rdef relation types
     for rtype in sorted(multirtypes):
-        for (subjectschema, objectschema), relation in sorted(schema[rtype].rdefs.iteritems()):
+        items = schema[rtype].rdefs.items()  # type: ignore
+        for (subjectschema, objectschema), relation in sorted(items):
             subjetype = subjectschema.type
             objetype = objectschema.type
 
diff --git a/yams/reader.py b/yams/reader.py
--- a/yams/reader.py
+++ b/yams/reader.py
@@ -99,7 +99,7 @@ def fill_schema(schema: _SchemaType,
     schema.finalize()
 
     # check permissions are valid on entities and relations
-    for erschema in schema.entities() + schema.relations():
+    for erschema in schema.entities() + schema.relations():  # type: ignore
         erschema.check_permission_definitions()
 
     # check unique together consistency
diff --git a/yams/schema.py b/yams/schema.py
--- a/yams/schema.py
+++ b/yams/schema.py
@@ -15,16 +15,18 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with yams. If not, see <http://www.gnu.org/licenses/>.
-from logilab.common.logging_ext import set_log_methods
-from typing import Dict, Any
-import logging
+
 """Classes to define generic Entities/Relations schemas."""
 
-__docformat__ = "restructuredtext en"
+from logilab.common.logging_ext import set_log_methods
+
+import logging
 
 import warnings
 from copy import deepcopy
 from itertools import chain
+from typing import (Dict, Any, Type, TYPE_CHECKING, Sequence, Generator, List, Optional, Set,
+                    Tuple, Union)
 
 from logilab.common.decorators import cached, clear_cache
 from logilab.common.interface import implements
@@ -34,20 +36,26 @@ from yams import (BASE_TYPES, MARKER, Va
                   KNOWN_METAATTRIBUTES, convert_default_value, DEFAULT_ATTRPERMS,
                   DEFAULT_COMPUTED_RELPERMS)
 from yams.interfaces import (ISchema, IRelationSchema, IEntitySchema,
-                             IVocabularyConstraint)
-from yams.constraints import BASE_CHECKERS, BASE_CONVERTERS, UniqueConstraint
-
-_ = str
+                             IConstraint, IVocabularyConstraint)
+from yams.constraints import BASE_CHECKERS, BASE_CONVERTERS, UniqueConstraint, BaseConstraint
+from yams.types import (_SchemaType, _ERSchemaType, _PermissionsType, _OnePermissionType,
+                        _ConvertersType, _CheckersType, _EntityTypeType, _RelationSchemaType,
+                        _RdefSchemaType, _EntitySchemaType, _RelationTypeType, _RdefType)
 
 
-def role_name(rtype, role):
+__docformat__: str = "restructuredtext en"
+
+_: Type[str] = str
+
+
+def role_name(rtype, role) -> str:
     """function to use for qualifying attribute / relation in ValidationError
     errors'dictionnary
     """
     return '%s-%s' % (rtype, role)
 
 
-def rehash(dictionary):
+def rehash(dictionary: Dict) -> dict:
     """this function manually builds a copy of `dictionary` but forces
     hash values to be recomputed. Note that dict(d) or d.copy() don't
     do that.
@@ -80,7 +88,7 @@ def rehash(dictionary):
     return dict(item for item in dictionary.items())
 
 
-def _format_properties(props):
+def _format_properties(props: Dict) -> str:
     res = [('%s=%s' % item) for item in props.items() if item[1]]
     return ','.join(res)
 
@@ -88,7 +96,7 @@ def _format_properties(props):
 class ERSchema(object):
     """Base class shared by entity and relation schema."""
 
-    def __init__(self, schema=None, erdef=None):
+    def __init__(self, schema: _SchemaType = None, erdef=None) -> None:
         """
         Construct an ERSchema instance.
 
@@ -98,62 +106,85 @@ class ERSchema(object):
         """
         if erdef is None:
             return
+
         self.schema = schema
-        self.type = erdef.name
-        self.description = erdef.description or ''
+        self.type: str = erdef.name
+        self.description: str = erdef.description or ''
         self.package = erdef.package
 
-    def __eq__(self, other):
+    def __eq__(self, other) -> bool:
         return self.type == getattr(other, 'type', other)
 
-    def __ne__(self, other):
+    def __ne__(self, other) -> bool:
         return not (self == other)
 
-    def __lt__(self, other):
+    def __lt__(self, other) -> bool:
         return self.type < getattr(other, 'type', other)
 
-    def __hash__(self):
+    def __hash__(self) -> int:
         try:
             return hash(self.type)
         except AttributeError:
             pass
         return hash(id(self))
 
-    def __deepcopy__(self, memo):
+    def __deepcopy__(self: _ERSchemaType, memo) -> _ERSchemaType:
         clone = self.__class__()
         memo[id(self)] = clone
         clone.type = deepcopy(self.type, memo)
         clone.schema = deepcopy(self.schema, memo)
         clone.__dict__ = deepcopy(self.__dict__, memo)
+
         return clone
 
-    def __str__(self):
+    def __str__(self) -> str:
         return self.type
 
 
 class PermissionMixIn(object):
     """mixin class for permissions handling"""
 
-    def action_permissions(self, action):
+    # https://github.com/python/mypy/issues/5837
+    if TYPE_CHECKING:
+        def __init__(self, schema: Optional[_SchemaType], rdef:
+                     Optional[_EntityTypeType], *args, **kwargs):
+            # fake init for mypy
+            self.permissions: _PermissionsType = rdef.__permissions__.copy() if rdef else {}
+            self.final: bool = True
+
+        @property
+        def ACTIONS(self) -> Tuple[str, ...]:
+            return ('read', 'add', 'update', 'delete')
+
+        def advertise_new_add_permission(self) -> None:
+            pass
+
+    def action_permissions(self, action: str) -> _OnePermissionType:
         return self.permissions[action]
 
-    def set_action_permissions(self, action, permissions):
+    def set_action_permissions(self, action: str, permissions: _OnePermissionType) -> None:
         assert type(permissions) is tuple, (
             'permissions is expected to be a tuple not %s' % type(permissions))
+
         assert action in self.ACTIONS, ('%s not in %s' % (action, self.ACTIONS))
+
         self.permissions[action] = permissions
 
-    def check_permission_definitions(self):
+    def check_permission_definitions(self) -> None:
         """check permissions are correctly defined"""
+
         # already initialized, check everything is fine
         for action, groups in self.permissions.items():
             assert action in self.ACTIONS, \
                 'unknown action %s for %s' % (action, self)
+
             assert isinstance(groups, tuple), \
                 ('permission for action %s of %s isn\'t a tuple as '
                  'expected' % (action, self))
+
         if self.final:
             self.advertise_new_add_permission()
+
         for action in self.ACTIONS:
             assert action in self.permissions, \
                 'missing expected permissions for action %s for %s' % (action, self)
@@ -169,32 +200,36 @@ class EntitySchema(PermissionMixIn, ERSc
     """
     __implements__ = IEntitySchema
 
-    ACTIONS = ('read', 'add', 'update', 'delete')
-    field_checkers = BASE_CHECKERS
-    field_converters = BASE_CONVERTERS
+    ACTIONS: Tuple[str, ...] = ('read', 'add', 'update', 'delete')
+    field_checkers: _CheckersType = BASE_CHECKERS
+    field_converters: _ConvertersType = BASE_CONVERTERS
 
     # XXX set default values for those attributes on the class level since
     # they may be missing from schemas obtained by pyro
-    _specialized_type = None
-    _specialized_by = []
+    _specialized_type: Optional[str] = None
+    _specialized_by: List = []
 
-    def __init__(self, schema=None, rdef=None, *args, **kwargs):
+    def __init__(self, schema: _SchemaType = None, rdef: _EntityTypeType = None,
+                 *args, **kwargs) -> None:
         super(EntitySchema, self).__init__(schema, rdef, *args, **kwargs)
+
         if rdef is not None:
             # quick access to bounded relation schemas
-            self.subjrels = {}
-            self.objrels = {}
+            self.subjrels: Dict[Union[str, _RelationSchemaType], _RelationSchemaType] = {}
+            self.objrels: Dict[Union[str, _RelationSchemaType], _RelationSchemaType] = {}
             self._specialized_type = rdef.specialized_type
             self._specialized_by = rdef.specialized_by
-            self.final = self.type in BASE_TYPES
-            self.permissions = rdef.__permissions__.copy()
-            self._unique_together = getattr(rdef, '__unique_together__', [])
+            self.final: bool = self.type in BASE_TYPES
+            self.permissions: Dict[str, Tuple[str, ...]] = rdef.__permissions__.copy()
+            self._unique_together: List = getattr(rdef, '__unique_together__', [])
+
         else:  # this happens during deep copy (cf. ERSchema.__deepcopy__)
             self._specialized_type = None
             self._specialized_by = []
 
-    def check_unique_together(self):
+    def check_unique_together(self) -> None:
         errors = []
+
         for unique_together in self._unique_together:
             for name in unique_together:
                 try:
@@ -205,35 +240,37 @@ class EntitySchema(PermissionMixIn, ERSc
                     if not (rschema.final or rschema.rtype.inlined):
                         errors.append('%s is not an attribute or an inlined '
                                       'relation' % name)
+
         if errors:
             msg = 'invalid __unique_together__ specification for %s: %s' % (self, ', '.join(errors))
             raise BadSchemaDefinition(msg)
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return '<%s %s - %s>' % (self.type,
                                  [rs.type for rs in self.subject_relations()],
                                  [rs.type for rs in self.object_relations()])
 
-    def _rehash(self):
+    def _rehash(self) -> None:
         self.subjrels = rehash(self.subjrels)
         self.objrels = rehash(self.objrels)
 
-    def advertise_new_add_permission(self):
+    def advertise_new_add_permission(self) -> None:
         pass
 
     # schema building methods #################################################
 
-    def add_subject_relation(self, rschema):
+    def add_subject_relation(self, rschema: _RelationSchemaType) -> None:
         """register the relation schema as possible subject relation"""
         self.subjrels[rschema] = rschema
+
         clear_cache(self, 'ordered_relations')
         clear_cache(self, 'meta_attributes')
 
-    def add_object_relation(self, rschema):
+    def add_object_relation(self, rschema: _RelationSchemaType) -> None:
         """register the relation schema as possible object relation"""
         self.objrels[rschema] = rschema
 
-    def del_subject_relation(self, rtype):
+    def del_subject_relation(self, rtype) -> None:
         try:
             del self.subjrels[rtype]
             clear_cache(self, 'ordered_relations')
@@ -241,7 +278,7 @@ class EntitySchema(PermissionMixIn, ERSc
         except KeyError:
             pass
 
-    def del_object_relation(self, rtype):
+    def del_object_relation(self, rtype) -> None:
         try:
             del self.objrels[rtype]
         except KeyError:
@@ -251,72 +288,95 @@ class EntitySchema(PermissionMixIn, ERSc
 
     # navigation ######################
 
-    def specializes(self):
-        if self._specialized_type:
+    def specializes(self) -> Any:
+        if self._specialized_type and self.schema is not None:
             return self.schema.eschema(self._specialized_type)
         return None
 
-    def ancestors(self):
+    def ancestors(self) -> List:
         specializes = self.specializes()
         ancestors = []
+
         while specializes:
             ancestors.append(specializes)
             specializes = specializes.specializes()
+
         return ancestors
 
-    def specialized_by(self, recursive=True):
+    def specialized_by(self, recursive: bool = True) -> List['EntitySchema']:
+        if not self.schema:
+            return []
+
         eschema = self.schema.eschema
         subschemas = [eschema(etype) for etype in self._specialized_by]
+
         if recursive:
             for subschema in subschemas[:]:
                 subschemas.extend(subschema.specialized_by(recursive=True))
+
         return subschemas
 
-    def has_relation(self, rtype, role):
+    def has_relation(self, rtype, role: str) -> bool:
         if role == 'subject':
             return rtype in self.subjrels
+
         return rtype in self.objrels
 
-    def subject_relations(self):
+    def subject_relations(self) -> List[_RelationSchemaType]:
         """return a list of relations that may have this type of entity as
         subject
         """
         return list(self.subjrels.values())
 
-    def object_relations(self):
+    def object_relations(self) -> List[_RelationSchemaType]:
         """return a list of relations that may have this type of entity as
         object
         """
         return list(self.objrels.values())
 
-    def rdef(self, rtype, role='subject', targettype=None, takefirst=False):
+    def rdef(self, rtype: Union[str, _RelationSchemaType],
+             role: str = 'subject',
+             targettype: Optional[Union[str, _EntitySchemaType]] = None,
+             takefirst: bool = False) -> _RdefSchemaType:
         """return a relation definition schema for a relation of this entity type
 
         Notice that when targettype is not specified and the relation may lead
         to different entity types (ambiguous relation), one of them is picked
         randomly. If also takefirst is False, a warning will be emitted.
         """
+        assert self.schema is not None
         rschema = self.schema.rschema(rtype)
+
         if targettype is None:
             if role == 'subject':
                 types = rschema.objects(self)
             else:
                 types = rschema.subjects(self)
+
             if len(types) != 1 and not takefirst:
                 warnings.warn('[yams 0.38] no targettype specified and there are several '
                               'relation definitions for rtype %s: %s. Yet you get the first '
                               'rdef.' % (rtype, [eschema.type for eschema in types]),
                               Warning, stacklevel=2)
+
             targettype = types[0]
+
         return rschema.role_rdef(self, targettype, role)
 
     @cached
-    def ordered_relations(self):
+    def ordered_relations(self) -> List[_RelationSchemaType]:
         """return subject relations in an ordered way"""
+        # mypy: "RelationDefinitionSchema" has no attribute "order"
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
         return sorted(self.subjrels.values(),
-                      key=lambda x: x.rdef(self, x.objects(self)[0]).order)
+                      key=lambda x: x.rdef(self, x.objects(self)[0]).order)  # type: ignore
 
-    def relation_definitions(self, includefinal=False):
+    _RelationDefinitionsReturnType = Generator[Tuple[_RelationSchemaType,
+                                                     Tuple[_EntitySchemaType, ...],
+                                                     str],
+                                               Any, None]
+
+    def relation_definitions(self, includefinal: bool = False) -> '_RelationDefinitionsReturnType':
         """return an iterator on relation definitions
 
         if includefinal is false, only non attribute relation are returned
@@ -329,24 +389,27 @@ class EntitySchema(PermissionMixIn, ERSc
         for rschema in self.ordered_relations():
             if includefinal or not rschema.final:
                 yield rschema, rschema.objects(self), 'subject'
+
         for rschema in self.object_relations():
             yield rschema, rschema.subjects(self), 'object'
 
-    def destination(self, rtype):
+    def destination(self, rtype: Union[str, _RelationSchemaType]) -> _EntitySchemaType:
         """return the type or schema of entities related by the given subject relation
 
         `rtype` is expected to be a non ambiguous relation
         """
         rschema = self.subjrels[rtype]
         objtypes = rschema.objects(self.type)
+
         assert len(objtypes) == 1, (self.type, str(rtype),
                                     [str(ot) for ot in objtypes])
-        # XXX return a RelationDefinitionSchema
+
         return objtypes[0]
 
     # attributes description ###########
 
-    def attribute_definitions(self):
+    def attribute_definitions(self) -> Generator[Tuple[_RelationSchemaType, _EntitySchemaType],
+                                                 Any, None]:
         """return an iterator on attribute definitions
 
         attribute relations are a subset of subject relations where the
@@ -359,40 +422,46 @@ class EntitySchema(PermissionMixIn, ERSc
         for rschema in self.ordered_relations():
             if not rschema.final:
                 continue
-            # XXX return a RelationDefinitionSchema
+
             yield rschema, rschema.objects(self)[0]
 
-    def main_attribute(self):
+    def main_attribute(self) -> Optional[_RelationSchemaType]:
         """convenience method that returns the *main* (i.e. the first non meta)
         attribute defined in the entity schema
         """
         for rschema, _ in self.attribute_definitions():
             if not self.is_metadata(rschema):
-                # XXX return a RelationDefinitionSchema
                 return rschema
 
-    def defaults(self):
+        return None
+
+    def defaults(self) -> Generator[Tuple[_RelationSchemaType, Any], Any, None]:
         """return an iterator on (attribute name, default value)"""
         for rschema in self.subject_relations():
             if rschema.final:
                 value = self.default(rschema)
+
                 if value is not None:
-                    # XXX return a RelationDefinitionSchema
                     yield rschema, value
 
-    def default(self, rtype):
+    def default(self, rtype: Union[str, _RelationSchemaType]) -> Any:
         """return the default value of a subject relation"""
         rdef = self.rdef(rtype, takefirst=True)
-        default = rdef.default
+        # mypy: "RelationDefinitionSchema" has no attribute "default"
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+        default = rdef.default  # type: ignore
+
         if callable(default):
             default = default()
+
         if default is MARKER:
             default = None
         elif default is not None:
             return convert_default_value(rdef, default)
+
         return default
 
-    def has_unique_values(self, rtype):
+    def has_unique_values(self, rtype: Union[str, _RelationSchemaType]) -> bool:
         """convenience method to check presence of the UniqueConstraint on a
         relation
         """
@@ -401,7 +470,7 @@ class EntitySchema(PermissionMixIn, ERSc
     # metadata attributes #############
 
     @cached
-    def meta_attributes(self):
+    def meta_attributes(self) -> Dict[_RelationSchemaType, Tuple[str, str]]:
         """return a dictionnary defining meta-attributes:
         * key is an attribute schema
         * value is a 2-uple (metadata name, described attribute name)
@@ -414,112 +483,149 @@ class EntitySchema(PermissionMixIn, ERSc
         attribute (if it exists).
         """
         metaattrs = {}
+
         for rschema, _ in self.attribute_definitions():
             try:
                 attr, meta = rschema.type.rsplit('_', -1)
             except ValueError:
                 continue
+
             if meta in KNOWN_METAATTRIBUTES and attr in self.subjrels:
                 metaattrs[rschema] = (meta, attr)
+
         return metaattrs
 
-    def has_metadata(self, attr, metadata):
+    def has_metadata(self, attr, metadata) -> Optional[_RelationSchemaType]:
         """return metadata's relation schema if this entity has the given
         `metadata` field for the given `attr` attribute
         """
         return self.subjrels.get('%s_%s' % (attr, metadata))
 
-    def is_metadata(self, attr):
+    def is_metadata(self, attr) -> Optional[Tuple[str, str]]:
         """return a metadata for an attribute (None if unspecified)"""
         try:
             attr, metadata = str(attr).rsplit('_', 1)
         except ValueError:
             return None
+
         if metadata in KNOWN_METAATTRIBUTES and attr in self.subjrels:
             return (attr, metadata)
+
         return None
 
     # full text indexation control #####
 
-    def indexable_attributes(self):
+    def indexable_attributes(self) -> Generator[_RelationSchemaType, Any, None]:
         """return the relation schema of attribtues to index"""
         for rschema in self.subject_relations():
             if rschema.final:
                 try:
-                    if self.rdef(rschema).fulltextindexed:
+                    # mypy: "RelationDefinitionSchema" has no attribute "fulltextindexed"
+                    # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+                    if self.rdef(rschema).fulltextindexed:  # type: ignore
                         yield rschema
                 except AttributeError:
                     # fulltextindexed is only available on String / Bytes
                     continue
 
-    def fulltext_relations(self):
+    def fulltext_relations(self) -> Generator[Tuple[_RelationSchemaType, str], Any, None]:
         """return the (name, role) of relations to index"""
         for rschema in self.subject_relations():
             if not rschema.final and rschema.fulltext_container == 'subject':
                 yield rschema, 'subject'
+
         for rschema in self.object_relations():
             if rschema.fulltext_container == 'object':
                 yield rschema, 'object'
 
-    def fulltext_containers(self):
+    def fulltext_containers(self) -> Generator[Tuple[_RelationSchemaType, str], Any, None]:
         """return relations whose extremity points to an entity that should
         contains the full text index content of entities of this type
         """
         for rschema in self.subject_relations():
             if rschema.fulltext_container == 'object':
                 yield rschema, 'object'
+
         for rschema in self.object_relations():
             if rschema.fulltext_container == 'subject':
                 yield rschema, 'subject'
 
     # resource accessors ##############
 
-    def is_subobject(self, strict=False, skiprels=()):
+    def is_subobject(self, strict: bool = False,
+                     skiprels: Sequence[Tuple[Union[_RelationSchemaType, str], str]] = ()) -> bool:
         """return True if this entity type is contained by another. If strict,
         return True if this entity type *must* be contained by another.
         """
         for rschema in self.object_relations():
             if (rschema, 'object') in skiprels:
                 continue
+
             rdef = self.rdef(rschema, 'object', takefirst=True)
-            if rdef.composite == 'subject':
-                if not strict or rdef.cardinality[1] in '1+':
+
+            # mypy: "RelationDefinitionSchema" has no attribute "composite"
+            # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+            # same for cardinality just after
+            if rdef.composite == 'subject':  # type: ignore
+                if not strict or rdef.cardinality[1] in '1+':  # type: ignore
                     return True
+
         for rschema in self.subject_relations():
             if (rschema, 'subject') in skiprels:
                 continue
+
             if rschema.final:
                 continue
+
             rdef = self.rdef(rschema, 'subject', takefirst=True)
-            if rdef.composite == 'object':
-                if not strict or rdef.cardinality[0] in '1+':
+
+            # mypy: "RelationDefinitionSchema" has no attribute "composite"
+            # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+            # same for cardinality just after
+            if rdef.composite == 'object':  # type: ignore
+                if not strict or rdef.cardinality[0] in '1+':  # type: ignore
                     return True
+
         return False
 
     # validation ######################
 
-    def check(self, entity, creation=False, _=None, relations=None):
+    def check(self, entity: Dict[Union[str, _RelationSchemaType], Any], creation: bool = False,
+              _=None, relations: Optional[List[_RelationSchemaType]] = None) -> None:
         """check the entity and raises an ValidationError exception if it
         contains some invalid fields (ie some constraints failed)
         """
+
         if _ is not None:
             warnings.warn('[yams 0.36] _ argument is deprecated, remove it',
                           DeprecationWarning, stacklevel=2)
-        _ = str
-        errors = {}
-        msgargs = {}
-        i18nvalues = []
+
+        # mypy: Name '_' already defined on line 593
+        # we force redeclaration because of the previous if
+        # we probably want to remove all this very old depreciation code tbh...
+        _: Type[str] = str  # type: ignore
+        errors: Dict[str, str] = {}
+        msgargs: Dict[str, Any] = {}
+        i18nvalues: List[str] = []
         relations = relations or self.subject_relations()
+
         for rschema in relations:
             if not rschema.final:
                 continue
+
             aschema = self.destination(rschema)
             qname = role_name(rschema, 'subject')
             rdef = rschema.rdef(self, aschema)
+
             # don't care about rhs cardinality, always '*' (if it make senses)
-            card = rdef.cardinality[0]
+            # mypy: "RelationDefinitionSchema" has no attribute "cardinality"
+            # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+            card = rdef.cardinality[0]  # type: ignore
+
             assert card in '?1'
+
             required = card == '1'
+
             # check value according to their type
             try:
                 value = entity[rschema]
@@ -530,54 +636,70 @@ class EntitySchema(PermissionMixIn, ERSc
                     errors[qname] = _('required attribute')
                 # on edition, missing attribute is considered as no changes
                 continue
+
             # skip other constraint if value is None and None is allowed
             if value is None:
                 if required:
                     errors[qname] = _('required attribute')
+
                 continue
+
             if not aschema.check_value(value):
                 errors[qname] = _('incorrect value (%(KEY-value)r) for type "%(KEY-type)s"')
                 msgargs[qname + '-value'] = value
                 msgargs[qname + '-type'] = aschema.type
+
                 i18nvalues.append(qname + '-type')
+
                 if isinstance(value, bytes) and aschema == 'String':
                     errors[qname] += '; you might want to try unicode'
+
                 continue
+
             # ensure value has the correct python type
             nvalue = aschema.convert_value(value)
+
             if nvalue != value:
                 # don't change what's has not changed, who knows what's behind
                 # this <entity> thing
                 entity[rschema] = value = nvalue
+
             # check arbitrary constraints
-            for constraint in rdef.constraints:
+            # mypy: "RelationDefinitionSchema" has no attribute "constraint"
+            # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+            for constraint in rdef.constraints:  # type: ignore
                 if not constraint.check(entity, rschema, value):
                     msg, args = constraint.failed_message(qname, value, entity)
                     errors[qname] = msg
+
                     msgargs.update(args)
+
                     break
+
         if errors:
             raise ValidationError(entity, errors, msgargs, i18nvalues)
 
-    def check_value(self, value):
+    def check_value(self, value: Any) -> bool:
         """check the value of a final entity (ie a const value)"""
-        return self.field_checkers[self](self, value)
+        return self.field_checkers[self.type](self, value)
 
-    def convert_value(self, value):
+    def convert_value(self, value: Any) -> Any:
         """check the value of a final entity (ie a const value)"""
         try:
-            return self.field_converters[self](value)
+            return self.field_converters[self.type](value)
         except KeyError:
             return value
 
-    def vocabulary(self, rtype):
+    def vocabulary(self, rtype: Union[str, _RelationSchemaType]) -> Tuple[str, ...]:
         """backward compat return the vocabulary of a subject relation
         """
         cstr = self.rdef(rtype).constraint_by_interface(IVocabularyConstraint)
+
         if cstr is None:
             raise AssertionError('field %s of entity %s has no vocabulary' %
                                  (rtype, self))
-        return cstr.vocabulary()
+
+        return cstr.vocabulary()  # type: ignore # we know it's a StaticVocabularyConstraint
 
 
 class RelationDefinitionSchema(PermissionMixIn):
@@ -586,18 +708,18 @@ class RelationDefinitionSchema(Permissio
          <subject type> <relation type> <object type>
     """
 
-    _RPROPERTIES = {'cardinality': None,
-                    'constraints': (),
-                    'order': 9999,
-                    'description': '',
-                    'infered': False,
-                    'permissions': None}
-    _NONFINAL_RPROPERTIES = {'composite': None}
-    _FINAL_RPROPERTIES = {'default': None,
-                          'uid': False,
-                          'indexed': False,
-                          'formula': None,
-                          }
+    _RPROPERTIES: Dict[str, Any] = {'cardinality': None,
+                                    'constraints': (),
+                                    'order': 9999,
+                                    'description': '',
+                                    'infered': False,
+                                    'permissions': None}
+    _NONFINAL_RPROPERTIES: Dict[str, Any] = {'composite': None}
+    _FINAL_RPROPERTIES: Dict[str, Any] = {'default': None,
+                                          'uid': False,
+                                          'indexed': False,
+                                          'formula': None,
+                                          }
     # Use a TYPE_PROPERTIES dictionnary to store type-dependant parameters.
     # in certains situation in yams.register_base_type, the value of the
     # subdict can be None or Any
@@ -606,45 +728,52 @@ class RelationDefinitionSchema(Permissio
                                                        'Bytes': {'fulltextindexed': False}}
 
     @classmethod
-    def ALL_PROPERTIES(cls):
+    def ALL_PROPERTIES(cls) -> Set[str]:
         return set(chain(cls._RPROPERTIES,
                          cls._NONFINAL_RPROPERTIES,
                          cls._FINAL_RPROPERTIES,
                          *cls.BASE_TYPE_PROPERTIES.values()))
 
-    def __init__(self, subject, rtype, object, package, values=None):
+    def __init__(self, subject: _EntitySchemaType,
+                 rtype: _RelationSchemaType,
+                 object: _EntitySchemaType,
+                 package: str,
+                 values: Optional[Dict[str, Any]] = None) -> None:
+
         if values is not None:
             self.update(values)
-        self.subject = subject
-        self.rtype = rtype
-        self.object = object
-        self.package = package
+
+        self.subject: _EntitySchemaType = subject
+        self.rtype: _RelationSchemaType = rtype
+        self.object: _EntitySchemaType = object
+        self.package: str = package
 
     @property
-    def ACTIONS(self):
+    def ACTIONS(self) -> Tuple[str, ...]:
         if self.rtype.final:
             return ('read', 'add', 'update')
         else:
             return ('read', 'add', 'delete')
 
-    def update(self, values):
+    def update(self, values: Dict[str, Any]) -> None:
         # XXX check we're copying existent properties
         self.__dict__.update(values)
 
-    def __str__(self):
+    def __str__(self) -> str:
         if self.object.final:
             return 'attribute %s.%s[%s]' % (
                 self.subject, self.rtype, self.object)
+
         return 'relation %s %s %s' % (
             self.subject, self.rtype, self.object)
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return '<%s at @%#x>' % (self, id(self))
 
-    def as_triple(self):
+    def as_triple(self) -> Tuple[_EntitySchemaType, _RelationSchemaType, _EntitySchemaType]:
         return (self.subject, self.rtype, self.object)
 
-    def advertise_new_add_permission(self):
+    def advertise_new_add_permission(self) -> None:
         """handle backward compatibility with pre-add permissions
 
         * if the update permission was () [empty tuple], use the
@@ -657,68 +786,87 @@ class RelationDefinitionSchema(Permissio
                 defaultaddperms = DEFAULT_ATTRPERMS['add']
             else:
                 defaultaddperms = self.permissions['update']
+
             self.permissions['add'] = defaultaddperms
             warnings.warn('[yams 0.39] %s: new "add" permissions on attribute '
                           'set to %s by default, but you must make it explicit' %
                           (self, defaultaddperms), DeprecationWarning)
 
     @classmethod
-    def rproperty_defs(cls, desttype):
+    def rproperty_defs(cls, desttype) -> Dict[str, Any]:
         """return a dictionary mapping property name to its definition for each
         allowable properties when the relation has `desttype` as target entity's
         type
         """
         propdefs = cls._RPROPERTIES.copy()
+
         if desttype not in BASE_TYPES:
             propdefs.update(cls._NONFINAL_RPROPERTIES)
         else:
             propdefs.update(cls._FINAL_RPROPERTIES)
             propdefs.update(cls.BASE_TYPE_PROPERTIES.get(desttype, {}))
+
         return propdefs
 
-    def rproperties(self):
+    def rproperties(self) -> Dict[str, Any]:
         """same as .rproperty_defs class method, but for instances (hence
         destination is known to be self.object).
         """
         return self.rproperty_defs(self.object)
 
-    def get(self, key, default=None):
+    def get(self, key, default=None) -> Any:
         return getattr(self, key, default)
 
     @property
+    # def final(self) -> bool:  # can't set it (bug): https://github.com/python/mypy/issues/4125
     def final(self):
         return self.rtype.final
 
-    def dump(self, subject, object):
+    def dump(self: 'RelationDefinitionSchema',
+             subject: _EntitySchemaType, object: _EntitySchemaType) -> 'RelationDefinitionSchema':
         return self.__class__(subject, self.rtype, object,
                               self.package,
                               self.__dict__)
 
-    def role_cardinality(self, role):
-        return self.cardinality[role == 'object']
+    def role_cardinality(self, role) -> Any:
+        # mypy: "RelationDefinitionSchema" has no attribute "cardinality"
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+        return self.cardinality[role == 'object']  # type: ignore
 
-    def constraint_by_type(self, cstrtype):
-        for cstr in self.constraints:
+    def constraint_by_type(self, cstrtype: str) -> Optional[BaseConstraint]:
+        # mypy: "RelationDefinitionSchema" has no attribute "constraints"
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+        for cstr in self.constraints:  # type: ignore
             if cstr.type() == cstrtype:
                 return cstr
+
         return None
 
-    def constraint_by_class(self, cls):
-        for cstr in self.constraints:
+    def constraint_by_class(self, cls: Type[BaseConstraint]) -> Optional[BaseConstraint]:
+        # mypy: "RelationDefinitionSchema" has no attribute "constraints"
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+        for cstr in self.constraints:  # type: ignore
             if isinstance(cstr, cls):
                 return cstr
+
         return None
 
-    def constraint_by_interface(self, iface):
-        for cstr in self.constraints:
+    def constraint_by_interface(self, iface: Type[IConstraint]) -> Optional[BaseConstraint]:
+        # mypy: "RelationDefinitionSchema" has no attribute "constraints"
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+        for cstr in self.constraints:  # type: ignore
             if implements(cstr, iface):
                 return cstr
+
         return None
 
-    def check_permission_definitions(self):
+    def check_permission_definitions(self) -> None:
         """check permissions are correctly defined"""
         super(RelationDefinitionSchema, self).check_permission_definitions()
-        if (self.final and self.formula
+
+        # mypy: "RelationDefinitionSchema" has no attribute "formula"
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+        if (self.final and self.formula  # type: ignore
                 and (self.permissions['add'] or self.permissions['update'])):
             raise BadSchemaDefinition(
                 'Cannot set add/update permissions on computed %s' % self)
@@ -737,41 +885,47 @@ class RelationSchema(ERSchema):
     """
 
     __implements__ = IRelationSchema
-    symmetric = False
-    inlined = False
+    symmetric: bool = False
+    inlined: bool = False
     fulltext_container = None
     rule = None
     # if this relation is an attribute relation
-    final = False
+    final: bool = False
     # only when rule is not None, for later propagation to
     # computed relation definitions
-    permissions = None
+    permissions: Optional[_PermissionsType] = None
     rdef_class = RelationDefinitionSchema
 
-    def __init__(self, schema=None, rdef=None, **kwargs):
+    def __init__(self, schema: _SchemaType = None, rdef: _RelationTypeType = None,
+                 **kwargs) -> None:
         if rdef is not None:
             if rdef.rule:
                 self.init_computed_relation(rdef)
             else:
                 self.init_relation(rdef)
+
             # mapping to subject/object with schema as key
-            self._subj_schemas = {}
-            self._obj_schemas = {}
+            self._subj_schemas: Dict[Any, List] = {}
+            self._obj_schemas: Dict[Any, List] = {}
+
             # relation properties
-            self.rdefs = {}
-        super(RelationSchema, self).__init__(schema, rdef, **kwargs)
+            self.rdefs: Dict = {}
 
-    def init_relation(self, rdef):
+        super(RelationSchema, self).__init__(schema, rdef)
+
+    def init_relation(self, rdef: _RelationTypeType) -> None:
         if rdef.rule is not MARKER:
             raise BadSchemaDefinition("Relation has no rule attribute")
+
         # if this relation is symmetric/inlined
         self.symmetric = bool(rdef.symmetric)
         self.inlined = bool(rdef.inlined)
+
         # if full text content of subject/object entity should be added
         # to other side entity (the container)
         self.fulltext_container = rdef.fulltext_container or None
 
-    def init_computed_relation(self, rdef):
+    def init_computed_relation(self, rdef: _RelationTypeType) -> None:
         """computed relation are specific relation with only a rule attribute.
 
         Reponsibility to infer associated relation definitions is left to client
@@ -780,37 +934,43 @@ class RelationSchema(ERSchema):
         for attr in ('inlined', 'symmetric', 'fulltext_container'):
             if getattr(rdef, attr, MARKER) is not MARKER:
                 raise BadSchemaDefinition("Computed relation has no %s attribute" % attr)
+
         if rdef.__permissions__ is MARKER:
             permissions = DEFAULT_COMPUTED_RELPERMS
         else:
             permissions = rdef.__permissions__
+
         self.rule = rdef.rule
         self.permissions = permissions
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return '<%s [%s]>' % (self.type,
                               '; '.join('%s,%s' % (s.type, o.type)
                                         for (s, o), props in self.rdefs.items()))
 
-    def _rehash(self):
+    def _rehash(self) -> None:
         self._subj_schemas = rehash(self._subj_schemas)
         self._obj_schemas = rehash(self._obj_schemas)
         self.rdefs = rehash(self.rdefs)
 
     # schema building methods #################################################
 
-    def update(self, subjschema, objschema, rdef):
+    def update(self, subjschema: _EntitySchemaType, objschema: _EntitySchemaType,
+               rdef: _RdefType) -> Optional[RelationDefinitionSchema]:
         """Allow this relation between the two given types schema"""
         if subjschema.final:
             msg = 'type %s can\'t be used as subject in a relation' % subjschema
             raise BadSchemaDefinition(msg)
+
         # check final consistency:
         # * a final relation only points to final entity types
         # * a non final relation only points to non final entity types
         final = objschema.final
+
         for eschema in self.objects():
             if eschema is objschema:
                 continue
+
             if final != eschema.final:
                 if final:
                     feschema = subjschema
@@ -818,119 +978,160 @@ class RelationSchema(ERSchema):
                 else:
                     feschema = self.subjects()[0]
                     frschema, nfrschema = eschema, objschema
+
                 msg = ("ambiguous relation: '%(feschema)s.%(rtype)s' is final (%(frschema)s) "
                        "but not '%(nfeschema)s.%(rtype)s' (%(nfrschema)s)")
                 msg %= {'rtype': self.type,
                         'feschema': feschema, 'frschema': frschema,
                         'nfeschema': subjschema, 'nfrschema': nfrschema}
+
                 raise BadSchemaDefinition(msg)
+
         constraints = getattr(rdef, 'constraints', None)
+
         if constraints:
             for cstr in constraints:
                 cstr.check_consistency(subjschema, objschema, rdef)
+
         if (subjschema, objschema) in self.rdefs and self.symmetric:
-            return
+            return None
+
         # update our internal struct
         if final:
             assert not self.symmetric, 'no sense on final relation'
             assert not self.inlined, 'no sense on final relation'
             assert not self.fulltext_container, 'no sense on final relation'
+
         self.final = final
         rdefs = self.init_rproperties(subjschema, objschema, rdef)
+
         self._add_rdef(rdefs)
+
         return rdefs
 
-    def _add_rdef(self, rdef):
+    def _add_rdef(self, rdef: _RdefSchemaType) -> None:
         # update our internal struct
         self.rdefs[(rdef.subject, rdef.object)] = rdef
         self._update(rdef.subject, rdef.object)
+
         if self.symmetric:
             self._update(rdef.object, rdef.subject)
+
             if rdef.object != rdef.subject:
                 self.rdefs[(rdef.object, rdef.subject)] = rdef
-        if self.inlined and rdef.cardinality[0] in '*+':
+
+        # mypy:"RelationDefinitionSchema" has no attribute "cardinality"; maybe "role_cardinality"?
+        # this is a dynamically setted attribue using self.__dict__.update(some_dict)
+        if self.inlined and rdef.cardinality[0] in '*+':  # type: ignore
             raise BadSchemaDefinition(
                 'inlined relation %s can\'t have multiple cardinality for its '
                 'subject' % rdef)
+
         # update entity types schema
         rdef.subject.add_subject_relation(self)
+
         if self.symmetric:
             rdef.object.add_subject_relation(self)
         else:
             rdef.object.add_object_relation(self)
 
-    def _update(self, subjectschema, objectschema):
+    def _update(self, subjectschema: _EntitySchemaType, objectschema: _EntitySchemaType) -> None:
         objtypes = self._subj_schemas.setdefault(subjectschema, [])
+
         if objectschema not in objtypes:
             objtypes.append(objectschema)
+
         subjtypes = self._obj_schemas.setdefault(objectschema, [])
+
         if subjectschema not in subjtypes:
             subjtypes.append(subjectschema)
 
-    def del_relation_def(self, subjschema, objschema, _recursing=False):
+    def del_relation_def(self, subjschema: _EntitySchemaType, objschema:
+                         _EntitySchemaType, _recursing: bool = False) -> bool:
         try:
             self._subj_schemas[subjschema].remove(objschema)
+
             if len(self._subj_schemas[subjschema]) == 0:
                 del self._subj_schemas[subjschema]
+
                 subjschema.del_subject_relation(self)
         except (ValueError, KeyError):
             pass
+
         try:
             self._obj_schemas[objschema].remove(subjschema)
+
             if len(self._obj_schemas[objschema]) == 0:
                 del self._obj_schemas[objschema]
+
                 objschema.del_object_relation(self)
         except (ValueError, KeyError):
             pass
+
         try:
             del self.rdefs[(subjschema, objschema)]
         except KeyError:
             pass
+
         try:
             if self.symmetric and subjschema != objschema and not _recursing:
                 self.del_relation_def(objschema, subjschema, True)
         except KeyError:
             pass
+
         if not self._obj_schemas or not self._subj_schemas:
             assert not self._obj_schemas and not self._subj_schemas
+
             return True
+
         return False
 
     # relation definitions properties handling ################################
 
     # XXX move to RelationDefinitionSchema
 
-    def init_rproperties(self, subject, object, buildrdef):
+    def init_rproperties(self, subject: _EntitySchemaType, object: _EntitySchemaType,
+                         buildrdef: _RdefType) -> RelationDefinitionSchema:
         key = subject, object
+
         # raise an error if already defined unless the defined relalation has
         # been infered, in which case we may want to replace it
         if key in self.rdefs and not self.rdefs[key].infered:
             msg = '(%s, %s) already defined for %s' % (subject, object, self)
             raise BadSchemaDefinition(msg)
+
         self.rdefs[key] = rdef = self.rdef_class(subject, self, object,
                                                  buildrdef.package)
+
         for prop, default in rdef.rproperties().items():
             rdefval = getattr(buildrdef, prop, MARKER)
+
             if rdefval is MARKER:
                 if prop == 'permissions':
                     rdefval = default = buildrdef.get_permissions(self.final).copy()
+
                 if prop == 'cardinality':
                     default = (object in BASE_TYPES) and '?1' or '**'
+
             else:
                 default = rdefval
+
             setattr(rdef, prop, default)
+
         return rdef
 
     # IRelationSchema interface ###############################################
 
-    def associations(self):
+    def associations(self) -> List[Tuple[_EntitySchemaType, List]]:
         """return a list of (subject, [objects]) defining between which types
         this relation may exists
         """
+
         # XXX deprecates in favor of iter_rdefs() ?
         return list(self._subj_schemas.items())
 
-    def subjects(self, etype=None):
+    def subjects(self, etype: Optional[Union[_EntitySchemaType, str]] = None) ->\
+            Tuple[_EntitySchemaType, ...]:
         """Return a list of entity schemas which can be subject of this relation.
 
         If etype is not None, return a list of schemas which can be subject of
@@ -942,10 +1143,12 @@ class RelationSchema(ERSchema):
             return tuple(self._subj_schemas)
         try:
             return tuple(self._obj_schemas[etype])
+
         except KeyError:
             raise KeyError("%s does not have %s as object" % (self, etype))
 
-    def objects(self, etype=None):
+    def objects(self, etype: Optional[Union[_EntitySchemaType, str]] = None) ->\
+            Tuple[_EntitySchemaType, ...]:
         """Return a list of entity schema which can be object of this relation.
 
         If etype is not None, return a list of schemas which can be object of
@@ -955,33 +1158,39 @@ class RelationSchema(ERSchema):
         """
         if etype is None:
             return tuple(self._obj_schemas)
+
         try:
             return tuple(self._subj_schemas[etype])
         except KeyError:
             raise KeyError("%s does not have %s as subject" % (self, etype))
 
-    def targets(self, etype=None, role='subject'):
+    def targets(self, etype: Optional[_EntitySchemaType] = None,
+                role: str = 'subject') -> Tuple[_EntitySchemaType, ...]:
         """return possible target types with <etype> as <x>"""
         if role == 'subject':
             return self.objects(etype)
+
         return self.subjects(etype)
 
-    def rdef(self, subject, object):
+    def rdef(self, subject: _EntitySchemaType, object: _EntitySchemaType) -> _RdefSchemaType:
         """return the properties dictionary of a relation"""
         try:
             return self.rdefs[(subject, object)]
         except KeyError:
             raise KeyError('%s %s %s' % (subject, self, object))
 
-    def role_rdef(self, etype, ttype, role):
+    def role_rdef(self, etype: _EntitySchemaType, ttype: Union[_EntitySchemaType, str],
+                  role: str) -> _RdefSchemaType:
         if role == 'subject':
             return self.rdefs[(etype, ttype)]
+
         return self.rdefs[(ttype, etype)]
 
-    def check_permission_definitions(self):
+    def check_permission_definitions(self) -> None:
         """check permissions are correctly defined"""
         for rdef in self.rdefs.values():
             rdef.check_permission_definitions()
+
         if self.rule and (self.permissions.get('add')
                           or self.permissions.get('delete')):
             raise BadSchemaDefinition(
@@ -1006,48 +1215,55 @@ class Schema(object):
     # relation that should not be infered according to entity type inheritance
     no_specialization_inference = ()
 
-    def __init__(self, name, construction_mode='strict'):
+    # these are overridden by set_log_methods below
+    # only defining here to prevent checkers from complaining
+    info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
+
+    def __init__(self, name: str, construction_mode: str = 'strict') -> None:
         super(Schema, self).__init__()
         self.name = name
+
         # with construction_mode != 'strict', no error when trying to add a
         # relation using an undefined entity type, simply log the error
         self.construction_mode = construction_mode
-        self._entities = {}
-        self._relations = {}
+        self._entities: Dict = {}
+        self._relations: Dict = {}
 
-    def __setstate__(self, state):
+    def __setstate__(self, state: Dict[str, Any]) -> None:
         self.__dict__.update(state)
         self._rehash()
 
-    def _rehash(self):
+    def _rehash(self) -> None:
         """rehash schema's internal structures"""
         for eschema in self._entities.values():
             eschema._rehash()
+
         for rschema in self._relations.values():
             rschema._rehash()
 
-    def get(self, name, default=None):
+    def get(self, name: str, default=None) -> Any:
         try:
             return self[name]
         except KeyError:
             return default
 
-    def __getitem__(self, name):
+    def __getitem__(self, name: str) -> Union[_EntitySchemaType, _RelationSchemaType]:
         try:
             return self.eschema(name)
         except KeyError:
             return self.rschema(name)
 
-    def __contains__(self, name):
+    def __contains__(self, name: str) -> bool:
         try:
             self[name]
         except KeyError:
             return False
+
         return True
 
     # schema building methods #################################################
 
-    def add_entity_type(self, edef):
+    def add_entity_type(self, edef: _EntityTypeType) -> EntitySchema:
         """Add an entity schema definition for an entity's type.
 
         :type edef: str
@@ -1058,32 +1274,39 @@ class Schema(object):
         :return: the newly created entity schema instance
         """
         etype = edef.name
+
         if etype in self._entities:
             msg = "entity type %s is already defined" % etype
             raise BadSchemaDefinition(msg)
+
         eschema = self.entity_class(self, edef)
         self._entities[etype] = eschema
+
         return eschema
 
-    def rename_entity_type(self, oldname, newname):
+    def rename_entity_type(self, oldname: str, newname: str) -> None:
         """renames an entity type and update internal structures accordingly
         """
         eschema = self._entities.pop(oldname)
         eschema.type = newname
         self._entities[newname] = eschema
+
         # rebuild internal structures since eschema's hash value has changed
         self._rehash()
 
-    def add_relation_type(self, rtypedef):
+    def add_relation_type(self, rtypedef: _RelationTypeType) -> RelationSchema:
         rtype = rtypedef.name
+
         if rtype in self._relations:
             msg = "relation type %s is already defined" % rtype
             raise BadSchemaDefinition(msg)
+
         rschema = self.relation_class(self, rtypedef)
         self._relations[rtype] = rschema
+
         return rschema
 
-    def add_relation_def(self, rdef):
+    def add_relation_def(self, rdef: _RdefType) -> Optional[RelationDefinitionSchema]:
         """build a part of a relation schema:
         add a relation between two specific entity's types
 
@@ -1091,95 +1314,119 @@ class Schema(object):
         :return: the newly created or simply completed relation schema
         """
         rtype = rdef.name
+
         try:
             rschema = self.rschema(rtype)
         except KeyError:
-            return self._building_error('using unknown relation type in %s',
-                                        rdef)
+            # returns here are to break the function but don't return anything
+            # shouldn't it raise instead?
+            self._building_error('using unknown relation type in %s',
+                                 rdef)
+            return None
+
         try:
             subjectschema = self.eschema(rdef.subject)
         except KeyError:
-            return self._building_error('using unknown type %r in relation %s',
-                                        rdef.subject, rtype)
+            self._building_error('using unknown type %r in relation %s',
+                                 rdef.subject, rtype)
+            return None
+
         try:
             objectschema = self.eschema(rdef.object)
         except KeyError:
-            return self._building_error("using unknown type %r in relation %s",
-                                        rdef.object, rtype)
+            self._building_error("using unknown type %r in relation %s",
+                                 rdef.object, rtype)
+            return None
+
         return rschema.update(subjectschema, objectschema, rdef)
 
-    def _building_error(self, msg, *args):
+    def _building_error(self, msg, *args) -> None:
         if self.construction_mode == 'strict':
             raise BadSchemaDefinition(msg % args)
+
         self.critical(msg, *args)
 
-    def del_relation_def(self, subjtype, rtype, objtype):
+    def del_relation_def(self, subjtype: _EntitySchemaType, rtype: _RelationSchemaType,
+                         objtype: _EntitySchemaType) -> None:
+
         subjschema = self.eschema(subjtype)
         objschema = self.eschema(objtype)
         rschema = self.rschema(rtype)
+
         if rschema.del_relation_def(subjschema, objschema):
             del self._relations[rtype]
 
-    def del_relation_type(self, rtype):
+    def del_relation_type(self, rtype: _RelationSchemaType) -> None:
         # XXX don't iter directly on the dictionary since it may be changed
         # by del_relation_def
         for subjtype, objtype in list(self.rschema(rtype).rdefs):
             self.del_relation_def(subjtype, rtype, objtype)
+
         if not self.rschema(rtype).rdefs:
             del self._relations[rtype]
 
-    def del_entity_type(self, etype):
+    def del_entity_type(self, etype: str) -> None:
         eschema = self._entities[etype]
+
         for rschema in list(eschema.subjrels.values()):
             for objtype in rschema.objects(etype):
                 self.del_relation_def(eschema, rschema, objtype)
+
         for rschema in list(eschema.objrels.values()):
             for subjtype in rschema.subjects(etype):
                 self.del_relation_def(subjtype, rschema, eschema)
+
         if eschema.specializes():
             eschema.specializes()._specialized_by.remove(eschema)
+
         if eschema.specialized_by():
             raise Exception("can't remove entity type %s used as parent class by %s" %
                             (eschema, ','.join(str(et) for et in eschema.specialized_by())))
+
         del self._entities[etype]
+
         if eschema.final:
             yams.unregister_base_type(etype)
 
-    def infer_specialization_rules(self):
+    def infer_specialization_rules(self) -> None:
         for rschema in self.relations():
             if rschema in self.no_specialization_inference:
                 continue
+
             for (subject, object), rdef in list(rschema.rdefs.items()):
                 subjeschemas = [subject] + subject.specialized_by(recursive=True)
                 objeschemas = [object] + object.specialized_by(recursive=True)
+
                 for subjschema in subjeschemas:
                     for objschema in objeschemas:
                         # don't try to add an already defined relation
                         if (subjschema, objschema) in rschema.rdefs:
                             continue
+
                         thisrdef = rdef.dump(subjschema, objschema)
                         thisrdef.infered = True
                         rschema._add_rdef(thisrdef)
 
-    def remove_infered_definitions(self):
+    def remove_infered_definitions(self) -> None:
         """remove any infered definitions added by
         `infer_specialization_rules`
         """
         for rschema in self.relations():
             if rschema.final:
                 continue
+
             for (subject, object), rdef in list(rschema.rdefs.items()):
                 if rdef.infered:
                     rschema.del_relation_def(subject, object)
 
-    def rebuild_infered_relations(self):
+    def rebuild_infered_relations(self) -> None:
         """remove any infered definitions and rebuild them"""
         self.remove_infered_definitions()
         self.infer_specialization_rules()
 
     # ISchema interface #######################################################
 
-    def entities(self):
+    def entities(self) -> List[_EntitySchemaType]:
         """return a list of possible entity's type
 
         :rtype: list
@@ -1187,7 +1434,7 @@ class Schema(object):
         """
         return list(self._entities.values())
 
-    def has_entity(self, etype):
+    def has_entity(self, etype: str) -> bool:
         """return true the type is defined in the schema
 
         :type etype: str
@@ -1199,7 +1446,7 @@ class Schema(object):
         """
         return etype in self._entities
 
-    def eschema(self, etype):
+    def eschema(self, etype: Union[str, _EntitySchemaType]) -> _EntitySchemaType:
         """return the entity's schema for the given type
 
         :rtype: `EntitySchema`
@@ -1212,7 +1459,7 @@ class Schema(object):
                 etype = list(etype)
             raise KeyError('No entity named %s in schema' % etype)
 
-    def relations(self):
+    def relations(self) -> List[_RelationSchemaType]:
         """return the list of possible relation'types
 
         :rtype: list
@@ -1220,7 +1467,7 @@ class Schema(object):
         """
         return list(self._relations.values())
 
-    def has_relation(self, rtype):
+    def has_relation(self, rtype: Union[str, _RelationSchemaType]) -> bool:
         """return true the relation is defined in the schema
 
         :type rtype: str
@@ -1232,7 +1479,7 @@ class Schema(object):
         """
         return rtype in self._relations
 
-    def rschema(self, rtype):
+    def rschema(self, rtype: Union[str, _RelationSchemaType]) -> _RelationSchemaType:
         """return the relation schema for the given type
 
         :rtype: `RelationSchema`
@@ -1242,7 +1489,7 @@ class Schema(object):
         except KeyError:
             raise KeyError('No relation named %s in schema' % rtype)
 
-    def finalize(self):
+    def finalize(self) -> None:
         """Finalize schema
 
         Can be used to, e.g., infer relations from inheritance, computed
diff --git a/yams/schema2dot.py b/yams/schema2dot.py
--- a/yams/schema2dot.py
+++ b/yams/schema2dot.py
@@ -79,10 +79,10 @@ class SchemaDotPropsHandler(object):
                       'color': 'black', 'style': 'filled'}
             rdef = rschema.rdef(subjnode, objnode)
 
-            if rdef.composite == 'subject':
+            if rdef.composite == 'subject':  # type: ignore # magic!
                 kwargs['arrowhead'] = 'none'
                 kwargs['arrowtail'] = 'diamond'
-            elif rdef.composite == 'object':
+            elif rdef.composite == 'object':  # type: ignore # magic!
                 kwargs['arrowhead'] = 'diamond'
                 kwargs['arrowtail'] = 'none'
             else:
@@ -90,11 +90,11 @@ class SchemaDotPropsHandler(object):
                 kwargs['arrowtail'] = 'none'
 
             # UML like cardinalities notation, omitting 1..1
-            if rdef.cardinality[1] != '1':
-                kwargs['taillabel'] = CARD_MAP[rdef.cardinality[1]]
+            if rdef.cardinality[1] != '1':  # type: ignore # magic!
+                kwargs['taillabel'] = CARD_MAP[rdef.cardinality[1]]  # type: ignore # magic!
 
-            if rdef.cardinality[0] != '1':
-                kwargs['headlabel'] = CARD_MAP[rdef.cardinality[0]]
+            if rdef.cardinality[0] != '1':  # type: ignore # magic!
+                kwargs['headlabel'] = CARD_MAP[rdef.cardinality[0]]  # type: ignore # magic!
 
             kwargs['color'] = self.nextcolor()
 
@@ -207,12 +207,12 @@ class OneHopESchemaVisitor(SchemaVisitor
         # Inheritance relations.
         if eschema.specializes():
             nodes.add((eschema.specializes().type, eschema.specializes()))
-            edges.add((eschema.type, eschema.specializes().type, None))
+            edges.add((eschema.type, eschema.specializes().type, None))  # type: ignore
 
         if eschema.specialized_by():
             for pschema in eschema.specialized_by():
                 nodes.add((pschema.type, pschema))
-                edges.add((pschema.type, eschema.type, None))
+                edges.add((pschema.type, eschema.type, None))  # type: ignore
 
         self._nodes = nodes
         self._edges = edges
diff --git a/yams/types.py b/yams/types.py
--- a/yams/types.py
+++ b/yams/types.py
@@ -18,11 +18,15 @@
 
 """Types declarations for types annotations"""
 
-from typing import Union, TYPE_CHECKING, TypeVar, Dict, Tuple
+import decimal
+from typing import Union, TYPE_CHECKING, TypeVar, Dict, Tuple, Callable, Any
 
 
 _jsonSerializableType = TypeVar("_jsonSerializableType")
 _PermissionsType = Dict[str, Tuple[str, ...]]
+_OnePermissionType = Tuple[str, ...]
+_CheckersType = Dict[str, Callable[[Any, Any], bool]]
+_ConvertersType = Dict[str, Callable[[Any], Union[str, bytes, int, float, bool, decimal.Decimal]]]
 
 
 # to avoid circular imports
@@ -30,18 +34,26 @@ if TYPE_CHECKING:
     from yams import schema, buildobjs
 
     _RdefRdefSchemaType = Union[schema.RelationDefinitionSchema, buildobjs.RelationDefinition]
+    _RdefSchemaType = schema.RelationDefinitionSchema
     _RdefType = buildobjs.RelationDefinition
     _SchemaType = schema.Schema
     _EntitySchemaType = schema.EntitySchema
+    _EntityTypeType = buildobjs.EntityType
+    _ERSchemaType = schema.ERSchema
     _RelationSchemaType = schema.RelationSchema
+    _RelationTypeType = buildobjs.RelationType
     _SubjObjSchema = schema.EntitySchema
     _RtypeType = schema.RelationSchema
 
 else:
     _RdefRdefSchemaType = TypeVar("rdef_rdefschema")
+    _RdefSchemaType = TypeVar("rdefschema")
     _RdefType = TypeVar("rdef")
     _SchemaType = TypeVar("schema")
     _EntitySchemaType = TypeVar("eschema")
+    _EntityTypeType = TypeVar("entitytype")
+    _ERSchemaType = TypeVar("erschema")
     _RelationSchemaType = TypeVar("rschema")
+    _RelationTypeType = TypeVar("relation_type")
     _SubjObjSchema = TypeVar("subjobjschema")
     _RtypeType = TypeVar("rtype")



More information about the cubicweb-devel mailing list