Giter Site home page Giter Site logo

gaogaotiantian / watchpoints Goto Github PK

View Code? Open in Web Editor NEW
462.0 10.0 19.0 99 KB

watchpoints is an easy-to-use, intuitive variable/object monitor tool for python that behaves similar to watchpoints in gdb.

License: Apache License 2.0

Makefile 1.19% Python 98.81%
python debugging python3 debugging-tool

watchpoints's Introduction

watchpoints

build coverage pypi support-version license commit

watchpoints is an easy-to-use, intuitive variable/object monitor tool for python that behaves similar to watchpoints in gdb.

Install

pip install watchpoints

Usage

watch

Simply watch the variables you need to monitor!

from watchpoints import watch

a = 0
watch(a)
a = 1

will generate

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (my_script.py:5):
>   a = 1
a:
0
->
1

It works on both variable change and object change

from watchpoints import watch

a = []
watch(a)
a.append(1)  # Trigger
a = {}  # Trigger

Even better, it can track the changes of the object after the changes of the variable

from watchpoints import watch

a = []
watch(a)
a = {}  # Trigger
a["a"] = 2  # Trigger

Without doubts, it works whenever the object is changed, even if it's not in the same scope

from watchpoints import watch

def func(var):
    var["a"] = 1

a = {}
watch(a)
func(a)
====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (my_script.py:8):
>   func(a)
  func (my_script.py:4):
>   var["a"] = 1
a:
{}
->
{'a': 1}

As you can imagine, you can monitor attributes of an object, or a specific element of a list or a dict

from watchpoints import watch

class MyObj:
    def __init__(self):
        self.a = 0

obj = MyObj()
d = {"a": 0}
watch(obj.a, d["a"])  # Yes you can do this
obj.a = 1  # Trigger
d["a"] = 1  # Trigger

Also, watchpoints supports native threading library for multi-threading. It will tell you which thread is changing the value as well.

====== Watchpoints Triggered ======
---- Thread-1 ----
Call Stack (most recent call last):
  _bootstrap (/usr/lib/python3.8/threading.py:890):
>   self._bootstrap_inner()
  _bootstrap_inner (/usr/lib/python3.8/threading.py:932):
>   self.run()
  run (my_script.py:15):
>   a[0] = i
a:
[0]
->
[1]

watchpoints will try to guess what you want to monitor, and monitor it as you expect(well most of the time)

unwatch

When you are done with the variable, you can unwatch it.

from watchpoints import watch, unwatch

a = 0
watch(a)
a = 1
unwatch(a)
a = 2  # nothing will happen

Or you can unwatch everything by passing no argument to it

unwatch()  # unwatch everything

print to different stream

Like the print function, you can choose the output stream for watch print using file argument. The default value is sys.stderr.

f = open("watch.log", "w")
a = 0
watch(a, file=f)
a = 1
f.close()

Be aware that the stream needs to be available when the variable is changed! So the following code WON'T WORK:

a = 0
with open("watch.log", "w") as f:
    watch(a, file=f)
a = 1

Or you could just give a filename to watch. It will append to the file.

watch(a, file="watch.log")

Use config if you want to make it global

watch.config(file="watch.log")

customize printer

You can use your own printer function to print the object, instead of the default objprint with custom_printer

# This will use built-in print function for the objects
watch(a, custom_printer=print)

Use config if you want to make it global

watch.config(custom_printer=print)

alias

You can give an alias to a monitored variable, so you can unwatch it anywhere. And the alias will be printed instead of the variable name

from watchpoints import watch, unwatch

watch(a, alias="james")
# Many other stuff, scope changes
unwatch("james")

conditional callback

You can give an extra condition filter to do "conditional watchpoints". Pass a function func(obj) which returns True if you want to trigger the callback to when of watch

a = 0
watch(a, when=lambda x: x > 0)
a = -1  # Won't trigger
a = 1  # Trigger

variable vs object

When you do watch() on an object, you are actually tracking both the object and the variable holding it. In most cases, that's what you want anyways. However, you can configure precisely which you want to track.

a = []
watch(a, track="object")
a.append(1)  # Trigger
a = {}  # Won't trigger because the list object does not change

a = []
watch(a, track="variable")
a.append(1)  #  Won't trigger, because "a" still holds the same object
a = {}  # Trigger

object compare and deepcopy

Nested object comparison is tricky. It's hard to find a solid standard to compare complicated customized objects. By default, watchpoints will do a shallow copy of the object. You can override this behavior by passing deepcopy=True to watch()

watch(a, deepcopy=True)

