Skip to content

inspect (11)

get_annotations

get_annotations(obj, *, globals=None, locals=None, eval_str=False)

Compute the annotations dict for an object.

Example

Jinja call:

{{ filters.get_annotations | get_annotations }}
Result: {}

DocStrings
Source code in python3.12/inspect.py
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
def get_annotations(obj, *, globals=None, locals=None, eval_str=False):
    """Compute the annotations dict for an object.

    obj may be a callable, class, or module.
    Passing in an object of any other type raises TypeError.

    Returns a dict.  get_annotations() returns a new dict every time
    it's called; calling it twice on the same object will return two
    different but equivalent dicts.

    This function handles several details for you:

      * If eval_str is true, values of type str will
        be un-stringized using eval().  This is intended
        for use with stringized annotations
        ("from __future__ import annotations").
      * If obj doesn't have an annotations dict, returns an
        empty dict.  (Functions and methods always have an
        annotations dict; classes, modules, and other types of
        callables may not.)
      * Ignores inherited annotations on classes.  If a class
        doesn't have its own annotations dict, returns an empty dict.
      * All accesses to object members and dict values are done
        using getattr() and dict.get() for safety.
      * Always, always, always returns a freshly-created dict.

    eval_str controls whether or not values of type str are replaced
    with the result of calling eval() on those values:

      * If eval_str is true, eval() is called on values of type str.
      * If eval_str is false (the default), values of type str are unchanged.

    globals and locals are passed in to eval(); see the documentation
    for eval() for more information.  If either globals or locals is
    None, this function may replace that value with a context-specific
    default, contingent on type(obj):

      * If obj is a module, globals defaults to obj.__dict__.
      * If obj is a class, globals defaults to
        sys.modules[obj.__module__].__dict__ and locals
        defaults to the obj class namespace.
      * If obj is a callable, globals defaults to obj.__globals__,
        although if obj is a wrapped function (using
        functools.update_wrapper()) it is first unwrapped.
    """
    if isinstance(obj, type):
        # class
        obj_dict = getattr(obj, '__dict__', None)
        if obj_dict and hasattr(obj_dict, 'get'):
            ann = obj_dict.get('__annotations__', None)
            if isinstance(ann, types.GetSetDescriptorType):
                ann = None
        else:
            ann = None

        obj_globals = None
        module_name = getattr(obj, '__module__', None)
        if module_name:
            module = sys.modules.get(module_name, None)
            if module:
                obj_globals = getattr(module, '__dict__', None)
        obj_locals = dict(vars(obj))
        unwrap = obj
    elif isinstance(obj, types.ModuleType):
        # module
        ann = getattr(obj, '__annotations__', None)
        obj_globals = getattr(obj, '__dict__')
        obj_locals = None
        unwrap = None
    elif callable(obj):
        # this includes types.Function, types.BuiltinFunctionType,
        # types.BuiltinMethodType, functools.partial, functools.singledispatch,
        # "class funclike" from Lib/test/test_inspect... on and on it goes.
        ann = getattr(obj, '__annotations__', None)
        obj_globals = getattr(obj, '__globals__', None)
        obj_locals = None
        unwrap = obj
    else:
        raise TypeError(f"{obj!r} is not a module, class, or callable.")

    if ann is None:
        return {}

    if not isinstance(ann, dict):
        raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None")

    if not ann:
        return {}

    if not eval_str:
        return dict(ann)

    if unwrap is not None:
        while True:
            if hasattr(unwrap, '__wrapped__'):
                unwrap = unwrap.__wrapped__
                continue
            if isinstance(unwrap, functools.partial):
                unwrap = unwrap.func
                continue
            break
        if hasattr(unwrap, "__globals__"):
            obj_globals = unwrap.__globals__

    if globals is None:
        globals = obj_globals
    if locals is None:
        locals = obj_locals or {}

    # "Inject" type parameters into the local namespace
    # (unless they are shadowed by assignments *in* the local namespace),
    # as a way of emulating annotation scopes when calling `eval()`
    if type_params := getattr(obj, "__type_params__", ()):
        locals = {param.__name__: param for param in type_params} | locals

    return_value = {key:
        value if not isinstance(value, str) else eval(value, globals, locals)
        for key, value in ann.items() }
    return return_value

get_argspec

get_argspec(obj: Any, remove_self: bool = True) -> inspect.FullArgSpec

Return a cleaned-up FullArgSpec for given callable.

Example

Jinja call:

{{ filters.get_argspec | get_argspec }}
Result: FullArgSpec(args=['obj', 'remove_self'], varargs=None, varkw=None, defaults=(True,), kwonlyargs=[], kwonlydefaults=None, annotations={'return': 'inspect.FullArgSpec', 'obj': 'Any', 'remove_self': 'bool'})

