1# sqlalchemy/inspect.py 
    2# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# 
    5# This module is part of SQLAlchemy and is released under 
    6# the MIT License: http://www.opensource.org/licenses/mit-license.php 
    7 
    8"""The inspection module provides the :func:`_sa.inspect` function, 
    9which delivers runtime information about a wide variety 
    10of SQLAlchemy objects, both within the Core as well as the 
    11ORM. 
    12 
    13The :func:`_sa.inspect` function is the entry point to SQLAlchemy's 
    14public API for viewing the configuration and construction 
    15of in-memory objects.   Depending on the type of object 
    16passed to :func:`_sa.inspect`, the return value will either be 
    17a related object which provides a known interface, or in many 
    18cases it will return the object itself. 
    19 
    20The rationale for :func:`_sa.inspect` is twofold.  One is that 
    21it replaces the need to be aware of a large variety of "information 
    22getting" functions in SQLAlchemy, such as 
    23:meth:`_reflection.Inspector.from_engine`, 
    24:func:`.orm.attributes.instance_state`, :func:`_orm.class_mapper`, 
    25and others.    The other is that the return value of :func:`_sa.inspect` 
    26is guaranteed to obey a documented API, thus allowing third party 
    27tools which build on top of SQLAlchemy configurations to be constructed 
    28in a forwards-compatible way. 
    29 
    30""" 
    31 
    32from . import exc 
    33from . import util 
    34 
    35 
    36_registrars = util.defaultdict(list) 
    37 
    38 
    39def inspect(subject, raiseerr=True): 
    40    """Produce an inspection object for the given target. 
    41 
    42    The returned value in some cases may be the 
    43    same object as the one given, such as if a 
    44    :class:`_orm.Mapper` object is passed.   In other 
    45    cases, it will be an instance of the registered 
    46    inspection type for the given object, such as 
    47    if an :class:`_engine.Engine` is passed, an 
    48    :class:`_reflection.Inspector` object is returned. 
    49 
    50    :param subject: the subject to be inspected. 
    51    :param raiseerr: When ``True``, if the given subject 
    52     does not 
    53     correspond to a known SQLAlchemy inspected type, 
    54     :class:`sqlalchemy.exc.NoInspectionAvailable` 
    55     is raised.  If ``False``, ``None`` is returned. 
    56 
    57    """ 
    58    type_ = type(subject) 
    59    for cls in type_.__mro__: 
    60        if cls in _registrars: 
    61            reg = _registrars[cls] 
    62            if reg is True: 
    63                return subject 
    64            ret = reg(subject) 
    65            if ret is not None: 
    66                break 
    67    else: 
    68        reg = ret = None 
    69 
    70    if raiseerr and (reg is None or ret is None): 
    71        raise exc.NoInspectionAvailable( 
    72            "No inspection system is " 
    73            "available for object of type %s" % type_ 
    74        ) 
    75    return ret 
    76 
    77 
    78def _inspects(*types): 
    79    def decorate(fn_or_cls): 
    80        for type_ in types: 
    81            if type_ in _registrars: 
    82                raise AssertionError( 
    83                    "Type %s is already " "registered" % type_ 
    84                ) 
    85            _registrars[type_] = fn_or_cls 
    86        return fn_or_cls 
    87 
    88    return decorate 
    89 
    90 
    91def _self_inspects(cls): 
    92    _inspects(cls)(True) 
    93    return cls