checkpointing.identifier.func_call.context

  1import inspect
  2from typing import List, Dict, Tuple, Callable, Any, Union
  3from collections import OrderedDict
  4from checkpointing._typing import ReturnValue
  5from types import FrameType
  6import copy
  7
  8
  9class FuncCallContext:
 10    """
 11    Context of information for a function call.
 12    """
 13
 14    def __init__(self, func: Callable[..., ReturnValue], args: Tuple, kwargs: Dict, def_frame: FrameType = None,) -> None:
 15        """
 16        Args:
 17            func: the function object that is being called
 18            args: the non-keywords arguments of the function call
 19            kwargs: the keyword arguments of the function call
 20            def_frame: the frame where the function is defined
 21        """
 22
 23        self.__func: Callable[..., ReturnValue] = func
 24        self.__args: Tuple = args
 25        self.__kwargs: Dict = kwargs
 26        self.__signature = inspect.signature(self.__func)
 27
 28        if def_frame is not None:
 29            self.__locals = copy.copy(def_frame.f_locals)
 30
 31        else:
 32            self.__locals = None
 33
 34    @property
 35    def arguments(self) -> Dict:
 36        """
 37        Dictionary of the function arguments and their actually applied parameter values in the function call.
 38
 39        >>> def foo(a, b=1, c=None, d=4):
 40        ...     pass
 41        >>>
 42        >>> # Equivalent to the context computed for: foo(1, 2, c=3)
 43        >>> ctx = FuncCallContext(foo, (1, 2), {"c": 3})
 44        >>>
 45        >>> dict(ctx.arguments)
 46        {'a': 1, 'b': 2, 'c': 3, 'd': 4}
 47
 48        The varargs and kwargs can also be captured:
 49
 50        >>> def bar(*args, **kwargs):
 51        ...     pass
 52        >>>
 53        >>> ctx = FuncCallContext(bar, (1, 2), {"c": 3})
 54        >>>
 55        >>> ctx.arguments
 56        {'args': (1, 2), 'kwargs': {'c': 3}}
 57        """
 58        args = self.__signature.bind(*self.__args, **self.__kwargs)
 59        args.apply_defaults()
 60        return dict(args.arguments)
 61
 62    @property
 63    def full_name(self) -> str:
 64        """
 65        The full name of the function.
 66        Specifically, the concatenation of a function's module and its [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name).
 67
 68        >>> def foo():
 69        ...     pass
 70        >>>
 71        >>> # Equivalent to the context computed for: foo()
 72        >>> ctx = FuncCallContext(foo, (), {})
 73        >>>
 74        >>> # foo is defined in checkpointing/identifier/func_call/context.py
 75        >>> ctx.full_name
 76        'checkpointing.identifier.func_call.context.foo'
 77        """
 78        return ".".join([self.module, self.qualified_name])
 79
 80    @property
 81    def module(self) -> str:
 82        """
 83        Module where the function is defined.
 84
 85        >>> def foo():
 86        ...     pass
 87        >>>
 88        >>> # Equivalent to the context computed for: foo()
 89        >>> ctx = FuncCallContext(foo, (), {})
 90        >>>
 91        >>> # foo is defined in checkpointing/identifier/func_call/context.py
 92        >>> ctx.module
 93        'checkpointing.identifier.func_call.context'
 94        """
 95
 96        return self.__func.__module__
 97
 98    @property
 99    def name(self) -> str:
100        """
101        Name of the function.
102
103        >>> def foo():
104        ...     pass
105        >>>
106        >>> # Equivalent to the context computed for: foo()
107        >>> ctx = FuncCallContext(foo, (), {})
108        >>> ctx.name
109        'foo'
110        """
111
112        return self.__func.__name__
113
114    @property
115    def qualified_name(self) -> str:
116        """
117        [Qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the function.
118
119        >>> class Foo:
120        ...     def bar(self):
121        ...         pass
122        >>>
123        >>> f = Foo()
124        >>>
125        >>> # Equivalent to the context computed for: f.bar()
126        >>> ctx = FuncCallContext(f.bar, (), {})
127        >>> ctx.qualified_name
128        'Foo.bar'
129        """
130
131        return self.__func.__qualname__
132
133    @property
134    def code(self) -> str:
135        r"""
136        The source code of the function, including the function signature,
137        formatted as-is, including any comments.
138
139        >>> def foo(a, b):
140        ...     c = a + b  # add a and b
141        ...     return c
142        >>>
143        >>> # Equivalent to the context computed for: foo(1, 2)
144        >>> ctx = FuncCallContext(foo, (1, 2), {})
145        >>>
146        >>> ctx.code
147        'def foo(a, b):\n    c = a + b  # add a and b\n    return c\n'
148        """
149
150        return inspect.getsource(self.__func)
151
152    def get_nonlocal_variable(self, varname: str) -> Any:
153        r"""
154        Try to get the nonlocal variable `varname` from the function call's
155        `locals()` and `globals()`.
156        If it doesn't exist in neither of them, return `None`.
157        If the caller_frame is not provided, the result is always `None`.
158
159        >>> import inspect
160        >>>
161        >>> a = 1
162        >>> def foo():
163        ...     pass
164        >>>
165        >>> ctx = FuncCallContext(foo, (), {}, inspect.currentframe())
166        >>> ctx.get_nonlocal_variable("a")
167        1
168        >>> ctx.get_nonlocal_variable("b") is None
169        True
170        """
171
172        if self.__locals is not None and varname in self.__locals:
173            return self.__locals[varname]
174
175        if self.__func.__globals__ is not None and varname in self.__func.__globals__:
176            return self.__func.__globals__[varname]
177
178        return None
class FuncCallContext:
 10class FuncCallContext:
 11    """
 12    Context of information for a function call.
 13    """
 14
 15    def __init__(self, func: Callable[..., ReturnValue], args: Tuple, kwargs: Dict, def_frame: FrameType = None,) -> None:
 16        """
 17        Args:
 18            func: the function object that is being called
 19            args: the non-keywords arguments of the function call
 20            kwargs: the keyword arguments of the function call
 21            def_frame: the frame where the function is defined
 22        """
 23
 24        self.__func: Callable[..., ReturnValue] = func
 25        self.__args: Tuple = args
 26        self.__kwargs: Dict = kwargs
 27        self.__signature = inspect.signature(self.__func)
 28
 29        if def_frame is not None:
 30            self.__locals = copy.copy(def_frame.f_locals)
 31
 32        else:
 33            self.__locals = None
 34
 35    @property
 36    def arguments(self) -> Dict:
 37        """
 38        Dictionary of the function arguments and their actually applied parameter values in the function call.
 39
 40        >>> def foo(a, b=1, c=None, d=4):
 41        ...     pass
 42        >>>
 43        >>> # Equivalent to the context computed for: foo(1, 2, c=3)
 44        >>> ctx = FuncCallContext(foo, (1, 2), {"c": 3})
 45        >>>
 46        >>> dict(ctx.arguments)
 47        {'a': 1, 'b': 2, 'c': 3, 'd': 4}
 48
 49        The varargs and kwargs can also be captured:
 50
 51        >>> def bar(*args, **kwargs):
 52        ...     pass
 53        >>>
 54        >>> ctx = FuncCallContext(bar, (1, 2), {"c": 3})
 55        >>>
 56        >>> ctx.arguments
 57        {'args': (1, 2), 'kwargs': {'c': 3}}
 58        """
 59        args = self.__signature.bind(*self.__args, **self.__kwargs)
 60        args.apply_defaults()
 61        return dict(args.arguments)
 62
 63    @property
 64    def full_name(self) -> str:
 65        """
 66        The full name of the function.
 67        Specifically, the concatenation of a function's module and its [qualified name](https://docs.python.org/3/glossary.html#term-qualified-name).
 68
 69        >>> def foo():
 70        ...     pass
 71        >>>
 72        >>> # Equivalent to the context computed for: foo()
 73        >>> ctx = FuncCallContext(foo, (), {})
 74        >>>
 75        >>> # foo is defined in checkpointing/identifier/func_call/context.py
 76        >>> ctx.full_name
 77        'checkpointing.identifier.func_call.context.foo'
 78        """
 79        return ".".join([self.module, self.qualified_name])
 80
 81    @property
 82    def module(self) -> str:
 83        """
 84        Module where the function is defined.
 85
 86        >>> def foo():
 87        ...     pass
 88        >>>
 89        >>> # Equivalent to the context computed for: foo()
 90        >>> ctx = FuncCallContext(foo, (), {})
 91        >>>
 92        >>> # foo is defined in checkpointing/identifier/func_call/context.py
 93        >>> ctx.module
 94        'checkpointing.identifier.func_call.context'
 95        """
 96
 97        return self.__func.__module__
 98
 99    @property