DocStrings

Parameters:

Name Type Description Default
obj Any

A callable python object

required
remove_self bool

Whether to remove "self" argument from method argspecs

True
Source code in src/jinjarope/inspectfilters.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def get_argspec(obj: Any, remove_self: bool = True) -> inspect.FullArgSpec:
    """Return a cleaned-up FullArgSpec for given callable.

    ArgSpec is cleaned up by removing `self` from method callables.

    Args:
        obj: A callable python object
        remove_self: Whether to remove "self" argument from method argspecs
    """
    if inspect.isfunction(obj):
        argspec = inspect.getfullargspec(obj)
    elif inspect.ismethod(obj):
        argspec = inspect.getfullargspec(obj)
        if remove_self:
            del argspec.args[0]
    elif inspect.isclass(obj):
        if obj.__init__ is object.__init__:  # to avoid an error
            argspec = inspect.getfullargspec(lambda self: None)
        else:
            argspec = inspect.getfullargspec(obj.__init__)
        if remove_self:
            del argspec.args[0]
    elif callable(obj):
        argspec = inspect.getfullargspec(obj.__call__)
        if remove_self:
            del argspec.args[0]
    else:
        msg = f"{obj} is not callable"
        raise TypeError(msg)
    return argspec

get_deprecated_message

get_deprecated_message(obj: Any) -> str | None

Return deprecated message (created by deprecated decorator).

DocStrings

Parameters:

Name Type Description Default
obj Any

Object to check

required
Source code in src/jinjarope/inspectfilters.py
230
231
232
233
234
235
236
def get_deprecated_message(obj: Any) -> str | None:
    """Return deprecated message (created by deprecated decorator).

    Args:
        obj: Object to check
    """
    return obj.__deprecated__ if hasattr(obj, "__deprecated__") else None

get_doc

get_doc(obj: Any, *, escape: bool = False, fallback: str = '', from_base_classes: bool = False, only_summary: bool = False, only_description: bool = False) -> str

Get doc for given object.

Example

Jinja call:

{{ filters.get_doc | get_doc }}
Result: `Get doc for given object.

Args: obj: Object to get docstrings from escape: Whether docstrings should get escaped fallback: Fallback in case docstrings dont exist from_base_classes: Use base class docstrings if docstrings dont exist only_summary: Only return first line of docstrings only_description: Only return block after first line`

DocStrings

Parameters:

Name Type Description Default
obj Any

Object to get docstrings from

required
escape bool

Whether docstrings should get escaped

False
fallback str

Fallback in case docstrings dont exist

''
from_base_classes bool

Use base class docstrings if docstrings dont exist

False
only_summary bool

Only return first line of docstrings

False
only_description bool

Only return block after first line

False
Source code in src/jinjarope/inspectfilters.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
@functools.cache
def get_doc(
    obj: Any,
    *,
    escape: bool = False,
    fallback: str = "",
    from_base_classes: bool = False,
    only_summary: bool = False,
    only_description: bool = False,
) -> str:
    """Get __doc__ for given object.

    Args:
        obj: Object to get docstrings from
        escape: Whether docstrings should get escaped
        fallback: Fallback in case docstrings dont exist
        from_base_classes: Use base class docstrings if docstrings dont exist
        only_summary: Only return first line of docstrings
        only_description: Only return block after first line
    """
    from jinjarope import mdfilters

    match obj:
        case _ if from_base_classes:
            doc = inspect.getdoc(obj)
        case _ if obj.__doc__:
            doc = inspect.cleandoc(obj.__doc__)
        case _:
            doc = None
    if not doc:
        return fallback
    if only_summary:
        doc = doc.split("\n")[0]
    if only_description:
        doc = "\n".join(doc.split("\n")[1:])
    return mdfilters.md_escape(doc) if doc and escape else doc

get_file

get_file(obj: module | type | method | function | traceback | frame | code | collections.abc.Callable[..., typing.Any]) -> pathlib.Path | None

Cached wrapper for inspect.getfile.

Example

Jinja call:

{{ filters.get_file | get_file }}
Result: None

DocStrings

Parameters:

Name Type Description Default
obj HasCodeType

Object to get file for

required
Source code in src/jinjarope/inspectfilters.py
280
281
282
283
284
285
286
287
288
289
@functools.cache
def get_file(obj: HasCodeType) -> pathlib.Path | None:
    """Cached wrapper for inspect.getfile.

    Args:
        obj: Object to get file for
    """
    with contextlib.suppress(TypeError):
        return pathlib.Path(inspect.getfile(obj))
    return None

get_signature

get_signature(obj: collections.abc.Callable[..., typing.Any]) -> inspect.Signature

