pythonvisual-studiogoogle-app-enginewebapp2ptvs

Debugging GAE in Python Tools for Visual Studio


I'm able to run my Google App Engine webapp2 app using Python Tools for Visual Studio 2012 without issues after following this tutorial, and even step through the server initialization code, but I can't get it to break at get or post methods when the website is loaded, similar to what is shown in this video with the main() method. When I pause the debugger, it always ends up in the following infinite loop in wsgi_server.py:

def _loop_forever(self):
  while True:
  self._select()

def _select(self):
  with self._lock:
    fds = self._file_descriptors
    fd_to_callback = self._file_descriptor_to_callback
if fds:
  if _HAS_POLL:
    # With 100 file descriptors, it is approximately 5x slower to
    # recreate and reinitialize the Poll object on every call to _select
    # rather reuse one. But the absolute cost of contruction,
    # initialization and calling poll(0) is ~25us so code simplicity
    # wins.
    poll = select.poll()
    for fd in fds:
      poll.register(fd, select.POLLIN)
    ready_file_descriptors = [fd for fd, _ in poll.poll(1)]
  else:
    ready_file_descriptors, _, _ = select.select(fds, [], [], 1)
  for fd in ready_file_descriptors:
    fd_to_callback[fd]()
else:
  # select([], [], [], 1) is not supported on Windows.
  time.sleep(1)

Is it possible to set breakpoints in a Google App Engine webapp2 app in PTVS, which are triggered when the page is loaded from localhost?

Edit: using cprcrack's settings, I was able to successfully run GAE, but when loading the main page I get the error

Traceback (most recent call last):
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver.py", line 3003, in _HandleRequest
    self._Dispatch(dispatcher, self.rfile, outfile, env_dict)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver.py", line 2862, in _Dispatch
    base_env_dict=env_dict)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver.py", line 719, in Dispatch
    base_env_dict=base_env_dict)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver.py", line 1797, in Dispatch
    self._module_dict)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\tools\dev_appserver.py", line 1648, in ExecuteCGI
    app_log_handler = app_logging.AppLogsHandler()
  File "C:\Python\lib\logging\__init__.py", line 660, in __init__
    _addHandlerRef(self)
  File "C:\Python\lib\logging\__init__.py", line 639, in _addHandlerRef
    _releaseLock()
  File "C:\Python\lib\logging\__init__.py", line 224, in _releaseLock
    _lock.release()
  File "C:\Python\lib\threading.py", line 138, in release
    self.__count = count = self.__count - 1
  File "C:\Python\lib\threading.py", line 138, in release
    self.__count = count = self.__count - 1
  File "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio\2.0\visualstudio_py_debugger.py", line 557, in trace_func
    return self._events[event](frame, arg)
  File "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio\2.0\visualstudio_py_debugger.py", line 650, in handle_line
    if filename == frame.f_code.co_filename or (not bound and filename_is_same(filename, frame.f_code.co_filename)):
  File "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio\2.0\visualstudio_py_debugger.py", line 341, in filename_is_same
    import ntpath
  File "C:\Python\lib\ntpath.py", line 8, in <module>
    import os
  File "C:\Python\lib\os.py", line 120, in <module>
    from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep,
ImportError: cannot import name curdir

Is this error occurring because I need to roll back to Python 2.5 to use the old dev_appserver?


Solution

  • UPDATE#2

    gcloud preview deprecated

    it's back to original method

    UPDATE#1

    gcloud preview (it's newer and simpler),

    replace this:

    General->Startup File:

    C:\Program Files\Google\Cloud SDK\google-cloud-sdk\lib\googlecloudsdk\gcloud\gcloud.py
    

    Debug->Script Arguments:

    preview app run app.yaml --python-startup-script "pydevd_startup.py" --max-module-instances="default:1"
    

    all rest is the same as the original answer below:


    ORIGINAL ANSWER:

    A.) Create A File to Inject remote debugger

    1. make a new python file "pydevd_startup.py"

    2. insert this:

      import json 
      import sys 
      if ':' not in config.version_id:  
      # The default server version_id does not contain ':'  
          sys.path.append("lib")  
          import ptvsd  #ptvsd.settrace() equivalent  
          ptvsd.enable_attach(secret = 'joshua')  
          ptvsd.wait_for_attach()
      
    3. Save it in your working directory of your app

    4. for more info look at the pytool remote debuging docu I mentioned above

    B.) Edit Project Settings in VS 2013

    Now open your Project Settings in VS and enter this:

    General->Startup File: C:\Cloud SDK\google-cloud-sdk\bin\dev_appserver.py
    General->Working Directory: .
    Debug->Search Paths: C:\Cloud SDK\google-cloud-sdk\lib
    Debug->Script Arguments: --python_startup_script=".\pydevd_startup.py" --automatic_restart=no --max_module_instances="default:1" ".\app.yaml"
    

    You could probably also use . instead of <path-to-your-app> but I wanted to be safe.

    C.) Run Debugger

    With Ctrl+F5 you run the debugger, without debugging. This sound weird, but we are actually not debugging right now, just running the dev server which than starts our script to inject the debugger code and wait for our remote debugger to connect, which will happen in the next step

    D.) Start Remote Debugger

    DEBUG->Attach to Process <Ctrl+Alt+P>
    Qualifier: tcp://joshua@localhost:5678 <ENTER>
    

    joshua is your secret key. If you want to change it (and you should), you also have to change it in the pydevd_startup.py. See pytool reference for more info.

    F.) Be really happy!

    You now can remote debug your application locally (erm, weird). To test this you probably should use a breakpoint at the start of your own script.

    If you have any question, please ask. In the end it seems really simple, but to get this going was rough. Especially because pytools said, they don't support it...

    G.) Start Debugging for real!

    Open http://localhost:8080 in a browser (or any other address you configure your app to use). Now it should invoke the breaking point. If you are done and reload the site, it starts all over again. If you really want to end debugging or change some code, you have to restart the server and attach again. Don't forget to close the terminal window with the server open (use <Crtl+C> )