<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Bill Prin</title>
        <link>undefined</link>
        <description>Bill Prin's blog on tech, entrepreneurship, and personal development</description>
        <lastBuildDate>Tue, 22 Apr 2025 21:50:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Bill Prin</title>
            <url>undefined/favicon.ico</url>
            <link>undefined</link>
        </image>
        <copyright>All rights reserved 2025</copyright>
        <item>
            <title><![CDATA[How to Use MCP to Let Your AI IDE See and Fix Browser Console Errors (with Cursor)]]></title>
            <link>undefined/articles/mcp-cursor-browser-errors</link>
            <guid>undefined/articles/mcp-cursor-browser-errors</guid>
            <pubDate>Tue, 22 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Let your AI see and fix its errors during web dev by leveraging MCP to connect Cursor AI code assistant to the browser consoe logs.]]></description>
            <content:encoded><![CDATA[<h2>Intro</h2>
<p><strong>MCP (Model Context Protocol)</strong> is an open standard developed by Anthropic that allows AI agents to interact with external tools.</p>
<p>By giving the AI access to real-world context, MCP reduces hallucinations and expands what agents can actually do — from querying live APIs to inspecting browser state.</p>
<p>One of the simplest and most effective use cases is <strong>letting the AI see your browser console logs</strong>. For web development, this is a huge unlock. Normally, if the AI builds something buggy, it&#x27;s completely blind to runtime errors unless you manually copy and paste the error into your chat window.</p>
<p>That’s not just tedious — it also breaks multi-step agent workflows. If an AI is trying to implement a feature but hits a runtime error, it often can’t recover without visibility into what went wrong.</p>
<p>Connecting console logs is the first step toward giving the AI full awareness of your browser — and it&#x27;s already incredibly useful.</p>
<p>While there’s a lot of hype around advanced MCP integrations (project tracking, build systems, etc.), debugging browser errors with MCP is the most accessible and practical place to start.</p>
<p>In this guide, you’ll learn how to set it up using Cursor IDE, the most popular AI coding assistant today. The same approach works with tools like Cline or Windsurf if you&#x27;re using one of those instead.</p>
<h2>The Plan</h2>
<p>This tutorial uses the <a href="https://github.com/AgentDeskAI/browser-tools-mcp">Agent Desk AI plugin</a>. While their docs are sufficient, they cover a lot of ground unnecessary to a simple setup. They&#x27;re also slightly out of sync with the current version of Cursor, and glossed over some important concepts such as the difference between SSE and function MCPs.</p>
<p>We want the simplest possible proof-of-concept that lets us verify the AI can see and fix a browser error. So given that, the plan is:</p>
<ol>
<li>Create a new NextJS app</li>
<li>Throw a simple error that will show in the browser logs</li>
<li>Install the BrowserDesk tool</li>
<li>Wire Cursor together to the MCP tool</li>
<li>Verify the AI agent can now see the console errors and fix the issue</li>
</ol>
<h3>Creating The App</h3>
<p>For this experiment we will just create a generic NextJS app:</p>
<pre><code>npx create-next-app@latest mcp-test 
</code></pre>
<p>and now we can just create a simple error in the main index file:</p>
<pre><code>  useEffect(() =&gt; {
    // Simulate a bug
    const broken = undefined;
    // console.log(broken.length); // This will throw an error - Commented out to fix
  }, []);
</code></pre>
<p>Now we have a nice big fat red error:</p>
<img alt="error in browser logs" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrowser_logs.4f4f635b.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrowser_logs.4f4f635b.png&amp;w=1080&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrowser_logs.4f4f635b.png&amp;w=1080&amp;q=75" width="456" height="221" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>If we use Cursor to fix the error, it will tell us it has no way of seeing the error.</p>
<h3>Installing and Configuring the Browser Desk Tool</h3>
<p>The AgentDesk plugin has 3 parts:</p>
<ol>
<li>A chrome extension that provides the information about the browser logs</li>
<li>A Node server that knows how to talk to the chrome extension</li>
<li>Another CLI tool that knows how to query the Node server for the browser information</li>
</ol>
<h3>MCP Function vs Server</h3>
<p>This is already a few different moving parts, but what makes this especially confusing is that you&#x27;re running a server but it&#x27;s
<em>not</em> AN MCP server. An MCP server is a specific standard that must implement features like SSE (server-side events) which
this server does not. Instead, the final tool is an MCP <em>function</em>, which talks to a custom non-MCP server.</p>
<h2>Installing the Chrome Extension</h2>
<p>Download the chrome extension <a href="https://github.com/AgentDeskAI/browser-tools-mcp/releases/download/v1.2.0/BrowserTools-1.2.0-extension.zip">here</a>, then go to Chrome -&gt; Extensions -&gt; Load Unpacked and select the
zip file. Make sure its running in Chrome.</p>
<h2>Installing the Server</h2>
<p>The server step is the simplest, as you just run:</p>
<pre><code> npx @agentdeskai/browser-tools-server@latest`
</code></pre>
<p>in your terminal. Note that thsi should be done before running the <code>browser-tools-mcp@latest</code> in the next step, despite the GitHub README mistakenly switching the order at one point.</p>
<h2>Configure the MCP</h2>
<p>The final step is to give Cursor the ability to run the command to talk to the server. To do that, we just go to Cursor-&gt;Settings-&gt;Install New MCP server and
drop off this JSON:</p>
<pre><code>{
 &quot;mcpServers&quot;: {
   &quot;browser-tools&quot;: {
     &quot;command&quot;: &quot;npx&quot;,
     &quot;args&quot;: [&quot;-y&quot;, &quot;@agentdeskai/browser-tools-mcp@1.2.0&quot;]
   }
 }
}
</code></pre>
<p>as you can see the MCP in this case is not a server but instead a CLI command.</p>
<h3>It Should Work</h3>
<p>You should see Cursor show that the MCP is now loaded:</p>
<img alt="mcp servers working" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmcp_servers.b9d1961e.png&amp;w=828&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmcp_servers.b9d1961e.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmcp_servers.b9d1961e.png&amp;w=1920&amp;q=75" width="803" height="233" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>And now if you ask a model in Cursor if it sees the error, it should tell you it sees the error and be able to fix it.</p>
<h3>Conclusion</h3>
<p>Browser dev logs are a no-brainer use case for MCP and the first anyone working in webdev should setup. Hit me on twitter @bill_prin or email <a href="mailto:waprin@gmail.com">waprin@gmail.com</a> with any comments.</p>]]></content:encoded>
            <author>waprin@gmail.com (Bill Prin)</author>
        </item>
        <item>
            <title><![CDATA[Why I Ditched Django for NextJS]]></title>
            <link>undefined/articles/why-i-ditched-django-for-nextjs</link>
            <guid>undefined/articles/why-i-ditched-django-for-nextjs</guid>
            <pubDate>Sun, 30 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Why I made the switch away from Python frameworks like Django and Flask to NextJS]]></description>
            <content:encoded><![CDATA[<img alt="" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdjangotonext.e0092ba4.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdjangotonext.e0092ba4.png&amp;w=1080&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdjangotonext.e0092ba4.png&amp;w=1080&amp;q=75" width="481" height="133" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>This post is about why I stopped using the web framework Django in favor of NextJS. More broadly, it’s about why I would entirely avoid Python web frameworks, such as Flask, if you plan to serve up any HTML. While I’ve only dabbled in the Ruby community, I imagine most of my points will also apply to frameworks like Rails.</p>
<p>The summary is that using a language like Python or Ruby for a significant web project has increasingly gotten less reasonable over time to the point where now, in 2022, it’s getting hard to justify. By not keeping your web stack in pure Javascript, you are making your life unnecessarily difficult (as usual, we’ll include languages like Typescript as part of the Javascript ecosystem). You will almost certainly invest a bunch of time-solving problems that would be automatically solved for you if you just stuck with Javascript.</p>
<p>I will provide specific examples of solving problems using Django that would have been trivially solved in NextJS.</p>
<p>I can only think of two valid reasons to use Python or Ruby for the web in 2022:</p>
<ul>
<li>You’re working on an existing project that hasn’t been migrated yet or is not worth migrating.</li>
<li>You are already a master of a Python or Ruby web stack, and you need to implement a new project as soon as possible, and you don’t have time to learn a better stack.</li>
</ul>
<p>Developer circles have a sentiment that it’s best to master a few tools, know them well, and stick with them. It’s good advice to avoid constantly chasing new tools and frameworks over getting any work done. The developers I know who are best at shipping things have a few solid tools they stick with and focus on shipping products rather than updating their tech stacks.  But, like many pieces of advice, you need to find a balance.</p>
<p>When I first found Django, I fell in love with how productive and &quot;batteries included&quot; it was and switched my &quot;side project stack&quot; from PHP to Django. I pushed to work on Python and Django and when I joined Google I led all the documentation around using Django on Google Cloud Platform, including docs on how to run it on App Engine (managed and flexible), Compute Engine, and Kubernetes / Container engine. My only Github repo that&#x27;s attracted non-negligible attention is a <a href="https://github.com/waprin/kubernetes_django_postgres_redis">Django on Kubernetes sample</a>. And in past few years the vast majority of my development focus has been on Python.</p>
<p>So I was not eager to get rid of Python, if anything my career has me tagged as a “Python guy”. In 2020, my non-technical friend really wanted to make a little side project where we listed music livestreams and hosted Zoom parties around them. We felt there was a cultural wave as part of Covid and wanted to move fast so I stuck with what I knew which was Django. In some sense it worked really well because I had several non-technical people use the admin console to update the website with minimal influence from me.</p>
<p>The issue is that I ran into several problems:</p>
<ul>
<li>At first, I thought I didn’t need fancy frameworks like React, and it’s better to use something simple, which was <a href="https://zeptojs.com/">ZeptoJS</a> (a more perfomant jQuery clone). But even the “simple” features our website needed like an infinite scroll of music livestreams started turning into spaghetti code. I wanted to move to React to achieve a more declarative approach but getting Django and React to play nicely together was non-trivial. NextJS makes it trivial to start a new React project, and even brings me back to the PHP days of mapping the filename to a single page</li>
<li>My webpage loaded a bunch of images and rendered them in a gallery which was very slow. I wanted to only load the image when the user scrolled to see it. To accomplish this, I had to use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">Intersection API</a>, which was fun, but <a href="https://nextjs.org/docs/api-reference/next/image">NextJS Image</a> component does this out of the box.</li>
<li>Because I wanted infinite scroll to make the website more modern, I had to render the grid components as they scrolled. It would have been better to do this server-side since it would be faster, but it also needed to be done client-side after the timezone was updated. (see my post on
<a href="/articles/how-to-show-local-timezones">How to show local timezones on a webpage without asking the user for their location
</a>)</li>
<li>Furthermore, I  wanted the first page load to be very quick and contain content so the SEO would be faster. Some of the content could be rendered ahead of time statically. But again, I had to rewrite the same code in Python and in Javascript</li>
</ul>
<p>So one of my biggest problem was wanting to render the same component, but some times statically ahead of time, sometimes dynamically on the server (for user specific cases), and sometimes on the client (to re-render any timezone updates).</p>
<p>Being able to easily render components statically, server-side, or client-side is one of the fundamental features of NextJS. This is in addition to a ton of commonly used frontend optimizations like Image viewport rendering that Django simply does not offer. Django is not really “batteries included” anymore now that the batteries have changed.</p>
<p>Besides these features, there’s a few other good reasons to stick with Javascript. For one, every web project uses it so almost all of the good tooling for other things you need such as CSS preprocessing will be Javascript-centric. You will inevitably need NodeJS in your project , and if you have two languages, now you need to worry about tooling for both (e.g. dependency upgrades).</p>
<p>When NodeJS first came out, Python still felt more usable in many ways. NodeJS would easily turn into callback hell. But in 2022, it’s Python that feels hobbled by an awkward async story, while NodeJS async/await feels much simpler. Typescript also feels more fleshed out than Python typing and the tooling around it feels easier to understand.</p>
<p>Python is still an amazing language and it’s still probably the best “jack-of-all trades” language there is. It might make sense to use something like Django Rest Framework if you plan to do a significant amount of machine learning and want to use one language to write all your models. Of course, I’m very indebted to all the amazing OSS contributions made by the Python and Django communities.</p>
<p>But going forward, Django is something I will look back fondly on rather than use for any projects.  Aside from quick experiments for learning purposes, I plan to stick entirely within the Javascript ecosystem for web projects.</p>]]></content:encoded>
            <author>waprin@gmail.com (Bill Prin)</author>
        </item>
        <item>
            <title><![CDATA[The Curious Physics of Slime Volleyball]]></title>
            <link>undefined/articles/curious-physics-of-slime-volleyball</link>
            <guid>undefined/articles/curious-physics-of-slime-volleyball</guid>
            <pubDate>Sun, 11 Oct 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Explaining 2D elastic collision through the lens of a classic 2D Flash game.]]></description>
            <content:encoded><![CDATA[<h2>2D Bouncing Balls Physics Tutorial (Elastic Collision)</h2>
<p>This post will explore the classic game Slime Volleyball and some of its physics, notably 2D ball elastic collision.</p>
<p>It will go over:</p>
<ul>
<li>Some history of Slime Volleyball and how it&#x27;s being used by cutting edge deep learning AI researchers</li>
<li>How Conservation of Momentum and Conservation of Kinetic Energy serve as the underlying equations for the calculations</li>
<li>Some practical tips on how to understand it intuitively, how to code it in up in a 2D game, and how the game physics diverges from real world physics</li>
</ul>
<p>This post was created while writing an implementation of <a href="http://github.com/waprin/gopher-volleyball">Slime Volleyball in Go using SDL 2</a>.</p>
<h1>Background</h1>
<p>Slime Volleyball was a viral Java applet game that emerged around the year 2000, and I have many fond memories of playing it as a kid. I was watching <a href="https://twitter.com/francesc">Francesc Campoy&#x27;s</a> excellent just for func video series where he coded up a <a href="https://github.com/campoy/flappy-gopher">Go version of Flappy Bird</a>, and I wanted to follow along with a different game, so I chose Slime Volleyball. However, when I got to the collision of the ball and the slime, I realized that I didn&#x27;t understand how to correctly code it. So I started looking at some other implementations and tried to learn a little more about how the calculations are derived.</p>
<p>One option would be to just reach for a physics engine like Box2D, but I wanted to learn more about the underlying physics, wanted to be able to tweak it more easily, and I didn&#x27;t want to bring in a big dependency for what was ultimately just a few lines of code.</p>
<p>In most games, it&#x27;s not super critical that the game physics closely match real world physics, and you often intentionally diverge from them either out of practical necessity or as an intentional part of the design. However, as humans we do have some intuition for how things work in the &quot;real world&quot; so having at least some understanding of how the real physics work is a good baseline to start from. Slime Volleyball physics both use some simplifications, some &quot;fudge&quot; factors, and then one major divergence from real world physics which I will review.</p>
<p>Along the way reviewing other implementations, I stumbled upon <a href="https://twitter.com/hardmaru">hardmaru</a>, a Google Brain researcher that uses Slime Volleyball to <a href="https://github.com/hardmaru/slimevolleygym">compare reinforcement learning algorithms</a>.</p>
<p>Like Bitcoin, the original creator of Slime Volleyball is unknown, though at least one version was maintained by Quin Pendragon and Daniel Wedge. Reddit user /u/marler8997 decompiled the Java applet and rewrote it in Javascript for an <a href="https://www.reddit.com/r/gaming/comments/3b7j47/html5_version_of_slime_volleyball/">HTML5 version</a> which I used as a reference for my Go version. However, his collision physics code isn&#x27;t exactly easy to understand.</p>
<pre class="language-javascript"><code class="language-javascript">
 <span class="token keyword">function</span> <span class="token function">collisionBallSlime</span><span class="token punctuation">(</span><span class="token parameter">s</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
   <span class="token keyword">var</span> dx <span class="token operator">=</span> <span class="token number">2</span> <span class="token operator">*</span> <span class="token punctuation">(</span>ball<span class="token punctuation">.</span><span class="token property-access">x</span> <span class="token operator">-</span> s<span class="token punctuation">.</span><span class="token property-access">x</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token keyword">var</span> dy <span class="token operator">=</span> ball<span class="token punctuation">.</span><span class="token property-access">y</span> <span class="token operator">-</span> s<span class="token punctuation">.</span><span class="token property-access">y</span><span class="token punctuation">;</span>
   <span class="token keyword">var</span> dist <span class="token operator">=</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">sqrt</span><span class="token punctuation">(</span>dx <span class="token operator">*</span> dx <span class="token operator">+</span> dy <span class="token operator">*</span> dy<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

   <span class="token keyword">var</span> dVelocityX <span class="token operator">=</span> ball<span class="token punctuation">.</span><span class="token property-access">velocityX</span> <span class="token operator">-</span> s<span class="token punctuation">.</span><span class="token property-access">velocityX</span><span class="token punctuation">;</span>
   <span class="token keyword">var</span> dVelocityY <span class="token operator">=</span> ball<span class="token punctuation">.</span><span class="token property-access">velocityY</span> <span class="token operator">-</span> s<span class="token punctuation">.</span><span class="token property-access">velocityY</span><span class="token punctuation">;</span>

   <span class="token keyword control-flow">if</span><span class="token punctuation">(</span>dy <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> dist <span class="token operator">&lt;</span> ball<span class="token punctuation">.</span><span class="token property-access">radius</span> <span class="token operator">+</span> s<span class="token punctuation">.</span><span class="token property-access">radius</span> <span class="token operator">&amp;&amp;</span> dist <span class="token operator">&gt;</span> <span class="token constant">FUDGE</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
     <span class="token keyword">var</span> oldBall <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span>ball<span class="token punctuation">.</span><span class="token property-access">x</span><span class="token punctuation">,</span><span class="token literal-property property">y</span><span class="token operator">:</span>ball<span class="token punctuation">.</span><span class="token property-access">y</span><span class="token punctuation">,</span><span class="token literal-property property">velocityX</span><span class="token operator">:</span>ball<span class="token punctuation">.</span><span class="token property-access">velocityX</span><span class="token punctuation">,</span><span class="token literal-property property">velocityY</span><span class="token operator">:</span>ball<span class="token punctuation">.</span><span class="token property-access">velocityY</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
      ball<span class="token punctuation">.</span><span class="token property-access">x</span> <span class="token operator">=</span> s<span class="token punctuation">.</span><span class="token property-access">x</span> <span class="token operator">+</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token property-access">radius</span> <span class="token operator">+</span> ball<span class="token punctuation">.</span><span class="token property-access">radius</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">*</span> dx <span class="token operator">/</span> dist<span class="token punctuation">)</span><span class="token punctuation">;</span>


     ball<span class="token punctuation">.</span><span class="token property-access">y</span> <span class="token operator">=</span> s<span class="token punctuation">.</span><span class="token property-access">y</span> <span class="token operator">+</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token property-access">radius</span> <span class="token operator">+</span> ball<span class="token punctuation">.</span><span class="token property-access">radius</span><span class="token punctuation">)</span> <span class="token operator">*</span> dy <span class="token operator">/</span> dist<span class="token punctuation">)</span><span class="token punctuation">;</span>
     <span class="token keyword">var</span> something <span class="token operator">=</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>dx <span class="token operator">*</span> dVelocityX <span class="token operator">+</span> dy <span class="token operator">*</span> dVelocityY<span class="token punctuation">)</span> <span class="token operator">/</span> dist<span class="token punctuation">)</span><span class="token punctuation">;</span>

     <span class="token keyword control-flow">if</span><span class="token punctuation">(</span>something <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
       ball<span class="token punctuation">.</span><span class="token property-access">velocityX</span> <span class="token operator">+=</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token property-access">velocityX</span> <span class="token operator">-</span> <span class="token number">2</span> <span class="token operator">*</span> dx <span class="token operator">*</span> something <span class="token operator">/</span> dist<span class="token punctuation">)</span><span class="token punctuation">;</span>
       ball<span class="token punctuation">.</span><span class="token property-access">velocityY</span> <span class="token operator">+=</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token property-access">velocityY</span> <span class="token operator">-</span> <span class="token number">2</span> <span class="token operator">*</span> dy <span class="token operator">*</span> something <span class="token operator">/</span> dist<span class="token punctuation">)</span><span class="token punctuation">;</span>
       <span class="token keyword control-flow">if</span><span class="token punctuation">(</span>     ball<span class="token punctuation">.</span><span class="token property-access">velocityX</span> <span class="token operator">&lt;</span> <span class="token operator">-</span><span class="token constant">MAX_VELOCITY_X</span><span class="token punctuation">)</span> ball<span class="token punctuation">.</span><span class="token property-access">velocityX</span> <span class="token operator">=</span> <span class="token operator">-</span><span class="token constant">MAX_VELOCITY_X</span><span class="token punctuation">;</span>
       <span class="token keyword control-flow">else</span> <span class="token keyword control-flow">if</span><span class="token punctuation">(</span>ball<span class="token punctuation">.</span><span class="token property-access">velocityX</span> <span class="token operator">&gt;</span>  <span class="token constant">MAX_VELOCITY_X</span><span class="token punctuation">)</span> ball<span class="token punctuation">.</span><span class="token property-access">velocityX</span> <span class="token operator">=</span>  <span class="token constant">MAX_VELOCITY_X</span><span class="token punctuation">;</span>
       <span class="token keyword control-flow">if</span><span class="token punctuation">(</span>     ball<span class="token punctuation">.</span><span class="token property-access">velocityY</span> <span class="token operator">&lt;</span> <span class="token operator">-</span><span class="token constant">MAX_VELOCITY_Y</span><span class="token punctuation">)</span> ball<span class="token punctuation">.</span><span class="token property-access">velocityY</span> <span class="token operator">=</span> <span class="token operator">-</span><span class="token constant">MAX_VELOCITY_Y</span><span class="token punctuation">;</span>
       <span class="token keyword control-flow">else</span> <span class="token keyword control-flow">if</span><span class="token punctuation">(</span>ball<span class="token punctuation">.</span><span class="token property-access">velocityY</span> <span class="token operator">&gt;</span>  <span class="token constant">MAX_VELOCITY_Y</span><span class="token punctuation">)</span> ball<span class="token punctuation">.</span><span class="token property-access">velocityY</span> <span class="token operator">=</span>  <span class="token constant">MAX_VELOCITY_Y</span><span class="token punctuation">;</span>
     <span class="token punctuation">}</span>
   <span class="token punctuation">}</span>
 <span class="token punctuation">}</span>

</code></pre>
<p>What is <code>something</code> variable supposed to be? I will review that now:</p>
<h2>Elastic Collision Physics</h2>
<p>The main concept to understand the bounce is <a href="https://en.wikipedia.org/wiki/Elastic_collision">Elastic Collision</a>. This means a collision with no loss of energy. In the real world there would be some friction and loss of energy, but we can ignore that in our slime world. In an elastic collision, two equations apply, the Conservation of Momentum and Conservation of Kinetic Energy (this one only applying in elastic idealized collisions). The Conservation of Momentum states that:</p>
<img alt="equation for conservation of momentum" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmomentum.e56244b8.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmomentum.e56244b8.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmomentum.e56244b8.png&amp;w=1920&amp;q=75" width="722" height="64" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>and the Conservation of Kinetic Energy states that:</p>
<img alt="equation for conservation of kinetic energy" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fkinetic.9c9a3b68.png&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fkinetic.9c9a3b68.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fkinetic.9c9a3b68.png&amp;w=1920&amp;q=75" width="880" height="80" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>Now what you can note is that, since we can assign whatever masses to our two circles that we want, and we know their initial velocities, there&#x27;s only two unknowns, which are the two final velocities. And we have two equations, and two unknowns, so we can solve for those two unknowns.</p>
<p>However, the actual algebraic derivations are quite tricky, so we can take Wikipedia&#x27;s word for it and use their final equations, particularly the &quot;angle-free&quot; equations which use dot-product calculations instead of using trigonometry on the angle of the collision:</p>
<img alt="derived equation for elastic collision" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ffinal_elastic.77221244.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ffinal_elastic.77221244.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ffinal_elastic.77221244.png&amp;w=3840&amp;q=75" width="1746" height="222" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>In our case, the slime is x1 with velocity v1 and mass m1, and the ball is at position <code>x2</code> with velocity <code>v2</code> and mass <code>m2</code>.</p>
<p>A helpful note, in this case <code>x1</code> and <code>x2</code> are the position values, which each have an <code>(x,y)</code> component. This <code>(x,y)</code> component represents the center of the circle (the slime itself is represented as a circle with the bottom half not rendered).</p>
<p>The parentheses in the Wikipedia equation represent the dot product, so we are taking the dot product of the difference in positions of the two circles and the difference in velocities, across the x-axis and y-axis. In the code this is represented by:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token punctuation">(</span>dx <span class="token operator">*</span> dVelocityX <span class="token operator">+</span> dy <span class="token operator">*</span> dVelocityY<span class="token punctuation">)</span>

</code></pre>
<p>There&#x27;s a few important takeaways from the &quot;angle free&quot; collision equation. Our new velocities are based on the initial velocities with some change.  The change of the ball velocity has three terms.</p>
<ul>
<li>The first term is based on the ratio of the masses <code>(2m1/(m1+m2))</code>. This will simplify to just <code>2</code>.</li>
<li>The second term is a dot product divided by a distance. This will be a scalar value.</li>
<li>The third term is the vector difference of the two positions <code>(x2-x1)</code>.  This will be a vector value.</li>
</ul>
<p>Importantly, the first two terms are scalar values (just a magnitude with no direction), while only the third term has a vector value.</p>
<p>We can simplify things by making the slime mass <code>m1</code> set to <code>1</code>, and the ball mass <code>m2</code> set to infinitesimal (rounds to 0), as if the slime has much more mass. This leaves the slime velocity unchanged, <code>v1 = v1</code>, since the velocity change is multiplied by the mass m2=0. This is a good simplification for the game since we don&#x27;t want the slime itself to bounce.</p>
<p>The direction of the ball&#x27;s velocity change is determined by the final <code>x2-x1</code> component, which is the vector difference between the two positions. That is also called the &quot;normal&quot; component of the collision, since it&#x27;s in the direction perpendicular to the tangent line between the two circles.</p>
<img alt="gopher tange nt demo" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_tangent_2.6de48e46.png&amp;w=384&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_tangent_2.6de48e46.png&amp;w=750&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_tangent_2.6de48e46.png&amp;w=750&amp;q=75" width="365" height="205" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>So now we know the direction of the force we are applying, we just need to know how much force we need to apply (the magnitude).</p>
<p>As mentioned, the first term, the ratio of the masses, should simplify to 2, so we just need to calculate the dot product of the position difference with the velocity. This is basically where the angle of the collision gets taken into account, as the dot product is a simpler way to write the product of the magnitudes and the cosine of the angle.</p>
<img alt="gopher dot product" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_volleyball_dot.16fb297f.png&amp;w=828&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_volleyball_dot.16fb297f.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_volleyball_dot.16fb297f.png&amp;w=1920&amp;q=75" width="783" height="356" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>Again, in the code this looks like:</p>
<pre class="language-javascript"><code class="language-javascript">
<span class="token punctuation">(</span>dx <span class="token operator">*</span> dVelocityX <span class="token operator">+</span> dy <span class="token operator">*</span> dVelocityY<span class="token punctuation">)</span>

</code></pre>
<p>So the closer to the top of the slime hits, the more the ball will be affected by the y-velocities, and the closer to the edge of the slime the ball hits, the more the ball will be affected by the x-velocities. This also aligns with our intuitive understanding of how the physics &quot;should&quot; work.</p>
<p>The final part of the equation is dividing by the distance between the two velocities, squared. In physics, it&#x27;s very common to divide a vector by a distance to &quot;normalize&quot; it, which turns a vector into a distance of 1. This makes sense for when you&#x27;re trying to calculate a direction without having the raw magnitudes affect the end result. My way of thinking about dividing by the distance squared is both the second (the dot product) and third terms (the direction) are being normalized, so only the relative masses affect the end calculations.</p>
<p>Tying back to the HTML5/Javascript implementation, it&#x27;s now clear that the <code>something</code> variable is the dot product value in the Wikipedia elastic collision equation, though normalized ahead of time.</p>
<h2>Coding The Physics In The Game</h2>
<p>In this last part, we will review some practical tips on implementing the &quot;bounce&quot; in the code.</p>
<p>The first important step is detecting the collision itself. Fortunately, this is super simple, as we represent both the ball as a circle, and the slime as a circle (the bottom half is simply not rendered). Since both circles have a radius, we can decide the ball collides with the slime if the distance between the two circle centers is less than or equal to the sum of the two radii.</p>
<img alt="gopher collision" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_col2.42f7c58d.png&amp;w=384&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_col2.42f7c58d.png&amp;w=828&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgopher_col2.42f7c58d.png&amp;w=828&amp;q=75" width="383" height="247" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>Once the collision is detected, you don&#x27;t want to apply the calculations right away. This is because if you change velocity, but in the very next frame the ball is still colliding, the velocities will be recomputed again based on the new post-collision velocities, which won&#x27;t be right and may create the ball to get stuck in some weird loop. So instead, once the collision is detected, we first move the ball away from the slime so that it&#x27;s no longer colliding and then calculate the new velocities.</p>
<pre class="language-javascript"><code class="language-javascript">  ball<span class="token punctuation">.</span><span class="token property-access">x</span> <span class="token operator">=</span> s<span class="token punctuation">.</span><span class="token property-access">x</span> <span class="token operator">+</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token property-access">radius</span> <span class="token operator">+</span> ball<span class="token punctuation">.</span><span class="token property-access">radius</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">*</span> dx <span class="token operator">/</span> dist<span class="token punctuation">)</span><span class="token punctuation">;</span>
  ball<span class="token punctuation">.</span><span class="token property-access">y</span> <span class="token operator">=</span> s<span class="token punctuation">.</span><span class="token property-access">y</span> <span class="token operator">+</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">trunc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span><span class="token property-access">radius</span> <span class="token operator">+</span> ball<span class="token punctuation">.</span><span class="token property-access">radius</span><span class="token punctuation">)</span> <span class="token operator">*</span> dy <span class="token operator">/</span> dist<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Another detail that the HTML5 version applies is capping both the new x and y velocities at a maximum value. While this shouldn&#x27;t be strictly necessary, it keeps the game more even paced and avoids a strange collision sending the ball rocketing.</p>
<p>As another adjustment, while in the &quot;real&quot; physics calculations our mass ratio term was 2, in the game we can basically consider this a fudge factor to be whatever we want so that the game feels fun and natural.</p>
<p>Finally, both the HTML5 version and hardmaru&#x27;s Python version make one significant divergence from true elastic collision physics. That is, during the collision when calculating the new velocities, they also add the slime&#x27;s current velocity to the new ball velocity. In the actual elastic collision detections, the slime&#x27;s velocity only plays a role in that the difference between the slime&#x27;s velocity and the ball&#x27;s velocity contribute to the dot product magnitude of the change.</p>
<p>What&#x27;s the impact of this divergence from real physics? The main impact is that the ball becomes a bit more controllable by the slime. For example, it&#x27;s easier to &quot;catch&quot; the ball by moving the slime backwards as the ball is coming towards it. If the physics calculations stuck to the real world, the ball&#x27;s velocity would bounce forward and fall and hit the ground. By also adding the slime&#x27;s velocity to the new velocity, the ball &quot;sticks&quot; the slime and moves with it, giving the player more fine-tuned control of the ball and letting the player make more surprising moves.</p>
<h1>Conclusion</h1>
<p>These days, most game developers use engines like Unity 3D that come with pre-packaged physics solutions, so you don&#x27;t need to learn about these topics to make a game. Still, creating a game from scratch with a minimalistic 2D layer like the HTML5 Canvas or libsdl allows you to learn a lot of interesting graphics, physics, and AI concepts from first principles. I had fun recreating one of my favorites games and learning from other implementations, and I hope this article might help anyone stuck on 2D game physics in the future.</p>]]></content:encoded>
            <author>waprin@gmail.com (Bill Prin)</author>
        </item>
        <item>
            <title><![CDATA[Showing Local Timezones in Javascript]]></title>
            <link>undefined/articles/how-to-show-local-timezones</link>
            <guid>undefined/articles/how-to-show-local-timezones</guid>
            <pubDate>Tue, 12 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[How to show local timezones on a webpage without asking the user for their location]]></description>
            <content:encoded><![CDATA[<p>Recently, due to the pandemic, I built a webpage to aggregate musician and DJ
livestreams on the web called <a href="https://allnight.fm">All Night FM</a> (July 2020 Edit, this was formerly alldayistream.com). Most
streams are on Twitch, however many are on Youtube, FB or Instagram Live, or custom
web pages. Additionally, Zoom parties around the livestream have been catching on
in a big way, so there&#x27;s been a lot of focus on coordinating those parties.</p>
<p>Of course, people get annoyed if you don&#x27;t render the start times of the stream in their local timezone, so while
we initially listed all the start times in EDT, one of the biggest initial complaints was for local timezone support.</p>
<p>The site is currently a Django webpage backed by Google App Engine. I am a lot more comfortable with Python than
Javascript so I was looking for a way to do the timezone localization server side.</p>
<p>I had previously mistakenly believed there were a few ways to accomplish this, none of which are great:</p>
<ul>
<li><strong>Ask the user to create an account and specify their timezone preference</strong></li>
<li><strong>Require the user to provide their location to get local timezones</strong></li>
<li><strong>Guess their timezone based on their IP address (not very accurate)</strong></li>
</ul>
<p>However, my friend and former colleague <a href="https://twitter.com/broady">Chris Broadfoot</a> pointed out there&#x27;s a better way
to accomplish this: Javascript! Of course, I&#x27;m sure it&#x27;s possible to do this <em>all</em> in frontend Javacript, but I know
Python a lot better and was hoping to leverage Python&#x27;s datetime libraries to do the heavy lifiting. Fortunately, you
only need a few lines of Javascript and can then let the server do the rest of the work. However, If you have a favorite Javascript library to accomplish this, let me know on <a href="https://twitter.com/waprin_io">Twitter</a>.</p>
<p>Any timezone you serve as part of your web page should not be delivered initially, but instead be served by an AJAX call.
Before you make this AJAX call, you use Javascript to get the user&#x27;s timezone or UTC offset, add that as a parameter to
the call, and then the server can use that to provide the local timezone.</p>
<h1>First, get the user&#x27;s timezone</h1>
<p>The key to get the user&#x27;s timezone is in two Javascript functions outline in <a href="https://stackoverflow.com/questions/1091372/getting-the-clients-timezone-offset-in-javascript">this Stackoverflow question</a>.
The first is <code>new Date().getTimezoneOffset()</code>, which just offers the offset from UTC. This is almost always supported, but makes it difficult to
render the user&#x27;s actual timezone, since multiple timezones can have the same offset. It&#x27;s also nice for the user if they see the abbreviation of their timezone
rendered (EDT, PDT for Americans, VET for a Venezuelan), which is only possible if you know the actual timezone, since if you just have the offset you have to guess.</p>
<p>Fortunately, there&#x27;s a way to get the actual timezone that&#x27;s usually supported as the second answer to that Stack Overflow answer:</p>
<p><code>console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)</code></p>
<p>Since this is usually but not alway supported, I wrap it in a <code>try/catch</code> that falls back to <code>getTimezoneOffset</code> if it fails.</p>
<h1>Now render the timezone on the server</h1>
<p>Once I have the timezone name, or the offset, I can use Python/Django&#x27;s excellent datetime libraries to render the timezone in a local format:</p>
<pre><code>    local_tz = None
    if tzInfo: # tzInfo is passed an AJAX parameter based on Intl.DateTimeFormat().resolvedOptions().timeZone
        try:
            local_tz = pytz.timezone(tzInfo)
            local_start = start_date.astimezone(local_tz)
            start_time = local_start.strftime(&quot;%I:%M %p&quot;) # renders as 12:30 PDT        
        except Exception as e:
            logger.info(f&quot;failed to load timezone {tzInfo}&quot;)
    
    # only on a few old browsers, fall back to tzOffset which was obtained by new Date().getTimezoneOffset()
    if local_tz is None: 
        tzName = &#x27;Local&#x27; # guess &#x27;local&#x27; if we can&#x27;t find it  in mapping 
        # if we don&#x27;t know the timezone , we can guess based on the offset
        # you have to manually fill this in with your best guess of a timezone based on offet, e.g. 240 -&gt; EDT
        tz_mapping = {
          ... 
        }
        if tzOffset in tz_mapping: 
            tz = tz_mapping[tzOffset]
        local_start = start_date + datetime.timedelta(minutes=(-1 * tzOffset))
        start_time = local_start.strftime(&quot;%I:%M &quot;) + tzName
     
</code></pre>
<p>And that&#x27;s it! With this approach, I can let Python and Django do the heavy lifting of timezone localization and
render the time in each user&#x27;s timezone without asking them for their location or preferences.</p>]]></content:encoded>
            <author>waprin@gmail.com (Bill Prin)</author>
        </item>
        <item>
            <title><![CDATA[A Simple Tox Tutorial]]></title>
            <link>undefined/articles/introducing-tox</link>
            <guid>undefined/articles/introducing-tox</guid>
            <pubDate>Thu, 21 May 2015 00:00:00 GMT</pubDate>
            <description><![CDATA[Simple introduction to the Python test automation tool]]></description>
            <content:encoded><![CDATA[<h2>Tox, The Python Test Automation Framework</h2>
<p>A while back, I noticed a lot of Python projects were starting to contain a file called &#x27;tox.ini&#x27;, which is
a file read by <a href="https://tox.readthedocs.org/en/latest/">tox</a>.</p>
<p>Of course, the tox documentation should be considered the canonical source.
It has lots of good examples, but I still found the docs a bit confusing at
first, so I thought I would take my own shot at explaining the basics.</p>
<p>This is what a complete tox.ini file might look like:</p>
<pre class="language-java"><code class="language-java">   <span class="token punctuation">[</span>tox<span class="token punctuation">]</span>
   envlist <span class="token operator">=</span> py26<span class="token operator">-</span>django<span class="token punctuation">{</span><span class="token number">15</span><span class="token punctuation">,</span><span class="token number">16</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
             py<span class="token punctuation">{</span><span class="token number">27</span><span class="token punctuation">,</span><span class="token number">33</span><span class="token punctuation">,</span><span class="token number">34</span><span class="token punctuation">}</span><span class="token operator">-</span>django<span class="token punctuation">{</span><span class="token number">15</span><span class="token punctuation">,</span><span class="token number">16</span><span class="token punctuation">,</span><span class="token number">17</span><span class="token punctuation">,</span><span class="token number">18</span><span class="token punctuation">}</span>
   install_command <span class="token operator">=</span> pip install <span class="token punctuation">{</span>opts<span class="token punctuation">}</span> <span class="token punctuation">{</span>packages<span class="token punctuation">}</span>
   
<span class="token punctuation">[</span>testenv<span class="token punctuation">]</span>
   basepython <span class="token operator">=</span>
       py26<span class="token operator">:</span> python2<span class="token punctuation">.</span><span class="token number">6</span>
       py27<span class="token operator">:</span> python2<span class="token punctuation">.</span><span class="token number">7</span>
       py33<span class="token operator">:</span> python3<span class="token punctuation">.</span><span class="token number">3</span>
       py34<span class="token operator">:</span> python3<span class="token punctuation">.</span><span class="token number">4</span>
   
   commands <span class="token operator">=</span>
       nosetests

   deps <span class="token operator">=</span>
       nose
       django15<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.5</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.6</span>
       django16<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.6</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.7</span>
       django17<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.7</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.8</span>
       django18<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.8</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.9</span>
</code></pre>
<p>At first, it was unclear to me what the point was at all, because we were
already either using unittest
or nose to actually run the tests. If we already have a test framework,
why do we need yet another tool?</p>
<p>The problem that tox is trying to solve is that your tests might be run using
multiple tools in a variety of different environments. So, for example, you might want to run
Python unit tests using the standard unittest tool, as well as check your
style with a tool like flake8, and your code coverage with a tool like
coveralls. You want to run these tools using both Python 2.7 and Python 3.
That means you have 3 different
tools and 2 different environments.
Tox helps you declare how all of this gets pieced together in one spot, and
helps manage situations like different environments requiring different
dependencies.</p>
<p>This combination of environments is generally what you specify at the top of
the tox file underneath the [tox] directive, like so:</p>
<pre class="language-java"><code class="language-java"><span class="token punctuation">[</span>tox<span class="token punctuation">]</span>
envlist <span class="token operator">=</span> py26<span class="token operator">-</span>django<span class="token punctuation">{</span><span class="token number">15</span><span class="token punctuation">,</span><span class="token number">16</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
          py<span class="token punctuation">{</span><span class="token number">27</span><span class="token punctuation">,</span><span class="token number">33</span><span class="token punctuation">,</span><span class="token number">34</span><span class="token punctuation">}</span><span class="token operator">-</span>django<span class="token punctuation">{</span><span class="token number">15</span><span class="token punctuation">,</span><span class="token number">16</span><span class="token punctuation">,</span><span class="token number">17</span><span class="token punctuation">,</span> <span class="token number">18</span><span class="token punctuation">}</span>
</code></pre>
<p>In the above example, we are creating 14 different environment names, 2
environments on the first
line and 12
environments on the second line. Using the braces means we want to repeat
each of the <em>factors</em> in the braces for a different environment. So the first
line
creates two environments, py26-django15 and py26-django16. The second line
creates 12 more environments, combining each of the three versions of Python
that we specify with each of the 4 different versions of Django that we specify.</p>
<p>Following that, we can put all our default settings that applies to all
environments underneath the [testenv] directive:</p>
<pre class="language-java"><code class="language-java"><span class="token punctuation">[</span>testenv<span class="token punctuation">]</span>
  configuration that applies <span class="token keyword">to</span> <span class="token namespace">every</span> environment goes here
</code></pre>
<p>Next, we configure specific environments by adding a a directive with the
environment name after the colon, so to
setup configuration specific to the Python 2.6/Django1.5 environment we created
above we would add:</p>
<pre class="language-java"><code class="language-java"><span class="token punctuation">[</span>testenv<span class="token operator">:</span>py26<span class="token operator">-</span>django15<span class="token punctuation">]</span>
</code></pre>
<p>and then we would put everything specific to that environment underneath it.</p>
<p>Until we configure an environment, it&#x27;s doesn&#x27;t have any special meaning.
py26-django15 is just a name
until we use
the basepython directive to match  the py26 &quot;factor&quot; to the python2.6
executable, and the deps command to match django15 &quot;factor&quot; to the Django1.5
dependency.</p>
<p>Here is how we can match our Python environment names to the correct Python executable:</p>
<pre class="language-java"><code class="language-java"><span class="token punctuation">[</span>testenv<span class="token punctuation">]</span>
basepython <span class="token operator">=</span>
    py26<span class="token operator">:</span> python2<span class="token punctuation">.</span><span class="token number">6</span>
    py27<span class="token operator">:</span> python2<span class="token punctuation">.</span><span class="token number">7</span>
    py33<span class="token operator">:</span> python3<span class="token punctuation">.</span><span class="token number">3</span>
    py34<span class="token operator">:</span> python3<span class="token punctuation">.</span><span class="token number">4</span>
</code></pre>
<p>Note how we are individually referencing just part of the environment name
, or factor, such as py26, and tox is smart enough to match that base command
with all the complete environment names that contain the  py26 factor.</p>
<p>We also need to add the correct dependencies, which we might do something
like this:</p>
<pre class="language-java"><code class="language-java">deps <span class="token operator">=</span>
    pytest
    django15<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.5</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.6</span>
    django16<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.6</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.7</span>
    django17<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.7</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.8</span>
    django18<span class="token operator">:</span> <span class="token class-name">Django</span><span class="token operator">&gt;=</span><span class="token number">1.8</span><span class="token punctuation">,</span><span class="token operator">&lt;</span><span class="token number">1.9</span>
    py26<span class="token operator">:</span> unittest2
</code></pre>
<p>My first question upon seeing the deps field in tox was, why are we using tox
to manage dependencies? I thought the general Python best practice was to
combine a requirements.txt file with something like pip?</p>
<p>While pip is a  popular approaches to dependency management, it&#x27;s not
the only ones, and tox tries to remain somewhat agnostic and not rely on pip
or the existence of a requirements.txt
. However, if your dependencies are nicely captured in a requirements.txt
file, tox supports that:</p>
<pre class="language-java"><code class="language-java"> deps <span class="token operator">=</span> <span class="token operator">-</span>rrequirements<span class="token punctuation">.</span>txt
</code></pre>
<p>Finally, we need to give tox something to actually run our commands. This is
literally just the command we need to run, so it&#x27;s something as simple as:</p>
<pre class="language-java"><code class="language-java">commands <span class="token operator">=</span>
    python test<span class="token punctuation">.</span>py
</code></pre>
<p>With all this done, we can just run &#x27;tox&#x27; and all our commands will be run
for each of our environments.</p>
<h2>Conclusion</h2>
<p>So that&#x27;s a basic introduction to tox and why you would use it. A lot of this
information is a rehash from the tox documentation, and of course the best
way to learn is by example, for which you can find many on the tox site and
floating around on Github.</p>]]></content:encoded>
            <author>waprin@gmail.com (Bill Prin)</author>
        </item>
    </channel>
</rss>