<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: debugging</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/debugging.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-07-27T23:07:21+00:00</updated><author><name>Simon Willison</name></author><entry><title>TIL: Exception.add_note</title><link href="https://simonwillison.net/2025/Jul/27/til-exception-add-note/#atom-tag" rel="alternate"/><published>2025-07-27T23:07:21+00:00</published><updated>2025-07-27T23:07:21+00:00</updated><id>https://simonwillison.net/2025/Jul/27/til-exception-add-note/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://daniel.feldroy.com/posts/til-2025-05-exception-add_note"&gt;TIL: Exception.add_note&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Neat tip from Danny Roy Greenfeld: Python 3.11 added a &lt;code&gt;.add_note(message: str)&lt;/code&gt; method to the &lt;code&gt;BaseException&lt;/code&gt; class, which means you can add one or more extra notes to any Python exception and they'll be displayed in the stacktrace!&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://peps.python.org/pep-0678/"&gt;PEP 678 – Enriching Exceptions with Notes&lt;/a&gt; by Zac Hatfield-Dodds proposing the new feature back in 2021.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/jqm47i/til_exception_add_note"&gt;Lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="python"/></entry><entry><title>snoop</title><link href="https://simonwillison.net/2024/Jan/31/snoop/#atom-tag" rel="alternate"/><published>2024-01-31T18:19:29+00:00</published><updated>2024-01-31T18:19:29+00:00</updated><id>https://simonwillison.net/2024/Jan/31/snoop/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/alexmojaki/snoop"&gt;snoop&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Neat Python debugging utility by Alex Hall: snoop lets you “import snoop” and then add “@snoop” as a decorator to any function, which causes that function’s source code to be output directly to the console with details of any variable state changes that occur while it’s running.&lt;/p&gt;

&lt;p&gt;I didn’t know you could make a Python module callable like that—turns out it’s running “sys.modules[’snoop’] = snoop” in the __init__.py module!


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="python"/></entry><entry><title>Finding and reporting an asyncio bug in Python 3.10</title><link href="https://simonwillison.net/2021/Oct/9/finding-and-reporting-a-bug/#atom-tag" rel="alternate"/><published>2021-10-09T04:46:30+00:00</published><updated>2021-10-09T04:46:30+00:00</updated><id>https://simonwillison.net/2021/Oct/9/finding-and-reporting-a-bug/#atom-tag</id><summary type="html">
    &lt;p&gt;I found a bug in Python 3.10 today! Some notes on how I found it and my process for handling it once I figured out what was going on.&lt;/p&gt;
&lt;h4&gt;Testing Datasette against Python 3.10&lt;/h4&gt;
&lt;p&gt;I finally got around to attempting to upgrade Datasette to the just-released Python 3.10 this morning. I started by &lt;a href="https://github.com/simonw/datasette/pull/1481/commits/18a490734fc724b52cfb5a20601af3b00748af8d"&gt;adding &lt;code&gt;"3.10"&lt;/code&gt;&lt;/a&gt; to the matrix of Python versions that Datasette is tested against by GitHub Actions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;strategy:
  matrix:
    python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That line previously looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python-version: [3.6, 3.7, 3.8, 3.9]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I switched to using quoted strings here based on &lt;a href="https://twitter.com/webology/status/1445394072492023811"&gt;a tip from Jeff Triplett&lt;/a&gt;, who pointed out that &lt;code&gt;3.10&lt;/code&gt; is actually a floating point number in YAML syntax which will be treated as referring to Python 3.1 instead!&lt;/p&gt;
&lt;p&gt;This one-line change in a pull request was all it took to run the tests against the new version of Python... and they failed, spectacularly.&lt;/p&gt;
&lt;p&gt;Annoyingly the root error wasn't displayed directly in the tests, because it was triggered and then caught by Datasette's own error handling and turned into a 500 HTTP error. So, I needed to run Python 3.10 locally and it the debugger.&lt;/p&gt;
&lt;h4&gt;Running Python 3.10 locally&lt;/h4&gt;
&lt;p&gt;I occasionally use &lt;a href="https://github.com/pyenv/pyenv"&gt;pyenv&lt;/a&gt; to manage multiple Python versions on my machine. I have this installed through Homebrew.&lt;/p&gt;
&lt;p&gt;I had to run &lt;code&gt;brew upgrade pyenv&lt;/code&gt; first,  because my installed version didn't know about Python 3.10. Then I ran this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pyenv install 3.10.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which gave me a Python 3.10 installation on my Mac in &lt;code&gt;~/.pyenv/versions/3.10.0/bin/python&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, I created a new virtual environment using this version of Python.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/.pyenv/versions/3.10.0/bin/python \
  -m venv /tmp/py310
