2384439a8b6169a03702722854f692c13b5c74d4
[hooke.git] / hooke / util / caller.py
1 # Copyright (C) 2010-2012 W. Trevor King <wking@tremily.us>
2 #
3 # This file is part of Hooke.
4 #
5 # Hooke is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU Lesser General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option) any
8 # later version.
9 #
10 # Hooke is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
13 # details.
14 #
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with Hooke.  If not, see <http://www.gnu.org/licenses/>.
17
18 """Define :func:`caller_name`.
19
20 This is useful, for example, to declare the `@callback` decorator for
21 making GUI writing less tedious.  See :mod:`hooke.util.callback` and
22 :mod:`hooke.ui.gui` for examples.
23 """
24
25 import sys
26
27
28 def frame(depth=1):
29     """Return the frame for the function `depth` up the call stack.
30
31     Notes
32     -----
33     The `ZeroDivisionError` trick is from stdlib's traceback.py.  See
34     the Python Refrence Manual on `traceback objects`_ and `frame
35     objects`_.
36
37     .. _traceback objects:
38       http://docs.python.org/reference/datamodel.html#index-873
39     .. _frame objects:
40       http://docs.python.org/reference/datamodel.html#index-870
41     """
42     try:
43         raise ZeroDivisionError
44     except ZeroDivisionError:
45         traceback = sys.exc_info()[2]
46     f = traceback.tb_frame
47     for i in range(depth):
48         f = f.f_back
49     return f
50
51 def caller_name(depth=1):
52     """Return the name of the function `depth` up the call stack.
53
54     Examples
55     --------
56
57     >>> def x(depth):
58     ...     y(depth)
59     >>> def y(depth):
60     ...     print caller_name(depth)
61     >>> x(1)
62     y
63     >>> x(2)
64     x
65     >>> x(0)
66     caller_name
67
68     Notes
69     -----
70     See the Python Refrence manual on `frame objects`_ and
71     `code objects`_.
72
73     .. _frame objects:
74       http://docs.python.org/reference/datamodel.html#index-870
75     .. _code objects:
76       http://docs.python.org/reference/datamodel.html#index-866
77     """
78     f = frame(depth=depth+1)
79     return f.f_code.co_name
80
81 def caller_names(depth=1):
82     """Iterate through the names of all functions up the call stack.
83
84     Examples
85     --------
86
87     >>> def x():
88     ...     y()
89     >>> def y():
90     ...     z()
91     >>> def z():
92     ...     print list(caller_names())
93     >>> x()  # doctest: +ELLIPSIS
94     ['z', 'y', 'x', ...]
95     >>> y()  # doctest: +ELLIPSIS
96     ['z', 'y', ...]
97     >>> z()  # doctest: +ELLIPSIS
98     ['z', ...]
99     """
100     depth = 2  # start at caller_names()'s caller.
101     while True:
102         try:
103             yield caller_name(depth=depth)
104         except AttributeError:
105             return
106         depth += 1