File: debugging.rst

package info (click to toggle)
python-traits 6.3.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 8,388 kB
  • sloc: python: 34,202; ansic: 4,173; makefile: 99
file content (142 lines) | stat: -rw-r--r-- 5,765 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
.. index:: debugging, exceptions, trace trait change events

=========================
Tips for debugging Traits
=========================


Re-raising exceptions in change handlers
========================================

Traits will typically log (instead of raise) exceptions when an exception is
encountered in a trait-change handler. This behavior is often preferred in
applications, since you usually want to avoid critical failures in
applications. However, when debugging these errors, the
``logging.Logger.exception`` only displays the tip of the traceback. For example,
the following code changes a ``constant``:

.. code-block:: python

   from traits.api import HasTraits, Int

   class Curmudgeon(HasTraits):
       constant = Int(1)
       def _constant_changed(self):
           raise ValueError()

   c = Curmudgeon()
   c.constant = 42

The ``constant`` trait-change handler raises an exception that is caught and
logged::

   Exception occurred in traits notification handler.
   Please check the log file for details.
   Exception occurred in traits notification handler for object:
   <__main__.Curmudgeon object at 0x107603050>, trait: constant, old value: 0, new value: 42 
     ...
     File "curmudgeon.py", line 12, in _constant_changed
       raise ValueError()
   ValueError

This logged exception, however, only contains the tip of the traceback. This
makes debugging a bit difficult. You can force exceptions to be re-raised
by adding a custom exception handler:

.. code-block:: python

   from traits.api import push_exception_handler
   push_exception_handler(reraise_exceptions=True)

(For example, you could add this to the top of the original code block.)

Re-running the original code example with this custom handler will now raise
the following traceback::

   Traceback (most recent call last):
     File "curmudgeon.py", line 15, in <module>
       c.constant = 42
     ...
     File "curmudgeon.py", line 12, in _constant_changed
       raise ValueError()
   ValueError

Notice that this traceback has information about *where* we changed
``constant``.  Note: This is a toy example; use ``Constant`` from
``traits.api`` if you actually want a constant trait.


Tracing Traits Change Events
============================

Occasionally it is necessary to find the chain of event dispatches in traits
classes. To help with debugging, a |record_events| context manager is provided
in mod:`traits.util.event_tracer`. Trait change events taking place inside the
context block will be recorded in a change event container (see example below)
and can be saved to files (a file for each thread) for further inspection.


Example:

.. code-block:: python

    from traits.api import *
    from traits.util.event_tracer import record_events


    class MyModel(HasTraits):

        number = Float(2.0)
        list_of_numbers = List(Float())
        count = Int(0)

        @on_trait_change('number')
        def _add_number_to_list(self, value):
            self.list_of_numbers.append(value)

        @on_trait_change('list_of_numbers[]')
        def _count_items(self):
            self.count = len(self.list_on_numbers)

        def add_to_number(self, value):
            self.number += value


    my_model = MyModel()

    with record_events() as change_event_container:
        my_model.number = 4.7
        my_model.number = 3

    # save files locally
    change_event_container.save_to_directory('./')


Running the above example will write a file named MAinThread.trace in the
local folder. The file contents will be similar to the lines below::

    2014-03-21 14:11:20.779000 -> 'number' changed from 2.0 to 4.7 in 'MyModel'
    2014-03-21 14:11:20.779000     CALLING: '_add_number_to_list' in example.py
    2014-03-21 14:11:20.780000 ---> 'list_of_numbers_items' changed from <undefined> to <traits.trait_handlers.TraitListEvent object at 0x03C85AF0> in 'MyModel'
    2014-03-21 14:11:20.780000       CALLING: 'handle_list_items_special' in C:\Users\itziakos\Projects\traits\traits\traits_listener.py
    2014-03-21 14:11:20.780000 -----> 'list_of_numbers_items' changed from [] to [4.7] in 'MyModel'
    2014-03-21 14:11:20.780000         CALLING: '_count_items' in exampler.py
    2014-03-21 14:11:20.780000 -------> 'trait_added' changed from <undefined> to 'list_on_numbers' in 'MyModel'
    2014-03-21 14:11:20.780000           CALLING: '_trait_added_changed' in C:\Users\itziakos\Projects\traits\traits\has_traits.py
    2014-03-21 14:11:20.780000 <------- EXIT: '_trait_added_changed'
    2014-03-21 14:11:20.780000 <----- EXIT: '_count_items' [EXCEPTION: 'MyModel' object has no attribute 'list_on_numbers']
    2014-03-21 14:11:20.780000 <--- EXIT: 'handle_list_items_special'
    2014-03-21 14:11:20.781000 <- EXIT: '_add_number_to_list'

    2014-03-21 14:11:20.781000 -> 'number' changed from 4.7 to 3.0 in 'MyModel'
    2014-03-21 14:11:20.781000     CALLING: '_add_number_to_list' in example.py
    2014-03-21 14:11:20.781000 ---> 'list_of_numbers_items' changed from <undefined> to <traits.trait_handlers.TraitListEvent object at 0x03C85A30> in 'MyModel'
    2014-03-21 14:11:20.781000       CALLING: 'handle_list_items_special' in C:\Users\itziakos\Projects\traits\traits\traits_listener.py
    2014-03-21 14:11:20.781000 -----> 'list_of_numbers_items' changed from [] to [3.0] in 'MyModel'
    2014-03-21 14:11:20.781000         CALLING: '_count_items' in example.py
    2014-03-21 14:11:20.781000 <----- EXIT: '_count_items' [EXCEPTION: 'MyModel' object has no attribute 'list_on_numbers']
    2014-03-21 14:11:20.782000 <--- EXIT: 'handle_list_items_special'
    2014-03-21 14:11:20.782000 <- EXIT: '_add_number_to_list'


.. |record_events| replace:: :func:`~traits.util.event_tracer.record_events`