watchpoints will honor __eq__ method for user-defined classes first. If __eq__ is not implemented, watchpoints will compare __dict__(basically attibures) of the object if using shallow copy, and raise an NotImplementedError if using deepcopy.

The reason behind this is, if you deepcopied a complicated structure, there's no way for watchpoints to figure out if it's the same object without user defined __eq__ function.

customize copy and compare

For your own data structures, you can provide a customized copy and/or customized compare function for watchpoints to better suit your need.

watchpoints will use the copy function you provide to copy the object for reference, and use your compare function to check if that object is changed. If copy function or compare function is not provided, it falls to default as mentioned above.

cmp argument takes a function that will take two objects as arguments and return a boolean representing whether the objects are different

def my_cmp(obj1, obj2):
    return obj1.id != obj2.id

watch(a, cmp=my_cmp)

copy argument takes a function that will take a object and return a copy of it

def my_copy(obj):
    return MyObj(id=obj.id)

watch(a, copy=my_copy)

stack limit

You can specify the call stack limit printed using watch.config(). The default value is 5, any positive integer is accepted. You can use None for unlimited call stack, which means it will prints out all the frames.

watch.config(stack_limit=10)

You can also set different stack limits for each monitored variable by passing stack_limit argument to watch

# This will only change stack_limit for a
watch(a, stack_limit=10)

customize callback

Of course sometimes you want to print in your own format, or even do something more than print. You can use your own callback for monitored variables

watch(a, callback=my_callback)

The callback function takes three arguments

def my_callback(frame, elem, exec_info)
  • frame is the current frame when a change is detected.
  • elem is a WatchElement object that I'm too lazy to describe for now.
  • exec_info is a tuple of (funcname, filename, lineno) of the line that changed the variable

You can also set change the callback function globally by

watch.config(callback=my_callback)

Use restore() to restore the default callback

watch.restore()

Integrating with pdb

watchpoints can be used with pdb with ease. You can trigger pdb just like using breakpoint() when your monitored variable is changed. Simply do

watch.config(pdb=True)

When you are in pdb, use q(uit) command to exit pdb, and the next change on the variable will trigger the pdb again.

Avoid import

Sometimes it's a hassle having to import the function in every single file. You can install the watch function to builtins and be able to call it in any files:

watch.install()  # or watch.install("func_name") and use it as func_name()
# Remove it from the builtins
watch.uninstall()  # if installed with a name, pass it to uninstall() as well

Limitations

  • watchpoints uses sys.settrace() so it is not compatible with other libraries that use the same function.
  • watchpoints will slow down your program significantly, like other debuggers, so use it for debugging purpose only
  • watch() needs to be used by itself, not nested in other functions, to be correctly parsed
  • at this point, there might be other issues because it's still in development phase

Bugs/Requests

Please send bug reports and feature requests through github issue tracker.

License

Copyright Tian Gao, 2020.

Distributed under the terms of the Apache 2.0 license.

watchpoints's People

Contributors

gaogaotiantian avatar kikem avatar lhfelis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

watchpoints's Issues

`FileNotFoundError: [Errno 2] No such file or directory: '<ipython-input-11-128260e0dc37>'`

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-11-128260e0dc37> in <module>
----> 1 watch(plt.rcParams['axes.titleweight'])

~/.local/lib/python3.8/site-packages/watchpoints/watch.py in __call__(self, *args, **kwargs)
     26         with self.set_lock:
     27             frame = inspect.currentframe().f_back