# Now activate that virtual environment:
source /tmp/py310/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I put this in my &lt;code&gt;/tmp&lt;/code&gt; directory so I don't have to remember to clean it up later.&lt;/p&gt;
&lt;p&gt;Having done this, I installed my Datasette test dependencies into the new environment and used &lt;code&gt;pytest&lt;/code&gt; to run my tests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% cd ~/Dropbox/Development/datasette
% pip install -e '.[test]'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;pytest -x --pdb&lt;/code&gt; stopped at the first failing test and dropped me into a debugger where I could finally access the full traceback, which looked something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  File "datasette/datasette/views/base.py", line 122, in dispatch_request
    await self.ds.refresh_schemas()
  File "datasette/datasette/app.py", line 344, in refresh_schemas
    await self._refresh_schemas()
  File "datasette/datasette/app.py", line 349, in _refresh_schemas
    await init_internal_db(internal_db)
  File "datasette/datasette/utils/internal_db.py", line 5, in init_internal_db
    await db.execute_write(
  File "datasette/datasette/database.py", line 102, in execute_write
    return await self.execute_write_fn(_inner, block=block)
  File "datasette/datasette/database.py", line 113, in execute_write_fn
    reply_queue = janus.Queue()
  File "janus/__init__.py", line 39, in __init__
    self._async_not_empty = asyncio.Condition(self._async_mutex)
  File "lib/python3.10/asyncio/locks.py", line 234, in __init__
    raise ValueError("loop argument must agree with lock")
ValueError: loop argument must agree with lock
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, something is going long with &lt;code&gt;asyncio.Condition()&lt;/code&gt;, which is being called by &lt;code&gt;janus.Queue()&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Some background on Janus&lt;/h4&gt;
&lt;p&gt;The &lt;a href="https://github.com/aio-libs/janus"&gt;Janus&lt;/a&gt; library referenced here describes itself as a "Thread-safe asyncio-aware queue for Python".&lt;/p&gt;
&lt;p&gt;I use it in Datasette for the &lt;a href="https://github.com/simonw/datasette/issues/682"&gt;write queue&lt;/a&gt; - a mechanism where writes can be safely made to a SQLite database by first queueing them up in memory.&lt;/p&gt;
&lt;p&gt;Janus provides a mechanism that lets asyncio event tasks send and receive messages to Python threads, and vice-versa, via a queue class that is modelled on Python's own standard library queues. It's really neat.&lt;/p&gt;
&lt;h4&gt;Tracking the investigation in issue comments&lt;/h4&gt;
&lt;p&gt;Any time I'm investigating a bug I make sure that there's an associated GitHub issue or pull request, to give me somewhere to build up detailed notes of my investigation.&lt;/p&gt;
&lt;p&gt;I used my &lt;a href="https://github.com/simonw/datasette/pull/1481"&gt;PR #1481&lt;/a&gt; for this. Now that I had an error message - "loop argument must agree with lock" - I did some initial Google searches and found to my surprise that it had very few existing mentions - my first clue that this might be related to the new Python 3.10 release itself.&lt;/p&gt;
&lt;p&gt;I tracked down the relevant source code in both Python and the Janus library based on the traceback and linked to them from the issue comments. I pasted in copies of the relevant code too, since GitHub only magically embeds code from the same repository, not code from other repositories. You can see &lt;a href="https://github.com/simonw/datasette/pull/1481#issuecomment-939078872"&gt;those comments here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I always like to include copies of the code most likely to be the cause of a bug in an issue comment, to save myself from having to dig around for it again later on and to act as a historical record once the bug has been fixed.&lt;/p&gt;
&lt;p&gt;I also started an issue in the Janus repo, with the title &lt;a href="https://github.com/aio-libs/janus/issues/358"&gt;Error with Python 3.10: "ValueError: loop argument must agree with lock"&lt;/a&gt;. I linked this to my own issue and posted relevant research in both places.&lt;/p&gt;
&lt;p&gt;The two lines of code in Janus that caused the bug where these ones (note my added comment):&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-s1"&gt;self&lt;/span&gt;.&lt;span class="pl-s1"&gt;_async_mutex&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;asyncio&lt;/span&gt;.&lt;span class="pl-v"&gt;Lock&lt;/span&gt;()
&lt;span class="pl-c"&gt;# "loop argument must agree with lock" exception is raised here:&lt;/span&gt;
&lt;span class="pl-s1"&gt;self&lt;/span&gt;.&lt;span class="pl-s1"&gt;_async_not_empty&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;asyncio&lt;/span&gt;.&lt;span class="pl-v"&gt;Condition&lt;/span&gt;(&lt;span class="pl-s1"&gt;self&lt;/span&gt;.&lt;span class="pl-s1"&gt;_async_mutex&lt;/span&gt;)&lt;/pre&gt;
&lt;p&gt;This is where I began to suspect a Python bug. The above code can be simplified to this:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-s1"&gt;asyncio&lt;/span&gt;.&lt;span class="pl-v"&gt;Condition&lt;/span&gt;(&lt;span class="pl-s1"&gt;asyncio&lt;/span&gt;.&lt;span class="pl-v"&gt;Lock&lt;/span&gt;())&lt;/pre&gt;
&lt;p&gt;This is &lt;a href="https://docs.python.org/3/library/asyncio-sync.html#asyncio.Condition"&gt;a documented way&lt;/a&gt; of using conditions in Python: you can instantiate conditions with an optional lock, which allows that lock to be shared by more than one condition.&lt;/p&gt;
&lt;p&gt;So I tried that in the Python 3.10 interpreter... and it didn't throw the error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% ~/.pyenv/versions/3.10.0/bin/python
Python 3.10.0 (default, Oct  7 2021, 13:45:58) [Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
&amp;gt;&amp;gt;&amp;gt; import asyncio
&amp;gt;&amp;gt;&amp;gt; print(asyncio.Condition(asyncio.Lock()))
&amp;lt;asyncio.locks.Condition object at 0x1062521d0 [unlocked]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From browsing the source code I could see that there was something going on here involving the event loop - so on a hunch I tried running that code in an event loop instead, like so:&lt;/p&gt;
&lt;div class="highlight highlight-text-python-console"&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; &lt;span class="pl-k"&gt;async&lt;/span&gt; &lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;example&lt;/span&gt;():
...     &lt;span class="pl-c1"&gt;print&lt;/span&gt;(asyncio.Condition(asyncio.Lock()))
... 
&amp;gt;&amp;gt;&amp;gt; asyncio.run(example())
Traceback (most recent call last):
  ...
  File "&amp;lt;stdin&amp;gt;", line 2, in example
  File "/Users/simon/.pyenv/versions/3.10.0/lib/python3.10/asyncio/locks.py", line 234, in __init__
    raise ValueError("loop argument must agree with lock")
ValueError: loop argument must agree with lock&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There's the exception!&lt;/p&gt;
&lt;p&gt;So it looked like this might be a genuine Python 3.10 bug. After all, I knew that this code worked in prior versions of Python (since Janus and Datasette worked fine there). In fact I could confirm it like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~ % python3.9
Python 3.9.7 (default, Sep  3 2021, 12:45:31) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
&amp;gt;&amp;gt;&amp;gt; import asyncio
&amp;gt;&amp;gt;&amp;gt; async def example():
...     print(asyncio.Condition(asyncio.Lock()))
... 
&amp;gt;&amp;gt;&amp;gt; asyncio.run(example())
&amp;lt;asyncio.locks.Condition object at 0x1049aab50 [unlocked]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Reporting the bug to the Python issue tracker&lt;/h4&gt;
&lt;p&gt;Python bugs are tracked on &lt;a href="https://bugs.python.org/"&gt;bugs.python.org&lt;/a&gt; - thankfully they support sign in with GitHub (and Google and even OpenID) so I signed in with my GitHub account to report the bug.&lt;/p&gt;
&lt;p&gt;I filed &lt;a href="https://bugs.python.org/issue45416"&gt;Issue 45416&lt;/a&gt; entitled:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;"loop argument must agree with lock" instantiating asyncio.Condition&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And included a brief description with my minimal Python script that demonstrated the bug.&lt;/p&gt;
&lt;p&gt;Then I added a comment pointing back to one of the related GitHub issues where I had been figuring this out, and another comment that linked to &lt;a href="https://github.com/python/cpython/blob/a1092f62492a3fcd6195bea94eccf8d5a300acb1/Lib/test/test_asyncio/test_locks.py#L722-L727"&gt;the most relevant test&lt;/a&gt; in Python's own test suite, which I found using GitHub search for the term &lt;code&gt;Condition&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Any time I file an issue against a project - my own or someone else's - I always like to do a bit of extra research and link to both the likely code at fault and the likely test.&lt;/p&gt;
&lt;p&gt;As a maintainer, this saves me a bunch of time - if the issue links straight to the code and tests I don't have to spend any time digging around looking for them.&lt;/p&gt;
&lt;p&gt;My hope is that this saves time for the people I am reporting the bug to as well, which increases the chance that they'll dig into it and help me find a fix!&lt;/p&gt;
&lt;p&gt;Then I tweeted about it. I have a bunch of followers with relevant experience, so I thought it was time to get a few more eyes on the problem.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;I think I /may/ have found a bug in Python 3.10, which is exciting!&lt;br /&gt;&lt;br /&gt;asyncio.Condition(asyncio.Lock()) raises a ValueError error if executed while an event loop is running&lt;br /&gt;&lt;br /&gt;Means Datasette doesn't work on 3.10 (yet)&lt;a href="https://t.co/ofd9MmzuJS"&gt;https://t.co/ofd9MmzuJS&lt;/a&gt; &lt;a href="https://t.co/ICQz8lf39M"&gt;pic.twitter.com/ICQz8lf39M&lt;/a&gt;&lt;/p&gt;- Simon Willison (@simonw) &lt;a href="https://twitter.com/simonw/status/1446576515940904962?ref_src=twsrc%5Etfw"&gt;October 8, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;p&gt;I included a screenshot of my steps-to-reproduce script to save anyone who might be casually interested from having to click through to the issue itself.&lt;/p&gt;
&lt;h4&gt;Łukasz Langa to the rescue&lt;/h4&gt;
&lt;p&gt;Łukasz is the "CPython Developer in Residence", which means he is paid by the Python Software Foundation to work on Python full-time.&lt;/p&gt;
&lt;p&gt;Less than an hour after I filed the issue he &lt;a href="https://bugs.python.org/msg403506"&gt;posted a comment&lt;/a&gt; that both confirmed the bug and provided two short-term workarounds for it!&lt;/p&gt;
&lt;p&gt;I really liked his first workaround, which looks like this:&lt;/p&gt;
&lt;div class="highlight highlight-text-python-console"&gt;&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; l &lt;span class="pl-k"&gt;=&lt;/span&gt; asyncio.Lock()
&amp;gt;&amp;gt;&amp;gt; &lt;span class="pl-c1"&gt;getattr&lt;/span&gt;(l, &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;_get_loop&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="pl-k"&gt;lambda&lt;/span&gt;: &lt;span class="pl-c1"&gt;None&lt;/span&gt;)()
&amp;lt;_UnixSelectorEventLoop running=True closed=False debug=False&amp;gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It works by calling the &lt;code&gt;lock._get_loop()&lt;/code&gt; method, but only if it exists - so even though that's an undocumented internal method this should be safe even when run against future Python versions that might remove the method.&lt;/p&gt;
&lt;p&gt;You can see &lt;a href="https://github.com/python/cpython/blob/v3.10.0/Lib/asyncio/mixins.py#L22-L31"&gt;that method here&lt;/a&gt; - it safely populates the &lt;code&gt;self._loop&lt;/code&gt; property, which helps work around the bug.&lt;/p&gt;
&lt;h4&gt;Submitting a PR to Janus&lt;/h4&gt;
&lt;p&gt;The best place to apply this workaround is to Janus - so I submitted a PR there which adds the workaround and updates their CI configuration to test against Python 3.10: &lt;a href="https://github.com/aio-libs/janus/pull/359"&gt;Janus PR #359&lt;/a&gt;. The GitHub Actions tests for that PR are now passing against Python 3.10.&lt;/p&gt;
&lt;p&gt;Their README &lt;a href="https://github.com/aio-libs/janus#communication-channels"&gt;links to a gitter chat&lt;/a&gt; so I dropped a link to my PR in there too. Hopefully that can be merged soon!&lt;/p&gt;
&lt;h4&gt;What I should have done&lt;/h4&gt;
&lt;p&gt;I'm feeling pretty good about this situation - the bug is set to be fixed, we have a great workaround thanks to Łukasz and I'm optimistic that the upstream library from Datasette will land the fix soon.&lt;/p&gt;
&lt;p&gt;If only I'd done this a few weeks earlier!&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.python.org/dev/peps/pep-0619/#schedule"&gt;Python 3.10 release schedule&lt;/a&gt; shipped the first alpha a year ago, in October 2020. It totalled 7 alphas, 4 betas and 2 release candidates before the final 3.10 release on Monday 4th October 2021.&lt;/p&gt;
&lt;p&gt;If I'd taken the time to get Datasette's test suite running against Python 3.10 for any of those pre-releases, I could have helped spot this bug before it landed in the final release!&lt;/p&gt;
&lt;p&gt;So lesson learned: Python has alphas and betas and RCs for a reason. For future Pythons I'm going to pay attention to testing against these for my projects before the final release lands.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/async"&gt;async&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github-actions"&gt;github-actions&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="async"/><category term="debugging"/><category term="python"/><category term="weeknotes"/><category term="github-actions"/></entry><entry><title>github-debug.com</title><link href="https://simonwillison.net/2018/Oct/10/github-debugcom/#atom-tag" rel="alternate"/><published>2018-10-10T19:32:35+00:00</published><updated>2018-10-10T19:32:35+00:00</updated><id>https://simonwillison.net/2018/Oct/10/github-debugcom/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github-debug.com/"&gt;github-debug.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is a neat trick: GitHub have a dedicated site for their support engineers to send you to if you can’t connect to them. The site tests download speeds from their various domains and then lets you click a button to have GitHub run a traceroute/ping from their servers to your detected IP address and output the results (use devtools to spy on their API method for doing this). Then you can paste the results into a message to their support team. Turns out fastly-debug.com and dropbox-debug.com implement a similar pattern for those services as well.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://blogs.dropbox.com/tech/2018/10/dropbox-traffic-infrastructure-edge-network/"&gt;Dropbox traffic infrastructure: Edge network&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/support"&gt;support&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="github"/><category term="support"/></entry><entry><title>Quoting @simonw</title><link href="https://simonwillison.net/2018/Feb/3/react/#atom-tag" rel="alternate"/><published>2018-02-03T05:23:36+00:00</published><updated>2018-02-03T05:23:36+00:00</updated><id>https://simonwillison.net/2018/Feb/3/react/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://twitter.com/simonw/status/959655662442590208"&gt;&lt;p&gt;Just switched to &lt;code&gt;{window.localStorage.getItem('debug') &amp;amp;&amp;amp; &amp;lt;pre&amp;gt;{JSON.stringify(this.state, null, 2)}&amp;lt;/pre&amp;gt;}&lt;/code&gt; - now I can ship to production and turn on debugging in my console with &lt;code&gt;localStorage.setItem('debug', 1)&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://twitter.com/simonw/status/959655662442590208"&gt;@simonw&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/react"&gt;react&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="react"/></entry><entry><title>Appending the request URL to SQL statements in Django</title><link href="https://simonwillison.net/2010/Jun/2/framewalking/#atom-tag" rel="alternate"/><published>2010-06-02T09:09:00+00:00</published><updated>2010-06-02T09:09:00+00:00</updated><id>https://simonwillison.net/2010/Jun/2/framewalking/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://chris-lamb.co.uk/2010/06/01/appending-request-url-sql-statements-django/"&gt;Appending the request URL to SQL statements in Django&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A clever frame-walking monkey-patch which pulls the most recent HttpRequest object out of the Python stack and adds the current request.path to each SQL query as an SQL comment, so you can see it in debugging tools such as slow query logs and the PostgreSQL “select * from pg_stat_activity” query.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chris-lamb"&gt;chris-lamb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/orm"&gt;orm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/monkeypatch"&gt;monkeypatch&lt;/a&gt;&lt;/p&gt;



</summary><category term="chris-lamb"/><category term="debugging"/><category term="django"/><category term="orm"/><category term="postgresql"/><category term="python"/><category term="sql"/><category term="recovered"/><category term="monkeypatch"/></entry><entry><title>Fun with TextMate and PDB</title><link href="https://simonwillison.net/2010/Mar/23/xattr/#atom-tag" rel="alternate"/><published>2010-03-23T09:48:09+00:00</published><updated>2010-03-23T09:48:09+00:00</updated><id>https://simonwillison.net/2010/Mar/23/xattr/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://listbot.org/blog/python/2010/03/22/pdb/"&gt;Fun with TextMate and PDB&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
TextMate bookmarks (against lines in a file) are stored as OS X extended attributes, which can be accessed from Python using the xattr module. Here’s a clever piece of code that uses bookmarks to set breakpoints in the command-line pdb debugger.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/macos"&gt;macos&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdb"&gt;pdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/textmate"&gt;textmate&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xattr"&gt;xattr&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="macos"/><category term="pdb"/><category term="python"/><category term="textmate"/><category term="xattr"/></entry><entry><title>Presenting django-devserver, a better runserver</title><link href="https://simonwillison.net/2010/Feb/10/devserver/#atom-tag" rel="alternate"/><published>2010-02-10T11:33:12+00:00</published><updated>2010-02-10T11:33:12+00:00</updated><id>https://simonwillison.net/2010/Feb/10/devserver/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.davidcramer.net/code/1174/presenting-django-devserver-a-better-runserver.html"&gt;Presenting django-devserver, a better runserver&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I really like this—it’s a Django management command (./manage.py rundevserver) which adds SQL logging and cache access summaries to the console output of Django’s local development server. It solves a similar set of problems to the debug toolbar, but requires slightly less setup and doesn’t inject additional HTML in to your pages. You can add your own custom modules to it as well.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/david-cramer"&gt;david-cramer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/devserver"&gt;devserver&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/runserver"&gt;runserver&lt;/a&gt;&lt;/p&gt;



</summary><category term="david-cramer"/><category term="debugging"/><category term="devserver"/><category term="django"/><category term="python"/><category term="runserver"/></entry><entry><title>Debugging in Python</title><link href="https://simonwillison.net/2009/Nov/18/debugging/#atom-tag" rel="alternate"/><published>2009-11-18T12:34:29+00:00</published><updated>2009-11-18T12:34:29+00:00</updated><id>https://simonwillison.net/2009/Nov/18/debugging/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://pythonconquerstheuniverse.wordpress.com/2009/09/10/debugging-in-python/"&gt;Debugging in Python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The missing manual for Python’s powerful pdb debugger.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugger"&gt;debugger&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdb"&gt;pdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugger"/><category term="debugging"/><category term="pdb"/><category term="python"/></entry><entry><title>Deep Tracing of Internet Explorer</title><link href="https://simonwillison.net/2009/Nov/18/tracing/#atom-tag" rel="alternate"/><published>2009-11-18T08:06:47+00:00</published><updated>2009-11-18T08:06:47+00:00</updated><id>https://simonwillison.net/2009/Nov/18/tracing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://ejohn.org/blog/deep-tracing-of-internet-explorer/"&gt;Deep Tracing of Internet Explorer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
dynaTrace Ajax looks like an awesome tool. For once, Internet Explorer has a development tool that other browsers can be jealous of.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ajax"&gt;ajax&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dynatraceajax"&gt;dynatraceajax&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/john-resig"&gt;john-resig&lt;/a&gt;&lt;/p&gt;



</summary><category term="ajax"/><category term="debugging"/><category term="dynatraceajax"/><category term="internet-explorer"/><category term="javascript"/><category term="john-resig"/></entry><entry><title>django-debug-toolbar</title><link href="https://simonwillison.net/2009/Sep/21/debug/#atom-tag" rel="alternate"/><published>2009-09-21T18:36:53+00:00</published><updated>2009-09-21T18:36:53+00:00</updated><id>https://simonwillison.net/2009/Sep/21/debug/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://robhudson.github.com/django-debug-toolbar/"&gt;django-debug-toolbar&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The new panel styling for the Django debug toolbar is really slick—here’s a neatly produced screencast demonstrating it (with Gypsy Jazz accompaniment).


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-debug-toolbar"&gt;django-debug-toolbar&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gypsyjazz"&gt;gypsyjazz&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/screencasts"&gt;screencasts&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="django-debug-toolbar"/><category term="gypsyjazz"/><category term="screencasts"/></entry><entry><title>Debugging Django in Production Revisited</title><link href="https://simonwillison.net/2009/Sep/7/debugging/#atom-tag" rel="alternate"/><published>2009-09-07T05:21:04+00:00</published><updated>2009-09-07T05:21:04+00:00</updated><id>https://simonwillison.net/2009/Sep/7/debugging/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://ericholscher.com/blog/2009/sep/5/debugging-django-production-revisited/"&gt;Debugging Django in Production Revisited&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Eric Holscher expands his show-technical-errors-to-superusers middleware to only show them to users in the group named “Technical Errors”.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/eric-holscher"&gt;eric-holscher&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/middleware"&gt;middleware&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="eric-holscher"/><category term="middleware"/><category term="python"/></entry><entry><title>Why I don't love JavaScript's Module Pattern</title><link href="https://simonwillison.net/2009/Apr/30/module/#atom-tag" rel="alternate"/><published>2009-04-30T19:59:39+00:00</published><updated>2009-04-30T19:59:39+00:00</updated><id>https://simonwillison.net/2009/Apr/30/module/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://snook.ca/archives/javascript/no-love-for-module-pattern"&gt;Why I don&amp;#x27;t love JavaScript&amp;#x27;s Module Pattern&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Jonathan Snook points out that the module pattern (where private functionality is hidden in a closure and only public methods are revealed to outside scopes) makes code a lot harder to debug. I use the module pattern for pretty much everything, not because I want to keep stuff private but more to avoid any chance of leaking out in to the global namespace. If I need to debug a value I temporarily assign it as a property on the global window object.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/closures"&gt;closures&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jonathan-snook"&gt;jonathan-snook&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/modulepattern"&gt;modulepattern&lt;/a&gt;&lt;/p&gt;



</summary><category term="closures"/><category term="debugging"/><category term="javascript"/><category term="jonathan-snook"/><category term="modulepattern"/></entry><entry><title>Finding and fixing memory leaks in Python</title><link href="https://simonwillison.net/2009/Apr/22/finding/#atom-tag" rel="alternate"/><published>2009-04-22T12:16:32+00:00</published><updated>2009-04-22T12:16:32+00:00</updated><id>https://simonwillison.net/2009/Apr/22/finding/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://amix.dk/blog/viewEntry/19420"&gt;Finding and fixing memory leaks in Python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Using Dozer, a clever piece of WSGI middleware which displays sparklines of Python object counts and allows you to introspect them, using the gc module under the hood.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/amir-salihefendic"&gt;amir-salihefendic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memory"&gt;memory&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memoryleaks"&gt;memoryleaks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/middleware"&gt;middleware&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wsgi"&gt;wsgi&lt;/a&gt;&lt;/p&gt;



</summary><category term="amir-salihefendic"/><category term="debugging"/><category term="memory"/><category term="memoryleaks"/><category term="middleware"/><category term="python"/><category term="wsgi"/></entry><entry><title>Announcing django-viewtools</title><link href="https://simonwillison.net/2009/Feb/17/announcing/#atom-tag" rel="alternate"/><published>2009-02-17T21:35:31+00:00</published><updated>2009-02-17T21:35:31+00:00</updated><id>https://simonwillison.net/2009/Feb/17/announcing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://eric.themoritzfamily.com/2009/02/17/announcing-django-viewtools/"&gt;Announcing django-viewtools&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A really excellent idea—run ./manage.py viewtools --pdb /path/on/site/ to debug a view in your Django project that is raising an error using the Python debugger, or use --profile to run the full request cycle for that URL through the profiler.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djangoviewtools"&gt;djangoviewtools&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/eric-moritz"&gt;eric-moritz&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdb"&gt;pdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/profiler"&gt;profiler&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="djangoviewtools"/><category term="eric-moritz"/><category term="pdb"/><category term="profiler"/><category term="python"/></entry><entry><title>ReferenceError: console is not defined</title><link href="https://simonwillison.net/2008/Dec/23/referenceerror/#atom-tag" rel="alternate"/><published>2008-12-23T22:22:39+00:00</published><updated>2008-12-23T22:22:39+00:00</updated><id>https://simonwillison.net/2008/Dec/23/referenceerror/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.codecouch.com/2008/10/referenceerror-console-is-not-defined/"&gt;ReferenceError: console is not defined&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Since Firebug 1.2 you need to call window.loadFirebugConsole() in order for console.log and friends to work.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="firebug"/><category term="firefox"/><category term="javascript"/></entry><entry><title>Introducing the Django Debug Toolbar</title><link href="https://simonwillison.net/2008/Sep/21/introducing/#atom-tag" rel="alternate"/><published>2008-09-21T18:32:24+00:00</published><updated>2008-09-21T18:32:24+00:00</updated><id>https://simonwillison.net/2008/Sep/21/introducing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://rob.cogit8.org/blog/2008/Sep/19/introducing-django-debug-toolbar/"&gt;Introducing the Django Debug Toolbar&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Another project inspired by DjangoCon: a component based debugging toolbar for Django. I like the architecture so far.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djangocon"&gt;djangocon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-debug-toolbar"&gt;django-debug-toolbar&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rob-hudson"&gt;rob-hudson&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="djangocon"/><category term="django-debug-toolbar"/><category term="rob-hudson"/></entry><entry><title>Django snippets: server with debugging backdoor</title><link href="https://simonwillison.net/2008/Sep/8/django/#atom-tag" rel="alternate"/><published>2008-09-08T22:15:45+00:00</published><updated>2008-09-08T22:15:45+00:00</updated><id>https://simonwillison.net/2008/Sep/8/django/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/1038/"&gt;Django snippets: server with debugging backdoor&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Six lines of code that uses spawning to fire up a Django server on port 8000 and a remote interactive interpreter backdoor on port 8001, so you can interogate the state of your server within the same process.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/spawning"&gt;spawning&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="python"/><category term="spawning"/></entry><entry><title>Super User Conditional Page Exception Reporting</title><link href="https://simonwillison.net/2008/Jul/31/django/#atom-tag" rel="alternate"/><published>2008-07-31T21:06:30+00:00</published><updated>2008-07-31T21:06:30+00:00</updated><id>https://simonwillison.net/2008/Jul/31/django/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/935/"&gt;Super User Conditional Page Exception Reporting&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The name is almost as long as the code snippet: this serves Django’s debug page to logged in super-users, falling back to the default 500 template for everyone else.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/exceptions"&gt;exceptions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/middleware"&gt;middleware&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="exceptions"/><category term="middleware"/><category term="python"/></entry><entry><title>Firebug Lite 1.2</title><link href="https://simonwillison.net/2008/Jul/27/firebuglite/#atom-tag" rel="alternate"/><published>2008-07-27T09:30:14+00:00</published><updated>2008-07-27T09:30:14+00:00</updated><id>https://simonwillison.net/2008/Jul/27/firebuglite/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://getfirebug.com/lite.html"&gt;Firebug Lite 1.2&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Huge upgrade to the IE/Opera/Safari bookmarklet version of Firebug—it now has DOM element inspection, XHR tracking and an interactive console, among other features.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://www.stevesouders.com/blog/2008/07/25/firebug-lite-12-released/"&gt;Steve Souders&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/bookmarklets"&gt;bookmarklets&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebuglite"&gt;firebuglite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="bookmarklets"/><category term="debugging"/><category term="firebug"/><category term="firebuglite"/><category term="javascript"/></entry><entry><title>Debugging Django, a slidecast</title><link href="https://simonwillison.net/2008/May/25/debugging/#atom-tag" rel="alternate"/><published>2008-05-25T14:47:02+00:00</published><updated>2008-05-25T14:47:02+00:00</updated><id>https://simonwillison.net/2008/May/25/debugging/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.slideshare.net/simon/debugging-django/"&gt;Debugging Django, a slidecast&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I used SlideShare’s slidecast tool for the first time to synchronize audio of my Django London User Group talk with the slides. The talk included several live demos which aren’t represented in the slides so it’s a bit piecemeal in places.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djugl"&gt;djugl&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/slidecast"&gt;slidecast&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/slideshare"&gt;slideshare&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/speaking"&gt;speaking&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/my-talks"&gt;my-talks&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="djugl"/><category term="slidecast"/><category term="slideshare"/><category term="speaking"/><category term="my-talks"/></entry><entry><title>modswgi: Debugging Techniques</title><link href="https://simonwillison.net/2008/May/25/modwsgi/#atom-tag" rel="alternate"/><published>2008-05-25T13:34:42+00:00</published><updated>2008-05-25T13:34:42+00:00</updated><id>https://simonwillison.net/2008/May/25/modwsgi/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://code.google.com/p/modwsgi/wiki/DebuggingTechniques"&gt;modswgi: Debugging Techniques&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
mod_wsgi is excellent software, and the documentation is equally superb. I used these instructions recently to run the Python debugger inside a running instance of Apache, which helped my track down some import errors that weren’t occurring with Django’s development server.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/modwsgi"&gt;modwsgi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdb"&gt;pdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wsgi"&gt;wsgi&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="django"/><category term="modwsgi"/><category term="pdb"/><category term="python"/><category term="wsgi"/></entry><entry><title>Debugging Django</title><link href="https://simonwillison.net/2008/May/22/debugging/#atom-tag" rel="alternate"/><published>2008-05-22T00:35:40+00:00</published><updated>2008-05-22T00:35:40+00:00</updated><id>https://simonwillison.net/2008/May/22/debugging/#atom-tag</id><summary type="html">
    &lt;p&gt;I gave a talk on Debugging Django applications at &lt;a href="http://upcoming.yahoo.com/event/546918/"&gt;Monday's inaugural meeting&lt;/a&gt; of DJUGL, the London Django Users Group. I wanted to talk about something that wasn't particularly well documented elsewhere, so I pitched the talk as "Bug Driven Development" - what happens when Test Driven Development goes the way of &lt;a href="http://www.shipmentoffail.com/fails/2008/04/horse-vs-car-fail/"&gt;this unfortunate pony&lt;/a&gt;.&lt;/p&gt;

The slides &lt;a href="http://www.slideshare.net/simon/debugging-django/"&gt;are up on SlideShare&lt;/a&gt;, but don't provide quite enough context so I'm going to cover the tips in full here.

&lt;h4&gt;Making the most of the error page&lt;/h4&gt;

&lt;p&gt;Django's default error page is great - it provides a detailed traceback with local variables, lets you expand out the lines of code around the problem, provides a plain text exception suitable for e-mailing to colleagues and even a one-click button to send details to &lt;a href="http://dpaste.com/"&gt;http://dpaste.com/&lt;/a&gt; so you can go and talk about the error on IRC. It also serves the same purpose as &lt;a href="http://www.php.net/phpinfo"&gt;phpinfo()&lt;/a&gt; - it shows you your application's settings, the GET, POST and COOKIE data from the request and the all important META fields assembled from the HTTP environment (great for remembering how to miss-spell HTTP_REFERER).&lt;/p&gt;

&lt;p&gt;Useful tip number one is that you can trigger the error page from any view just by adding the following line:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;assert False&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can serve up an expression with the assertion as well; it will be displayed at the top of the error page:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;assert False, request.GET&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One particularly useful place to use this is when you are building a complex form. If you want to see the data that was submitted, drop an assert False in to the view that the form targets and use the error page to inspect the data.&lt;/p&gt;

&lt;h4&gt;Logging to the development server console&lt;/h4&gt;

&lt;p&gt;If you want to know what's going on inside a view, the quickest way is to drop in a print statement. The development server outputs any print statements directly to the terminal; it's the server-side alternative to a JavaScript alert().&lt;/p&gt;

&lt;p&gt;If you want to be a bit more sophisticated with your logging, it's worth turning to Python's logging module (part of the standard library). You can configure it in your settings.py:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;import logging
logging.basicConfig(
    level = logging.DEBUG,
    format = '%(asctime)s %(levelname)s %(message)s',
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then call it from any of your views:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;def my_view(request):
    import logging
    logging.debug("A log message")
    ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, this will log things to the terminal where the development server is running. If you want to log things to a file you can do so by extending the basicConfig call:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;logging.basicConfig(
    level = logging.DEBUG,
    format = '%(asctime)s %(levelname)s %(message)s',
    filename = '/tmp/myapp.log',
    filemode = 'w'
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can then use &lt;samp&gt;tail -f /tmp/myapp.log&lt;/samp&gt; to see log lines being appended to that file in real-time. This can be used in production as well as development.&lt;/p&gt;

&lt;p&gt;The above just scratches the surface of Python's logging module; with a bit of &lt;a href="http://docs.python.org/lib/module-logging.html" title="Loggingi facility for Python"&gt;digging around in the documentation&lt;/a&gt; you can use it to rotate log files, send log messages over the network and even POST log events to an HTTP server somewhere.&lt;/p&gt;

&lt;p&gt;Often you find yourself dealing with an error that only occurs in certain circumstances - a function might be called from dozens of different places in your program but only runs in to trouble in a very specific case. You can use the &lt;a href="http://docs.python.org/lib/module-traceback.html"&gt;traceback module&lt;/a&gt; to log the current stack, which will allow you to tell how a function was called when something went wrong:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;import logging, traceback, pprint

def my_buggy_function(arg):
    ...
    if error_condition:
        stack = pprint.pformat(traceback.extract_stack())
        logging.debug('An error occurred: %s' % stack)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The tuple returned by &lt;code class="python"&gt;traceback.extract_stack()&lt;/code&gt; includes line numbers, function names and paths to Python files so you can use it to reconstruct a good amount of information about your program.&lt;/p&gt;

&lt;h4&gt;Using the debugger&lt;/h4&gt;

&lt;p&gt;By far the most powerful weapon in my debugging toolkit is the Python debugger, &lt;a href="http://docs.python.org/lib/module-pdb.html"&gt;pdb&lt;/a&gt;. Again, this ships with the standard library so there's nothing extra to install. pdb is a command line debugger (if you want a GUI options include &lt;a href="http://sourceforge.net/projects/pyeclipse/"&gt;PyEclipse&lt;/a&gt; and &lt;a href="http://www.activestate.com/Products/komodo_ide/index.mhtml"&gt;Komodo&lt;/a&gt;, but I haven't used either myself). There are a bunch of ways to activate pdb, but the most straight forward is to simply drop the following line directly in to a Django view function:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;import pdb; pdb.set_trace()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you try to load that page in your browser, the browser will hang - the page will appear to be loading extremely slowly. What's actually happened is the developer server has paused execution and thrown up the pdb interface - you can switch over to your console and start interacting directly with the server mid view.&lt;/p&gt;

&lt;p&gt;Did I mention you should never, ever leave this on in production?&lt;/p&gt;

&lt;p&gt;So, you've got a hung development server and a pdb prompt. What can you do with it? The answer is pretty much anything. I won't provide a full pdb tutorial here (&lt;a href="http://www.onlamp.com/pub/a/python/2005/09/01/debugger.html"&gt;this is a good introduction&lt;/a&gt;), but the commands I find most useful are the following:&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;list&lt;/dt&gt;
    &lt;dd&gt;Shows the lines of source code around your current point of execution. You can run it multiple times to increase the amount of source code displayed.&lt;/dd&gt;
    &lt;dt&gt;n&lt;/dt&gt;
    &lt;dd&gt;Execute the next line&lt;/dd&gt;
    &lt;dt&gt;s&lt;/dt&gt;
    &lt;dd&gt;Same as n, but steps in to any functions that are called. You can quickly get lost in a twisty maze of code with this command, but that's OK because...&lt;/dd&gt;
    &lt;dt&gt;r&lt;/dt&gt;
    &lt;dd&gt;Continues execution until the current function returns&lt;/dd&gt;
    &lt;dt&gt;u&lt;/dt&gt;
    &lt;dd&gt;Goes UP one level in the stack - so you can see the function that called the function you are currently in&lt;/dd&gt;
    &lt;dt&gt;d&lt;/dt&gt;
    &lt;dd&gt;Goes DOWN again&lt;/dd&gt;
    &lt;dt&gt;locals()&lt;/dt&gt;
    &lt;dd&gt;not a pdb command, but handy for seeing what's in your current scope&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;The pdb docs have &lt;a href="http://www.python.org/doc/current/lib/debugger-commands.html"&gt;a full list of commands&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The pdb prompt doubles up as a full Python interactive shell, so you can not only access variables but you can modify them, call functions and generally mess around with the internals of your application as much as you like, while it's running. It's kind of a poor man's imitation of being a Smalltalk developer.&lt;/p&gt;

&lt;p&gt;Remember though, the whole time you're messing around in pdb your browser is still stuck there, waiting for the HTTP request to come back. If you hit "c" (for continue) your application will kick in again, the request will be served and your browser will breathe a sigh of relief.&lt;/p&gt;

&lt;p&gt;Thankfully you don't have to use pdb in a way that freezes your development server; it also works great in the interactive shell. If you've got a buggy function, one way to explore it is to run it interactively, then use the following idiom:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; def function_that_raises_an_exception():
...   assert False
... 
&amp;gt;&amp;gt;&amp;gt; function_that_raises_an_exception()
Traceback (most recent call last):
  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;
  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 2, in function_that_raises_an_exception
AssertionError
&amp;gt;&amp;gt;&amp;gt; import pdb; pdb.pm()
&amp;gt; &amp;lt;stdin&amp;gt;(2)function_that_raises_an_exception()
(Pdb)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;pdb.pm()&lt;/code&gt; stands for post-mortem, and is probably my favourite feature of the debugger - it lets you jump back in to debug the most recently raised exception, even if you hadn't imported pdb at the time the exception was raised.&lt;/p&gt;

&lt;p&gt;One last pdb tip: you can use it to debug Python command line scripts such as Django's custom &lt;samp&gt;./manage.py&lt;/samp&gt; commands. The trick is to run the script like this:&lt;/p&gt;

&lt;p&gt;&lt;samp&gt;python -i manage.py buggy_command&lt;/samp&gt;&lt;/p&gt;

&lt;p&gt;The &lt;samp&gt;-i&lt;/samp&gt; argument causes Python to drop in to the interactive prompt after executing the script. If the script raised an exception, you can then use &lt;code&gt;pdb.pm()&lt;/code&gt; to debug it.&lt;/p&gt;

&lt;h4&gt;Handling errors in production&lt;/h4&gt;

&lt;p&gt;Django's default behaviour in production (that is, when the DEBUG setting is set to False) is to e-mail exception reports to anyone listed in the ADMINS section. You can also turn on e-mail reports on every 404 error with the SEND_BROKEN_LINK_EMAILS setting, which will send them to addresses in the MANAGERS setting. As far as I know these settings don't do anything else - they're a pretty ancient bit of Django.&lt;/p&gt;

&lt;p&gt;On a high traffic site you probably don't want to be e-mailed on every server error. One neat alternative is David Cramer's &lt;a href="http://code.google.com/p/django-db-log/"&gt;django-db-log&lt;/a&gt;, which logs exceptions to a database table. It cleverly uses an MD5 hash of the traceback to aggregate many reports of the same error. More importantly though, it acts as a really straight forward example of how to use Django middleware's process_exception hook to roll your own error reporting. Take a look &lt;a href="http://code.google.com/p/django-db-log/source/browse/trunk/djangodblog/__init__.py"&gt;at the code&lt;/a&gt; to see how simple this is.&lt;/p&gt;

&lt;h4&gt;More useful middleware&lt;/h4&gt;

&lt;p&gt;In the talk I demoed a couple of other handy pieces of middleware. The first was the &lt;a href="http://www.djangosnippets.org/snippets/727/"&gt;ProfilerMiddleware&lt;/a&gt; (one of several profiling tools on &lt;a href="http://www.djangosnippets.org/"&gt;Django Snippets&lt;/a&gt;) which allows you to add &lt;samp&gt;?prof&lt;/samp&gt; to the end of any URL to see the output of Python's &lt;a href="http://docs.python.org/lib/module-profile.html"&gt;cProfile module&lt;/a&gt; run against that request. The second is one that I've just released: &lt;a href="http://www.djangosnippets.org/snippets/766/"&gt;DebugFooter&lt;/a&gt;, which adds a footer showing exactly which templates were loaded from where (handy for debugging complex template paths) as well as every executed SQL query and how long each one took.&lt;/p&gt;

&lt;h4&gt;Abusing the test client&lt;/h4&gt;

&lt;p&gt;A final tip for exploring your application interactively is to learn to use &lt;a href="http://www.djangoproject.com/documentation/testing/"&gt;Django's TestClient&lt;/a&gt;. Although designed for use in unit tests, this tool is equally useful for use at the interactive prompt. It allows you to simulate an in-process request against your application from within your Python code. Here's an example:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; from django.test.client import Client
&amp;gt;&amp;gt;&amp;gt; c = Client()
&amp;gt;&amp;gt;&amp;gt; response = c.get(&amp;quot;/&amp;quot;) # The homepage
&amp;gt;&amp;gt;&amp;gt; response
&amp;lt;django.http.HttpResponse object at 0x2300470&amp;gt;
&amp;gt;&amp;gt;&amp;gt; print response
Vary: Cookie
Content-Type: text/html; charset=utf-8

&amp;lt;!DOCTYPE HTML PUBLIC &amp;quot;-//W3C//DTD HTML 4.01//EN&amp;quot;
    &amp;quot;http://www.w3.org/TR/html4/strict.dtd&amp;quot;&amp;gt;
&amp;lt;html&amp;gt;
...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The response object you get back is the &lt;code class="python"&gt;HttpResponse&lt;/code&gt; returned by the view, ready to be explored interactively.&lt;/p&gt;

&lt;p&gt;There's another function from the unit testing tools that can help with interactively exploring an application: &lt;code class="python"&gt;setup_test_environment()&lt;/code&gt;. This function monkey-patches in some additional hooks used by the unit tests, including one that intercepts template render calls and adds information on them to the request object. Here's an example:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; from django.test.utils import setup_test_environment
&amp;gt;&amp;gt;&amp;gt; setup_test_environment()
&amp;gt;&amp;gt;&amp;gt; from django.test.client import Client
&amp;gt;&amp;gt;&amp;gt; c = Client()
&amp;gt;&amp;gt;&amp;gt; response = c.get(&amp;quot;/&amp;quot;)
&amp;gt;&amp;gt;&amp;gt; response.template
[&amp;lt;django.template.Template object at 0x2723dd0&amp;gt;,
 &amp;lt;django.template.Template object at 0x2723f30&amp;gt;,
 &amp;lt;django.template.Template object at 0x273ee10&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; response.context
[ list of Context objects ]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This allows you to explore not just the HTML returned by a view, but also the templates and contexts that were used to render it.&lt;/p&gt;

&lt;h4&gt;Your tips welcome&lt;/h4&gt;

&lt;p&gt;If you have any useful tips on debugging Django applications, please share them in the comments on this entry.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/djugl"&gt;djugl&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/middleware"&gt;middleware&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/my-talks"&gt;my-talks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/testing"&gt;testing&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="debugging"/><category term="django"/><category term="djugl"/><category term="middleware"/><category term="my-talks"/><category term="testing"/></entry><entry><title>Firebug Command Line API</title><link href="https://simonwillison.net/2008/May/16/firebug/#atom-tag" rel="alternate"/><published>2008-05-16T12:14:34+00:00</published><updated>2008-05-16T12:14:34+00:00</updated><id>https://simonwillison.net/2008/May/16/firebug/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://getfirebug.com/commandline.html"&gt;Firebug Command Line API&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Another thing I didn’t know about Firebug: you can set a breakpoint at the start of a function with “debug(fn)” and log all calls to it with “monitor(fn)”.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="firebug"/><category term="javascript"/></entry><entry><title>Opera Dragonfly</title><link href="https://simonwillison.net/2008/May/6/opera/#atom-tag" rel="alternate"/><published>2008-05-06T19:04:41+00:00</published><updated>2008-05-06T19:04:41+00:00</updated><id>https://simonwillison.net/2008/May/6/opera/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.opera.com/products/dragonfly/"&gt;Opera Dragonfly&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Opera’s new Firebug-style developer console. Out in alpha and it shows (slow to load and the interactive console leaves a lot to be desired) but still looks incredibly promising, especially the remote debugging tools for working with Opera on phones and games consoles.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dragonfly"&gt;dragonfly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/opera"&gt;opera&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="dragonfly"/><category term="firebug"/><category term="opera"/></entry><entry><title>Advanced JavaScript Debugging Techniques</title><link href="https://simonwillison.net/2008/Apr/4/sitepen/#atom-tag" rel="alternate"/><published>2008-04-04T07:51:05+00:00</published><updated>2008-04-04T07:51:05+00:00</updated><id>https://simonwillison.net/2008/Apr/4/sitepen/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.sitepen.com/blog/2008/04/03/advanced-javascript-debugging-techniques/"&gt;Advanced JavaScript Debugging Techniques&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
There’s more to JavaScript debugging than just Firebug.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sitepen"&gt;sitepen&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="firebug"/><category term="javascript"/><category term="sitepen"/></entry><entry><title>Firebug + Dijit tips</title><link href="https://simonwillison.net/2008/Mar/17/dojocampus/#atom-tag" rel="alternate"/><published>2008-03-17T02:08:23+00:00</published><updated>2008-03-17T02:08:23+00:00</updated><id>https://simonwillison.net/2008/Mar/17/dojocampus/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://dojocampus.org/content/?p=68"&gt;Firebug + Dijit tips&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
News to me: Firebug has a magic $1 variable which corresponds to the currently selected node. Very handy.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dijit"&gt;dijit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dojo"&gt;dojo&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="dijit"/><category term="dojo"/><category term="firebug"/><category term="javascript"/></entry><entry><title>Quoting Jon Hicks</title><link href="https://simonwillison.net/2007/Dec/20/coda/#atom-tag" rel="alternate"/><published>2007-12-20T15:09:02+00:00</published><updated>2007-12-20T15:09:02+00:00</updated><id>https://simonwillison.net/2007/Dec/20/coda/#atom-tag</id><summary type="html">
    &lt;blockquote cite="http://www.hicksdesign.co.uk/journal/my-2007-in-blogs-music-events-and-apps"&gt;&lt;p&gt;I don't even use Firefox and Firebug anymore, the revised Web Inspector in Leopard has been incorporated in Coda and that does everything I need and more.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="http://www.hicksdesign.co.uk/journal/my-2007-in-blogs-music-events-and-apps"&gt;Jon Hicks&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/coda"&gt;coda&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jon-hicks"&gt;jon-hicks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/leopard"&gt;leopard&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/software"&gt;software&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webinspector"&gt;webinspector&lt;/a&gt;&lt;/p&gt;



</summary><category term="coda"/><category term="css"/><category term="debugging"/><category term="firebug"/><category term="firefox"/><category term="html"/><category term="jon-hicks"/><category term="leopard"/><category term="software"/><category term="webinspector"/></entry><entry><title>DebugBar</title><link href="https://simonwillison.net/2007/Nov/25/debugbar/#atom-tag" rel="alternate"/><published>2007-11-25T10:32:14+00:00</published><updated>2007-11-25T10:32:14+00:00</updated><id>https://simonwillison.net/2007/Nov/25/debugbar/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.debugbar.com/"&gt;DebugBar&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Suggested at BarCamp London 3 as a useful tool for developing with IE; apparently includes a great JavaScript debugger.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/barcamplondon3"&gt;barcamplondon3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugbar"&gt;debugbar&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugger"&gt;debugger&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="barcamplondon3"/><category term="debugbar"/><category term="debugger"/><category term="debugging"/><category term="internet-explorer"/><category term="javascript"/></entry><entry><title>jQuery Logging</title><link href="https://simonwillison.net/2007/Oct/19/jquery/#atom-tag" rel="alternate"/><published>2007-10-19T12:52:50+00:00</published><updated>2007-10-19T12:52:50+00:00</updated><id>https://simonwillison.net/2007/Oct/19/jquery/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://happygiraffe.net/blog/archives/2007/09/26/jquery-logging"&gt;jQuery Logging&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Brilliant four line jQuery plugin that lets you insert Firebug console.log() calls directly in to chains.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://happygiraffe.net/blog/"&gt;Jabbering Giraffe&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/debugging"&gt;debugging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dominic-mitchell"&gt;dominic-mitchell&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firebug"&gt;firebug&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jquery"&gt;jquery&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/logging"&gt;logging&lt;/a&gt;&lt;/p&gt;



</summary><category term="debugging"/><category term="dominic-mitchell"/><category term="firebug"/><category term="javascript"/><category term="jquery"/><category term="logging"/></entry></feed>