Python Argentina •  Planeta Full

viernes, 03 febrero

19:29

Mariano Reingart: A new web2py online python debugger

I've finished a new online debugger, based on my previous work on qdb module (bdb/pdb enhancement):



In contrast with pdb, this debugger is based on a client/server model, so it should be more stable and extensible than my previous approach (a piped command line loop, see current debug.py module, a naive attempt to use pdb in  a web enviroment, it is mostly undocumented as requires some advanced python skills to use pdb commands without blocking the whole web2py server).
In fact, qdb is based on the python bdb debugger framework (borrowing most commands from pdb), and it comes with a CLI, a separate GUI frontend (using wxpython), and for web2py, this a totally web based interface.

Although this blog post is about web2py, qdb can be used to make debuggers for other frameworks too, you can use WebDebugger as a base to make controller/views with your favourite web stack.

Download:

The web2py debugger can be downloaded from my web2py repo clone in googlecode:


You can download it with mercurial: 


Also, if you have the latest trunk, you can patch it with:


(the changes are in the default branch so will not create nasty repository effects)

Brief Changelog:

web2py is great because it is small and concise, so most changes are less than 100 lines:
  • added gluon.contrib.qdb , the debug backend and basic frontend
  • updated gluon.debug to create the qdb client/server debug queues and the web frontend
  • updated applications/admin/controller/debug with interact and breakpoints controllers
  • added applications/admin/views/debug/interact.html and breakpoints.html
  • updated applications/admin/views/default/edit.html to add toggle breakpoint button
  • updated applications/admin/models/menu.py to add top-level debug menu
  • updated applications/admin/js/static/ajax_editor.js (toggle breakpoints) and style.css
  • updated gluon.html.CODE and gluon.highligh to show the context relevant lines 
The full change log is available in the repository here. It took me less than 8 hours to add a debugger to web2py, very nice!

Usage:

Basic interaction (step, next, continue, return, stop, etc.), with the highlighted code, locals/globals and a interactive console to execute python statements in the debug environment.
Access it from the added "Debug" main menu button (or go to http://localhost:8000/admin/debug/interact):



To evaluate an expression, enter it in the second textarea, press enter and it will be executed in the debugger.

The result, if any, will be shown in the upper textarea. You can execute any valid python command, including python assignment statements.

To execute the current statement, press step. If you do not want to enter to functions, press next. To run the program until it finish or a breakpoint is reached, press continue. To cancel execution, press stop.

Breakpoints
 (including temporary and conditional ones, with hit count) can be accessed from the Breakpoints button at the main debug page (or go to http://localhost:8000/admin/debug/breakpoints):



Temporary breakpoints are deleted automatically after the first hit, and conditional breakpoints only matches if the associated python expression evaluates to True. 
Also, the breakpoint page can show the context source code according to the line number specified.

The breakpoints can also be added and removed from the edit window (new Toggle Button near back/docs, for example, in http://localhost:8000/admin/default/edit/welcome/controllers/default.py):


 


It also can debug exceptions (handled or unhandled, note the exception info and traceback). After you inspect the local or global variables, press continue so normal exception handling will be done:


  


With this changes, web2py can offer a complete online and ubiquitous Integrated Development Environment, so you don't need to learn any external tool to create web sites!

This debugger was tested this on Ubuntu and Windows XP, but it should work in mac and other linux flavours too.

Future enhancements (suported by the backend, but not implemented already in the web frontend):
  • Jump to line
  • Moving Up/down through the stack trace
  • Watch variables (now they can be manually inspected with the interactive console)
Current drawbacks and limitations:
  • from gluon.debug import dbg;  dbg.do_debug() or dbg.set_trace() should be called from the model/controller to run under debug control (if not, normal web2py dispatch occurs, no breakpoint is honoured). UPDATE: this is done automatically if debug page is opened.
  • The debugger is threaded, so beware of apache prefork and similar (the backend support remote debugging, but it is more trickier to set the breakpoints). This also applies to the current shell and similar tools like paste. See modwsgi Debugging Techniques (Browser Based Debugger). Anyway, I'm working in a full remote multiprocess debugger (that is supported by qdb CLI and GUI right now)
  • Secondary effects can appear if debug more than a function at a time (it will not die, but it is more difficult to follow)
  • I didn't find a way to add markers to editarea yet (ie. a red circle near the line number to indicate a breakpoint)
  • Debugging cannot be limited per application (FILTER_APPS and multi user mode cannot be enforced)
  • Compiled apps cannot be debugged as easily as non-compiled ones (breakpoints must be set manually with set_trace)
  • Some style/layout details are missing

So, if you want to try it, just set breakpoints and execute your controller to start debugging and enjoy ;-)

martes, 31 enero

15:08

Roberto Alsina: Sacar la basura trae sus problemas

Una continuación rapidita de The problem is is, is it not? Esto no es mío, lo saqué de reddit

Esto no debería sorprenderte:

>>> a = [1,2]
>>> b = [3,4]
>>> a is b
False
>>> a == b
False
>>> id(a) == id(b)
False

Después de todo, a y b son cosas distintas. Sin embargo:

>>> [1,2] is [3,4]
False
>>> [1,2] == [3,4]
False
>>> id([1,2]) == id([3,4])
True

Resulta que si uno usa literales, una de esas cosas no es como las demás.

Primero la explicación. Cuando uno no tiene más referencias a un dato, va a ser "garbage collected", la memoria se libera para que se pueda usar para otra cosa.

En el primer caso, las variables a y b guardan referencia a las listas. Es decir que tienen que existir todo el tiempo, ya que yo podría decir print a y python tiene que poder responderme con el valor de a.

En el segundo caso, uso literales, lo que quiere decir que no hay referencias a las listas después de que se usan. Cuando python evalúa id([1,2]) == id([3,4]) evalúa primero el lado izquierdo del ==. Después de que termina con eso, no hace falta mantener el [1,2] a mano, así que se borra. Entonces, al evaluar el lado derecho, crea [3,4].

Por pura casualidad, lo pone en exactamente el mismo lugar en que estaba el [1,2], asi que id devuelve el mismo valor. Esto sirve para recordar dos cosas:

  1. a is b es usualmente (pero no siempre) equivalente a id(a) == id(b)
  2. La recolección de basura tiene efectos secundarios que en una de esas no esperabas.


miércoles, 25 enero

07:33

Roberto Alsina: Gente haciendo cosas útiles con mis juguetes

Hace cosa de un año escribí un pequeño web browser llamado De Vicenzo un poco en joda.

¡Pero de golpe alguien fué y lo hizo hacer algo útil! Específicamente, para tener previews cuando edita documentos en sphinx

Está bueno :)


jueves, 19 enero

15:14

Roberto Alsina: PyQt Quickie: QTimer

QTimer es una clase sencillita: la usás cuando querés que algo pase "dentro de un rato" o "cada tanto".

El primer caso es así:

# llamar f() en 3 segundos
QTimer.singleShot(3000, f)

El segundo es así:

# Creamos un QTimer
timer = QTimer()
# Lo conectamos a f
timer.timeout.connect(f)
# Llamamos a f() cada 5 segundos
timer.start(5000)

¿Fácil, no? Bueno, sí, pero tiene un par de trampas.

  1. Hay que guardar la referencia a timer

    Si no, lo recoge el basurero, y nunca se llama a f()

  2. Capaz que son más de 5 segundos

    Va a llamar a f() más o menos cada 5 segundos después de que entre al event loop. Tal vez eso no sea enseguida después de que arrancás el timer!

  3. Capaz que se pisan las llamadas

    Si f() tarda mucho en terminar, y vuelve a entrar al event loop (por ejemplo, llamando a processEvents) tal vez timer se dispare antes que f() termine, y la llame de nuevo. Eso casi nunca es buena idea.

Una alternativa:

def f():
    try:
        # Hacé cosas
    finally:
        QTimer.singleShot(5000, f)

f()

Ese fragmento llama a f() una sola vez, pero ella misma se pone en cola para correr en 5 segundos. Ya que lo hace en un finally lo va a hacer aún si las cosas se rompen.

O sea, no se va a pisar. También quiere decir que no son 5 segundos, sino 5 más lo que tarde f. Y no hace falta guardar referencias al QTimer.

Último tipo: podés usar QTimer para que algo se haga "apenas estés en el event loop"

QTimer.singleShot(0, f)

¡Ojalá sirva!


Administración y hosting cortesía de Net Managers SRL

Tema por Andrés Antista

Banner por Joaquín Sorianello

Grazr OPML - FOAF - RSS - Log

Blogs

Alejandro Santos
Anthony Lenton
Manuel Muradás
Gonzalo Sainz Trápaga
Marcos Vanetta
Alberto Paparelli
Santiago Peresón (Yaco)
Margarita Manterola
Santiago Bruno
Paul M. Dorr
Esteban Ordano
Evita
Joaquin Sorianello
Marcelo Fernández
Roberto Alsina
Juan Pedro Fisanotti
Fabián Gallina
Gabriel Patiño
Guillermo Heizenreder
Humitos
Mauro Lizaur
Mariano Guerra
Nicolás Miyasato
Martín Gaitán
Pablo Alejandro Costesich
Sebastián Bassi
PyAr en la OLPC
Carlos Joel Delgado Pizarro
Pycon Argentina
Mariano Reingart
Ramiro Morales
San Cayetano
Martín Cerdeira
PyDay Cordoba
Mariano Draghi (cHagHi)
Juanjo Conti
Héctor Sánchez (Karucha)
Pablo Benjamín Arroyo
Andrés Gattinoni
Facundo Batista