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`
|