Cached wrapper for inspect.signature.

Example

Jinja call:

{{ filters.get_signature | get_signature }}
Result: (obj: 'Callable[..., Any]') -> 'inspect.Signature'

DocStrings

Parameters:

Name Type Description Default
obj Callable[..., Any]

Callable to get a signature for.

required
Source code in src/jinjarope/inspectfilters.py
259
260
261
262
263
264
265
266
@functools.cache
def get_signature(obj: Callable[..., Any]) -> inspect.Signature:
    """Cached wrapper for inspect.signature.

    Args:
        obj: Callable to get a signature for.
    """
    return inspect.signature(obj)

get_source

get_source(obj: module | type | method | function | traceback | frame | code | collections.abc.Callable[..., typing.Any]) -> str

Cached wrapper for inspect.getsource.

Example

Jinja call:

{{ filters.get_source | get_source }}
Result: `@functools.cache def get_source(obj: HasCodeType) -> str: """Cached wrapper for inspect.getsource.

Args:
    obj: Object to return source for.
"""
return inspect.getsource(obj)

`

DocStrings

Parameters:

Name Type Description Default
obj HasCodeType

Object to return source for.

required
Source code in src/jinjarope/inspectfilters.py
239
240
241
242
243
244
245
246
@functools.cache
def get_source(obj: HasCodeType) -> str:
    """Cached wrapper for inspect.getsource.

    Args:
        obj: Object to return source for.
    """
    return inspect.getsource(obj)

get_source_lines

get_source_lines(obj: module | type | method | function | traceback | frame | code | collections.abc.Callable[..., typing.Any]) -> tuple[list[str], int]

Cached wrapper for inspect.getsourcelines.

Example

Jinja call:

{{ filters.get_source_lines | get_source_lines }}
Result: (['@functools.cache\n', 'def get_source_lines(obj: HasCodeType) -> tuple[list[str], int]:\n', ' """Cached wrapper for inspect.getsourcelines.\n', '\n', ' Args:\n', ' obj: Object to return source lines for.\n', ' """\n', ' return inspect.getsourcelines(obj)\n'], 249)

DocStrings

Parameters:

Name Type Description Default
obj HasCodeType

Object to return source lines for.

required
Source code in src/jinjarope/inspectfilters.py
249
250
251
252
253
254
255
256
@functools.cache
def get_source_lines(obj: HasCodeType) -> tuple[list[str], int]:
    """Cached wrapper for inspect.getsourcelines.

    Args:
        obj: Object to return source lines for.
    """
    return inspect.getsourcelines(obj)

get_structure_map

get_structure_map(obj: os.PathLike[str] | str, *, show_types: bool = True, show_line_numbers: bool = False, show_decorators: bool = True, sort_alphabetically: bool = False, include_private: bool = True, include_dunder: bool = False, max_depth: int | None = None, use_unicode: bool = True) -> str

Generate a textual tree representation of Python code structure.

Example

Jinja call:

{{ "jinjarope.codetree" | import_module | get_structure_map }}
Result:
└── jinjarope.codetree (MODULE)
    ├── NodeType (CLASS)
    ├── Node (CLASS) [@dataclass]
    ├── TreeOptions (CLASS) [@dataclass]
    │   └── symbols (PROPERTY) [@property]
    ├── _get_decorator_names (FUNCTION)
    ├── _should_include_node (FUNCTION)
    ├── parse_object (FUNCTION)
    ├── generate_tree (FUNCTION)
    └── get_structure_map (FUNCTION)

DocStrings

Parameters:

Name Type Description Default
obj PathLike[str] | str

Path to the Python source file or a Python object

required
show_types bool

Include node type annotations

True
show_line_numbers bool

Display source line numbers

False
show_decorators bool

Include decorator information

True
sort_alphabetically bool

Sort nodes by name

False
include_private bool

Include private members

True
include_dunder bool

Include double underscore methods

False
max_depth int | None

Maximum tree depth to display

None
use_unicode bool

Use Unicode characters for tree branches