100    def name(self) -> str:
101        """
102        Name of the function.
103
104        >>> def foo():
105        ...     pass
106        >>>
107        >>> # Equivalent to the context computed for: foo()
108        >>> ctx = FuncCallContext(foo, (), {})
109        >>> ctx.name
110        'foo'
111        """
112
113        return self.__func.__name__
114
115    @property
116    def qualified_name(self) -> str:
117        """
118        [Qualified name](https://docs.python.org/3/glossary.html#term-qualified-name) of the function.
119
120        >>> class Foo:
121        ...     def bar(self):
122        ...         pass
123        >>>
124        >>> f = Foo()
125        >>>
126        >>> # Equivalent to the context computed for: f.bar()
127        >>> ctx = FuncCallContext(f.bar, (), {})
128        >>> ctx.qualified_name
129        'Foo.bar'
130        """
131
132        return self.__func.__qualname__
133
134    @property
135    def code(self) -> str:
136        r"""
137        The source code of the function, including the function signature,
138        formatted as-is, including any comments.
139
140        >>> def foo(a, b):
141        ...     c = a + b  # add a and b
142        ...     return c
143        >>>
144        >>> # Equivalent to the context computed for: foo(1, 2)
145        >>> ctx = FuncCallContext(foo, (1, 2), {})
146        >>>
147        >>> ctx.code
148        'def foo(a, b):\n    c = a + b  # add a and b\n    return c\n'
149        """
150
151        return inspect.getsource(self.__func)
152
153    def get_nonlocal_variable(self, varname: str) -> Any:
154        r"""
155        Try to get the nonlocal variable `varname` from the function call's
156        `locals()` and `globals()`.
157        If it doesn't exist in neither of them, return `None`.
158        If the caller_frame is not provided, the result is always `None`.
159
160        >>> import inspect
161        >>>
162        >>> a = 1
163        >>> def foo():
164        ...     pass
165        >>>
166        >>> ctx = FuncCallContext(foo, (), {}, inspect.currentframe())
167        >>> ctx.get_nonlocal_variable("a")
168        1
169        >>> ctx.get_nonlocal_variable("b") is None
170        True
171        """
172
173        if self.__locals is not None and varname in self.__locals:
174            return self.__locals[varname]
175
176        if self.__func.__globals__ is not None and varname in self.__func.__globals__:
177            return self.__func.__globals__[varname]
178
179        return None

Context of information for a function call.

FuncCallContext( func: Callable[..., ~ReturnValue], args: Tuple, kwargs: Dict, def_frame: frame = None)
15    def __init__(self, func: Callable[..., ReturnValue], args: Tuple, kwargs: Dict, def_frame: FrameType = None,) -> None:
16        """
17        Args:
18            func: the function object that is being called
19            args: the non-keywords arguments of the function call
20            kwargs: the keyword arguments of the function call
21            def_frame: the frame where the function is defined
22        """
23
24        self.__func: Callable[..., ReturnValue] = func
25        self.__args: Tuple = args
26        self.__kwargs: Dict = kwargs
27        self.__signature = inspect.signature(self.__func)
28
29        if def_frame is not None:
30            self.__locals = copy.copy(def_frame.f_locals)
31
32        else:
33            self.__locals = None
Args
  • func: the function object that is being called
  • args: the non-keywords arguments of the function call
  • kwargs: the keyword arguments of the function call
  • def_frame: the frame where the function is defined
