jonathanslenders / ptpdb

prompt_toolkit/ptpython pdb frontend
183 stars 20 forks source link

set_trace not working inside django #7

Open Grokzen opened 9 years ago

Grokzen commented 9 years ago

I got this stacktrace when i was testing ptpdb inside django with set_trace()

Environment:

Request Method: POST
Request URL: http://localhost:8080/

Django Version: 1.6.1
Python Version: 2.7.6
Installed Applications:
('suit',
 'django.contrib.auth',
 'django.contrib.admin',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'celery',
 'south',
 'cmdb',
 'debug_toolbar')
Installed Middleware:
('django.middleware.csrf.CsrfViewMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'debug_toolbar.middleware.DebugToolbarMiddleware')

Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
  114.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py" in wrapper
  432.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py" in _wrapped_view
  99.                     response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/cache.py" in _wrapped_view_func
  52.         response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/sites.py" in inner
  198.             return view(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py" in _wrapper
  29.             return bound_func(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py" in _wrapped_view
  99.                     response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/utils/decorators.py" in bound_func
  25.                 return func(self, *args2, **kwargs2)
File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py" in changelist_view
  1347.                 response = self.response_action(request, queryset=cl.get_queryset(request))
File "/usr/local/lib/python2.7/dist-packages/django/contrib/admin/options.py" in response_action
  1084.             response = func(self, request, queryset)
File "/srv/support/utils.py" in wrapper
  52.             return action(modeladmin, request, queryset, **kwargs)
File "/srv/support/unit/admin.py" in action_disable_service_mode
  291.                 unit_names.append(unit.name)
File "/srv/support/unit/admin.py" in action_disable_service_mode
  291.                 unit_names.append(unit.name)
File "/usr/lib/python2.7/bdb.py" in trace_dispatch
  49.             return self.dispatch_line(frame)
File "/usr/lib/python2.7/bdb.py" in dispatch_line
  67.             self.user_line(frame)
File "/usr/lib/python2.7/pdb.py" in user_line
  158.             self.interaction(frame, None)
File "/usr/lib/python2.7/pdb.py" in interaction
  210.         self.cmdloop()
File "/usr/local/lib/python2.7/dist-packages/ptpdb/__init__.py" in cmdloop
  160.                     line = self._get_input()
File "/usr/local/lib/python2.7/dist-packages/ptpdb/__init__.py" in _get_input
  223.             return self.cli.cli.read_input().text
File "/usr/local/lib/python2.7/dist-packages/prompt_toolkit/interface.py" in read_input
  321.                 self.eventloop.run(self.stdin, self.create_eventloop_callbacks())
File "/usr/local/lib/python2.7/dist-packages/prompt_toolkit/eventloop/posix.py" in run
  57.         with call_on_sigwinch(received_winch):
File "/usr/local/lib/python2.7/dist-packages/prompt_toolkit/eventloop/posix.py" in __enter__
  155.         self.previous_callback = signal.signal(signal.SIGWINCH, lambda *a: self.callback())

Exception Type: ValueError at /unit/
Exception Value: signal only works in main thread
jonathanslenders commented 9 years ago

Try to run Django in single threaded mode. (i think it's with the -s option.)

joequery commented 9 years ago

The single threaded option is --nothreading, but that doesn't resolve the issue for me. I might poke around if I can find some time.

Grokzen commented 9 years ago

Found the same thing happening inside a Flask application

  File "/usr/lib/python2.7/bdb.py", line 49, in trace_dispatch
    return self.dispatch_line(frame)
  File "/usr/lib/python2.7/bdb.py", line 67, in dispatch_line
    self.user_line(frame)
  File "/usr/lib/python2.7/pdb.py", line 158, in user_line
    self.interaction(frame, None)
  File "/usr/lib/python2.7/pdb.py", line 210, in interaction
    self.cmdloop()
  File "/home/grok/.virtualenvs/foo/lib/python2.7/site-packages/ptpdb/__init__.py", line 160, in cmdloop
    line = self._get_input()
  File "/home/grok/.virtualenvs/foo/lib/python2.7/site-packages/ptpdb/__init__.py", line 223, in _get_input
    return self.cli.cli.read_input().text
  File "/home/grok/.virtualenvs/foo/lib/python2.7/site-packages/prompt_toolkit/interface.py", line 321, in read_input
    self.eventloop.run(self.stdin, self.create_eventloop_callbacks())
  File "/home/grok/.virtualenvs/foo/lib/python2.7/site-packages/prompt_toolkit/eventloop/posix.py", line 57, in run
    with call_on_sigwinch(received_winch):
  File "/home/grok/.virtualenvs/foo/lib/python2.7/site-packages/prompt_toolkit/eventloop/posix.py", line 155, in __enter__
    self.previous_callback = signal.signal(signal.SIGWINCH, lambda *a: self.callback())
ValueError: signal only works in main thread
morenoh149 commented 8 years ago

found the same thing a year later. set_trace() causes a No exception message supplied error when placed inside a View's get_queryset function. At least using the django rest framework view filtering

jonathanslenders commented 8 years ago

Hi @morenoh149, Does it work with the --nothreading option? Does it still give the same error message? Does the normal pdb work?

arshbot commented 5 years ago

I know this is an old issue, but I can confirm this works in django

Grokzen commented 5 years ago

@arshbot I have not tried this for a long time now, i will give it another shot and see if it is resolved or not.

x-yuri commented 5 years ago

I believe I've run into the issue with pdb and Django. When I set a breakpoint here. It stops a couple of times before Django boots (before it starts a Django process, and performs checks). But when I do a request (http://localhost/admin/something/add/), it fails with the error as in the original post. Django 2.0.2, Python 3.7.3.

The thing about pdb is that if you stop in the main thread before stopping in a non-main thread, in the latter case pdb would fail:

1.py:

#!/usr/bin/env python
import threading

def thread():
    print('-- thread', threading.get_ident(), threading.enumerate(), threading.main_thread())
    import pdb; pdb.set_trace()

print('-- main', threading.get_ident(), threading.enumerate(), threading.main_thread())
# import pdb; pdb.set_trace()   # (1)
# import pdb; pdb.Pdb(nosigint=True).set_trace()   # (2)
t = threading.Thread(target=thread)
t.start()
t.join()
print('-- main', threading.get_ident(), threading.enumerate(), threading.main_thread())
import pdb; pdb.set_trace()
$ ./1.py
-- main 139987190187648 [<_MainThread(MainThread, started 139987190187648)>] <_MainThread(MainThread, started 139987190187648)>
-- thread 139987184482048 [<_MainThread(MainThread, started 139987190187648)>, <Thread(Thread-1, started 139987184482048)>] <_MainThread(MainThread, started 139987190187648)>
--Return--
> /home/yuri/_/2/1.py(6)thread()->None
-> import pdb; pdb.set_trace()
(Pdb) c
-- main 139987190187648 [<_MainThread(MainThread, started 139987190187648)>] <_MainThread(MainThread, started 139987190187648)>
--Return--
> /home/yuri/_/2/1.py(15)<module>()->None
-> import pdb; pdb.set_trace()
(Pdb) c

With line (1) uncommented:

$ ./1.py
-- main 140351081735808 [<_MainThread(MainThread, started 140351081735808)>] <_MainThread(MainThread, started 140351081735808)>
> /home/yuri/_/2/1.py(11)<module>()
-> t = threading.Thread(target=thread)
(Pdb) c
-- thread 140351074244352 [<_MainThread(MainThread, started 140351081735808)>, <Thread(Thread-1, started 140351074244352)>] <_MainThread(MainThread, started 140351081735808)>
--Return--
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.7/threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "./1.py", line 6, in thread
    import pdb; pdb.set_trace()
  File "/usr/lib/python3.7/bdb.py", line 92, in trace_dispatch
    return self.dispatch_return(frame, arg)
  File "/usr/lib/python3.7/bdb.py", line 151, in dispatch_return
    self.user_return(frame, arg)
  File "/usr/lib/python3.7/pdb.py", line 293, in user_return
    self.interaction(frame, None)
  File "/usr/lib/python3.7/pdb.py", line 344, in interaction
    signal.signal(signal.SIGINT, Pdb._previous_sigint_handler)
  File "/usr/lib/python3.7/signal.py", line 47, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread

-- main 140351081735808 [<_MainThread(MainThread, started 140351081735808)>] <_MainThread(MainThread, started 140351081735808)>
--Return--
> /home/yuri/_/2/1.py(11)<module>()->None
-> t = threading.Thread(target=thread)
(Pdb) c

One solution is to not create threads. There are 2 sources of threads in Django:

  1. autoreload. The outer process (django-admin) starts a Django process in an infinite loop (while true; do fork; done). The Django process starts Django in a non-main thread, the main thread waits for changes and exits. So most of the time pdb works in a non-main thread.
  2. threading (using separate thread to handle requests).

As such, the solution is ./manage.py runserver --nothreading --noreload.

The other solution is to not make it stop in the main thread before a non-main one. The quick and dirty way is:

aaa = False
class ...:
    def ...:
        global aaa
        aaa += 1
        if aaa > 2:   # the exact value depends
            import pdb; pdb.set_trace()

The third option is: import pdb; pdb.Pdb(nosigint=True).set_trace().

Supposedly related bug.