# coding: utf-8 # # Table of Contents #
# # *"Living in a noisy world..."*, using James Powell's (`dutc`) `rwatch` module # # Goal : I want to write a [context manager](https://docs.python.org/3.5/library/stdtypes.html#typecontextmanager) for [Python 3.5+](https://www.Python.org/), so that inside the context manager, every number is seen noisy (with a white Gaussian noise, for instance). # # > It will be like being drunk, except that your it will be my Python interpretor and not me ! # # For instance, I will like to have this feature: # ```python # >>> x = 120193 # >>> print(x) # 120193 # >>> np.random.seed(1234) # >>> with WhiteNoise(): # >>> print(x) # 120193.47143516373249306 # ``` # ---- # ## Requirements and links # First, we will need [numpy](https://docs.scipy.org/doc/numpy/user/whatisnumpy.html) to have some random number generator, as `numpy.random.normal` to have some 1D Gaussian noise. # In[1]: import numpy as np # In[2]: np.random.seed(1234) np.random.normal() # Then, the core part will be to install and import [James Powell (`dutc`)](https://github.com/dutc/) [`rwatch`](https://github.com/dutc/rwatch) module. # If you don't have it installed : # # 1. Be sure to have CPython 3.5. `rwatch` patches the CPython eval loop, so it's fixed to specific versions of Python & the author lazy about keeping it updated. # 2. Then `pip install dutc-rwatch`. It should work, but it fails for me. # 3. (alternative) You can just `cd /tmp/ && git clone https://github.com/dutc/rwatch && cd rwatch/src/ && make` and copy the `rwatch.so` dynamic library wherever you need... # In[5]: get_ipython().run_cell_magic('bash', '', 'tmpdir=$(mktemp -d)\ncd $tmpdir\ngit clone https://github.com/dutc/rwatch\ncd rwatch/src/\nmake\nls -larth ./rwatch.so\nfile ./rwatch.so\n# cp ./rwatch.so /where/ver/you/need/ # ~/publis/notebook/ for me') # Anyhow, if `rwatch` is installed, we can import it, and it enables two new functions in the `sys` module: # In[6]: import rwatch from sys import setrwatch, getrwatch setrwatch({}) # clean any previously installed rwatch getrwatch() # Finally, we need the [`collections`](https://docs.python.org/3/library/collections.html) module and its [`defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict) magical datastructure. # In[7]: from collections import defaultdict # ---- # ## Defining a debugging context manager, just to try # # This is the first example given in [James presentation]() at PyCon Canada 2016. # # We will first define and add a `rwatch` for just one object, let say a variable `x`, and then for any object using `defaultdict`. # From there, writing a context manager that enables this feature only locally is easy. # ### Watching just one object # 1. Write the function, that needs two argument `frame, obj`, and should return `obj`, # 2. Install it... # 3. Check it! # In[15]: def basic_view(frame, obj): print("Python saw the object {} from frame {}".format(obj, frame)) return obj # In[16]: x = "I am alive!" # In[17]: setrwatch({ id(x): basic_view }) # In[18]: print(x) # That's awesome, it works! # ### Can we delete the `rwatch` ? # Sure! # In[19]: def delrwatch(idobj): getrwatch().pop(idobj, None) # In[20]: print(x) delrwatch(id(x)) print(x) # no more rwatch on this! print(x) # no more rwatch on this! # We can also delete rwatches that are not defined, without a failure: # In[21]: y = "I am Zorro !" print(y) delrwatch(y) # No issue! print(y) # ### More useful debuggin information # What is this `frame` thing? # It is described in the documentation of the [`inspect`](https://docs.python.org/3/library/inspect.html) module. # # We can actually use it to display some useful information about the object and where was it called etc. # In[22]: from inspect import getframeinfo def debug_view(frame, obj): info = getframeinfo(frame) msg = '- Access to {!r} (@{}) at {}:{}:{}' print(msg.format(obj, hex(id(obj)), info.filename, info.lineno, info.function)) return obj # In[23]: setrwatch({}) setrwatch({ id(x): debug_view }) getrwatch() # In[24]: print(x) # That can be quite useful! # ### Watching *any* object # We can actually pass a `defaultdict` to the `setrwatch` function, so that *any* object will have a rwatch! # # > **Warning**: obviously, this will crazily slowdown your interpreter! # # So let be cautious, and only deal with strings here. # # But I want to be safe, so it will only works if the frame indicate that the variable does not come from a file. # In[25]: setrwatch({}) # In[26]: def debug_view_for_str(frame, obj): if isinstance(obj, str): info = getframeinfo(frame) if '