arguments: Dict

Dictionary of the function arguments and their actually applied parameter values in the function call.

>>> def foo(a, b=1, c=None, d=4):
...     pass
>>>
>>> # Equivalent to the context computed for: foo(1, 2, c=3)
>>> ctx = FuncCallContext(foo, (1, 2), {"c": 3})
>>>
>>> dict(ctx.arguments)
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

The varargs and kwargs can also be captured:

>>> def bar(*args, **kwargs):
...     pass
>>>
>>> ctx = FuncCallContext(bar, (1, 2), {"c": 3})
>>>
>>> ctx.arguments
{'args': (1, 2), 'kwargs': {'c': 3}}
full_name: str

The full name of the function. Specifically, the concatenation of a function's module and its qualified name.

>>> def foo():
...     pass
>>>
>>> # Equivalent to the context computed for: foo()
>>> ctx = FuncCallContext(foo, (), {})
>>>
>>> # foo is defined in checkpointing/identifier/func_call/context.py
>>> ctx.full_name
'checkpointing.identifier.func_call.context.foo'
module: str

Module where the function is defined.

>>> def foo():
...     pass
>>>
>>> # Equivalent to the context computed for: foo()
>>> ctx = FuncCallContext(foo, (), {})
>>>
>>> # foo is defined in checkpointing/identifier/func_call/context.py
>>> ctx.module
'checkpointing.identifier.func_call.context'
name: str

Name of the function.

>>> def foo():
...     pass
>>>
>>> # Equivalent to the context computed for: foo()
>>> ctx = FuncCallContext(foo, (), {})
>>> ctx.name
'foo'
qualified_name: str

Qualified name of the function.

>>> class Foo:
...     def bar(self):
...         pass
>>>
>>> f = Foo()
>>>
>>> # Equivalent to the context computed for: f.bar()
>>> ctx = FuncCallContext(f.bar, (), {})
>>> ctx.qualified_name
'Foo.bar'
code: str

The source code of the function, including the function signature, formatted as-is, including any comments.

>>> def foo(a, b):
...     c = a + b  # add a and b
...     return c
>>>
>>> # Equivalent to the context computed for: foo(1, 2)
>>> ctx = FuncCallContext(foo, (1, 2), {})
>>>
>>> ctx.code
'def foo(a, b):\n    c = a + b  # add a and b\n    return c\n'
def get_nonlocal_variable(self, varname: str) -> Any:
153    def get_nonlocal_variable(self, varname: str) -> Any:
154        r"""
155        Try to get the nonlocal variable `varname` from the function call's
156        `locals()` and `globals()`.
157        If it doesn't exist in neither of them, return `None`.
158        If the caller_frame is not provided, the result is always `None`.
159
160        >>> import inspect
161        >>>
162        >>> a = 1
163        >>> def foo():
164        ...     pass
165        >>>
166        >>> ctx = FuncCallContext(foo, (), {}, inspect.currentframe())
167        >>> ctx.get_nonlocal_variable("a")
168        1
169        >>> ctx.get_nonlocal_variable("b") is None
170        True
171        """
172
173        if self.__locals is not None and varname in self.__locals:
174            return self.__locals[varname]
175
176        if self.__func.__globals__ is not None and varname in self.__func.__globals__:
177            return self.__func.__globals__[varname]
178
179        return None

Try to get the nonlocal variable varname from the function call's locals() and globals(). If it doesn't exist in neither of them, return None. If the caller_frame is not provided, the result is always None.

>>> import inspect
>>>
>>> a = 1
>>> def foo():
...     pass
>>>
>>> ctx = FuncCallContext(foo, (), {}, inspect.currentframe())
>>> ctx.get_nonlocal_variable("a")
1
>>> ctx.get_nonlocal_variable("b") is None
True