---> 28             argnodes = getargnodes(frame)
     29             for node, name in argnodes:
     30                 self.watch_list.append(

~/.local/lib/python3.8/site-packages/watchpoints/util.py in getargnodes(frame)
     41     get the list of arguments of the current line function
     42     """
---> 43     line = getline(frame)
     44     m = re.match(r".*?\((.*)\)", line)
     45     if not m:  # pragma: no cover

~/.local/lib/python3.8/site-packages/watchpoints/util.py in getline(frame)
     17     filename = frame.f_code.co_filename
     18 
---> 19     with open(filename, "r", encoding="utf-8") as f:
     20         lines = f.readlines()
     21         if sys.version_info.minor <= 7:

FileNotFoundError: [Errno 2] No such file or directory: '<ipython-input-11-128260e0dc37>'

Let me know if you need more information. Running in jupyter notebook. This is my first time trying to use watchpoints. Does it not work with ipython? Will rework my code to run in a script; in the meantime will leave this post here in case you can help. Thanks.

Doesnt work with pandas dataframes

File "C:\Users\Rwsin\myproject\venv\Lib\site-packages\watchpoints\watch.py", line 50, in call
self.start_trace(frame)
File "C:\Users\Rwsin\myproject\venv\Lib\site-packages\watchpoints\watch.py", line 66, in start_trace
threading.settrace(self.tracefunc)
File "C:\Users\Rwsin\AppData\Local\Programs\Python\Python38\Lib\threading.py", line 67, in settrace
def settrace(func):
File "C:\Users\Rwsin\myproject\venv\Lib\site-packages\watchpoints\watch.py", line 130, in tracefunc
changed, exist = elem.changed(frame)
File "C:\Users\Rwsin\myproject\venv\Lib\site-packages\watchpoints\watch_element.py", line 90, in changed
return self.obj_changed(self.prev_obj), True
File "C:\Users\Rwsin\myproject\venv\Lib\site-packages\watchpoints\watch_element.py", line 110, in obj_changed
return not guess
File "C:\Users\Rwsin\myproject\venv\Lib\site-packages\pandas\core\generic.py", line 1442, in nonzero
raise ValueError(
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Print to file/IO

We should have a feature to print not only to stdout, but also file/IO. We can achieve this using callback now, but it's too complicated for users.

Need conditional expressions

Great idea, as are all of your repos.

This really needs the ability to limit prints to only when certain boolean expressions are true that utilize the watchpoint variable. Otherwise, the logs could get too big too fast. Are conditional expressions already in there?

FileNotFoundError: [Errno 2] No such file or directory: '<stdin>'

When I run the first example in the terminal:

from watchpoints import watch
a = 0
watch(a)

I get:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/watchpoints/watch.py", line 28, in __call__
    argnodes = getargnodes(frame)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/watchpoints/util.py", line 43, in getargnodes
    line = getline(frame)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/watchpoints/util.py", line 19, in getline
    with open(filename, "r", encoding="utf-8") as f:
FileNotFoundError: [Errno 2] No such file or directory: '<stdin>'

Compatibility with Google Colab

I tried watchpoints in Google Colab as follows:

!pip install watchpoints
from watchpoints import watch
x = 0
watch(x)

but it raised the following exception:

Exception                                 Traceback (most recent call last)
[<ipython-input-6-68c97bf397ec>](https://localhost:8080/#) in <cell line: 3>()
      1 from watchpoints import watch
      2 x = 0
----> 3 watch(x)

1 frames
[/usr/local/lib/python3.10/dist-packages/watchpoints/watch.py](https://localhost:8080/#) in __call__(self, *args, **kwargs)
     25         with self.set_lock:
     26             frame = inspect.currentframe().f_back
---> 27             argnodes = getargnodes(frame)
     28             for node, name in argnodes:
     29                 self.watch_list.append(

[/usr/local/lib/python3.10/dist-packages/watchpoints/util.py](https://localhost:8080/#) in getargnodes(frame)
     58     m = re.match(r".*?\((.*)\)", line)
     59     if not m:  # pragma: no cover
---> 60         raise Exception(f"Unable to locate watch line {line}")
     61     args = ["".join(s.strip().split()) for s in m.group(1).split(",")]
     62     try:

Exception: Unable to locate watch line from watchpoints import watch

I think that it will be difficult to make watchpoints compatible to Google Colab and Jupyter Notebook, but I will be happy if watchpoints will be available on them.

Add options to use pprint.

I suggest to add an option to use pprint because pprint is good at printing data structures. It is also customizable for different situations.

For example, in nested list and dictionary they can customize the length with compact argument or the indent with the indent argument. Another Example, in dictionaries they can print the dictionary with an option to sort it with sort_dicts.

I hope you can consider this suggestion.

ENH : __eq__ operator when a pandas.DataFrame is involved

Hi!

First of all, I would like to thank you. This is an incredibly useful project.

Now, when I was attempting to use it on my code I have runned into an interesting problem: when watch is applied to a pandas.DataFrame, the __eq__ operator returns the comparison element-wise.

Thus, the code cannot correctly asses if both elements are equal, since there is a reduction step missing to tell if all elements are equal or not.

Any ideas on how I could go around this issue?

AttributeError: 'NoneType' object has no attribute 'exists'

import os
from watchpoints import watch

os.environ['a'] = 'before'
watch(os.environ)
os.environ['a'] = 'after'

results in

====== Watchpoints Triggered ======
Call Stack (most recent call last):
Exception ignored in: <async_generator object _ag at 0x7feacd0fc320>
Traceback (most recent call last):
  File "/usr/lib64/python3.6/types.py", line 27, in _ag
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py", line 133, in tracefunc
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py", line 158, in _default_callback
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch_print.py", line 36, in __call__
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch_print.py", line 48, in _file_string
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch_print.py", line 56, in getsourceline
AttributeError: 'NoneType' object has no attribute 'exists'

strange behavior when watching `os.environ`

running the code

import os
from watchpoints import watch

os.environ['a'] = 'before'
watch(os.environ['a'])
os.environ['a'] = 'after'

results in the following

verbose output
====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:5):
>   watch(os.environ['a'])
  __call__ (/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py:50):
>   self.start_trace(frame)
  start_trace (/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py:66):
>   threading.settrace(self.tracefunc)
  <module> (./watch_environ.py:5):
>   watch(os.environ['a'])
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:5):
>   watch(os.environ['a'])
  __call__ (/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py:50):
>   self.start_trace(frame)
  start_trace (/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py:66):
>   threading.settrace(self.tracefunc)
  settrace (/usr/lib64/python3.6/threading.py:60):
>   def settrace(func):
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:5):
>   watch(os.environ['a'])
  __call__ (/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py:50):
>   self.start_trace(frame)
  start_trace (/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py:66):
>   threading.settrace(self.tracefunc)
  settrace (/usr/lib64/python3.6/threading.py:68):
>   _trace_hook = func
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  settrace (/usr/lib64/python3.6/threading.py:68):
>   _trace_hook = func
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:672):
>   def __setitem__(self, key, value):
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:673):
>   key = self.encodekey(key)
  __setitem__ (/usr/lib64/python3.6/os.py:673):
>   key = self.encodekey(key)
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:673):
>   key = self.encodekey(key)
  encode (/usr/lib64/python3.6/os.py:742):
>   def encode(value):
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:673):
>   key = self.encodekey(key)
  encode (/usr/lib64/python3.6/os.py:743):
>   if not isinstance(value, str):
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:673):
>   key = self.encodekey(key)
  encode (/usr/lib64/python3.6/os.py:745):
>   return value.encode(encoding, 'surrogateescape')
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  encode (/usr/lib64/python3.6/os.py:745):
>   return value.encode(encoding, 'surrogateescape')
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:674):
>   value = self.encodevalue(value)
  __setitem__ (/usr/lib64/python3.6/os.py:674):
>   value = self.encodevalue(value)
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:674):
>   value = self.encodevalue(value)
  encode (/usr/lib64/python3.6/os.py:742):
>   def encode(value):
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:674):
>   value = self.encodevalue(value)
  encode (/usr/lib64/python3.6/os.py:743):
>   if not isinstance(value, str):
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:674):
>   value = self.encodevalue(value)
  encode (/usr/lib64/python3.6/os.py:745):
>   return value.encode(encoding, 'surrogateescape')
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  encode (/usr/lib64/python3.6/os.py:745):
>   return value.encode(encoding, 'surrogateescape')
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:675):
>   self.putenv(key, value)
os.environ['a']:
before
->
before

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
  __setitem__ (/usr/lib64/python3.6/os.py:676):
>   self._data[key] = value
os.environ['a']:
before
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  __setitem__ (/usr/lib64/python3.6/os.py:676):
>   self._data[key] = value
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  <module> (./watch_environ.py:6):
>   os.environ['a'] = 'after'
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1279):
>   def _shutdown():
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1285):
>   tlock = _main_thread._tstate_lock
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1288):
>   assert tlock is not None
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1289):
>   assert tlock.locked()
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1290):
>   tlock.release()
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
  _stop (/usr/lib64/python3.6/threading.py:966):
>   def _stop(self):
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
  _stop (/usr/lib64/python3.6/threading.py:983):
>   lock = self._tstate_lock
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
  _stop (/usr/lib64/python3.6/threading.py:984):
>   if lock is not None:
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
  _stop (/usr/lib64/python3.6/threading.py:985):
>   assert not lock.locked()
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
  _stop (/usr/lib64/python3.6/threading.py:986):
>   self._is_stopped = True
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1291):
>   _main_thread._stop()
  _stop (/usr/lib64/python3.6/threading.py:987):
>   self._tstate_lock = None
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _stop (/usr/lib64/python3.6/threading.py:987):
>   self._tstate_lock = None
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1292):
>   t = _pickSomeNonDaemonThread()
  _shutdown (/usr/lib64/python3.6/threading.py:1292):
>   t = _pickSomeNonDaemonThread()
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1292):
>   t = _pickSomeNonDaemonThread()
  _pickSomeNonDaemonThread (/usr/lib64/python3.6/threading.py:1297):
>   def _pickSomeNonDaemonThread():
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1292):
>   t = _pickSomeNonDaemonThread()
  _pickSomeNonDaemonThread (/usr/lib64/python3.6/threading.py:1298):
>   for t in enumerate():
  _pickSomeNonDaemonThread (/usr/lib64/python3.6/threading.py:1298):
>   for t in enumerate():
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======
Call Stack (most recent call last):
  _shutdown (/usr/lib64/python3.6/threading.py:1292):
>   t = _pickSomeNonDaemonThread()
  _pickSomeNonDaemonThread (/usr/lib64/python3.6/threading.py:1298):
>   for t in enumerate():
  enumerate (/usr/lib64/python3.6/threading.py:1260):
>   def enumerate():
os.environ['a']:
after
->
after

====== Watchpoints Triggered ======

the execution than hangs as if in a deadlock, and killing it prints

^CException ignored in: <module 'threading' from '/usr/lib64/python3.6/threading.py'>
Traceback (most recent call last):
  File "/usr/lib64/python3.6/threading.py", line 1292, in _shutdown
    t = _pickSomeNonDaemonThread()
  File "/usr/lib64/python3.6/threading.py", line 1298, in _pickSomeNonDaemonThread
    for t in enumerate():
  File "/usr/lib64/python3.6/threading.py", line 1269, in enumerate
    return list(_active.values()) + list(_limbo.values())
  File "/usr/lib64/python3.6/threading.py", line 1269, in enumerate
    return list(_active.values()) + list(_limbo.values())
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py", line 133, in tracefunc
    self._callback(frame, elem, (self._prev_funcname, self._prev_filename, self._prev_lineno))
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch.py", line 158, in _default_callback
    elem.watch_print(frame, elem, exec_info)
  File "/home/conan/.local/lib/python3.6/site-packages/watchpoints/watch_print.py", line 20, in __call__
    if threading.active_count() > 1:
  File "/usr/lib64/python3.6/threading.py", line 1251, in active_count
    with _active_limbo_lock:
KeyboardInterrupt: 

It doesn't seems to work with pdb in terminal?

非常的Amazing as it is, i don't think it can work in a terminal like pdb cuz it is a package...
Personally, I won't be happy if I had to code so much watch and unwatch and debug over and over and over..

Windows 10 Error : UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 3322: character maps to <undefined>

When running on Windows 10 watchpoint crashes with an exception and the following error message:

UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 3322: character maps to

The problem is the open of the source code file in write_print.py on line 53:

`with open(exec_info[1]) as f:`

The encoding needs to be specified in the open call as below:

with open(exec_info[1],"r",encoding='utf-8') as f:

Make object compare better

Object change check needs to be polished.

  • __eq__ should be prioritized if exists
  • If deepcopy is used, we need deep compare
  • If shallow copy is used, we can use __dict__
  • Give user an option to control?

Output the result in oneline.

I tried to watch my list and it outputs in a horizontal way, one item each line.
I wish it can output in one line so I can compare the list faster.

Call Stack (most recent call last):
  <module> (D:\OCR\New folder\safe prototype gui.py:449):
>   per_picture.append(fil_result)
per_picture:
[
  [
    'a',
    'b',
    'c',
    'd',
    'e',
    'f',
    'g',
    'h',
    'i',
    'j',
    'k',
    'l',
    'm',
    'n',
    'o',
    'p',
    'q',
    'r',
    's',
    't',
    'u',
    'v',
    'w',
    'x',
    'y',
    'z',
    '

'
  ]
]
->
[
  [
    'a',
    'b',
    'c',
    'd',
    'e',
    'f',
    'g',
    'h',
    'i',
    'j',
    'k',
    'l',
    'm',
    'n',
    'o',
    'p',
    'q',
    'r',
    's',
    't',
    'u',
    'v',
    'w',
    'x',
    'y',
    'z',
    '

'
  ],
  [
    'a',
    'b',
    'c',
    'd',
    'e',
    'f',
    'g',
    'h',
    'i',
    'j',
    'k',
    'l',
    'm',
    'n',
    'o',
    'p',
    'q',
    'r',
    's',
    't',
    'u',
    'v',
    'w',
    'x',
    'y',
    'z',
    '

'
  ]
]```

Compatibility with Flask

I've been trying to use watch with Flask, but the output doesn't appear in the terminal until after the Flask application has terminated.

from watchpoints import watch
from flask import Flask

app = Flask('app')

x = None
watch(x)

@app.route('/')
def change_x():
	global x
	x = "Something"		# no output until after app.run terminates
	print("manual print")	# prints immediately
	return x

app.run('0.0.0.0', 500, debug=True, use_reloader=True)

Is there a way to output immediately?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.