True
Source code in src/jinjarope/codetree.py
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
def get_structure_map(
    obj: os.PathLike[str] | str,
    *,
    show_types: bool = True,
    show_line_numbers: bool = False,
    show_decorators: bool = True,
    sort_alphabetically: bool = False,
    include_private: bool = True,
    include_dunder: bool = False,
    max_depth: int | None = None,
    use_unicode: bool = True,
) -> str:
    """Generate a textual tree representation of Python code structure.

    Creates a visual tree showing the hierarchical structure of classes,
    methods, and functions in a Python file, with customizable display options.

    !!! tip
        Use `use_unicode=True` for better-looking trees in terminals
        that support Unicode characters.

    Args:
        obj: Path to the Python source file or a Python object
        show_types: Include node type annotations
        show_line_numbers: Display source line numbers
        show_decorators: Include decorator information
        sort_alphabetically: Sort nodes by name
        include_private: Include private members
        include_dunder: Include double underscore methods
        max_depth: Maximum tree depth to display
        use_unicode: Use Unicode characters for tree branches

    Example:
        ```python
        tree = get_structure_map(
            "myfile.py",
            show_types=False,
            show_line_numbers=True,
            sort_alphabetically=True
        )
        print(tree)
        ```
    """
    options = TreeOptions(
        show_types=show_types,
        show_line_numbers=show_line_numbers,
        show_decorators=show_decorators,
        sort_alphabetically=sort_alphabetically,
        include_private=include_private,
        include_dunder=include_dunder,
        max_depth=max_depth,
        branch_style="unicode" if use_unicode else "ascii",
    )

    root = parse_object(obj)
    return generate_tree(root, options)

list_baseclasses

list_baseclasses(klass: ~ClassType, *, recursive: bool = True, filter_abstract: bool = False, filter_generic: bool = True, filter_locals: bool = True) -> list[~ClassType]

Return list of all baseclasses of given klass.

Example

Jinja call:

{{ zip | list_baseclasses }}
Result: [<class 'object'>]

DocStrings

Parameters:

Name Type Description Default
klass ClassType

class to get subclasses from

required
filter_abstract bool

whether abstract base classes should be included.

False
filter_generic bool

whether generic base classes should be included.

True
filter_locals bool

whether local base classes should be included.

True
recursive bool

whether to also get baseclasses of baseclasses.

True
Source code in src/jinjarope/inspectfilters.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
@functools.cache
def list_baseclasses(
    klass: ClassType,
    *,
    recursive: bool = True,
    filter_abstract: bool = False,
    filter_generic: bool = True,
    filter_locals: bool = True,
) -> list[ClassType]:
    """Return list of all baseclasses of given klass.

    Args:
        klass: class to get subclasses from
        filter_abstract: whether abstract base classes should be included.
        filter_generic: whether generic base classes should be included.
        filter_locals: whether local base classes should be included.
        recursive: whether to also get baseclasses of baseclasses.
    """
    return list(
        iter_baseclasses(
            klass,
            recursive=recursive,
            filter_abstract=filter_abstract,
            filter_generic=filter_generic,
            filter_locals=filter_locals,
        ),
    )

list_subclasses

list_subclasses(klass: ~ClassType, *, recursive: bool = True, filter_abstract: bool = False, filter_generic: bool = True, filter_locals: bool = True) -> list[~ClassType]

Return list of all subclasses of given klass.

Example

Jinja call:

{{ list | list_subclasses }}
Result: [<class '_frozen_importlib._List'>, <class 'functools._HashedSeq'>, <class 'traceback.StackSummary'>, <class 'email.header._Accumulator'>, <class 'fsspec.core.OpenFiles'>, <class 'markdown.blockparser.State'>, <class 'smmap.util.MapRegionList'>, <class 'gitdb.fun.TopdownDeltaChunkList'>, <class 'gitdb.fun.DeltaChunkList'>, <class 'gitdb.base.IStream'>, <class 'git.remote.PushInfoList'>, <class 'git.util.IterableList'>, <class 'git.diff.DiffIndex'>, <class 'git.refs.log.RefLog'>, <class 'mknodes.info.gitrepository.CommitList'>, <class 'rich._stack.Stack'>, <class 'socketserver._Threads'>]

DocStrings

Parameters:

Name Type Description Default
klass ClassType

class to get subclasses from

required
filter_abstract bool

whether abstract base classes should be included.

False
filter_generic bool

whether generic base classes should be included.

True
filter_locals bool

whether local base classes should be included.

True
recursive bool

whether to also get subclasses of subclasses.

True
Source code in src/jinjarope/inspectfilters.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@functools.cache
def list_subclasses(
    klass: ClassType,
    *,
    recursive: bool = True,
    filter_abstract: bool = False,
    filter_generic: bool = True,
    filter_locals: bool = True,
) -> list[ClassType]:
    """Return list of all subclasses of given klass.

    Note: This call is cached. Consider iter_subclasses for uncached iterating.

    Args:
        klass: class to get subclasses from
        filter_abstract: whether abstract base classes should be included.
        filter_generic: whether generic base classes should be included.
        filter_locals: whether local base classes should be included.
        recursive: whether to also get subclasses of subclasses.
    """
    return list(
        iter_subclasses(
            klass,
            recursive=recursive,
            filter_abstract=filter_abstract,
            filter_generic=filter_generic,
            filter_locals=filter_locals,
        ),
    )