tag:blogger.com,1999:blog-42936480698033177112024-03-29T04:03:44.088-07:00Iffy CanUnknownnoreply@blogger.comBlogger26125tag:blogger.com,1999:blog-4293648069803317711.post-81186604181464041892014-09-23T14:35:00.003-07:002014-09-23T14:35:34.352-07:00Be encouraged; everyone starts somewhereIt's easy to become discouraged when you compare the work of successful companies to your own initial attempts. But remember that the successful companies didn't start as successful. Here's a sample of how some company websites have changed over time. Take courage from their early... attempts.<br />
<br />
All of these images come from the <a href="https://archive.org/index.php" rel="nofollow" target="_blank">Internet Archive</a>.<br />
<br />
<h2>
DigitalOcean (from 2001)</h2>
<div>
This is the oldest site that I looked at, and it shows. Here's what the site looked like in 2001:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW5LalCxYD5nzYZ1j7b-b-X0MlLlj6HMYtCNkgdtssYLkfE2jX_IqofF4rrXDK1SzKoLUTCUdqKwWReW2awgoGsE7rcAPNCRkAu3Oo36LXN267vby6artlRbUzc19Soh82n_JuoLo3DC7J/s1600/digitalocean_20010927201426.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW5LalCxYD5nzYZ1j7b-b-X0MlLlj6HMYtCNkgdtssYLkfE2jX_IqofF4rrXDK1SzKoLUTCUdqKwWReW2awgoGsE7rcAPNCRkAu3Oo36LXN267vby6artlRbUzc19Soh82n_JuoLo3DC7J/s1600/digitalocean_20010927201426.png" height="310" width="320" /></a></div>
<div>
<br /></div>
<div>
And here is today:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbOSi97X8qEF1oX4AlT6k4luBoKgY2bBtbutd6LC866HVgFJGCjYJ7GZmMNsx8IjpGSNa8bh7DnUniJyWF8XwaBxw1UmMESxP9lPnVW-DdPXC8G7HyYka5teqF8VhgE-sXdCMU7SGeEq11/s1600/digitalocean_20140807204130.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbOSi97X8qEF1oX4AlT6k4luBoKgY2bBtbutd6LC866HVgFJGCjYJ7GZmMNsx8IjpGSNa8bh7DnUniJyWF8XwaBxw1UmMESxP9lPnVW-DdPXC8G7HyYka5teqF8VhgE-sXdCMU7SGeEq11/s1600/digitalocean_20140807204130.png" /></a></div>
<div>
<br /></div>
<div>
And here's all the ones in between:</div>
<div>
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyJh7CBrdT2g7DYiJicLnPkVvY4XMkevtdpcFDukq-WmM-6DYrOaqlPs0Vnq-ezC-USWfp9xJDocpvpcs_l1L3XbJqQf7DNnPwZZ_h4MIcdqs9sqINYdt2BaR2ho7yB-cn0VD_bEGvjZ_U/s1600/digitalocean.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyJh7CBrdT2g7DYiJicLnPkVvY4XMkevtdpcFDukq-WmM-6DYrOaqlPs0Vnq-ezC-USWfp9xJDocpvpcs_l1L3XbJqQf7DNnPwZZ_h4MIcdqs9sqINYdt2BaR2ho7yB-cn0VD_bEGvjZ_U/s1600/digitalocean.gif" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2 style="clear: both; text-align: left;">
Duolingo (from 2010)</h2>
Duolingo's site has become the least friendly to being recorded by the Internet Archive. Here's the first site featuring the owl mascot (does he have a name?) from 2010:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhW2vVQwl0SW_darL6rdcX-JIROdJSFZpGmI10j4Kjn0VDTIB8LgJo5swfHcCGYU0rHTek_A0ohjdpNbmgsiFY4_O6JNj-pLmHve5CtPzNIbZfg0EKblyh3wsmpZtCLGQ7FGzYlF34SX63/s1600/20101225183448.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhW2vVQwl0SW_darL6rdcX-JIROdJSFZpGmI10j4Kjn0VDTIB8LgJo5swfHcCGYU0rHTek_A0ohjdpNbmgsiFY4_O6JNj-pLmHve5CtPzNIbZfg0EKblyh3wsmpZtCLGQ7FGzYlF34SX63/s1600/20101225183448.png" height="197" width="320" /></a></div>
<br />
Here's today (but all stretchy):<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE-DDXDXxS1a8C3ks-FWiV7A7QXnsZAeSFU2ye3-_9E3CakGNy9s3BhxcqjUB0mbaIj0Si-U1irh4qC8X4DiBgP8JL1iSvl3Q5EeRXqt1nOn9nDZBZeS59pj6vz7B26tBHZql321geNzI2/s1600/today.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE-DDXDXxS1a8C3ks-FWiV7A7QXnsZAeSFU2ye3-_9E3CakGNy9s3BhxcqjUB0mbaIj0Si-U1irh4qC8X4DiBgP8JL1iSvl3Q5EeRXqt1nOn9nDZBZeS59pj6vz7B26tBHZql321geNzI2/s1600/today.png" height="320" width="171" /></a></div>
<br />
Everything in between:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBM3LMcNW5R8R7ebbkWSPVLJQ3JnBzcLs_EAPREaFutrSkastzn3HFcuwiSmy-9JDvkWOSlARMU2IxHMDmj3F7hBoVUkZDYe95PrTL_YkCZIZm1jG7m2YmH6M7iv-aXOrBnDottL8ltgWc/s1600/duolingo.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBM3LMcNW5R8R7ebbkWSPVLJQ3JnBzcLs_EAPREaFutrSkastzn3HFcuwiSmy-9JDvkWOSlARMU2IxHMDmj3F7hBoVUkZDYe95PrTL_YkCZIZm1jG7m2YmH6M7iv-aXOrBnDottL8ltgWc/s1600/duolingo.gif" /></a></div>
<h2>
GitHub (from 2008)</h2>
<div class="separator" style="clear: both; text-align: left;">
The early GitHub landing page featured beautiful briefcases. Mmm... wonderful briefcases of 2008.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvj37KvIth2INmQIr6QXuxBPkmp7gHe0Zphb9EgSCr7NQdpvVThaZXD1eiKkUJnAxH1DVFP2sExRjL_9tTXyQYED_X-U_TcSoksCUu6aYJnh161BVrcW0zVwiqJCXTfnfzPmyEuhDBZ2TI/s1600/github_20081228043244.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvj37KvIth2INmQIr6QXuxBPkmp7gHe0Zphb9EgSCr7NQdpvVThaZXD1eiKkUJnAxH1DVFP2sExRjL_9tTXyQYED_X-U_TcSoksCUu6aYJnh161BVrcW0zVwiqJCXTfnfzPmyEuhDBZ2TI/s1600/github_20081228043244.png" height="640" width="299" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Watch it change over time.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3xAC1QOhVi_Yar7-wNao06FJMSfwPv_BEofAg5ADNFkDMzn7hrWo6zpZWt26TH8zuFIw6JcruwvZ6eyZ9Cz7OryvPTsrvSqR1ds2cAXNoSLnbeChjsR5jnVfxOo2gkAGG24vDYRxjXQjH/s1600/github.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3xAC1QOhVi_Yar7-wNao06FJMSfwPv_BEofAg5ADNFkDMzn7hrWo6zpZWt26TH8zuFIw6JcruwvZ6eyZ9Cz7OryvPTsrvSqR1ds2cAXNoSLnbeChjsR5jnVfxOo2gkAGG24vDYRxjXQjH/s1600/github.gif" /></a></div>
<h2>
Heroku (from 2007)</h2>
<div>
Heroku has gone from light to dark to light, always maintaining the purple. (It started out bluer). Here's their earliest home page from 2007:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjd2kxGKoDqvzNEO-up7aUzYusXzURi7vxAwF_5DG6Hk2dFPEAoo4IsbtTyzc15HCaakfAA_68eLnTopu3Ozp6KqP7xZBFW-vQ_FSe2v6jaD4uWQy8usHD7BY1Jf6h-kYmpPEctyymPBsI/s1600/heroku_20071105005403.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjd2kxGKoDqvzNEO-up7aUzYusXzURi7vxAwF_5DG6Hk2dFPEAoo4IsbtTyzc15HCaakfAA_68eLnTopu3Ozp6KqP7xZBFW-vQ_FSe2v6jaD4uWQy8usHD7BY1Jf6h-kYmpPEctyymPBsI/s1600/heroku_20071105005403.png" height="480" width="640" /></a></div>
<div>
<br /></div>
<div>
And here's the evolution:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaRaj4eQpriuhUM8ct7vlwCd3uizb8nWc766vwNKkKpLMLVsmI1EDLYaK3GAgjM_gYOfk6MwsrhYf1aOh3bNSoWb956m9gA9XEgLCejM6J1CpXyDDU6SzFmFedaYpjLqqZRzsLnTcgVx9s/s1600/heroku.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaRaj4eQpriuhUM8ct7vlwCd3uizb8nWc766vwNKkKpLMLVsmI1EDLYaK3GAgjM_gYOfk6MwsrhYf1aOh3bNSoWb956m9gA9XEgLCejM6J1CpXyDDU6SzFmFedaYpjLqqZRzsLnTcgVx9s/s1600/heroku.gif" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Yes, that pony you see is actually <a href="https://web.archive.org/web/20121108085035/http://www4.heroku.com/" rel="nofollow" target="_blank">how the Internet Archive retrieved it on November 8th, 2012</a>.</div>
<h2>
Stripe (from 2011)</h2>
<div class="separator" style="clear: both; text-align: left;">
Stripe is kind of the discouraging exception to this group (at least as far as website design is concerned). They started out with this beautiful site in 2011:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBA12d5w4LVjTGrJ7YSVMYlYZE9I9T7uE_vh4s0DzJBQHUj54O8sgFTd2Ocs-Y6owzEiiKJmfnKcJL0KOxf49tYusp09bhxdZ3RG4gsQhOSqTxOUd1yydqfqJkq6pVCrqgh9S3K8KS3BfY/s1600/20111007130738.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBA12d5w4LVjTGrJ7YSVMYlYZE9I9T7uE_vh4s0DzJBQHUj54O8sgFTd2Ocs-Y6owzEiiKJmfnKcJL0KOxf49tYusp09bhxdZ3RG4gsQhOSqTxOUd1yydqfqJkq6pVCrqgh9S3K8KS3BfY/s1600/20111007130738.png" height="640" width="292" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<br /></div>
And didn't fundamentally change it until late 2013:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH6DDUMCcvu2fCVmxtLZ34bt9SIfHEKoD7HUopClpUMsmxscKWM6Fz2fXZu3DPba7SlZuDLwviaw_torgSjzUmCAWBRYssFWYzdSWyQLo02BZSK_6TADzINPnskN5TyIyHUAmZj9DG70L4/s1600/20131020093943.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhH6DDUMCcvu2fCVmxtLZ34bt9SIfHEKoD7HUopClpUMsmxscKWM6Fz2fXZu3DPba7SlZuDLwviaw_torgSjzUmCAWBRYssFWYzdSWyQLo02BZSK_6TADzINPnskN5TyIyHUAmZj9DG70L4/s1600/20131020093943.png" height="640" width="510" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The latest version isn't too different:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBJaUlQtxCd6asYLEY5V3_eRbF6g3KhLuDkrt4ie4fGrlrov6flTTfnNyBF_juKKdK9YQego-BE2mk0M9I6bgyAC59mEl5-SqaA1ypYBK4y49EMr4VCpEULdB-5tOiBWIoQ8Whx0xDT0My/s1600/20140731220732.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBJaUlQtxCd6asYLEY5V3_eRbF6g3KhLuDkrt4ie4fGrlrov6flTTfnNyBF_juKKdK9YQego-BE2mk0M9I6bgyAC59mEl5-SqaA1ypYBK4y49EMr4VCpEULdB-5tOiBWIoQ8Whx0xDT0My/s1600/20140731220732.png" height="640" width="526" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br /></div>
<div>
<br />
So, keep trying. Don't worry too much about being perfect now. Any other great "ugly ducklings" you know of?<br />
<br /></div>
Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-4293648069803317711.post-2589741450136825952014-08-19T09:27:00.000-07:002014-08-19T09:27:11.786-07:00Dear Weather.comI like how weather.com organizes their site. Here's what a typical view looks like. Hi, Flo.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV6XxXFqEklQbMbL14HYJq65oiyga9eb-E1kAVVGTkraQ3XmuixFjcakVahqAsX7X3t_tukkIFoIncKBBb4skoy8UKG0yu-05GjRwZ_ca7iVjraNWo2jUMsVCbe5bSLrO7jNMtFitVjIj8/s1600/original.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV6XxXFqEklQbMbL14HYJq65oiyga9eb-E1kAVVGTkraQ3XmuixFjcakVahqAsX7X3t_tukkIFoIncKBBb4skoy8UKG0yu-05GjRwZ_ca7iVjraNWo2jUMsVCbe5bSLrO7jNMtFitVjIj8/s1600/original.png" height="616" width="640" /></a></div>
<br />
And here I've outlined the distinct regions of the page:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQKwtUdoWk2t430bto4hMx0E3t88qUAWO_88xv6XveaVeBPrf-6-O82wkoCcQXXR-SzaxheWS4lEXiDEUi_BFltK8lM7PVF4fLudAAHP2cRi8yq5uXQUDbCM9E_Ls7uY2dbmRmX9aZUaE8/s1600/colored.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQKwtUdoWk2t430bto4hMx0E3t88qUAWO_88xv6XveaVeBPrf-6-O82wkoCcQXXR-SzaxheWS4lEXiDEUi_BFltK8lM7PVF4fLudAAHP2cRi8yq5uXQUDbCM9E_Ls7uY2dbmRmX9aZUaE8/s1600/colored.png" height="616" width="640" /></a></div>
<br />
I need, somehow, to train my muscle memory to type something other than "weather" when I want to see the weather. Something like "wunder."Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-25750813111379621902014-07-27T16:26:00.005-07:002014-07-27T16:37:21.850-07:00Who is this?One problem I have when looking at my family tree is that I get lost. I end up looking at the details of a person and forget how I'm related to them. I think there's <a href="http://iffycan.blogspot.com/2014/02/gaming-family-history.html" target="_blank">probably a way to make visualizing and exploring family trees clear, orientable and memorable</a>, but we're not there yet (or at least I'm not satisfied yet).<br />
<br />
Maybe one way to fix the "how am I related to this person, again?" problem is with a graph that's really easy to display succinctly. Here are some ideas:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf7pCgLrsFbVFICg46ddgSXKqrz9shtDtiT9JlPxTjXz480LB47t2JmH3LpOOvkxk8MOO52lq2PpQmYAcTlhuA2GxKjKL0LflGuUiKLLuLlpnwecPgWZxkknIm0hzLLVqspNKKHF5r8CI1/s1600/relationshipshorthand.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf7pCgLrsFbVFICg46ddgSXKqrz9shtDtiT9JlPxTjXz480LB47t2JmH3LpOOvkxk8MOO52lq2PpQmYAcTlhuA2GxKjKL0LflGuUiKLLuLlpnwecPgWZxkknIm0hzLLVqspNKKHF5r8CI1/s1600/relationshipshorthand.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
All of the above drawings represent my dad's mom's dad's dad's mom. The first three are nice because they vary in two dimensions (and seem easy to grasp at a glance). The last two are nice because they have a uniform height.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
1. Without color, the gender of the person on the left is ambiguous (unless you know it's you).</div>
<div class="separator" style="clear: both; text-align: left;">
2. Could you extend this to show cousin/sibling relationships? Maybe something like:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1N5GZ75J5UeZ5IKejnOmnmXmzHuj7NAHIqQCJAHhscCI192foo71kcCrxdpDrbN7gGnT12okjUEc3RNYtFm-BMqAsFqMuaSPM7MSVDeKhuXDtYOxGWL-VIYBBC268MpsGaMaJOhopSLtD/s1600/sibling.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1N5GZ75J5UeZ5IKejnOmnmXmzHuj7NAHIqQCJAHhscCI192foo71kcCrxdpDrbN7gGnT12okjUEc3RNYtFm-BMqAsFqMuaSPM7MSVDeKhuXDtYOxGWL-VIYBBC268MpsGaMaJOhopSLtD/s1600/sibling.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
A: My mom's mom's sister's child.</div>
<div class="separator" style="clear: both; text-align: left;">
B: My dad's brother's son (here the lines on the left without dots indicate gender)</div>
<div class="separator" style="clear: both; text-align: left;">
C: My mom's mom's sibling (gender is ambiguous unless a dot-less left line was included on the leaf dots).</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
After drawing the MMFMMF one from above, I thought, "How about a series of interlocking Ms and Ws?"</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPJNsgTSjF6LkS2_QkkJBaKvaYQEO1byuxg-fo_7a6TxDedDBohPim2wVUF_c_Jw7beSeXk4s7Ka3pUOw4E-2iO4heCwXI77WO43N06BigT1-NTUwNUojM3tJHl5HRRq-_wbVg62QwvX8r/s1600/emandws.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPJNsgTSjF6LkS2_QkkJBaKvaYQEO1byuxg-fo_7a6TxDedDBohPim2wVUF_c_Jw7beSeXk4s7Ka3pUOw4E-2iO4heCwXI77WO43N06BigT1-NTUwNUojM3tJHl5HRRq-_wbVg62QwvX8r/s1600/emandws.png" /></a></div>
Voila! :)<br />
<br />
What do you think?Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4293648069803317711.post-3449736600440193132014-05-14T09:32:00.001-07:002014-05-14T09:40:28.794-07:00Do UTA a favor<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTi1Hf4YuVCyzKQXf6zXpXhtxP7pZhqdiV03fbQ_SFZhGBXy_x0dOoEQP8ZpyIenqSBjDggxiKarIZemrj8tZLt85oHSO1yVC2YdhVPLWwJoW7WWOsUvOmj_GLeG6LnFpevIPc6K9mN_EC/s1600/bus.png" style="border: none; box-shadow: none; outline: none;" /></div>
<br />
I think I've figured out why <a href="http://en.wikipedia.org/wiki/Hyperbole" target="_blank">no one</a> takes the bus in Utah County. Let me explain:<br />
<br />
I love riding the bus. It's slower, kind of inconvenient and kind of expensive (since I already have a car).<br />
<br />
But taking a bus gives me a little thrill -- I get more exercise (3.4 miles walking to/from work) and time to think. And I feel like I'm doing my civic duty and saving the environment. When I dump my quarters into the slot, I feel like I'm doing my community and <a href="http://www.rideuta.com/" target="_blank">UTA</a> a favor and that my fare is a generous offering to a good cause. It feels good.<br />
<br />
And <i>that</i> is exactly why no one rides the bus. The situation is backward.<br />
<br />
Certainly there is a list of specific problems, but<br />
<br />
<blockquote class="tr_bq">
No one will ride the bus until they feel like <i>the bus</i> is doing <i>them</i> a favor (not the other way around).</blockquote>
That is the principle that should guide decisions at UTA: instead of making us feel like we're doing you a favor, do us a favor and make riding the bus way better than using our cars.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4293648069803317711.post-19654291450784367542014-02-24T10:25:00.000-08:002014-02-24T10:25:28.870-08:00Gaming Family HistoryI had an idea last night.<br />
<h2>
Pop Quiz</h2>
In your scriptures, where on the page is John 3:16?<br />
<div class="separator" style="clear: both; font-size: medium; font-weight: normal; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjswt9GcbEFi8j5zqs8CoOUDzpe04S0zh7n468tUCLo_yay3aPQuRfuOTU4l12yois9YUxtBqIDDZur3n9GBLFR2aXrEh7OqrOp4CQzSgxumrEQVoCnOjW7Tr8rsYnE8il-UKdpSKwnK-zH/s1600/john316.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjswt9GcbEFi8j5zqs8CoOUDzpe04S0zh7n468tUCLo_yay3aPQuRfuOTU4l12yois9YUxtBqIDDZur3n9GBLFR2aXrEh7OqrOp4CQzSgxumrEQVoCnOjW7Tr8rsYnE8il-UKdpSKwnK-zH/s1600/john316.png" height="300" width="400" /></a></div>
<div class="separator" style="clear: both; font-size: medium; font-weight: normal;">
<br /></div>
<div class="separator" style="clear: both; font-weight: normal;">
Right page, first column, near the top. I know right where it is. If you read the Bible, you likely do too (though it might be in a different spot in your scriptures).</div>
<div class="separator" style="clear: both; font-weight: normal;">
<br /></div>
<div class="separator" style="clear: both; font-weight: normal;">
The tangible page provides a frame of reference that jives well with my memory. The same is <i>not</i> true of digital scriptures in their current state.</div>
<h2>
Driving v. Riding</h2>
<div>
The act of driving a car to a destination solidifies how to get there much more than simply riding in the car. I'm not sure of all the reasons for this, but something about the tangible interaction makes it more memorable.</div>
<h2>
Alive Family</h2>
Before I met my (now) wife's family, I had a hard time remembering who was who when my wife would talk about them. I couldn't remember who was married to who, what their kids were named or where they were from. It was a big muddled puddle in my head.<br />
<br />
But after I met them in person I didn't have trouble anymore. (You'll be proud to know that I still know all the names of my in laws <b>and</b> their spouses and children).<br />
<br />
After I'd met them, I had a reference.<br />
<h2>
Dead Family</h2>
I have a similar problem with the people in my own family that I've never met -- my ancestors who have long since passed. But unlike my wife's family, I can't meet them in person. I have no way to create a reference for remembering details about their lives. To help provide some kind of reference people have made visualizations:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhhTiCmeP8AV5OOeWIkcLNwqdWyitfUUye99AIZPbcB1xQm0nMLTXb_n-zNx1oK7AMC9mcJOm3ywP7usu6605lJjk_PNMvRQWOElOzCZa0XwZQCEwKMh_le1asKtW3029vJEIJw436sGnZ/s1600/pedchart.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhhTiCmeP8AV5OOeWIkcLNwqdWyitfUUye99AIZPbcB1xQm0nMLTXb_n-zNx1oK7AMC9mcJOm3ywP7usu6605lJjk_PNMvRQWOElOzCZa0XwZQCEwKMh_le1asKtW3029vJEIJw436sGnZ/s1600/pedchart.gif" height="320" width="272" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Pedigree chart</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTLQde6M1c5l-I9H4G4qG8HVku-imOwcm7rdpGwFcv1NbSpSwmXh7dANJHn3C-oKA-Qrq8yZIaVf2_4j9Q_j4hPQiId3ebhC-v6yLcsfw3yG36bx7IrXIQwpGk2fo_olyR3e4X5cBgK7ml/s1600/fanchart.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTLQde6M1c5l-I9H4G4qG8HVku-imOwcm7rdpGwFcv1NbSpSwmXh7dANJHn3C-oKA-Qrq8yZIaVf2_4j9Q_j4hPQiId3ebhC-v6yLcsfw3yG36bx7IrXIQwpGk2fo_olyR3e4X5cBgK7ml/s1600/fanchart.jpg" height="267" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fan chart</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
These charts make it easy to see the names and some vital data about a person's direct ancestors. The first chart has details on 4 generations, while the second has basic information about 8 generations. Both are excellent at emphasizing incomplete information: blanks are easy to see. Both also have some deficiencies:</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<ol>
<li>Adding more generations requires exponential space.</li>
<li>Limited space prevents showing siblings and descendants for each person.</li>
<li>There are no photos or stories -- there's no life -- for these once-living, real people.</li>
<li>The charts misrepresent time. Not all my 4th great grandparents lived at the same time (but the chart leads me to think they did). <a href="https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=5&ved=0CE4QFjAE&url=http%3A%2F%2Fvis.berkeley.edu%2Fcourses%2Fcs294-10-sp10%2Fwiki%2Fimages%2Ff%2Ff2%2FFamily_Tree_Visualization_-_Final_Paper.pdf&ei=BGwLU7urCoLlrQGAhICoBg&usg=AFQjCNEZQg4Ijm8eKgp1ZZjcD-OBCVpsMw&sig2=nK7YAnW1qPQeO_wJvepO-Q&bvm=bv.61725948,d.aWM&cad=rja" target="_blank">These people</a> have an interesting solution for this.</li>
</ol>
<h2>
2D v. 3D</h2>
<div>
Since a big limitation (and benefit) of paper is its 2D nature, I've tried to imagine how 3 dimensions might help. 3D printers are becoming more common. Maybe you could print a family tree with detachable branches? (Someone please do this!)<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://www.3ders.org/images/infosculpture-3d-printed-people-wood-1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://www.3ders.org/images/infosculpture-3d-printed-people-wood-1.jpg" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">(from http://www.3ders.org/)</td></tr>
</tbody></table>
But a tangible 3D object will still become exponentially unwieldy at some point.<br />
<h2>
Interaction</h2>
<div>
Another limitation (and benefit) of paper is that it is not interactive. I can't "click" a link on a piece of paper to get more information. The lack of interactivity is partially what enables the remembering-scripture-location trick, but the Internet and its hyperlinks have shown how valuable interaction can be. Also, it's the very interaction while driving that makes a trip more memorable.</div>
<div>
<br /></div>
<div>
I think interaction will need to be part of a good solution. All this leads me to:</div>
<h2>
Video Games</h2>
<div>
Before you balk, consider that people have done <b>real</b> things with video games, such as <a href="http://en.wikipedia.org/wiki/Foldit" target="_blank">protein folding research</a>.</div>
<div>
<br /></div>
<div>
Also consider all the ten-year-olds (and 30-year-olds) in your neighborhood who play <a href="https://minecraft.net/" target="_blank">Minecraft</a>.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://minecraft-multiplayer.org/wp-content/uploads/2013/11/minecraft-land.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://minecraft-multiplayer.org/wp-content/uploads/2013/11/minecraft-land.jpg" height="360" width="640" /></a></div>
<div>
<br /></div>
<div>
Have you watched them play? (have you played yourself?) If you haven't, go find someone who plays and watch them for a few minutes. One thing you'll notice is that they <i>know their way around the virtual world. </i>They know right where the secret door to their mansion is and where their sheep farm is. They know this because of landmarks that provide reference and because of the memory of creating many of the world's interesting features.</div>
</div>
<h2>
My own personal village?</h2>
<div>
What if your family history was represented as village of houses in Minecraft? There could be a cemetery with gravestones on the edge of town near the lake (complete with birth and death records). You could visit your grandparents house over the hill to the east. Inside you find pictures of them on their walls and books full of their stories.</div>
<div>
<br /></div>
<div>
Oh, you discovered the name of your 5th great uncle? Build him a house by the quarry.</div>
<h2>
What about Earth?</h2>
<div>
There's a small problem with this Minecraft model. If I (Matt) build the village, <i>I</i> will know where everything is, but no one else would know why I decided that Great Aunt Heloise's house should be on an island in the middle of a lava pit. I could unintentionally (or intentionally) lose valuable information.</div>
<div>
<br /></div>
<div>
Incidentally, this problem is prevalent with people's personal piles of family research, organized (or not) according to their system and out of reach to everyone else.</div>
<div>
<br /></div>
<div>
So what if, instead of a fictitious virtual world, we used a fact-based virtual world: Earth? An earth inhabited only with my ancestors and their descendants?<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg-sOBScwaVoUSvybumHCNYBLM84kY3XS6NsKfpSj-CYRbCdRJ95uXv88YJRUOxkx8bchvsTZEUK9JoKdZJRarsavr-y91gdplW-gxx2SEC1lro_UC3rqbrHmW54uxol1yrZXyEr6BrsvI/s1600/Screen+shot+2014-02-24+at+11.23.45+AM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg-sOBScwaVoUSvybumHCNYBLM84kY3XS6NsKfpSj-CYRbCdRJ95uXv88YJRUOxkx8bchvsTZEUK9JoKdZJRarsavr-y91gdplW-gxx2SEC1lro_UC3rqbrHmW54uxol1yrZXyEr6BrsvI/s1600/Screen+shot+2014-02-24+at+11.23.45+AM.png" height="257" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">From Google Earth</td></tr>
</tbody></table>
There could be a gravestones on the earth in the spot where a person died, a house where they lived, a crib where they were born, etc... In each home, there could be a list of children born to a couple; you select a person and the HUD will tune the compass to point toward significant places in a person's life.</div>
<div>
<br /></div>
<div>
And instead of clicking links to go from place to place, you walk or swim across the earth. It wouldn't be practical to walk at the speed of a human, but you might lose the benefit of the memorable journey if you flew like bird. Maybe you could grow to giant size, climb over mountains and jump over oceans? Like an MMO, people could "play" at the same time and talk and collaborate.<br />
<br />
There are lingering questions (e.g. what if we don't know where a person was born?) but it's a start. And such a visualization of family history data wouldn't solve every problem, but it would certainly make it easier for me to remember.</div>
<div>
<br /></div>
<hr />
<br />
Anyway, tell me what you think of the idea.Unknownnoreply@blogger.com18tag:blogger.com,1999:blog-4293648069803317711.post-83026613479816988512013-12-16T13:24:00.000-08:002013-12-16T13:24:23.191-08:00YNO-phasic Sleep<style>a.citation, a.ref { background: #bdc3c7; font-size: 0.8em; padding: 0.1em 0.3em; margin-left: 0.1em; font-weight: bold; vertical-align: super;}a.ref { font-size: 1em; vertical-align: auto; font-weight: normal;}.chart { text-align: left;}.slice { stroke: #000; stroke-width: 0;}.awake { fill: #f1c40f;}.asleep { fill: #2c3e50;}path.line { fill: none; stroke: #fff; stroke-width: 2;}</style><script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script><script>function drawpie(elem, data) { var w = 200, h = 200, r = 90; var vis = d3.select(elem) .append('svg:svg') .data([data]) .attr('width', w) .attr('height', h) .append('svg:g') .attr('transform', 'translate(' + r + ',' + r + ')'); var arc = d3.svg.arc() .outerRadius(r); var pie = d3.layout.pie() .value(function(d) { return d.value; }) .sort(null); var arcs = vis.selectAll('g.slice') .data(pie) .enter() .append('svg:g') .attr('class', function(d) { return 'slice ' + d.data.kind; }); arcs.append('svg:path') .attr('d', arc);}function sleeptransition(elem, data, smooth) { var h = 100, w = 600, pad = 5; var x = d3.scale.linear().domain([0, 24]).range([pad, w-pad]); var y = d3.scale.linear().domain([-1, 1]).range([h-pad, pad]); var line = d3.svg.line() .x(function(d) { return x(d.hour);}) .y(function(d) { return y(d.value);}); if (smooth == true) { line.interpolate('basis'); } var vis = d3.select(elem) .append('svg:svg') .attr('height', h) .attr('width', w); var gradient = vis.append('svg:defs') .append('svg:linearGradient') .attr('id', 'gradient') .attr('x1', '0%') .attr('y1', '0%') .attr('x2', '100%') .attr('y2', '0%') .attr('spreadMethod', 'pad'); /* middle of the day */ gradient.append('svg:stop') .attr('offset', '0%') .attr('stop-color', '#f1c40f') .attr('stop-opacity', 1); /* start of evening */ gradient.append('svg:stop') .attr('offset', '24%') .attr('stop-color', '#f1c40f') .attr('stop-opacity', 1); /* start of night */ gradient.append('svg:stop') .attr('offset', '26%') .attr('stop-color', '#2c3e50') .attr('stop-opacity', 1); /* start of morning */ gradient.append('svg:stop') .attr('offset', '74%') .attr('stop-color', '#2c3e50') .attr('stop-opacity', 1); /* start of day */ gradient.append('svg:stop') .attr('offset', '76%') .attr('stop-color', '#f1c40f') .attr('stop-opacity', 1); /* middle of the day */ gradient.append('svg:stop') .attr('offset', '100%') .attr('stop-color', '#f1c40f') .attr('stop-opacity', 1); var g = vis.append('svg:g') .attr('transform', 'translate(0, ' + 0 + ')'); g.append('rect') .attr('width', w) .attr('height', h) .style('fill', 'url(#gradient)'); var paths = g.append('path') .datum(data) .attr('class', 'line') .attr('d', line); }</script><p> Recently, we've seen an increased interest in <a href="http://en.wikipedia.org/wiki/Polyphasic_sleep" target="_blank">polyphasic sleep</a>. Proponents claim to be able to extract extra hours out of the day, as well as other benefits. However, not as much interest has been paid to a more ancient and natural sleep pattern: <a href="http://en.wikipedia.org/wiki/Young_Needy_Offspring_phasic_sleep" target="_blank">Young Needy Offspring (YNO) phasic sleep</a>.</p><h2>Tools</h2><p> <a href="http://www.intelclinic.com/" target="_blank"><img src="http://amd.c.yimg.jp/amd/20131205-00000067-mycomj-000-1-view.jpg"></a> <br/>(© <a href="http://www.intelclinic.com/" target="_blank">Intelclinic</a>) <br/> Unlike polyphasic sleep, which may require <a href="http://www.intelclinic.com/" target="_blank">additional equipment</a> to be effective, YNO-phasic sleep simply requires one or more offspring. Typically more, younger offspring produce more dramatic results.</p><h2>24-hour cycle</h2><p> The charts below show sample 24-hour cycles for monophasic, polyphasic and YNO-phasic sleep: <table width="100%"> <tr> <td class="chart" id="mono-chart1" width="33%"></td> <td class="chart" id="poly-chart1" width="33%"></td> <td class="chart" id="yno-chart1" width="33%"></td> </tr> <tr> <td valign="top"><b>Monophasic</b><a class="citation" href="#foot-monochart">1</a> <br/>8 hours of sleep followed by 16 hours of wakefulness</td> <td valign="top"><b>Polyphasic (Biphasic TED)</b> <br/>4 hours of sleep, 2 hours of wakefulness, 4 hours of sleep, 14 hours of wakefulness</td> <td valign="top"><b>YNO-phasic</b> <br/>~3 hours of sleep followed by random intervals of wakefulness and sleep-like wakefulness. At 6:15am, the offspring will awake for the day, fully rested and ready to play.</td> </tr> </table></p><h2>Sleep transitions</h2><h3>Monophasic</h3><p> A preferred sleep cycle involves gentle transitions from sleep to wakefulness. The following chart shows a desirable monophasic sleep cycle. <div id="mono-chart2"></div> Note the gentle transitions from wakefulness down to sleeping and back up to wakefulness.</p><h3>YNO-phasic</h3><p> YNO-phasic sleep cycles are characterized by abrupt changes from sleeping to wakefulness. The return transition from wakefulness to sleeping does not happen again until the next evening. <div id="yno-chart2"></div> Abrupt changes to wakefulness are typically caused by offspring producing highly audible noises, foul odors and surprisingly strong slaps to the face <a class="citation" href="#foot-ynooo">2</a>. Not infrequently, changes to wakefulness also happen due to the discomfort caused by a spouse's hands lightly strangling the sleeper while moaning, "I can't take it anymore!" through clenched teeth.</p><h2>Direct benefits</h2><p> Critics of YNO-phasic sleep<a class="citation" href="#foot-critics">3</a> claim that such sleep has no benefits, but it is obvious from research that this is unfounded. YNO-phasic sleep provides several benefits:</p><ul> <li> <b>Longer days</b> <p> As shown above, a person following YNO-phasic sleep will have more wakeful hours during the day, sometimes eliminating sleep altogether for several days (especially when the initial transition is made from monophasic to YNO-phasic sleep). The implications for productivity are obvious. </p> </li> <li> <b>Reading time</b> <p> Many people wish they had more time to read. YNO-phasic sleep offers ample opportunity for reading. Just last night, at 3am, I was able to spend 30 minutes repeatedly reading <a href="http://en.wikipedia.org/wiki/Goodnight_Moon" target="_blank">Goodnight Moon</a>. The kittens end up on the chair, and the mouse ends up looking out the window. </p> </li> <li> <b>Improved Resistance</b> <p> Those following YNO-phasic sleep build up resistence to <a href="http://en.wikipedia.org/wiki/Sleep_deprivation#Interrogation" target="_blank">some forms of torture</a>, and may earn credits toward bypassing portions of <a href="http://en.wikipedia.org/wiki/United_States_Navy_SEAL_selection_and_training#Phase_1:_Physical_Conditioning_.287_weeks.29" target="_blank">Navy SEAL training</a> or <a href="http://www.military.com/special-operations/army-ranger-training.html" target="_blank">Army Ranger Training</a>. </p> </li></ul><h2>Indirect benefits</h2><p> In addition to direct benefits of YNO-phasic sleep, people report many indirect benefits, such as:</p><ul> <li> <b>Increased chance of progeny</b> <p> It has been proven that those who have offspring have a greater chance of having descendents than those who don't. </p> </li> <li> <b>Laughter</b> <p> The first time the offspring emits laughter<a class="citation" href="#foot-giggle">4</a> is very nice. Subsequent times are also nice. </p> </li> <li> <b>Amazement</b> <p> After some time of <span style="text-decoration: line-through">enduring</span> following YNO-phasic sleep, adherents frequently report astonishment and surprise at what their offspring can do (e.g. <i>walking</i>, <i>talking</i>, <i>teasing</i>, <i>telling jokes</i>, <i>performing</i>, etc...) </p> </li></ul><h2>Notes</h2><p> This article has been peer reviewed (I had a peer review it).</p><p> <a class="ref" name="foot-monochart">1</a> Studies have shown that adherents to YNO-phasic sleep who are shown monophasic sleep diagrams exhibit increased levels of sarcasm and violence.</p><p> <a class="ref" name="foot-ynooo">2</a> Because of the abrupt and frequent nature of changes to wakefulness, adherents to YNO-phasic sleep have been known to vocalize the name during the night as "Why?! <a href="https://www.youtube.com/watch?v=WWaLxFIVX1s" target="_blank">Nooooo!</a>"</p><p> <a class="ref" name="foot-critics">3</a> Interestingly, the most ardent critics of YNO-phasic sleep are often the strictest adherents. The reason for this overlap has yet to be researched.</p><p> <a class="ref" name="foot-giggle">4</a> also, <i>giggles</i>, <i>belly laughs</i>, <i>smiles</i>, <i>funny faces</i>.</p><script type="text/javascript">drawpie('#mono-chart1', [ {kind: 'asleep', value: 8}, {kind: 'awake', value: 16}]);drawpie('#poly-chart1', [ {kind: 'asleep', value: 4}, {kind: 'awake', value: 2}, {kind: 'asleep', value: 4}, {kind: 'awake', value: 14},]);var data = [ {kind: 'asleep', value: 3},];var timesupatnight = 10.0;var night_time = 7;for (var i=0; i < timesupatnight; i++) { var segment = night_time / timesupatnight; var awake = Math.random() * segment; data.push({kind: 'awake', value: awake}); data.push({kind: 'asleep', value: segment-awake});}data.push({kind: 'awake', value: 14});drawpie('#yno-chart1', data);</script><script type="text/javascript">var data = [ {hour: 0, value: 1}, {hour: 10, value: 1}, {hour: 10.5, value: -1}, {hour: 18, value: -1}, {hour: 18.5, value: 1}, {hour: 24, value: 1},];sleeptransition('#mono-chart2', data, true);sleeptransition('#yno-chart2', [ {hour: 0, value: 0.7}, {hour: 10, value: 0.7}, {hour: 10.5, value: -1}, {hour: 13, value: -1}, {hour: 13.01, value: 1}, {hour: 13.5, value: 0}, {hour: 14, value: 0.5}, {hour: 15, value: -0.5}, {hour: 15.1, value: 1}, {hour: 17, value: 0.5}, {hour: 18.5, value: 0.5}, {hour: 24, value: 0.7},]);</script>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4293648069803317711.post-82213558513521718642013-10-07T13:09:00.000-07:002013-10-07T13:09:02.270-07:00Tell, don't Ask.<h2>tl;dr</h2><p> If you rely on <em>asking</em>, you're asking for trouble. Instead:</p><ol> <li>Tell functions what to do, don't make them ask.</li> <li>Tell processes what to do, don't make them ask.</li> <li>Keep all your environment variable queries in one place, apart from the rest of the code.</li></ol><h2>You probably think this is obvious, but it isn't</h2><p> The principle of asking versus telling has many faces in programming. Some of the faces are obvious—others are more subtle. This articles moves from the more obvious to the more subtle.</p><p> So if you find yourself saying, "Well, duh!" Keep reading.</p><h2>Telling v. Asking</h2><p> This Python code illustrates <i>telling</i>:</p><div class="code python"><pre><span class="c"># teller.py</span><br /><span class="kn">import</span> <span class="nn">sqlite3</span><br /><br /><span class="k">def</span> <span class="nf">connectToDatabase</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span><br /> <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><br /></pre></div><p> The <code>connectToDatabase</code> function accepts an argument for the database connection details. Other code that calls <code>connectionToDatabase</code> <strong>tells</strong> the function what it wants to do.</p><p> This Python code illustrates <i>asking</i>:</p><div class="code python"><pre><span class="c"># asker.py</span><br /><span class="kn">import</span> <span class="nn">sqlite3</span><br /><br /><span class="n">DATABASE_FILENAME</span><span class="o">=</span><span class="s">'/tmp/database.sqlite'</span><br /><br /><span class="k">def</span> <span class="nf">connectToDatabase</span><span class="p">():</span><br /> <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">DATABASE_FILENAME</span><span class="p">)</span><br /></pre></div><p> The <code>connectToDatabase</code> function in the above snippet is not told database connection details. Instead, the function <strong>asks</strong> for the connection details—in this case, it asks from the global scope (which is <a href="https://www.google.com/search?q=avoid+global+state" target="_blank">a bad place to be in</a>).</p><h2>Telling > Asking</h2><p> <i>Telling</i>, as described above, is better than <i>Asking</i> for the following reasons: <ol> <li> The code is more flexible for reuse. <p> I can more easily connect to different databases using the <code>teller.py</code>. </p> </li> <li> The code is easier to test. <p> Because <code>teller.py</code> is more flexible for reuse, I can use the code in tests very easily. </p> </li> <li> It's easier to know how to use the code and harder to use incorrectly. <p> It's obvious in <code>teller.py</code> that I must provide a filename to connect to (because of the argument spec of the function). I can't accidentally connect to <code>/tmp/database.sqlite</code>. </p> <p> To use <code>asker.py</code> I must know that the function looks at <code>DATABASE_FILENAME</code> either from reading the source code or the docstring of the function (which is absent in this case). This would be much more difficult to do if <code>connectToDatabase</code> called other functions in other files which accessed a global variable. </p> </li> </ol></p><h2>Abusing Environment Variables</h2><p> In <code>asker.py</code> the function <em>asks</em> for information from the global scope. The global scope is just <em>one</em> place to get information from. The environment is another. Take a look at this:</p><div class="code python"><pre><span class="c"># asker-env.py</span><br /><span class="kn">import</span> <span class="nn">sqlite3</span><br /><span class="kn">import</span> <span class="nn">os</span><br /><br /><span class="k">def</span> <span class="nf">connectToDatabase</span><span class="p">():</span><br /> <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'DATABASE_FILENAME'</span><span class="p">])</span><br /></pre></div><p> Instead of asking for the database filename from the global scope, <code>asker-env.py</code> asks the environment of the process for the database filename. This is more reusable that <code>asker.py</code> but is still not as good as <code>teller.py</code> because: <ul> <li>it still suffers from problem #3: that you must rely on the docstring or reading the source code to understand how to use it, and </li> <li>you can only have one connection per process. </li> </ul></p><p> A better approach would be to <em>tell</em> the process which database filename to use as in this example:</p><div class="code python"><pre><span class="c"># teller-cli.py</span><br /><span class="kn">import</span> <span class="nn">sqlite3</span><br /><span class="kn">import</span> <span class="nn">argparse</span><br /><br /><span class="k">def</span> <span class="nf">connectToDatabase</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span><br /> <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><br /><br /><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span><br /> <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">()</span><br /> <span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'filename'</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Filename of the SQLite database"</span><span class="p">)</span><br /> <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span><br /> <span class="n">connectToDatabase</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span><br /></pre></div><p> <code>teller-cli.py</code> is better than <code>asker-env.py</code> because you can ask it (using <code><nobr>teller-cli.py --help</nobr></code>) what you need to tell it instead of having to read docstrings or source code. Having a <code>--help</code> option which fully describes configuration options and is enforced when running the process is similar to <code>teller.py</code> having an argument spec that is enforced by Python.</p><h2>But I thought environment variables were good...</h2><img src="http://upload.wikimedia.org/wikipedia/commons/1/1a/Bachalpseeflowers.jpg" style="border: 0; outline: 0; width:100%; box-shadow: none;">(this is a picture of an environment -- much better than the one on your computer)<p> If you subscribe to <a href="http://12factor.net/config" target="_blank">The Twelve-Factor App's</a> ideas, you will store all your configuration in environment variables. Or if you use <a href="https://travis-ci.org/" target="_blank">Travis-CI</a> or <a href="https://www.heroku.com/" target="_blank">Heroku</a> you will also have used environment variables to great effect. Environment variables seem like a great way to do configuration.</p><p> Environment variables are cross-language and easy to change. They have huge benefits. Environment variables <em>are</em> a great way to do configuration! It would be nice to leverage the great qualities of environment variables along with the great qualities of code that is <em>told</em>.</p><p> You can!</p><h2>Convert Asking to Telling</h2><p> To write <em>Telling</em> code that also uses environment variables, restrict the environment variable querying to a single, documented place. Consider this:</p><div class="code python"><pre><span class="c"># env-runner.py</span><br /><span class="kn">import</span> <span class="nn">argparse</span><br /><span class="kn">import</span> <span class="nn">os</span><br /><br /><span class="c"># Define ALL the environment variables this process might use.</span><br /><span class="n">env_vars</span> <span class="o">=</span> <span class="p">[</span><br /> <span class="p">(</span><span class="s">'DATABASE_FILENAME'</span><span class="p">,</span> <span class="s">'Filename of the SQLite database.'</span><span class="p">),</span><br /><span class="p">]</span><br /><br /><span class="c"># Read the environment. This function must only be called from within this</span><br /><span class="c"># module if you want to prevent writing asking code.</span><br /><span class="k">def</span> <span class="nf">getArgs</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">config</span><span class="p">):</span><br /> <span class="n">ret</span> <span class="o">=</span> <span class="p">{}</span><br /> <span class="k">for</span> <span class="p">(</span><span class="n">env_name</span><span class="p">,</span> <span class="n">description</span><span class="p">)</span> <span class="ow">in</span> <span class="n">config</span><span class="p">:</span><br /> <span class="k">try</span><span class="p">:</span><br /> <span class="n">ret</span><span class="p">[</span><span class="n">env_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">environ</span><span class="p">[</span><span class="n">env_name</span><span class="p">]</span><br /> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span><br /> <span class="k">print</span> <span class="s">'Missing env var: </span><span class="si">%s</span><span class="s"> </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">env_name</span><span class="p">,</span> <span class="n">description</span><span class="p">)</span><br /> <span class="k">raise</span><br /> <span class="k">return</span> <span class="n">ret</span><br /><br /><br /><span class="k">def</span> <span class="nf">main</span><span class="p">():</span><br /> <span class="kn">from</span> <span class="nn">teller</span> <span class="kn">import</span> <span class="n">connectToDatabase</span><br /> <span class="n">args</span> <span class="o">=</span> <span class="n">getArgs</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">,</span> <span class="n">env_vars</span><span class="p">)</span><br /> <span class="n">db</span> <span class="o">=</span> <span class="n">connectToDatabase</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="s">'DATABASE_FILENAME'</span><span class="p">])</span><br /> <span class="c"># ...</span><br /></pre></div><p> The above snippet has all the benefits of telling code and the flexibility of asking code: <ol> <li> The code is flexible for reuse. </li> <li> The code is easy to test. </li> <li> It's easy to know how to use this code and hard to use incorrectly. </li> </ol></p><h2>Real-world examples</h2><p> As proof that the concept of <em>Telling</em> instead of <em>Asking</em> is not obvious, here are some real-world examples (both good and bad):</p><h3>Klein's improvement on Flask</h3><p> <a href="http://flask.pocoo.org/" target="_blank">Flask</a>, a micro web framework for Python, loves the global scope. This is the Hello, World! from the front page:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><br /><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span><br /><br /><span class="nd">@app.route</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span><br /><span class="k">def</span> <span class="nf">hello</span><span class="p">():</span><br /> <span class="k">return</span> <span class="s">"Hello World!"</span><br /><br /><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span><br /> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><p> And this is the <a href="http://flask.pocoo.org/docs/tutorial/dbcon/" target="_blank">recommended way for accessing a database</a>:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">g</span><br /><br /><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span><br /><br /><span class="nd">@app.before_request</span><br /><span class="k">def</span> <span class="nf">before_request</span><span class="p">():</span><br /> <span class="n">g</span><span class="o">.</span><span class="n">db</span> <span class="o">=</span> <span class="n">connect_db</span><span class="p">()</span><br /><br /><span class="nd">@app.route</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span><br /><span class="k">def</span> <span class="nf">hello</span><span class="p">():</span><br /> <span class="n">db</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">db</span><br /> <span class="c"># ...</span><br /></pre></div><p> You use a package-global named <code>g</code>, which is "magical" and comes with appropriate warnings:</p><blockquote> We store our current database connection on the special <code>g</code> object that Flask provides for us. This object stores information for one request only and is available from within each function. Never store such things on other objects because this would not work with threaded environments. That special <code>g</code> object does some magic behind the scenes to ensure it does the right thing. <cite><a href="http://flask.pocoo.org/docs/tutorial/dbcon/" target="_blank">http://flask.pocoo.org/docs/tutorial/dbcon/</a></cite></blockquote><p> Consider the improvement offered by <a href="https://github.com/twisted/klein" target="_blank">Klein</a>, a similar micro web framework. With Klein you can easily make apps with <a href="https://github.com/twisted/klein#non-global-state" target="_blank">Non-global state</a>:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">klein</span> <span class="kn">import</span> <span class="n">Klein</span><br /><br /><span class="k">class</span> <span class="nc">MyApp</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span><br /><br /> <span class="n">app</span> <span class="o">=</span> <span class="n">Klein</span><span class="p">()</span><br /><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">db_connection</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">db_connection</span> <span class="o">=</span> <span class="n">db_connection</span><br /><br /> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span><br /> <span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span><br /> <span class="n">db</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">db_connection</span><br /> <span class="c"># ...</span><br /></pre></div><p> There is no magical, global <code>g</code> here. You can instantiate <code>MyApp</code> with a database connection, or even have three different instances of <code>MyApp</code> with three different database connections all running in the same app.</p><p> Klein lets you <em>tell</em> instead of <em>ask</em>.</p><h3>Ansible</h3><p> <a href="http://www.ansibleworks.com/" target="_blank">Ansible</a> is a (really good) configuration management tool. It's mostly straightforward, but it's easy to use it in an <em>asking</em> way—which becomes unmaintainable.</p><p> For instance, if in one of our tasks we want to download a resource from <code>http://dev.example.com</code> if we're in the development environment or from <code>https://production.example.com</code> if we're in the production environment, Ansible easily lets us do this:</p><div class="code yaml"><pre><span class="c1"># main.yml</span><br /><span class="p-Indicator">-</span> <span class="l-Scalar-Plain">name</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Get the files</span><br /> <span class="l-Scalar-Plain">command</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">wget {{ source_server }}/thefile.tgz /tmp/thefile.tgz</span><br /> <span class="l-Scalar-Plain">creates=/tmp/thefile.tgz</span><br /></pre></div><p> The command <em>asks</em> for <code>source_server</code>. This task likely lives in a role's task file, which could be in <code>roles/mymachine/tasks/main.yml</code>, deep within the directory structure of my configuration. The problem is that I have no way of knowing (short of manually parsing the task file) when writing my inventory file or anything else that uses/includes <code>main.yml</code>, that <code>source_server</code> is a needed variable.</p><p> Ansible lets you <em>ask</em> yourself into an unmaintainable hole. To be more maintainable, Ansible should provide a mechanism for specifying the parameters needed by tasks. Perhaps something like:</p><div class="code yaml"><pre><span class="c1"># main-with-vars.yml</span><br /><span class="p-Indicator">-</span> <span class="l-Scalar-Plain">variables</span><span class="p-Indicator">:</span><br /> <span class="p-Indicator">-</span> <span class="l-Scalar-Plain">name</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">source_server</span><br /> <span class="l-Scalar-Plain">description</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">URL of the server to download source files from. For</span><br /> <span class="l-Scalar-Plain">example</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">http://foo.com</span><br /> <span class="l-Scalar-Plain">default</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">http://example.com</span><br /><br /><span class="p-Indicator">-</span> <span class="l-Scalar-Plain">name</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">Get the files</span><br /> <span class="l-Scalar-Plain">command</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">wget {{ source_server }}/thefile.tgz /tmp/thefile.tgz</span><br /> <span class="l-Scalar-Plain">creates=/tmp/thefile.tgz</span><br /></pre></div><p> Such a file would allow you to produce a list of all the configurable variables for a role/task and then be able to <em>tell</em> instead of <em>ask</em>.</p><h3>AngularJS</h3><p> AngularJS does a lot to help you avoid <em>asking</em> through <a href="http://docs.angularjs.org/guide/di" target="_blank">dependency injection</a>.</p><h3>Twisted's Reactor</h3><p> Twisted is currently working toward <a href="http://twistedmatrix.com/documents/current/api/twisted.internet.reactor.html" target="_blank">making the reactor not global</a> in an effort to make testing easier and perhaps allow for new features.</p><h2>Conclusion</h2><p> In conclusion, read the tl;dr at the top :) Also, do you have some example of <em>telling</em> v. <em>asking</em>? Or counter-arguments? Post a comment.</p>Unknownnoreply@blogger.com29tag:blogger.com,1999:blog-4293648069803317711.post-75682486279920081242013-09-17T13:39:00.000-07:002013-09-17T13:39:04.146-07:00When GitHub is down, BitTorrent Sync saves the day!<a href="https://status.github.com/messages/2013-09-17" target="_blank">GitHub was down momentarily</a> which wouldn't normally be a problem (I'd wait a few minutes until it came back up). But it happened to be down right when I was leaving for work (and wanted what I was working on at work).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEieL97nOfCiDXg4j2_st1DOPGHGl1N_s9vr90MdnUxwPtioXNRxwIO_a60bO5tOFVlQbutWVUZHOU2-7NhBLfOZ7pZznc2y1OSDDxhncdqCMzJ0ykLFQk1gaaD-lNciyW58fXEPl7ZyT/s1600/bittorrent.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEieL97nOfCiDXg4j2_st1DOPGHGl1N_s9vr90MdnUxwPtioXNRxwIO_a60bO5tOFVlQbutWVUZHOU2-7NhBLfOZ7pZznc2y1OSDDxhncdqCMzJ0ykLFQk1gaaD-lNciyW58fXEPl7ZyT/s1600/bittorrent.png" /></a></div>
<br />
<a href="http://labs.bittorrent.com/experiments/sync.html" target="_blank">BitTorrent Sync</a> to the rescue! I have a directory full of bare repositories that I share with BitTorrent Sync. I just push there, then, by the time I'm back at work, the code has been copied to my computer here.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-7514986904533676722013-09-16T06:38:00.000-07:002013-09-17T08:18:02.796-07:00To the FairTax ProponentsThe idea of FairTax excites me. I value simplicity and the FairTax <i>seems</i> simple.<br />
<br />
Let me say again (because the rest of this post will probably make you think I <i>don't</i> support this legislation): I support the principles that FairTax seems to be built on. The principles <i>seem</i> to be comprehensibility, simplicity, fairness and a lack of loopholes.<br />
<br />
But you, legislators trying to pass this bill, do not currently have my support. Mostly, because of problems with <a href="http://fairtax.org/">FairTax.org</a> -- problems that have little to do with the content of the bill and everything to do with bad delivery and marketing.<br />
<br />
<h2>
1. Where are the Cons?</h2>
<div>
<a href="http://fairtax.org/">FairTax.org</a> leads me to conclude that the FairTax is practically perfect in every way. But, from experience, I know that there's no way FairTax can be perfect. There are downsides to this (and every other) legislation, and it is dishonest to pretend that there are not.</div>
<div>
<br /></div>
<div>
I would like to see a set of pros and cons, if for no other reason than to show that the legislators are actually thinking this all the way through. Perhaps there should be more than one set of pros and cons. It would be informative to see pros and cons for the wealthy, the poor, corporations, religious institutions, government agencies, brick and mortar stores, online stores, insurance companies, hospitals, etc...</div>
<div>
<br /></div>
<div>
If you can't acknowledge faults, then I can't accept that you've thought this all the way through and I will not support you.</div>
<h2>
2. The FAQ is not helpful</h2>
I tend to agree with <a href="http://digital.cabinetoffice.gov.uk/2013/07/25/faqs-why-we-dont-have-them/" target="_blank">this article's estimation of the FAQ as a format</a>. And after using the <a href="http://fairtax.org/">FairTax.org</a> FAQ, I agree even more for two reasons:<br />
<br />
<ol>
<li>That I have to click every single question before the answer shows is ridiculous. And I can't have more than one answer open at a time. <b>Just give me the info!</b> I can scroll past the information that isn't relevant.</li>
<li>I read about how other governments (e.g. State of Florida) have implemented similar tax structures. About an hour later, I wanted to show my wife and I couldn't find the same question again. I found myself trying iterating through possible ways the question could be phrased. If all the text was on the page, I would have just searched the page for "Florida."</li>
</ol>
<div>
Please take some time to reorganize the FAQ into a navigable document and stop hiding so much. It feels like you're trying to hide stuff.</div>
<div>
<br /></div>
<br />
<h2>
3. Link to the text</h2>
All of the text and videos on <a href="http://fairtax.org/">FairTax.org</a> are nothing compared to the actual text of the legislation. You can say all you want, however you want, but it's not FairTax.org that's going to be put into law, it's <a href="http://thomas.loc.gov/cgi-bin/query/z?c113:H.R.25:" target="_blank">H.R. 25</a>. Please provide a link to the text of the bill. Then, rather than telling my what you think the bill says, quote the bill directly. A side benefit to quoting the actual text is that it might motivate the authors of the text to make it more intelligible to those who aren't politicians.<br />
<br />
<h2>
4. Lead with 30%</h2>
<div>
The most prominent number on <a href="http://fairtax.org/">FairTax.org</a> is 23%. And the FairTax is a sales tax, not an income tax. So, in the FAQ when people see the question <a href="http://www.fairtax.org/site/PageServer?pagename=FAQs#36" target="_blank">"What will be the rate of the sales tax be at the retail counter?"</a> I will bet that 90% of them will react the same way I did. "It's 23%, right?"</div>
<div>
<br /></div>
<div>
*click*</div>
<div>
<br /></div>
<div>
"30 percent?! They lied!"</div>
<div>
<br /></div>
<div>
The text goes on to accurately explain how 23% refers to sales tax based on a tax-inclusive income so that we're comparing apples with blah blah blah. The math is accurate. I don't dispute that. But the feeling of distrust I experienced was very real. It is misleading to tout the FairTax as a 23% tax that's a sales tax, when it's actually a 30% sales tax.</div>
<div>
<br /></div>
<div>
The feeling you get when you've been led along with 23% only to find see, intentionally hidden in the FAQ is that you've been had and that the people authoring this bill are deceptive.</div>
<div>
<br /></div>
<div>
Instead of leading with 23% and trying to explain away 30%, lead with the more obvious 30% and explain how it's actually 23%. If you can't lead with 30%, this bill won't pass. Be more forthright.</div>
<div>
<br /></div>
<h2>
5. Corporations are fictions?</h2>
<div>
Under the question <a href="http://www.fairtax.org/site/PageServer?pagename=FAQs#50" target="_blank">"Will corporations get a windfall with the abolition of the corporate tax?"</a> (which you would think is a yes or no question [until you read the answer]) it begins "Corporations are legal fictions..."</div>
<div>
<br /></div>
<div>
In this post I'm offering a critique of the delivery of the message (not the content of the bill). I have my reservations about the exceptional status offered to corporations by this bill, but what I take issue with here is the silly notion of calling corporations fictions.</div>
<div>
<br /></div>
<div>
I can see what they're trying to say: that people bear the burdens of taxes on business. But corporations are not fictions. I work for a corporation. It's no fiction. The corporation buys real goods, feeds us real food sometimes, spends real money in the community, donates real money to campaigns. "Fiction" is too strong a word. Please rephrase the answer to this question (including perhaps a Yes or No at the beginning).</div>
<div>
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-19098596258720802902013-08-22T14:55:00.000-07:002013-08-22T14:55:23.796-07:00Angular AJAX Upload<table> <tr> <td width="50%"> <img style="border: 0; box-shadow: none; width: 100%;" src="http://upload.wikimedia.org/wikipedia/commons/8/83/Glossy_3d_blue_arrow_up.png"> </td> <td> <img style="border: 0; box-shadow: none; width:100%;" src="https://si0.twimg.com/profile_images/2149314222/square.png"> </td> </tr></table><p> Though the Internet would have you believe otherwise, uploading a file asynchronously from AngularJS isn't that hard. I don't want fancy colors or previews or progress bars or any of that. I want to upload a file from my AngularJS-backed webapp without reloading the page. Also, I don't care about old browsers. If you do, then this might not work for you.</p><p> After struggling with <a href="http://blueimp.github.io/jQuery-File-Upload/angularjs.html" target="_blank">blueimp's library</a> for way too long, I decided to just implement the part I needed.</p><p> Uploading a file using AJAX + AngularJS requires three things: <ol> <li>AJAX</li> <li>AngularJS</li> <li>AJAX + AngularJS</li> </ol></p><h2>1. AJAX</h2><div class="code javascript"><pre><span class="kd">function</span> <span class="nx">upload</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">file</span><span class="p">)</span> <span class="p">{</span><br /> <span class="kd">var</span> <span class="nx">formdata</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FormData</span><span class="p">(),</span><br /> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span><br /><br /> <span class="nx">formdata</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'myfile'</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span><br /><br /> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">if</span> <span class="p">(</span><span class="mi">4</span> <span class="o">===</span> <span class="k">this</span><span class="p">.</span><span class="nx">readyState</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span><br /> <span class="c1">// success</span><br /> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span><br /> <span class="c1">// failure</span><br /> <span class="p">}</span><br /> <span class="p">}</span><br /> <span class="p">}</span><br /> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">"POST"</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span><br /> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">formdata</span><span class="p">);</span><br /><span class="p">}</span><br /></pre></div><p> The file will be posted to the server as the parameter named <code>myfile</code>.</p><h2>2. AngularJS</h2><div class="code javascript"><pre><span class="nx">app</span><span class="p">.</span><span class="nx">directive</span><span class="p">(</span><span class="s1">'fileChange'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="p">{</span><br /> <span class="nx">restrict</span><span class="o">:</span> <span class="s1">'A'</span><span class="p">,</span><br /> <span class="nx">link</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">scope</span><span class="p">,</span> <span class="nx">element</span><span class="p">,</span> <span class="nx">attrs</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">element</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="s1">'change'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="nx">scope</span><span class="p">.</span><span class="nx">$apply</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="nx">scope</span><span class="p">[</span><span class="nx">attrs</span><span class="p">[</span><span class="s1">'fileChange'</span><span class="p">]](</span><span class="nx">element</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">files</span><span class="p">);</span><br /> <span class="p">})</span><br /> <span class="p">})</span><br /> <span class="p">},</span><br /> <span class="p">}</span><br /><span class="p">})</span><br /></pre></div><p> If you use the above directive like this: <div class="code html"><pre><span class="nt"><input</span> <span class="na">type=</span><span class="s">"file"</span> <span class="na">file-change=</span><span class="s">"runSomething"</span><span class="nt">></span><br /></pre></div></p><p> when the user chooses a file to upload, <code>runSomething</code> will be called with a <code><a href="http://www.w3.org/TR/FileAPI/#dfn-filelist" target="_blank">FileList</a></code>. You can pass the first element in that list as the second arg to the <code>upload</code> function above.</p><h2>3. AJAX + AngularJS</h2><p> I can't provide a complete demo (because this blog isn't backed by a server I control). But this will probably get you really close:</p><div class="code html"><pre><span class="cp"><!DOCTYPE html></span><br /><span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">></span><br /><span class="nt"><body</span> <span class="na">ng-app=</span><span class="s">"myapp"</span> <span class="na">ng-controller=</span><span class="s">"UploadCtrl"</span><span class="nt">></span><br /> <span class="nt"><input</span> <span class="na">type=</span><span class="s">"file"</span> <span class="na">file-change=</span><span class="s">"upload"</span><span class="nt">></span><br /><br /> <span class="nt"><script </span><span class="na">src=</span><span class="s">"https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"</span><span class="nt">></script></span><br /> <span class="nt"><script></span><br /> <span class="c1">// the javascript</span><br /> <span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'myapp'</span><span class="p">,</span> <span class="p">[]);</span><br /><br /> <span class="c1">//</span><br /> <span class="c1">// Reusable Uploader service.</span><br /> <span class="c1">//</span><br /> <span class="nx">app</span><span class="p">.</span><span class="nx">factory</span><span class="p">(</span><span class="s1">'Uploader'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$q</span><span class="p">,</span> <span class="nx">$rootScope</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">this</span><span class="p">.</span><span class="nx">upload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">file</span><span class="p">)</span> <span class="p">{</span><br /> <span class="kd">var</span> <span class="nx">deferred</span> <span class="o">=</span> <span class="nx">$q</span><span class="p">.</span><span class="nx">defer</span><span class="p">(),</span><br /> <span class="nx">formdata</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FormData</span><span class="p">(),</span><br /> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span><br /><br /> <span class="nx">formdata</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s1">'file'</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span><br /><br /> <span class="nx">xhr</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">if</span> <span class="p">(</span><span class="mi">4</span> <span class="o">===</span> <span class="k">this</span><span class="p">.</span><span class="nx">readyState</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">$rootScope</span><span class="p">.</span><span class="nx">$apply</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="nx">deferred</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">xhr</span><span class="p">);</span> <br /> <span class="p">});</span><br /> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span><br /> <span class="nx">$rootScope</span><span class="p">.</span><span class="nx">$apply</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="nx">deferred</span><span class="p">.</span><span class="nx">reject</span><span class="p">(</span><span class="nx">xhr</span><span class="p">);</span> <br /> <span class="p">});</span><br /> <span class="p">}</span><br /> <span class="p">}</span><br /> <span class="p">}</span><br /> <span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">"POST"</span><span class="p">,</span> <span class="nx">url</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span><br /> <span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">formdata</span><span class="p">);</span><br /> <span class="k">return</span> <span class="nx">deferred</span><span class="p">.</span><span class="nx">promise</span><span class="p">;</span><br /> <span class="p">};</span><br /> <span class="k">return</span> <span class="k">this</span><span class="p">;</span><br /> <span class="p">})</span><br /><br /><br /> <span class="c1">//</span><br /> <span class="c1">// fileChange directive because ng-change doesn't work for file inputs.</span><br /> <span class="c1">//</span><br /> <span class="nx">app</span><span class="p">.</span><span class="nx">directive</span><span class="p">(</span><span class="s1">'fileChange'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="p">{</span><br /> <span class="nx">restrict</span><span class="o">:</span> <span class="s1">'A'</span><span class="p">,</span><br /> <span class="nx">link</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">scope</span><span class="p">,</span> <span class="nx">element</span><span class="p">,</span> <span class="nx">attrs</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">element</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="s1">'change'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="nx">scope</span><span class="p">.</span><span class="nx">$apply</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="nx">scope</span><span class="p">[</span><span class="nx">attrs</span><span class="p">[</span><span class="s1">'fileChange'</span><span class="p">]](</span><span class="nx">element</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">files</span><span class="p">);</span><br /> <span class="p">})</span><br /> <span class="p">})</span><br /> <span class="p">},</span><br /> <span class="p">}</span><br /> <span class="p">})</span><br /><br /> <span class="c1">//</span><br /> <span class="c1">// Example controller</span><br /> <span class="c1">//</span><br /> <span class="nx">app</span><span class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s1">'UploadCtrl'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">$http</span><span class="p">,</span> <span class="nx">Uploader</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">$scope</span><span class="p">.</span><span class="nx">upload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">files</span><span class="p">)</span> <span class="p">{</span><br /> <span class="kd">var</span> <span class="nx">r</span> <span class="o">=</span> <span class="nx">Uploader</span><span class="p">.</span><span class="nx">upload</span><span class="p">(</span><span class="s1">'/uploads'</span><span class="p">,</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span><br /> <span class="nx">r</span><span class="p">.</span><span class="nx">then</span><span class="p">(</span><br /> <span class="kd">function</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p">{</span><br /> <span class="c1">// success</span><br /> <span class="p">},</span><br /> <span class="kd">function</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="p">{</span><br /> <span class="c1">// failure</span><br /> <span class="p">});</span><br /> <span class="p">}</span><br /> <span class="p">});</span><br /> <span class="nt"></script></span><br /><span class="nt"></body></span><br /><span class="nt"></html></span><br /></pre></div><h2>More</h2><p> You can do more things like handle multiple files, monitor progress, preview images, etc... But if you don't need all that, and you are using modern browsers, this should do just fine.</p>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-4293648069803317711.post-1398513203699102482013-08-15T12:46:00.000-07:002013-08-15T12:46:15.511-07:00Practical event-driven programming with Python and Twisted<h2>Introduction</h2><p> A article from 2008 entitled <a href="http://www.ibm.com/developerworks/aix/library/au-threadingpython/" target="_blank">Practical threaded programming with Python</a> was posted to HN today. And I thought, "how would those examples look with Twisted?"</p><p> For a great explanation about how Twisted does concurrency, see <a href="http://krondo.com/?page_id=1327" target="_blank">krondo's Twisted Introduction</a>. On to the code: </p><h2>Hello World</h2><p> The <a href="http://www.ibm.com/developerworks/aix/library/au-threadingpython/#N1007E" target="_blank">first example in the article</a> demonstrates that threads have IDs. Since we're not using threads, the most equiavelent way to do the same thing with Twisted is to not use Twisted at all:</p><p><div class="code python"><pre><span class="kn">import</span> <span class="nn">datetime</span><br /><br /><br /><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">what</span><span class="p">):</span><br /> <span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><br /> <span class="k">print</span> <span class="s">'</span><span class="si">%s</span><span class="s"> says Hello World at time: </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">what</span><span class="p">,</span> <span class="n">now</span><span class="p">)</span><br /><br /><br /><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">):</span><br /> <span class="n">run</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><br /></pre></div></p><p> Output:<div class="code text"><pre>0 says Hello World at time: 2013-08-15 13:45:17.164933<br />1 says Hello World at time: 2013-08-15 13:45:17.165442<br /></pre></div></p><h2>Using queues</h2><p> The <a href="http://www.ibm.com/developerworks/aix/library/au-threadingpython/#N100AA" target="_blank">next example</a> shows first a <a href="http://www.ibm.com/developerworks/aix/library/au-threadingpython/#url_fetch_serial" target="_blank">serial approach</a> and then a <a href="http://www.ibm.com/developerworks/aix/library/au-threadingpython/#url_fetch_threaded" target="_blank">threaded approach</a> to "grab a URL of a website, and print out the first 1024 bytes of the page." Here are the synchronous/serial and threaded versions.</p><p> I should note that I've modified them to get all the page (instead of the first 1024 bytes) and to print a hash of the content (so as not to clutter up this post). It's interesting that only apple.com and ibm.com return the same hash every time.</p><h3>Synchronous version</h3><p><div class="code python"><pre><span class="kn">import</span> <span class="nn">urllib2</span><br /><span class="kn">import</span> <span class="nn">time</span><br /><span class="kn">import</span> <span class="nn">hashlib</span><br /><br /><span class="n">hosts</span> <span class="o">=</span> <span class="p">[</span><span class="s">"http://yahoo.com"</span><span class="p">,</span> <span class="s">"http://google.com"</span><span class="p">,</span> <span class="s">"http://amazon.com"</span><span class="p">,</span><br /><span class="s">"http://ibm.com"</span><span class="p">,</span> <span class="s">"http://apple.com"</span><span class="p">]</span><br /><br /><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span><br /><span class="c">#grabs urls of hosts and prints first 1024 bytes of page</span><br /><span class="k">for</span> <span class="n">host</span> <span class="ow">in</span> <span class="n">hosts</span><span class="p">:</span><br /> <span class="n">url</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">host</span><span class="p">)</span><br /> <span class="k">print</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha1</span><span class="p">(</span><span class="n">url</span><span class="o">.</span><span class="n">read</span><span class="p">())</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(),</span> <span class="n">host</span><br /><br /><span class="k">print</span> <span class="s">"Elapsed Time: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span><br /></pre></div></p><p> Output:<div class="code text"><pre>2430771cc3723e965b64eda2d69dd22b697dd4a0 http://yahoo.com<br />790ace256c1b683a585226d286859f9f2910d9b0 http://google.com<br />63fbbe761817ebef066f9562e96209ca25a6f0b3 http://amazon.com<br />dd2f34c7c4f47b49272d7922e4f17f7c1cafd3aa http://ibm.com<br />562ffc06504dc0557386524b382372448d6e953a http://apple.com<br />Elapsed Time: 3.34798121452<br /></pre></div></p><h3>Threaded version</h3><p><div class="code python"><pre><span class="c">#!/usr/bin/env python</span><br /><span class="kn">import</span> <span class="nn">Queue</span><br /><span class="kn">import</span> <span class="nn">threading</span><br /><span class="kn">import</span> <span class="nn">urllib2</span><br /><span class="kn">import</span> <span class="nn">time</span><br /><span class="kn">import</span> <span class="nn">hashlib</span><br /><br /><span class="n">hosts</span> <span class="o">=</span> <span class="p">[</span><span class="s">"http://yahoo.com"</span><span class="p">,</span> <span class="s">"http://google.com"</span><span class="p">,</span> <span class="s">"http://amazon.com"</span><span class="p">,</span><br /><span class="s">"http://ibm.com"</span><span class="p">,</span> <span class="s">"http://apple.com"</span><span class="p">]</span><br /><br /><span class="n">queue</span> <span class="o">=</span> <span class="n">Queue</span><span class="o">.</span><span class="n">Queue</span><span class="p">()</span><br /><br /><span class="k">class</span> <span class="nc">ThreadUrl</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">):</span><br /> <span class="sd">"""Threaded Url Grab"""</span><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">queue</span><span class="p">):</span><br /> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span> <span class="o">=</span> <span class="n">queue</span><br /><br /> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">while</span> <span class="bp">True</span><span class="p">:</span><br /> <span class="c">#grabs host from queue</span><br /> <span class="n">host</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">get</span><span class="p">()</span><br /><br /> <span class="c">#grabs urls of hosts and prints first 1024 bytes of page</span><br /> <span class="n">url</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">host</span><span class="p">)</span><br /> <span class="k">print</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha1</span><span class="p">(</span><span class="n">url</span><span class="o">.</span><span class="n">read</span><span class="p">())</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(),</span> <span class="n">host</span><br /><br /> <span class="c">#signals to queue job is done</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">task_done</span><span class="p">()</span><br /><br /><br /><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span><br /><span class="k">def</span> <span class="nf">main</span><span class="p">():</span><br /> <span class="c">#spawn a pool of threads, and pass them queue instance </span><br /> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span><br /> <span class="n">t</span> <span class="o">=</span> <span class="n">ThreadUrl</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span><br /> <span class="n">t</span><span class="o">.</span><span class="n">setDaemon</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span><br /> <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span><br /><br /> <span class="c">#populate queue with data </span><br /> <span class="k">for</span> <span class="n">host</span> <span class="ow">in</span> <span class="n">hosts</span><span class="p">:</span><br /> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">host</span><span class="p">)</span><br /><br /> <span class="c">#wait on the queue until everything has been processed</span><br /> <span class="n">queue</span><span class="o">.</span><span class="n">join</span><span class="p">()</span><br /><br /><span class="n">main</span><span class="p">()</span><br /><span class="k">print</span> <span class="s">"Elapsed Time: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span><br /></pre></div></p><p> Output:<div class="code text"><pre>562ffc06504dc0557386524b382372448d6e953a http://apple.com<br />fb6fe32cb270f7929157bec5f29ee44f729949fd http://google.com<br />dd2f34c7c4f47b49272d7922e4f17f7c1cafd3aa http://ibm.com<br />3643a39f4dd641a3c08f8e5c409d0f5bc6407aed http://amazon.com<br />3072477b1680fc2650d9cb0674e5ef7972873bf6 http://yahoo.com<br />Elapsed Time: 1.23798894882<br /></pre></div></p><h3>Twisted version</h3><p> Here's one way to do the same thing with Twisted:</p><p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">defer</span><span class="p">,</span> <span class="n">task</span><br /><span class="kn">from</span> <span class="nn">twisted.web.client</span> <span class="kn">import</span> <span class="n">getPage</span><br /><span class="kn">import</span> <span class="nn">time</span><br /><span class="kn">import</span> <span class="nn">hashlib</span><br /><br /><span class="n">hosts</span> <span class="o">=</span> <span class="p">[</span><span class="s">"http://yahoo.com"</span><span class="p">,</span> <span class="s">"http://google.com"</span><span class="p">,</span> <span class="s">"http://amazon.com"</span><span class="p">,</span><br /><span class="s">"http://ibm.com"</span><span class="p">,</span> <span class="s">"http://apple.com"</span><span class="p">]</span><br /><br /><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span><br /><br /><span class="k">def</span> <span class="nf">printHash</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">host</span><span class="p">):</span><br /> <span class="k">print</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha1</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(),</span> <span class="n">host</span><br /><br /><br /><span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">reactor</span><span class="p">,</span> <span class="n">hosts</span><span class="p">):</span><br /> <span class="n">dlist</span> <span class="o">=</span> <span class="p">[]</span><br /> <span class="k">for</span> <span class="n">host</span> <span class="ow">in</span> <span class="n">hosts</span><span class="p">:</span><br /> <span class="n">d</span> <span class="o">=</span> <span class="n">getPage</span><span class="p">(</span><span class="n">host</span><span class="p">)</span><br /> <span class="c"># when we have the content, call printHash with it</span><br /> <span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">printHash</span><span class="p">,</span> <span class="n">host</span><span class="p">)</span><br /> <span class="n">dlist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">d</span><span class="p">)</span><br /><br /> <span class="c"># finish the process when the "queue" is done</span><br /> <span class="k">return</span> <span class="n">defer</span><span class="o">.</span><span class="n">gatherResults</span><span class="p">(</span><span class="n">dlist</span><span class="p">)</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">printElapsedTime</span><span class="p">)</span><br /><br /><br /><span class="k">def</span> <span class="nf">printElapsedTime</span><span class="p">(</span><span class="n">ignore</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">"Elapsed Time: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span><br /><br /><br /><span class="n">task</span><span class="o">.</span><span class="n">react</span><span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="p">[</span><span class="n">hosts</span><span class="p">])</span><br /></pre></div></p><p> Output:<div class="code text"><pre>188eecd4da73515a9d1b3fde88d81ccc3a1e6028 http://google.com<br />562ffc06504dc0557386524b382372448d6e953a http://apple.com<br />dd2f34c7c4f47b49272d7922e4f17f7c1cafd3aa http://ibm.com<br />968fc83c1c7717575af03d43b236baf508134d0f http://yahoo.com<br />90c51ab729261bb72db922fb5ad22c0ae33c09da http://amazon.com<br />Elapsed Time: 1.36157393456<br /></pre></div></p><p> The run times of the threaded version and the Twisted version are comparable. Running them each multiple times, sometimes the threaded version is faster and sometimes the Twisted version is faster. They are both consistently faster than the synchronous version. Either way, this isn't a great benchmark and doesn't say much about how ansynchronous v. threaded will work in your particular case.</p><h2>Working with multiple queues</h2><p> The article's <a href="http://www.ibm.com/developerworks/aix/library/au-threadingpython/#N100FF" target="_blank">third bit of code</a> shows how to use multiple queues to get the URL's body in one thread, then process it in another thread.</p><h3>Threaded version</h3><p><div class="code python"><pre><span class="kn">import</span> <span class="nn">Queue</span><br /><span class="kn">import</span> <span class="nn">threading</span><br /><span class="kn">import</span> <span class="nn">urllib2</span><br /><span class="kn">import</span> <span class="nn">time</span><br /><span class="kn">from</span> <span class="nn">BeautifulSoup</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span><br /><br /><span class="n">hosts</span> <span class="o">=</span> <span class="p">[</span><span class="s">"http://yahoo.com"</span><span class="p">,</span> <span class="s">"http://google.com"</span><span class="p">,</span> <span class="s">"http://amazon.com"</span><span class="p">,</span><br /> <span class="s">"http://ibm.com"</span><span class="p">,</span> <span class="s">"http://apple.com"</span><span class="p">]</span><br /><br /><span class="n">queue</span> <span class="o">=</span> <span class="n">Queue</span><span class="o">.</span><span class="n">Queue</span><span class="p">()</span><br /><span class="n">out_queue</span> <span class="o">=</span> <span class="n">Queue</span><span class="o">.</span><span class="n">Queue</span><span class="p">()</span><br /><br /><span class="k">class</span> <span class="nc">ThreadUrl</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">):</span><br /> <span class="sd">"""Threaded Url Grab"""</span><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">queue</span><span class="p">,</span> <span class="n">out_queue</span><span class="p">):</span><br /> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span> <span class="o">=</span> <span class="n">queue</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">out_queue</span> <span class="o">=</span> <span class="n">out_queue</span><br /><br /> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">while</span> <span class="bp">True</span><span class="p">:</span><br /> <span class="c">#grabs host from queue</span><br /> <span class="n">host</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">get</span><span class="p">()</span><br /><br /> <span class="c">#grabs urls of hosts and then grabs chunk of webpage</span><br /> <span class="n">url</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">host</span><span class="p">)</span><br /> <span class="n">chunk</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><br /><br /> <span class="c">#place chunk into out queue</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">out_queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span><br /><br /> <span class="c">#signals to queue job is done</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">task_done</span><span class="p">()</span><br /><br /><span class="k">class</span> <span class="nc">DatamineThread</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">):</span><br /> <span class="sd">"""Threaded Url Grab"""</span><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">out_queue</span><span class="p">):</span><br /> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">out_queue</span> <span class="o">=</span> <span class="n">out_queue</span><br /><br /> <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">while</span> <span class="bp">True</span><span class="p">:</span><br /> <span class="c">#grabs host from queue</span><br /> <span class="n">chunk</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">out_queue</span><span class="o">.</span><span class="n">get</span><span class="p">()</span><br /><br /> <span class="c">#parse the chunk</span><br /> <span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span><br /> <span class="k">print</span> <span class="n">soup</span><span class="o">.</span><span class="n">findAll</span><span class="p">([</span><span class="s">'title'</span><span class="p">])</span><br /><br /> <span class="c">#signals to queue job is done</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">out_queue</span><span class="o">.</span><span class="n">task_done</span><span class="p">()</span><br /><br /><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span><br /><span class="k">def</span> <span class="nf">main</span><span class="p">():</span><br /><br /> <span class="c">#spawn a pool of threads, and pass them queue instance</span><br /> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span><br /> <span class="n">t</span> <span class="o">=</span> <span class="n">ThreadUrl</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">out_queue</span><span class="p">)</span><br /> <span class="n">t</span><span class="o">.</span><span class="n">setDaemon</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span><br /> <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span><br /><br /> <span class="c">#populate queue with data</span><br /> <span class="k">for</span> <span class="n">host</span> <span class="ow">in</span> <span class="n">hosts</span><span class="p">:</span><br /> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">host</span><span class="p">)</span><br /><br /> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span><br /> <span class="n">dt</span> <span class="o">=</span> <span class="n">DatamineThread</span><span class="p">(</span><span class="n">out_queue</span><span class="p">)</span><br /> <span class="n">dt</span><span class="o">.</span><span class="n">setDaemon</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span><br /> <span class="n">dt</span><span class="o">.</span><span class="n">start</span><span class="p">()</span><br /><br /><br /> <span class="c">#wait on the queue until everything has been processed</span><br /> <span class="n">queue</span><span class="o">.</span><span class="n">join</span><span class="p">()</span><br /> <span class="n">out_queue</span><span class="o">.</span><span class="n">join</span><span class="p">()</span><br /><br /><span class="n">main</span><span class="p">()</span><br /><span class="k">print</span> <span class="s">"Elapsed Time: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span><br /></pre></div></p><p> Output:<div class="code text"><pre>[<title>Apple</title>]<br />[<title>Google</title>]<br />[<title>IBM - United States</title>]<br />[<title>Amazon.com: Online Shopping for Electronics, Apparel, Computers, Books, DVDs &amp; more</title>]<br />[<title>Yahoo!</title>]<br />Elapsed Time: 1.65801095963<br /></pre></div></p><h3>Twisted version</h3><p> For this simple example, it makes sense to just do the processing right after receiving the body. That would look like this:</p><p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">defer</span><span class="p">,</span> <span class="n">task</span><br /><span class="kn">from</span> <span class="nn">twisted.web.client</span> <span class="kn">import</span> <span class="n">getPage</span><br /><span class="kn">import</span> <span class="nn">time</span><br /><span class="kn">from</span> <span class="nn">BeautifulSoup</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span><br /><br /><span class="n">hosts</span> <span class="o">=</span> <span class="p">[</span><span class="s">"http://yahoo.com"</span><span class="p">,</span> <span class="s">"http://google.com"</span><span class="p">,</span> <span class="s">"http://amazon.com"</span><span class="p">,</span><br /><span class="s">"http://ibm.com"</span><span class="p">,</span> <span class="s">"http://apple.com"</span><span class="p">]</span><br /><br /><span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span><br /><br /><span class="k">def</span> <span class="nf">printTitle</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="n">host</span><span class="p">):</span><br /> <span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">content</span><span class="p">)</span><br /> <span class="k">print</span> <span class="n">soup</span><span class="o">.</span><span class="n">findAll</span><span class="p">([</span><span class="s">'title'</span><span class="p">])</span><br /><br /><br /><span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">reactor</span><span class="p">,</span> <span class="n">hosts</span><span class="p">):</span><br /> <span class="n">dlist</span> <span class="o">=</span> <span class="p">[]</span><br /> <span class="k">for</span> <span class="n">host</span> <span class="ow">in</span> <span class="n">hosts</span><span class="p">:</span><br /> <span class="n">d</span> <span class="o">=</span> <span class="n">getPage</span><span class="p">(</span><span class="n">host</span><span class="p">)</span><br /> <span class="c"># when we have the content, call printTitle with it</span><br /> <span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">printTitle</span><span class="p">,</span> <span class="n">host</span><span class="p">)</span><br /> <span class="n">dlist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">d</span><span class="p">)</span><br /><br /> <span class="c"># finish the process when the "queue" is done</span><br /> <span class="k">return</span> <span class="n">defer</span><span class="o">.</span><span class="n">gatherResults</span><span class="p">(</span><span class="n">dlist</span><span class="p">)</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">printElapsedTime</span><span class="p">)</span><br /><br /><br /><span class="k">def</span> <span class="nf">printElapsedTime</span><span class="p">(</span><span class="n">ignore</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">"Elapsed Time: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span><br /><br /><br /><span class="n">task</span><span class="o">.</span><span class="n">react</span><span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="p">[</span><span class="n">hosts</span><span class="p">])</span><br /></pre></div></p><p> Output:<div class="code text"><pre>[<title>Google</title>]<br />[<title>Apple</title>]<br />[<title>IBM - United States</title>]<br />[<title>Amazon.com: Online Shopping for Electronics, Apparel, Computers, Books, DVDs &amp; more</title>]<br />[<title>Yahoo!</title>]<br />Elapsed Time: 1.80365180969<br /></pre></div></p><p> (As with the previous examples, neither the threaded nor the Twisted version are much different in speed.)</p><h2>Hey!</h2><p> "Hey! Those aren't the same!" I hear you say. You are right. They are not. The threaded version could extract the title in <code>ThreadUrl.run</code> instead of putting the content in queue for a <code>DatamineThread</code>.</p><p> I think the author was trying to show how you can make two threads work together on something... big? I haven't come up with a problem where it makes sense to write something in the Twisted version other than <code>d.addCallback(printTitle, ...)</code>. If you have an idea post a comment, and I'll happily update this post (or make another post).</p><h2>Conclusion</h2><p> You can do things with threading. You can do things with Twisted. You should investigate Twisted (mostly for reasons not mentioned in this post). As noted above, <a href="http://krondo.com/?page_id=1327" target="_blank">krondo's Twisted Introduction</a> is good, or there's some <a href="http://iffycan.blogspot.com/p/bite-sized-twisted-index.html" target="_blank">stuff I've written</a>.</p><p> Also, if anyone can think of a better scenario for the two-kinds-of-thread-workers model, I'll update (or post again) with what a Twisted version might look like.</p>Unknownnoreply@blogger.com21tag:blogger.com,1999:blog-4293648069803317711.post-55526670776864919762013-07-10T15:37:00.000-07:002013-07-10T15:37:57.092-07:00Angular injection<p>tl;dr is marked throughout by ∴</p><p> I don't like magical code. AngularJS is magical. I must fix this.</p><img style="border: 0; box-shadow: none;" src="http://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Magic_wand.svg/200px-Magic_wand.svg.png"><p> Dependency injection was one of <a href="http://angularjs.org" target="_blank">AngularJS's</a> first evil magicks I encountered. The idea that calling this function<div class="code javascript"><pre><span class="kd">function</span> <span class="nx">myFunction</span><span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">$http</span><span class="p">)</span> <span class="p">{</span><br /> <span class="p">...</span><br /><span class="p">}</span><br /></pre></div> will magically reach out to the universe and grab the correct values for <code>$scope</code> and <code>$http</code> runs contrary to all the JavaScript I've ever used. You can't do that!</p><p> So I dug in to discover the magicks. And now it's not magic! It's great! It's rougly equivalent to <code>import</code> in Python or <code>require</code> in Ruby. Here's how it works:</p><h2>Modules</h2><p> AngularJS groups injectable things together into modules. The following code will: <ol> <li>make a module named <code>woods</code></li> <li>add a provider to the <code>woods</code> module named <code>Eeyore</code>, which has a constant value</li> </ol><div class="code javascript"><pre><span class="kd">var</span> <span class="nx">woods</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'woods'</span><span class="p">,</span> <span class="p">[]);</span><br /><span class="nx">woods</span><span class="p">.</span><span class="nx">value</span><span class="p">(</span><span class="s1">'Eeyore'</span><span class="p">,</span> <span class="s1">'sad'</span><span class="p">)</span><br /></pre></div></p><p> Here's some of the source for the <code>module</code> function plus context (see the <a href="https://github.com/angular/angular.js/blob/v1.1.5/src/loader.js#L67" target="_blank">full source here</a> — the comments are helpful):<div class="code javascript"><pre><span class="c1">// from setupModuleLoader()</span><br /><span class="kd">function</span> <span class="nx">ensure</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">factory</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="nx">obj</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">||</span> <span class="p">(</span><span class="nx">obj</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="nx">factory</span><span class="p">());</span><br /><span class="p">}</span><br /><br /><span class="c1">// ...</span><br /><br /><span class="kd">var</span> <span class="nx">modules</span> <span class="o">=</span> <span class="p">{};</span><br /><span class="k">return</span> <span class="kd">function</span> <span class="nx">module</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">requires</span><span class="p">,</span> <span class="nx">configFn</span><span class="p">)</span> <span class="p">{</span><br /> <span class="c1">// ...</span><br /> <span class="k">return</span> <span class="nx">ensure</span><span class="p">(</span><span class="nx">modules</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="c1">// ...</span><br /> <span class="kd">var</span> <span class="nx">moduleInstance</span> <span class="o">=</span> <span class="p">{</span><br /> <span class="c1">// ...</span><br /> <span class="nx">requires</span><span class="o">:</span> <span class="nx">requires</span><span class="p">,</span><br /> <span class="nx">name</span><span class="o">:</span> <span class="nx">name</span><span class="p">,</span><br /> <span class="nx">provider</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$provide'</span><span class="p">,</span> <span class="s1">'provider'</span><span class="p">),</span><br /> <span class="nx">factory</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$provide'</span><span class="p">,</span> <span class="s1">'factory'</span><span class="p">),</span><br /> <span class="nx">service</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$provide'</span><span class="p">,</span> <span class="s1">'service'</span><span class="p">),</span><br /> <span class="nx">value</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$provide'</span><span class="p">,</span> <span class="s1">'value'</span><span class="p">),</span><br /> <span class="nx">constant</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$provide'</span><span class="p">,</span> <span class="s1">'constant'</span><span class="p">,</span> <span class="s1">'unshift'</span><span class="p">),</span><br /> <span class="nx">filter</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$filterProvider'</span><span class="p">,</span> <span class="s1">'register'</span><span class="p">),</span><br /> <span class="nx">controller</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$controllerProvider'</span><span class="p">,</span> <span class="s1">'register'</span><span class="p">),</span><br /> <span class="nx">directive</span><span class="o">:</span> <span class="nx">invokeLater</span><span class="p">(</span><span class="s1">'$compileProvider'</span><span class="p">,</span> <span class="s1">'directive'</span><span class="p">),</span><br /> <span class="c1">// ...</span><br /> <span class="p">};</span><br /> <span class="c1">// ...</span><br /> <span class="k">return</span> <span class="nx">moduleInstance</span><span class="p">;</span><br /> <span class="c1">// ...</span><br /> <span class="p">});</span><br /><span class="p">};</span><br /></pre></div></p><p> <ol> <li> The <code>ensure(obj, name, factory)</code> function makes sure that <code>obj</code> has an attribute named <code>name</code>, creating it by calling <code>factory</code> if it doesn't. </li> <li> The <code>module(name, requires, configFn)</code> function adds a <code>moduleInstance</code> named <code>name</code> to the global-ish <code>modules</code> object (by using <code>ensure</code>). </li> </ol></p><p> <span style="font-size: 300%; position: absolute; left: -1em; font-weight: bold; color: #08c; margin-top: -0.5em;">∴</span> <code>angular.module(...)</code> adds a module to some global-ish module registry.</p><h2>Injectors</h2><p> Injectors find providers from among the modules it knows about. By default, AngularJS creates an injector through the bootstrapping process. We can also make an injector with <code>angular.injector()</code> and use it to access providers within modules:<div class="code javascript"><pre><span class="c1">// Run this in a JavaScript console (on a page that has AngularJS)</span><br /><br /><span class="c1">// Make a woods module with an Eeyore provider</span><br /><span class="kd">var</span> <span class="nx">woods</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'woods'</span><span class="p">,</span> <span class="p">[]);</span><br /><span class="nx">woods</span><span class="p">.</span><span class="nx">value</span><span class="p">(</span><span class="s1">'Eeyore'</span><span class="p">,</span> <span class="s1">'sad'</span><span class="p">)</span><br /><br /><span class="c1">// Make an injector that knows about the 'woods' module.</span><br /><span class="kd">var</span> <span class="nx">injector</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">injector</span><span class="p">([</span><span class="s1">'woods'</span><span class="p">])</span><br /><br /><span class="c1">// Get poor Eeyore out of the module</span><br /><span class="nx">injector</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">'Eeyore'</span><span class="p">);</span><br /><span class="c1">// -> "sad"</span><br /></pre></div></p><p> The creation of injectors and how they know where things are is somewhat recursive (and the code is a little hard to read). I will unravel that magic in another post as it was making this post too long. For now, just know that</p><p> <span style="font-size: 300%; position: absolute; left: -1em; font-weight: bold; color: #08c; margin-top: -0.5em;">∴</span> Injectors can find the providers you add to modules (e.g. through <code>.value(...)</code> or <code>.factory(...)</code>) and can find modules that were previously added to the global-ish module registry.</p><h2>Invoke</h2><p> Using an injector, we can invoke functions with dependency injection:<div class="code javascript"><pre><span class="c1">// Run this in a JavaScript console (on a page that has AngularJS)</span><br /><br /><span class="c1">// Make a woods module with an Eeyore provider</span><br /><span class="kd">var</span> <span class="nx">woods</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'woods'</span><span class="p">,</span> <span class="p">[]);</span><br /><span class="nx">woods</span><span class="p">.</span><span class="nx">value</span><span class="p">(</span><span class="s1">'Eeyore'</span><span class="p">,</span> <span class="s1">'sad'</span><span class="p">)</span><br /><br /><span class="c1">// Make an injector that knows about the 'woods' module.</span><br /><span class="kd">var</span> <span class="nx">injector</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">injector</span><span class="p">([</span><span class="s1">'woods'</span><span class="p">])</span><br /><br /><span class="c1">// Imbue a function with sadness</span><br /><span class="kd">function</span> <span class="nx">eatEmotion</span><span class="p">(</span><span class="nx">Eeyore</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="s1">'I am '</span> <span class="o">+</span> <span class="nx">Eeyore</span><span class="p">;</span><br /><span class="p">}</span><br /><span class="nx">injector</span><span class="p">.</span><span class="nx">invoke</span><span class="p">(</span><span class="nx">eatEmotion</span><span class="p">);</span><br /><span class="c1">// -> "I am sad"</span><br /></pre></div></p> <h2>But how does it KNOOooooowwwWWW??</h2><p> How does AngularJS know the names of the arguments a function is expecting? How does it know that my <code>weather</code> function's arguments is named <code>sunny</code>?<div class="code javascript"><pre><span class="kd">function</span> <span class="nx">weather</span><span class="p">(</span><span class="nx">sunny</span><span class="p">)</span> <span class="p">{</span><br /> <span class="p">...</span><br /><span class="p">}</span><br /></pre></div></p><p> That's an internal detail of <code>weather</code>, inaccessible from the outside, no? I've done introspection with Python, but this is JavaScript.</p><p> How AngularJS gets the argument names made me laugh out loud when I found it. It's a dirty (effective) trick found in the <code>annontate</code> function (<a href="https://github.com/angular/angular.js/blob/v1.1.5/src/auto/injector.js#L45" target="_blank">full source</a>):<div class="code javascript"><pre><span class="kd">var</span> <span class="nx">FN_ARGS</span> <span class="o">=</span> <span class="sr">/^function\s*[^\(]*\(\s*([^\)]*)\)/m</span><span class="p">;</span><br /><span class="kd">var</span> <span class="nx">FN_ARG_SPLIT</span> <span class="o">=</span> <span class="sr">/,/</span><span class="p">;</span><br /><span class="kd">var</span> <span class="nx">FN_ARG</span> <span class="o">=</span> <span class="sr">/^\s*(_?)(\S+?)\1\s*$/</span><span class="p">;</span><br /><span class="kd">var</span> <span class="nx">STRIP_COMMENTS</span> <span class="o">=</span> <span class="sr">/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg</span><span class="p">;</span><br /><span class="kd">function</span> <span class="nx">annotate</span><span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="p">{</span><br /> <span class="kd">var</span> <span class="nx">$inject</span><span class="p">,</span><br /> <span class="nx">fnText</span><span class="p">,</span><br /> <span class="nx">argDecl</span><span class="p">,</span><br /> <span class="nx">last</span><span class="p">;</span><br /><br /> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">fn</span> <span class="o">==</span> <span class="s1">'function'</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">$inject</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">$inject</span><span class="p">))</span> <span class="p">{</span><br /> <span class="nx">$inject</span> <span class="o">=</span> <span class="p">[];</span><br /> <span class="nx">fnText</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">replace</span><span class="p">(</span><span class="nx">STRIP_COMMENTS</span><span class="p">,</span> <span class="s1">''</span><span class="p">);</span><br /> <span class="nx">argDecl</span> <span class="o">=</span> <span class="nx">fnText</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">FN_ARGS</span><span class="p">);</span><br /> <span class="nx">forEach</span><span class="p">(</span><span class="nx">argDecl</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">split</span><span class="p">(</span><span class="nx">FN_ARG_SPLIT</span><span class="p">),</span> <span class="kd">function</span><span class="p">(</span><span class="nx">arg</span><span class="p">){</span><br /> <span class="nx">arg</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">FN_ARG</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">all</span><span class="p">,</span> <span class="nx">underscore</span><span class="p">,</span> <span class="nx">name</span><span class="p">){</span><br /> <span class="nx">$inject</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">name</span><span class="p">);</span><br /> <span class="p">});</span><br /> <span class="p">});</span><br /> <span class="nx">fn</span><span class="p">.</span><span class="nx">$inject</span> <span class="o">=</span> <span class="nx">$inject</span><span class="p">;</span><br /> <span class="p">}</span><br /> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">fn</span><span class="p">))</span> <span class="p">{</span><br /> <span class="nx">last</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span><br /> <span class="nx">assertArgFn</span><span class="p">(</span><span class="nx">fn</span><span class="p">[</span><span class="nx">last</span><span class="p">],</span> <span class="s1">'fn'</span><span class="p">);</span><br /> <span class="nx">$inject</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">last</span><span class="p">);</span><br /> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span><br /> <span class="nx">assertArgFn</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="s1">'fn'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span><br /> <span class="p">}</span><br /> <span class="k">return</span> <span class="nx">$inject</span><span class="p">;</span><br /><span class="p">}</span><br /></pre></div></p><p> <span style="font-size: 300%; position: absolute; left: -1em; font-weight: bold; color: #08c; margin-top: -0.5em;">∴</span> If you pass a function to <code>annotate</code> it will convert that function to a string and use regular expressions to get the names of the arguments.</p><p> I should note, however, that the practice of depending on argument names for injection is discouraged (because of how the names get munged during minification). It makes the code look cleaner, though. Maybe we should work on changing minification to handle this introspective kind of injection.</p><h2>Which functions have it? Which don't?</h2><p> When you're just starting with AngularJS, it's a little frustrating that some functions are magic (i.e. are called with injection) and some are seemingly inert. For instance, when writing a directive, <code>link</code> is not called with dependency injection, but <code>controller</code> is.</p><p> The provider methods are called with injection (<code>factory</code>, <code>value</code>, etc...). And directive controllers are called with injection. From the <a href="http://docs.angularjs.org/guide/di" target="_blank">official docs</a>: <blockquote>DI is pervasive throughout Angular. It is typically used in controllers and factory methods.</blockquote></p><p> <span style="font-size: 300%; position: absolute; left: -1em; font-weight: bold; color: #08c; margin-top: -0.5em;">∴</span> Sadly, the only way to know if a function is called with dependency injection is to... know. Read the docs or the source, and build up an ample supply of doing it wrong :)</p><h2 style="text-decoration: line-through;">Namespacing</h2><p> Modules provided to an injector will stomp on each other's providers:<div class="code javascript"><pre><span class="c1">// Run this in a JavaScript console (on a page that has AngularJS)</span><br /><br /><span class="kd">function</span> <span class="nx">mineFor</span><span class="p">(</span><span class="nx">Thing</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="s2">"I found "</span> <span class="o">+</span> <span class="nx">Thing</span> <span class="o">+</span> <span class="s2">"!"</span><span class="p">;</span><br /><span class="p">}</span><br /><br /><br /><span class="c1">// Make two modules that each define a Thing provider</span><br /><span class="kd">var</span> <span class="nx">good_module</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'good'</span><span class="p">,</span> <span class="p">[]);</span><br /><span class="nx">good_module</span><span class="p">.</span><span class="nx">value</span><span class="p">(</span><span class="s1">'Thing'</span><span class="p">,</span> <span class="s1">'gold'</span><span class="p">);</span><br /><br /><span class="kd">var</span> <span class="nx">bad_module</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'bad'</span><span class="p">,</span> <span class="p">[]);</span><br /><span class="nx">bad_module</span><span class="p">.</span><span class="nx">value</span><span class="p">(</span><span class="s1">'Thing'</span><span class="p">,</span> <span class="s1">'sour milk'</span><span class="p">);</span><br /><br /><span class="c1">// Make an injector</span><br /><span class="kd">var</span> <span class="nx">injector</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">injector</span><span class="p">([</span><span class="s1">'good'</span><span class="p">,</span> <span class="s1">'bad'</span><span class="p">]);</span><br /><br /><span class="nx">injector</span><span class="p">.</span><span class="nx">invoke</span><span class="p">(</span><span class="nx">mineFor</span><span class="p">);</span><br /><span class="c1">// -> "I found sour milk!"</span><br /></pre></div></p><p> I don't know if this is by design or if there are plans to address it. Be aware of it.</p><h2>In summary</h2><p> <span style="font-size: 300%; position: absolute; left: -1em; font-weight: bold; color: #08c; margin-top: -0.5em;">∴</span> Dependency injection in AngularJS is roughly equivalent to other languages' including and importing, but scoped to functions. Some of the magic is accomplished by exploiting <code>function.toString()</code> and regular expressions.</p><p> Read the <a href="http://docs.angularjs.org/guide/di" target="_blank">official doc about Dependency Injection</a> for some of the motivation behind its use.</p>Unknownnoreply@blogger.com433tag:blogger.com,1999:blog-4293648069803317711.post-51395224310207266262013-05-24T08:36:00.000-07:002013-05-24T08:36:36.046-07:00Angular service or factory?<img style="border: 0; box-shadow: none;" src="http://pixabay.com/static/uploads/photo/2012/04/18/02/19/man-36570_150.png"><p>tl;dr is at the end</p><p>In various <a href="http://angularjs.org" target="_blank">AngularJS</a> tutorials and documentation, the authors choose to use <code>service</code> or <code>factory</code> but don't explain <em>why</em> you would use one or the other. Few mention that <code>value</code> and <code>constant</code> are also options.</p><p> Let's see why you would use one over the other. We should also understand how providers work:</p><h2>provider</h2><p>Here's the source for the <code>provider</code> method:</p><div class="code javascript"><pre><span class="kd">function</span> <span class="nx">provider</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">provider_</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">if</span> <span class="p">(</span><span class="nx">isFunction</span><span class="p">(</span><span class="nx">provider_</span><span class="p">)</span> <span class="o">||</span> <span class="nx">isArray</span><span class="p">(</span><span class="nx">provider_</span><span class="p">))</span> <span class="p">{</span><br /> <span class="nx">provider_</span> <span class="o">=</span> <span class="nx">providerInjector</span><span class="p">.</span><span class="nx">instantiate</span><span class="p">(</span><span class="nx">provider_</span><span class="p">);</span><br /> <span class="p">}</span><br /> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">provider_</span><span class="p">.</span><span class="nx">$get</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">throw</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">'Provider '</span> <span class="o">+</span> <span class="nx">name</span> <span class="o">+</span> <span class="s1">' must define $get factory method.'</span><span class="p">);</span><br /> <span class="p">}</span><br /> <span class="k">return</span> <span class="nx">providerCache</span><span class="p">[</span><span class="nx">name</span> <span class="o">+</span> <span class="nx">providerSuffix</span><span class="p">]</span> <span class="o">=</span> <span class="nx">provider_</span><span class="p">;</span><br /><span class="p">}</span><br /></pre></div><p> <code>name</code> is a string. <code>provider_</code> can be one of three things: <ol> <li> function <p> If a function is passed in, the function is called with <a href="http://docs.angularjs.org/guide/di" target="_blank">dependency injection</a> and should return an object with a <code>$get</code> method. </p> </li> <li> array <p> An array will be treated like a function using <a href="http://docs.angularjs.org/guide/di" target="_blank">Inline Annotation</a>. It must also return an object with a <code>$get</code> method. </p> </li> <li> object <p> If an object is passed in, it is simply expected to have a <code>$get</code> method. </p> </li> </ol></p><p> Whatever the second arg to <code>provider</code> is, you eventually end up with an object that has a <code>$get</code> method. Here's an example showing what happens:</p> <div class="code javascript"><pre><span class="c1">// You can run this</span><br /><br /><span class="c1">// Create a module</span><br /><span class="kd">var</span> <span class="nx">hippo</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'hippo'</span><span class="p">,</span> <span class="p">[]);</span><br /><br /><span class="c1">// Register an object provider</span><br /><span class="nx">hippo</span><span class="p">.</span><span class="nx">provider</span><span class="p">(</span><span class="s1">'awesome'</span><span class="p">,</span> <span class="p">{</span><br /> <span class="nx">$get</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="s1">'awesome data'</span><span class="p">;</span><br /> <span class="p">}</span><br /><span class="p">});</span><br /><br /><span class="c1">// Get the injector (this happens behind the scenes in angular apps)</span><br /><span class="kd">var</span> <span class="nx">injector</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">injector</span><span class="p">([</span><span class="s1">'hippo'</span><span class="p">,</span> <span class="s1">'ng'</span><span class="p">]);</span><br /><br /><span class="c1">// Call a function with dependency injection</span><br /><span class="nx">injector</span><span class="p">.</span><span class="nx">invoke</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">awesome</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'awesome == '</span> <span class="o">+</span> <span class="nx">awesome</span><span class="p">);</span><br /><span class="p">});</span><br /></pre></div><p> Once you understand providers you will see that <code>factory</code>, <code>service</code>, <code>value</code> and <code>constant</code> are just convenience methods for making providers.</p><h2>factory</h2><p>Here's the source:</p><div class="code javascript"><pre><span class="kd">function</span> <span class="nx">factory</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">factoryFn</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="nx">provider</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="p">{</span> <span class="nx">$get</span><span class="o">:</span> <span class="nx">factoryFn</span> <span class="p">});</span><br /><span class="p">}</span><br /></pre></div><p> So it lets you shorten the <code>awesome</code> provider creation code to this:</p> <div class="code javascript"><pre><span class="nx">hippo</span><span class="p">.</span><span class="nx">factory</span><span class="p">(</span><span class="s1">'awesome'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="s1">'awesome data'</span><span class="p">;</span><br /><span class="p">})</span><br /></pre></div><h2>service</h2><p>Here's the source:</p><div class="code javascript"><pre><span class="kd">function</span> <span class="nx">service</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">constructor</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="nx">factory</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="p">[</span><span class="s1">'$injector'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$injector</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="nx">$injector</span><span class="p">.</span><span class="nx">instantiate</span><span class="p">(</span><span class="nx">constructor</span><span class="p">);</span><br /> <span class="p">}]);</span><br /><span class="p">}</span><br /></pre></div><p> So it lets you make a factory that will instantiate a "class". For example:</p><div class="code javascript"><pre><span class="kd">var</span> <span class="nx">gandalf</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'gandalf'</span><span class="p">,</span> <span class="p">[]);</span><br /><br /><span class="kd">function</span> <span class="nx">Gandalf</span><span class="p">()</span> <span class="p">{</span><br /> <span class="k">this</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="s1">'grey'</span><span class="p">;</span><br /><span class="p">}</span><br /><span class="nx">Gandalf</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">comeBack</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span><br /> <span class="k">this</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="s1">'white'</span><span class="p">;</span><br /><span class="p">}</span><br /><br /><span class="nx">gandalf</span><span class="p">.</span><span class="nx">service</span><span class="p">(</span><span class="s1">'gandalfService'</span><span class="p">,</span> <span class="nx">Gandalf</span><span class="p">);</span><br /><br /><span class="kd">var</span> <span class="nx">injector</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">injector</span><span class="p">([</span><span class="s1">'gandalf'</span><span class="p">,</span> <span class="s1">'ng'</span><span class="p">]);</span><br /><br /><span class="nx">injector</span><span class="p">.</span><span class="nx">invoke</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">gandalfService</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">gandalfService</span><span class="p">.</span><span class="nx">color</span><span class="p">);</span><br /> <span class="nx">gandalfService</span><span class="p">.</span><span class="nx">comeBack</span><span class="p">()</span><br /> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">gandalfService</span><span class="p">.</span><span class="nx">color</span><span class="p">);</span><br /><span class="p">});</span><br /></pre></div><p> The above code will instantiate <code>Gandalf</code>, but remember that everything that uses the service will get <strong>the same instance!</strong> (which is a good thing).</p><h2>value</h2><p>Here's the source:</p><div class="code javascript"><pre><span class="kd">function</span> <span class="nx">value</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span><br /> <span class="k">return</span> <span class="nx">factory</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">valueFn</span><span class="p">(</span><span class="nx">value</span><span class="p">));</span><br /><span class="p">}</span><br /></pre></div><p> Using <code>value</code> would let you shorten the <code>awesome</code> provider to:</p><div class="code javascript"><pre><span class="nx">hippo</span><span class="p">.</span><span class="nx">value</span><span class="p">(</span><span class="s1">'awesome'</span><span class="p">,</span> <span class="s1">'awesome data'</span><span class="p">);</span><br /></pre></div><h2>constant</h2><p>Here's the source</p><div class="code javascript"><pre><span class="kd">function</span> <span class="nx">constant</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">providerCache</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span><br /> <span class="nx">instanceCache</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span><br /><span class="p">}</span><br /></pre></div><p> <code>constant</code> differs from <code>value</code> in that it's accessible during config. Here's how you use it:</p><div class="code javascript"><pre><span class="kd">var</span> <span class="nx">joe</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'joe'</span><span class="p">,</span> <span class="p">[]);</span><br /><br /><span class="nx">joe</span><span class="p">.</span><span class="nx">constant</span><span class="p">(</span><span class="s1">'bobTheConstant'</span><span class="p">,</span> <span class="s1">'a value'</span><span class="p">);</span><br /><span class="nx">joe</span><span class="p">.</span><span class="nx">value</span><span class="p">(</span><span class="s1">'samTheValue'</span><span class="p">,</span> <span class="s1">'a different value'</span><span class="p">);</span><br /><br /><span class="nx">joe</span><span class="p">.</span><span class="nx">config</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">bobTheConstant</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">bobTheConstant</span><span class="p">);</span><br /><span class="p">});</span><br /><br /><span class="nx">joe</span><span class="p">.</span><span class="nx">config</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">samTheValue</span><span class="p">)</span> <span class="p">{</span><br /> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">samTheValue</span><span class="p">);</span><br /><span class="p">});</span><br /><br /><span class="c1">// This will fail with "Error: Unknown provider: samTheValue from joe"</span><br /><span class="kd">var</span> <span class="nx">injector</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">injector</span><span class="p">([</span><span class="s1">'joe'</span><span class="p">,</span> <span class="s1">'ng'</span><span class="p">]);</span><br /></pre></div><p> Read Module Loading & Dependencies in <a href="http://docs.angularjs.org/guide/module" target="_blank">the Modules doc</a> for more information on usage.</p><h2>In summary</h2><p> If you want your function to be called like a normal function, use <code>factory</code>. If you want your function to be instantiated with the <code>new</code> operator, use <code>service</code>. If you don't know the difference, use <code>factory</code>.</p><p>This is the (great) documentation for each function in the AngularJS source:<ol> <li> <code>factory</code> <p> A short hand for configuring services if only `$get` method is required. </p> </li> <li> <code>service</code> <p> A short hand for registering service of given class. </p> </li> <li> <code>value</code> <p> A short hand for configuring services if the `$get` method is a constant. </p> </li> <li> <code>constant</code> <p> A constant value, but unlike {@link AUTO.$provide#value value} it can be injected into configuration function (other modules) and it is not interceptable by {@link AUTO.$provide#decorator decorator}. </p> </li></ol></p>Unknownnoreply@blogger.com199tag:blogger.com,1999:blog-4293648069803317711.post-36118359292628298272013-05-22T06:53:00.001-07:002013-05-22T06:53:26.838-07:00Angular and componentsI've been playing around with <a href="http://angularjs.org/" target="_blank">AngularJS</a> lately. It's okay. It's very weird. They've named some things poorly (and magically), the documentation is not great and there are some unexpected quirks. I feel like AngularJS is the first step toward a new class of JavaScript libraries, but it's not the end.<br />
<br />
Something is going to come along and choose good names (without magic '@', '=', '&') with great documentation. Just today, I happened on this talk:<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/fqULJBBEVQE" width="560"></iframe><br />
<br />
Which does a lot of AngularJS things.<br />
<br />
I haven't explained thoroughly... sorry. Maybe when I have more time, I'll post more.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4293648069803317711.post-13456806546007541892013-05-10T09:58:00.000-07:002013-05-10T09:58:57.737-07:00@#$%!<br />
To all you producers of words: please stop swearing. For the sake of your audience, elevate your language above crude speech. You lose respect when you swear.<br />
<br />
<br />
<h2>
You're not dying!</h2>
I once watched a video about API design. Rather, I once watched <i>part</i> of a video about API design. I had to stop after the 5th or 6th expletive.<br />
<br />
API design.<br />
<br />
API design?! A discussion about API design is probably the last thing in the world that deserves to be littered with swearing!<br />
<br />
You're not dying. Your family isn't dying. Your arm isn't being severed. And even these terrible situations don't merit debasing yourself by swearing -- much less API design.<br />
<br />
<br />
<h2>
I really want(ed) to share your stuff...</h2>
Too frequently, I'm reading an interesting article when <b><span style="color: #0b5394; font-size: large;">@#$&!</span></b> Out of nowhere (and completely out of context) I'm jarred by a four-letter word. Why did they ruin it? I wanted to share this and now I don't.<br />
<br />
I don't want to share with my coworkers, family members or friends something profane -- for sharing implies endorsement, and I don't want to be associated with such things.<br />
<br />
If you want it to be shareable, leave out the swear words.<br />
<br />
<h2>
Make me laugh, not cringe</h2>
Instead of attacking me by swearing, invite me to laugh with you. Replace poor words with ridiculous or benign ones. Or channel an unloveable critter/substance/thing to express your shock/displeasure/pain: Rats! Mucus! Taxes!<br />
<br />
(Let me be clear: that was "Taxes" not "Texas.")<br />
<br />
Yes, swearing sometimes makes some people laugh. Bathroom humor makes some people laugh, too. There are much better ways of making people laugh: choose a better way.<br />
<br />
And, yes, swearing will garner attention. A baseball bat to the head will also garner a certain amount of attention. You don't want that kind of attention. And in the end, it's better to lead people through charisma and intelligence than through head shots.<br />
<br />
Use honey, not vinegar.<br />
<br />
<h2>
You're smarter than that</h2>
We don't live in a high school locker room. Swearing is a cheap alternative to thoughtfulness. Demonstrate your intelligence by expressing yourself accurately and powerfully through well-chosen words. People think you're dumb when you swear. Go ahead, A/B test it.<br />
<br />
When your mouth is foul, your brain is discredited by association.<br />
<br />
<h2>
It's who I am</h2>
Some will say, "It's who I am. Deal with it." Well, they'd probably say, "$#%!, it's who I am *$!@. @$#& deal with it. *@&# $%@!"<br />
<br />
Swearing isn't an identity. You control your mouth. You have a brain. Use it wisely.<br />
<br />
<h2>
Thank you</h2>
<div>
To those who speak well, thank you. I frequent an IRC channel that has an enforced family-friendly policy. The admins gently remind people to keep it clean, which they generally do. And it's great! I feel comfortable there. I respect the admins and others there.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Keep it clean and make the world better, <a href="https://www.youtube.com/watch?v=xBYV_7a0FQs&t=0m57s" target="_blank">muskrat</a>!</div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4293648069803317711.post-135469289445798442013-02-05T09:09:00.000-08:002013-02-05T09:09:02.838-08:00What I wish Puppet and Salt would do differently<br />
I like the idea of configuration management tools like <a href="https://puppetlabs.com/" target="_blank">Puppet</a> and <a href="http://saltstack.org/" target="_blank">Salt</a>; they have promise. They attempt to solve a huge, complex problem. I haven't come up with a working solution that is any better. But if I ever had time to work on a solution it would improve on the current solutions in the following ways:<br />
<br />
<h2>
I wish they would use a standard for state definition</h2>
<br />
If each tool worked from a standard state description syntax, then people could try different tools without so much more effort. Perhaps this could be achieved by developing a standard for describing machine state, and then developing tools that convert from the standard state definition to the tool-specific state definition.<br />
<br />
A standard would also allow for more general tools to create state definitions.<br />
<br />
And a standard would allow interoperability with things that are not configuration management tools: state visualization tools, for instance.<br />
<br />
<br />
<h2>
I wish they would stop claiming that you describe state only</h2>
<br />
Both tools inspire awe when they claim that you only have to describe "the desired state" of resources, rather than a script of things to do. But they lie!<br />
<br />
Puppet manifest files and Salt <span style="font-family: Courier New, Courier, monospace;">sls</span> files both include syntax for restarting services, running scripts based on changes or doing other one-time events. In other words, you are describing both state AND events.<br />
<br />
I think the tools could be simplified if they would stop pretending that you can manage a system by describing state alone.<br />
<br />
<br />
<h2>
I wish they would focus on getting out of the way instead of cute, colored output</h2>
Colored output can be useful. But when you're coloring output instead of providing useful data, the color is infuriating.<br />
<br />
I spent an hour with Salt trying to create a user on a machine. Every time it failed to create the user, this is the error I would see:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">'Failed to create new user lighttpd'</span><br />
<br />
It didn't show the command it was trying to execute. It didn't show the stdout/stderr from the shell. The logs didn't help either. The output was very cute, colorful and useless. When something runs, I should be able to see the arguments used to spawn the process, the environment variables for the process and the stdin, stdout, stderr and exit code of the process. Why replace that great debugging information with cute, custom messages and colors?<br />
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-78857393854395170652012-07-05T08:00:00.000-07:002012-07-05T08:00:00.541-07:00Bite-sized Twisted:Telnet<p>Finally, in this post, we'll monitor keypresses instead of lines of text. This means that you won't have to press enter after each command.</p><p>Strictly speaking, the changes we'll make to the code are for Telnet, and not really Twisted-specific -- though Twisted's support for Telnet makes it easier.</p><h2>Telnet's modes</h2><p>On my computer (running Ubuntu), running telnet will default to line by line mode. I can see this by starting the obstinate server:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Protocol</span><span class="p">,</span> <span class="n">Factory</span><br /><span class="kn">from</span> <span class="nn">twisted.internet.endpoints</span> <span class="kn">import</span> <span class="n">TCP4ServerEndpoint</span><br /><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><br /><span class="k">class</span> <span class="nc">ObstinateProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">transport</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'no</span><span class="se">\r\n</span><span class="s">'</span><span class="p">)</span><br /><br /><span class="n">factory</span> <span class="o">=</span> <span class="n">Factory</span><span class="p">()</span><br /><span class="n">factory</span><span class="o">.</span><span class="n">protocol</span> <span class="o">=</span> <span class="n">ObstinateProtocol</span><br /><span class="n">endpoint</span> <span class="o">=</span> <span class="n">TCP4ServerEndpoint</span><span class="p">(</span><span class="n">reactor</span><span class="p">,</span> <span class="mi">8900</span><span class="p">)</span><br /><span class="n">endpoint</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">factory</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption filename">obstinate1.py</div><p>Then if I start telnet and send some stuff, I see that it reacts to the lines I send:</p><div class="code text"><pre>$ telnet 127.0.0.1 8900<br />Trying 127.0.0.1...<br />Connected to 127.0.0.1.<br />Escape character is '^]'.<br />Wanna play?<br />no<br />Are you obstinate?<br />no<br />Are you a duck?<br />no<br /></pre></div><p>We can switch the telnet client to character mode by typing the escape character <code>Control ]</code>, then typing <code>mode char</code>. From then on, the server will receive each byte as we type it:</p><div class="code text"><pre>^]mode char<br /><br />telnet> mode char<br />no<br />no<br />no<br />no<br />no<br />no<br /></pre></div><p>Each of those <code>no</code> lines was printed after pressing single keys. Great! We don't have to press enter anymore to play boom!</p><h2>You can force the client to character mode</h2><p>by telling the Telnet client that you will echo, will supress go-ahead and won't use linemode. We'll use Twisted's <code>Telnet</code> protocol from the <code>conch</code> package (changed lines are highlighted):</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Protocol</span><span class="p">,</span> <span class="n">Factory</span><br /><span class="hll"><span class="kn">from</span> <span class="nn">twisted.conch.telnet</span> <span class="kn">import</span> <span class="n">Telnet</span><span class="p">,</span> <span class="n">SGA</span><span class="p">,</span> <span class="n">LINEMODE</span><span class="p">,</span> <span class="n">ECHO</span><br /></span><span class="kn">from</span> <span class="nn">twisted.internet.endpoints</span> <span class="kn">import</span> <span class="n">TCP4ServerEndpoint</span><br /><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><br /><span class="hll"><span class="k">class</span> <span class="nc">ObstinateProtocol</span><span class="p">(</span><span class="n">Telnet</span><span class="p">):</span> <br /></span><br /><span class="hll"> <span class="k">def</span> <span class="nf">connectionMade</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <br /></span><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">will</span><span class="p">(</span><span class="n">ECHO</span><span class="p">)</span> <br /></span><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">will</span><span class="p">(</span><span class="n">SGA</span><span class="p">)</span> <br /></span><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">wont</span><span class="p">(</span><span class="n">LINEMODE</span><span class="p">)</span> <br /></span><br /><span class="hll"> <span class="k">def</span> <span class="nf">enableLocal</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">option</span><span class="p">):</span> <br /></span><span class="hll"> <span class="k">return</span> <span class="n">option</span> <span class="ow">in</span> <span class="p">(</span><span class="n">ECHO</span><span class="p">,</span> <span class="n">SGA</span><span class="p">)</span> <br /></span> <br /><span class="hll"> <span class="k">def</span> <span class="nf">applicationDataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /></span> <span class="bp">self</span><span class="o">.</span><span class="n">transport</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'no</span><span class="se">\r\n</span><span class="s">'</span><span class="p">)</span><br /><br /><br /><span class="n">factory</span> <span class="o">=</span> <span class="n">Factory</span><span class="p">()</span><br /><span class="n">factory</span><span class="o">.</span><span class="n">protocol</span> <span class="o">=</span> <span class="n">ObstinateProtocol</span><br /><span class="n">endpoint</span> <span class="o">=</span> <span class="n">TCP4ServerEndpoint</span><span class="p">(</span><span class="n">reactor</span><span class="p">,</span> <span class="mi">8900</span><span class="p">)</span><br /><span class="n">endpoint</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">factory</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption filename">lessobstinate.py</div><p>Telnet into this and you should see the same behavior as when we set the client to character mode manually.</p><h2>Finally, a playable game</h2><p>I've pushed changes to the <a href="https://github.com/iffy/boom/branches/tx-telnet-2">tx-telnet-2 branch on GitHub</a>. You can <a href="https://github.com/iffy/boom/compare/tx-telnet-1...tx-telnet-2">see everything that's changed since the last post here.</a></p><p>To play the game, get the <code>tx-telnet-2</code> branch and run <code>run.py</code> as before. Then telnet to port 8900 multiple times from multiple computers. If you die, you'll have to telnet in again :)</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-34621653833306927092012-06-21T16:30:00.000-07:002012-06-21T21:14:08.651-07:00Bite-sized Twisted:Networking Triforce<script src="http://d3js.org/d3.v2.js"></script><p>This post begins a series of posts about networking -- an area where Twisted shines.</p><h2>Network Input/Output == Bytes</h2><p>In the <a href="/2012/06/bite-sized-twisted-bytes-and-standard.html">second Bite-sized Twisted post</a> we accepted input (bytes) from stdin asynchronously. In this post, we'll accept input (bytes) over the network. I'm intentionally emphasizing bytes (bytes). It's all just bytes.</p><p>It might be worth reviewing <a href="/2012/06/bite-sized-twisted-bytes-and-standard.html">that second post</a>, so the concepts are fresh.</p><h2>Parts of the Triforce</h2><img src="http://goo.gl/EHjWM"><p>In Twisted, dealing with networking (moving bytes) usually involves three things:<ol> <li>Protocol - determines fate of bytes</li> <li>Transport - carries bytes between protocol and remote</li> <li>Factory - creates protocols</li></ol><h2>Protocol</h2><p>A <code>Protocol's</code> job is to do application-specific things with bytes. Code for deciding what bytes should be sent back across the wire is also in the <code>Protocol</code>. When it decides to send bytes across the wire, it will invoke the <code>Transport's</code> <code>write</code> method.</p><p>Here's an <code>ObstinateProtocol</code> which sends the bytes <code>'no\r\n'</code> in response to all data received:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Protocol</span><br /><br /><span class="k">class</span> <span class="nc">ObstinateProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">transport</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'no</span><span class="se">\r\n</span><span class="s">'</span><span class="p">)</span><br /></pre></div><div class="caption filename">protocol1.py</div><h2>Transport</h2><p>As seen above, the <code>Transport</code> provides an interface for interacting with the remote side. It has a <code>write</code> method which sends bytes to the remote side. There is generally one <code>Transport</code> per <code>Protocol</code>. You can access the transport through a <code>Protocol's</code> <code>transport</code> attribute.</p><h2>Factory</h2><p>A <code>Factory's</code> job is to make a <code>Protocol</code> for a connection. In general, there is one <code>Factory</code> for many <code>Protocol</code> instances.</p><p>Standard <code>Factories</code> determine what <code>Protocol</code> to make based on their <code>protocol</code> attribute. This snippet shows a <code>Factory</code> that will make <code>ObstinateProtocol</code> protocols:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Factory</span><br /><span class="n">factory</span> <span class="o">=</span> <span class="n">Factory</span><span class="p">()</span><br /><span class="n">factory</span><span class="o">.</span><span class="n">protocol</span> <span class="o">=</span> <span class="n">ObstinateProtocol</span><br /></pre></div><div class="caption filename">factory1.py</div><p>Notice that <code>factory.protocol</code> is set to the class <code>ObstinateProtocol</code> and not an instance <code>ObstinateProtocol()</code>.</p><h2>Magic glue</h2><p>One other component is needed for the Networking Triforce to work: magic glue. Here's a complete example that serves the <code>ObstinateProtocol</code> using TCP on port 8900 (magic glue is highlighted):</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Factory</span><span class="p">,</span> <span class="n">Protocol</span><br /><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="hll"><span class="kn">from</span> <span class="nn">twisted.internet.endpoints</span> <span class="kn">import</span> <span class="n">TCP4ServerEndpoint</span><br /></span><br /><span class="k">class</span> <span class="nc">ObstinateProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">transport</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'no</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span><br /><br /><span class="n">factory</span> <span class="o">=</span> <span class="n">Factory</span><span class="p">()</span><br /><span class="n">factory</span><span class="o">.</span><span class="n">protocol</span> <span class="o">=</span> <span class="n">ObstinateProtocol</span><br /><span class="hll"><span class="n">endpoint</span> <span class="o">=</span> <span class="n">TCP4ServerEndpoint</span><span class="p">(</span><span class="n">reactor</span><span class="p">,</span> <span class="mi">8900</span><span class="p">)</span><br /></span><span class="hll"><span class="n">endpoint</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="n">factory</span><span class="p">)</span><br /></span><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption filename">glue1.py</div><p>Start <i>glue1.py</i> in one shell, then open another shell and start <code>telnet</code> with this command:</p><div class="code bash"><pre>telnet 127.0.0.1 8900<br /></pre></div><p>You've just connected to the obstinate server. Type something then press enter; you should see 'no' written back. For example:</p><div class="code text"><pre>$ telnet 127.0.0.1 8900<br />Trying 127.0.0.1...<br />Connected to 127.0.0.1.<br />Escape character is '^]'.<br />Are you there?<br />no<br />Are you lying?<br />no<br />Are you sure?<br />no<br /></pre></div><p>If you have another computer nearby, start <i>glue1.py</i> here, then start telnet on the other computer, changing <code>127.0.0.1</code> to this computer's IP (you can even do this while the other telnet session is still going).</p><h2>A thousand words to explain the magic glue</h2><p>I don't like magical code. So, here's the pertinent code for hooking up the Triforce (from <i>twisted/internet/tcp.py</i>). First, the factory is asked to build a protocol:</p><div class="code python"><pre><span class="n">protocol</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">buildProtocol</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_buildAddr</span><span class="p">(</span><span class="n">addr</span><span class="p">))</span><br /></pre></div><div class="caption filename">portion of twisted/internet/tcp.py</div><p><code>Factory.buildProtocol</code> looks like this:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">Factory</span><span class="p">:</span><br /><br /> <span class="k">def</span> <span class="nf">buildProtocol</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">addr</span><span class="p">):</span><br /> <span class="n">p</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">protocol</span><span class="p">()</span><br /> <span class="n">p</span><span class="o">.</span><span class="n">factory</span> <span class="o">=</span> <span class="bp">self</span><br /> <span class="k">return</span> <span class="n">p</span><br /></pre></div><div class="caption filename">portion of twisted/internet/protocol.py</div><p>Then a transport is created:</p><div class="code python"><pre><span class="n">transport</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">transport</span><span class="p">(</span><span class="n">skt</span><span class="p">,</span> <span class="n">protocol</span><span class="p">,</span> <span class="n">addr</span><span class="p">,</span> <span class="bp">self</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">reactor</span><span class="p">)</span><br /></pre></div><div class="caption filename">portion of twisted/internet/tcp.py</div><p>The transport is connected to the protocol:</p><div class="code python"><pre><span class="n">protocol</span><span class="o">.</span><span class="n">makeConnection</span><span class="p">(</span><span class="n">transport</span><span class="p">)</span><br /></pre></div><div class="caption filename">portion of twisted/internet/tcp.py</div><p>which looks like this:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">BaseProtocol</span><span class="p">:</span><br /><br /> <span class="k">def</span> <span class="nf">makeConnection</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">transport</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">connected</span> <span class="o">=</span> <span class="mi">1</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">transport</span> <span class="o">=</span> <span class="n">transport</span><br /></pre></div><div class="caption filename">portion of twisted/internet/protocol.py</div><p>Here's a thousand words describing it:</p><style>.tx-bite-8 text { fill: #fff;}.tx-bite-8 text.label { fill: #000;}.tx-bite-8 line { stroke: #999; stroke-width: 4;}.tx-bite-8 rect.client { fill: steelblue;}.tx-bite-8 rect.factory { fill: #933;}.tx-bite-8 rect.protocol { fill: #c60;}.tx-bite-8 rect.transport { fill: #393;}.tx-bite-8 circle.byte { fill: steelblue;}</style><div id="network-triforce-diag-1" class="tx-bite-8"></div><script>var w = 600;var h = 250;var radius = 35;var rx = d3.scale.linear().domain([0,5]).range([0,w]);var ry = d3.scale.linear().domain([0,3]).range([0,h]);function midx(val) { return rx(val) + radius;}function midy(val) { return ry(val) + radius;}function makeBox(node, x, y, text, cls, animate) { var g = node.append('g') .attr('transform', 'translate('+rx(x)+','+ry(y)+')'); var box = g.append('rect') .attr('class', cls) .attr('x', 0) .attr('y', 0) .attr('rx', 4) .attr('ry', 4) .attr('width', 2*radius) .attr('height', 2*radius); var txt = g.append('svg:text') .attr('class', cls+' text') .attr('x', radius) .attr('y', 2*radius/3) .attr('text-anchor', 'middle') .text(text); if (animate) { g.attr('transform', 'translate('+rx(x)+','+ry(y)+'),scale(1 0)') .transition() .duration(500) .attr('transform', 'translate('+rx(x)+','+ry(y)+')'); } return g;}function makeLine(node, a, b, cls) { return node.append('line') .attr('x1', rx(a[0])+radius) .attr('x2', rx(b[0])+radius) .attr('y1', ry(a[1])+radius) .attr('y2', ry(b[1])+radius);}function makeCircle(node, x, y, cls) { return node.append('circle') .attr('class', cls) .attr('cx', midx(x)) .attr('cy', midy(y)) .attr('r', radius/3);}function makeLabel(node, x, y, text) { return node.append('svg:text') .attr('class', 'label') .text(text) .attr('x', x) .attr('y', y);}var div = d3.select('#network-triforce-diag-1');var button, svg, fg, bg;var label;function step1() { div.html(''); button = div.append('input') .attr('type', 'button'); div.append('br'); div.append('br'); svg = div.append('svg:svg') .attr('width', w) .attr('height', h); bg = svg.append('g'); fg = svg.append('g'); makeBox(fg, 1, 0, 'Glue', 'glue'); makeBox(fg, 4, 0, 'Factory', 'factory'); button.attr('value', 'Connect a client') .on('click', step2);}step1();function step2(y) { client1 = makeBox(fg, 0, 0, 'Client', 'client'); client1_socket = makeLine(bg, [0,0], [1,0]); button.attr('value', 'Build a protocol') .on('click', step3);}function step3(y) { y = y || 1; label = makeLabel(fg, rx(3)+2*radius, midy(y), 'factory.buildProtocol(...)'); var proto1 = makeBox(fg, 3, y, 'Protocol', 'protocol', true); makeLine(bg, [4,0], [4,0]) .transition() .duration(1000) .attr('x2', midx(3)) .attr('y2', midy(y)); button.attr('value', 'Create a transport, and hook up socket') .on('click', step4);}function step4(y) { y = y || 1; label.remove(); var transport1 = makeBox(fg, 2, y, 'Transport', 'transport', true); client1_socket.transition() .duration(1000) .attr('x2', midx(2)) .attr('y2', midy(y)) .transition() .duration(2000) .attr('y1', midy(y)); client1.transition() .duration(2000) .attr('transform', 'translate('+rx(0)+','+ry(y)+')'); button.attr('value', 'Connect transport to protocol') .on('click', step5);}function step5(y) { y = y || 1; var line = makeLine(bg, [2,y], [2,y]); label = makeLabel(fg, rx(2), ry(1)-10, "protocol.makeConnection(transport)"); line.transition() .duration(1000) .attr('x2', midx(3)); button.attr('value', 'Client send bytes') .on('click', step6);}function step6(y) { y = y || 1; label.remove(); label = makeLabel(fg, rx(2), ry(1)-10, "protocol.dataReceived(bytes)"); var circle = makeCircle(bg, 0, y, 'byte') .attr('cx', midx(0)); circle.transition() .duration(1500) .attr('cx', midx(2)) .transition() .delay(1500) .duration(1000) .attr('cx', midx(3)) .transition() .delay(2500) .duration(500) .remove(); button.attr('value', 'Server send bytes') .on('click', step7);}function step7(y) { y = y || 1; label.remove(); label = makeLabel(fg, rx(2), ry(1)-10, "transport.write(bytes)"); var circle = makeCircle(bg, 0, y, 'byte') .attr('cx', midx(3)); circle.transition() .duration(1000) .attr('cx', midx(2)) .transition() .delay(1000) .duration(1500) .attr('cx', midx(0)) .transition() .delay(2500) .duration(500) .remove(); button.attr('value', 'Connect another client') .on('click', step8);}function step8() { label.remove(); step2(2); step3(2); step4(2); step5(2); label.remove(); button.attr('value', 'Start over') .on('click', step1);} </script><h2>Toward a better boom</h2><p>Here's a Protocol and Factory that will let you telnet in to play.</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Protocol</span><span class="p">,</span> <span class="n">Factory</span><br /><span class="kn">import</span> <span class="nn">string</span><br /><br /><br /><span class="kn">from</span> <span class="nn">boom.game</span> <span class="kn">import</span> <span class="n">Pawn</span><span class="p">,</span> <span class="n">YoureDead</span><span class="p">,</span> <span class="n">IllegalMove</span><br /><br /><br /><span class="k">class</span> <span class="nc">SimpleProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span><br /><br /> <span class="n">num</span> <span class="o">=</span> <span class="mi">0</span><br /><br /> <span class="n">move_mapping</span> <span class="o">=</span> <span class="p">{</span><br /> <span class="s">'w'</span><span class="p">:</span> <span class="s">'u'</span><span class="p">,</span><br /> <span class="s">'a'</span><span class="p">:</span> <span class="s">'l'</span><span class="p">,</span><br /> <span class="s">'d'</span><span class="p">:</span> <span class="s">'r'</span><span class="p">,</span><br /> <span class="s">'s'</span><span class="p">:</span> <span class="s">'d'</span><span class="p">,</span><br /> <span class="p">}</span><br /><br /> <span class="k">def</span> <span class="nf">connectionMade</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">protocols</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><br /> <span class="n">name</span> <span class="o">=</span> <span class="n">string</span><span class="o">.</span><span class="n">uppercase</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">num</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">string</span><span class="o">.</span><span class="n">uppercase</span><span class="p">)]</span><br /> <span class="n">SimpleProtocol</span><span class="o">.</span><span class="n">num</span> <span class="o">+=</span> <span class="mi">1</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">pawn</span> <span class="o">=</span> <span class="n">Pawn</span><span class="p">(</span><span class="n">name</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">insertPawn</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">pawn</span><span class="p">)</span><br /><br /><br /> <span class="k">def</span> <span class="nf">connectionLost</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">reason</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">protocols</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">factory</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">pawns</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pawn</span><span class="p">)</span><br /><br /><br /> <span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span><br /> <span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">move_mapping</span><span class="p">:</span><br /> <span class="k">try</span><span class="p">:</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">pawn</span><span class="o">.</span><span class="n">move</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">move_mapping</span><span class="p">[</span><span class="n">k</span><span class="p">])</span><br /> <span class="k">except</span> <span class="n">YoureDead</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span><br /> <span class="k">pass</span><br /> <span class="k">except</span> <span class="n">IllegalMove</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span><br /> <span class="k">pass</span><br /> <span class="k">elif</span> <span class="n">k</span> <span class="o">==</span> <span class="s">'e'</span><span class="p">:</span><br /> <span class="k">try</span><span class="p">:</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">pawn</span><span class="o">.</span><span class="n">dropBomb</span><span class="p">()</span><br /> <span class="k">except</span> <span class="n">YoureDead</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span><br /> <span class="k">pass</span><br /> <span class="k">except</span> <span class="n">IllegalMove</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span><br /> <span class="k">pass</span><br /><br /><br /><br /><span class="k">class</span> <span class="nc">SimpleFactory</span><span class="p">(</span><span class="n">Factory</span><span class="p">):</span><br /> <span class="sd">"""</span><br /><span class="sd"> A factory for making L{SimpleProtocol}s</span><br /><span class="sd"> </span><br /><span class="sd"> @ivar board: the game board on which I'll be playing</span><br /><span class="sd"> @ivar protocols: A list of L{SimpleProtocol} instances currently</span><br /><span class="sd"> in use.</span><br /><span class="sd"> """</span><br /> <br /> <span class="n">protocol</span> <span class="o">=</span> <span class="n">SimpleProtocol</span><br /> <br /> <br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">board</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">board</span> <span class="o">=</span> <span class="n">board</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">protocols</span> <span class="o">=</span> <span class="p">[]</span><br /></pre></div><div class="caption filename">boom/protocol.py</div><p>The code is available from GitHub in the <a href="https://github.com/iffy/boom/tree/tx-telnet-1">tx-telnet-1 branch</a>. Do this:</p><div class="code bash"><pre>git clone -b tx-telnet-1 git://github.com/iffy/boom boom.git<br /><span class="nb">cd </span>boom.git<br /><span class="nv">PYTHONPATH</span><span class="o">=</span>. python run.py<br /></pre></div><p>Or if you don't have Git:</p><div class="code bash"><pre>wget https://github.com/iffy/boom/tarball/tx-telnet-1<br />tar xf tx-telnet-1 <br /><span class="nb">cd </span>iffy-boom-f5c6433/<br /><span class="nv">PYTHONPATH</span><span class="o">=</span>. python run.py<br /></pre></div><p>Then connect with:</p><div class="code bash"><pre>telnet 127.0.0.1 8900<br /></pre></div><p>Next time, we'll remove the need to press enter after each move. In the meantime, have a look at <a href="http://twistedmatrix.com/documents/current/core/howto/servers.html">Twisted's guide to writing servers</a> for more information.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-51086095229113640462012-06-18T07:00:00.000-07:002012-06-18T07:00:05.278-07:00Bite-sized Twisted: Asynchronous Testing and the ClockIn this, the seventh Bite-sized Twisted post, I'll go over a little asynchronous testing and the <code>Clock</code>.<h2>Impatient runners</h2><p>Let's test that a lit bomb explodes with a message when it should. Or in other words, that a function returns a <code>Deferred</code> which fires after a time. For convenience, I'm going to put the code to be tested in the same file as the test.</p><p>We'll need to import some things:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">unittest</span> <span class="kn">import</span> <span class="n">TestCase</span><br /><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /></pre></div><div class="caption filename">test_bomb1.py:1-3</div><p>Here's the test:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">BombTest</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">test_explodes</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">result</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="s">'exploded'</span><span class="p">)</span><br /> <span class="n">d</span> <span class="o">=</span> <span class="n">lightBomb</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">'exploded'</span><span class="p">)</span><br /> <span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">check</span><span class="p">)</span><br /></pre></div><div class="caption filename">test_bomb1.py:5-11</div><p>And here's the function:</p><div class="code python"><pre><span class="k">def</span> <span class="nf">lightBomb</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span><br /> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="n">defer</span><span class="o">.</span><span class="n">callback</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span><br /> <span class="k">return</span> <span class="n">defer</span><br /></pre></div><div class="caption filename">test_bomb1.py:12-16</div><p>When we run it, we see that it passes the test:</p><div class="code text"><pre>test_bomb1<br /> BombTest<br /> test_explodes ... [OK]<br /><br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.001s<br /><br />PASSED (successes=1)<br /></pre></div><div class="caption shell">trial test_bomb1.py</div><p>Yay!</p><p>No, not yay. Change the test to this:</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">test_explodes</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">result</span><span class="p">):</span><br /><span class="hll"> <span class="k">print</span> <span class="s">"I was executed with </span><span class="si">%r</span><span class="s">"</span> <span class="o">%</span> <span class="n">result</span> <br /></span> <span class="n">d</span> <span class="o">=</span> <span class="n">lightBomb</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">'exploded'</span><span class="p">)</span><br /> <span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">check</span><span class="p">)</span><br /></pre></div><div class="caption filename">test_bomb2.py:7-11</div><p>And see that <code>check</code> is never called:</p><div class="code text"><pre>test_bomb2<br /> BombTest<br /> test_explodes ... [OK]<br /><br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.001s<br /><br />PASSED (successes=1)<br /></pre></div><div class="caption shell">trial test_bomb2.py</div><p>What gives?</p><h2>Wait up!</h2><p>Does it seem a little suspicious that the bomb is supposed to go off in 2 seconds and the test runs in less than 1 second? The problem is that the test runner is not waiting for the <code>Deferred</code> to be called back. To wait for the <code>Deferred</code>, we'll have to use <code>twisted.trial.unittest.TestCase</code> and return the <code>Deferred</code> from the test method:</p><div class="code python"><pre><span class="hll"><span class="kn">from</span> <span class="nn">twisted.trial.unittest</span> <span class="kn">import</span> <span class="n">TestCase</span><br /></span><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /></pre></div><div class="caption filename">test_bomb3.py:1-3</div><div class="code python"><pre> <span class="k">def</span> <span class="nf">test_explodes</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">result</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="s">'exploded'</span><span class="p">)</span><br /> <span class="n">d</span> <span class="o">=</span> <span class="n">lightBomb</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">'exploded'</span><span class="p">)</span><br /> <span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">check</span><span class="p">)</span><br /><span class="hll"> <span class="k">return</span> <span class="n">d</span> <br /></span></pre></div><div class="caption filename">test_bomb3.py:7-12</div><div class="code text"><pre>test_bomb3<br /> BombTest<br /> test_explodes ... [OK]<br /><br />-------------------------------------------------------------------------------<br />Ran 1 tests in 2.012s<br /><br />PASSED (successes=1)<br /></pre></div><div class="caption shell">trial test_bomb3.py</div><p>See how the test took more than 2 seconds to run this time?</p><h2>Timeout</h2><p>What if <code>lightBomb</code> looked like this instead?</p><div class="code python"><pre><span class="k">def</span> <span class="nf">lightBomb</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span><br /> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="k">return</span> <span class="n">defer</span><br /></pre></div><div class="caption filename">test_hang.py:14-16</div><p>Run the test and it will hang. Add a timeout to fail any test that takes too long:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">BombTest</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span><br /><br /><span class="hll"> <span class="n">timeout</span> <span class="o">=</span> <span class="mi">3</span><br /></span></pre></div><div class="caption filename">test_timeout.py:5-7</div><div class="code text"><pre>test_timeout<br /> BombTest<br /> test_explodes ... [ERROR]<br /><br />===============================================================================<br />[ERROR]<br />Traceback (most recent call last):<br />Failure: twisted.internet.defer.TimeoutError: <test_timeout.BombTest testMethod=test_explodes> (test_explodes) still running at 3.0 secs<br /><br />test_timeout.BombTest.test_explodes<br />-------------------------------------------------------------------------------<br />Ran 1 tests in 3.015s<br /><br />FAILED (errors=1)<br /></pre></div><div class="caption shell">trial test_timeout.py</div><h2>Hello, Clock</h2><p>If you have a lot of tests dealing with scheduled events and timing, running all of them will take a long time. To avoid that, we can simulate the passage of time with the <code>Clock</code>.</p><p>Here's the same test as <i>test_bomb3.py</i> using the <code>Clock</code> (changed lines are highlighted):</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.trial.unittest</span> <span class="kn">import</span> <span class="n">TestCase</span><br /><span class="hll"><span class="kn">from</span> <span class="nn">twisted.internet.task</span> <span class="kn">import</span> <span class="n">Clock</span><br /></span><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">class</span> <span class="nc">BombTest</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">test_explodes</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">result</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="s">'exploded'</span><span class="p">)</span><br /><span class="hll"> <span class="n">clock</span> <span class="o">=</span> <span class="n">Clock</span><span class="p">()</span><br /></span><span class="hll"> <span class="n">d</span> <span class="o">=</span> <span class="n">lightBomb</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">'exploded'</span><span class="p">,</span> <span class="n">reactor</span><span class="o">=</span><span class="n">clock</span><span class="p">)</span><br /></span> <span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">check</span><span class="p">)</span><br /><span class="hll"> <span class="n">clock</span><span class="o">.</span><span class="n">advance</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><br /></span> <span class="k">return</span> <span class="n">d</span> <br /><br /><span class="hll"><span class="k">def</span> <span class="nf">lightBomb</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">reactor</span><span class="o">=</span><span class="n">reactor</span><span class="p">):</span><br /></span> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="n">defer</span><span class="o">.</span><span class="n">callback</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span><br /> <span class="k">return</span> <span class="n">defer</span><br /></pre></div><div class="caption filename">test_clock.py</div><div class="code text"><pre>test_clock<br /> BombTest<br /> test_explodes ... [OK]<br /><br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.005s<br /><br />PASSED (successes=1)<br /></pre></div><div class="caption shell">trial test_clock.py</div><p>Yes! Much faster! <a href="http://twistedmatrix.com/documents/current/api/twisted.internet.task.Clock.html">Here are the Clock docs</a> if you are interested. Note in <i>test_clock.py</i> how we had to make <code>lightBomb</code> accept an optional <code>reactor</code>.</p><h2>Boom tests</h2><p>All of the code for the current version of <code>boom</code> was test-driven. Perhaps reading some of the tests will be enlightening -- though I make no claim at being a testing expert. The <a href="https://github.com/iffy/boom/blob/tx-single-player/boom/test/test_game.py">test code is available on GitHub.</a></p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-38919604723920098172012-06-14T07:00:00.000-07:002012-06-14T07:00:02.170-07:00Bite-sized Twisted: A simple, working programThere's an asynchronous, single-player Bomberman clone at the end of this post.<h2>Quick overview</h2><p>Very quickly, these are the components of the game:<ol> <li><b>players</b> (the things running around dropping bombs)</li> <li><b>bombs</b></li> <li><b>fire</b> (from the bombs)</li> <li>the game <b>board</b></li></ol>This post will build them in the reverse order: the <b>board</b>, <b>fire</b>, <b>bombs</b> then <b>players</b>. I'm going to skim through the normal Python, only emphasizing the points where the Twisted library does something neat.</p><p>Coordinates throughout the program are x,y tuples (e.g. <code>(3,2)</code>).</p><h2>Big fat disclaimer</h2><p>I'm not a game programmer. I've previously written simple games like Tetris. I know there are cool optimizations that game makers use (for dealing with lag and minimizing network traffic and such). I'm not going to prematurely optimize. I <i>will</i> accept good patches though :)</p><h2>Da board</h2><p>The game board will be divided into layers. At the bottom is the bottom layer... which, for now, consists of identical background tiles. One layer up is the layer of destructable and indestructable tiles. We'll call these the foreground tiles. Here's how we'll implement the foreground tiles and a method to generate a basic board:</p><div class="code python"><pre><span class="n">EMPTY</span> <span class="o">=</span> <span class="mi">0</span><br /><span class="n">HARD</span> <span class="o">=</span> <span class="mi">1</span><br /><span class="n">SOFT</span> <span class="o">=</span> <span class="mi">2</span><br /><br /><span class="k">class</span> <span class="nc">Board</span><span class="p">:</span><br /><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span> <span class="o">=</span> <span class="p">{}</span><br /><br /> <span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span><br /> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="n">width</span><span class="p">):</span><br /> <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="n">height</span><span class="p">):</span><br /> <span class="n">coord</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">)</span><br /> <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">%</span> <span class="mi">2</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">2</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="n">HARD</span><br /> <span class="k">else</span><span class="p">:</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="n">SOFT</span><br /></pre></div><div class="caption filename">board1.py</div><p><code>EMPTY</code> means there's no foreground tile, <code>SOFT</code> tiles are destructable and <code>HARD</code> tiles are indestructable. A basic 5 x 5 board looks like this:</p><div class="code text"><pre>+-----+<br />|:::::|<br />|:#:#:|<br />|:::::|<br />|:#:#:|<br />|:::::|<br />+-----+<br /><br />: = SOFT<br /># = HARD<br /></pre></div><p>The keys for <code>fg_tiles</code> are coordinate tuples (e.g. <code>(4,5)</code>). There's nothing asynchronous or Twisted-y about that code. Let's move along.</p><h2>Start the fire!</h2><p>Generally, an exploding bomb will make fire appear on the board. Once a bomb makes the fire, however, the bomb has nothing more to do with it. Let's implement the post-bomb portion of the fire.</p><p>We'll keep track of fires in a dictionary on <code>Board</code>. The keys into the <code>fires</code> dictionary are x,y coordinate tuples:</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span> <span class="o">=</span> <span class="p">{}</span><br /><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span> <span class="o">=</span> <span class="p">{}</span><br /></span></pre></div><div class="caption filename">fire1.py</div><p>Fires have an asynchronous component: they ignite, burn for some amount of time, then die. We'll use <code>reactor.callLater</code> to do the "burn for some amount time" bit and use a <code>Deferred</code> to signal to the fire-starter when the fire is extinguished:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="o">...</span><br /><br /> <span class="k">def</span> <span class="nf">startFire</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">,</span> <span class="n">burntime</span><span class="p">):</span><br /> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><br /> <span class="c"># Put the fire out after a bit.</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">burntime</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">stopFire</span><span class="p">,</span> <span class="n">coord</span><span class="p">)</span><br /><br /> <span class="c"># Record that there's a fire on the board.</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="n">defer</span><br /> <br /> <span class="c"># Destroy whatever tile is here</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="n">EMPTY</span><br /> <span class="k">return</span> <span class="n">defer</span><br /><br /><br /> <span class="k">def</span> <span class="nf">stopFire</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">):</span><br /> <span class="n">defer</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <br /> <br /> <span class="c"># Remove the fire from the board</span><br /> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <br /> <span class="c"># notify people who care that the fire is out</span><br /> <span class="n">defer</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span><br /></pre></div><div class="caption filename">fire2.py</div><p>The <code>Deferred</code> is created, stored in the <code>fires</code> dictionary, then returned to the caller (they can add callbacks to it if they want). Also, the tile at that coordinate is "destroyed" (made <code>EMPTY</code>).</p><p>The <code>callLater</code> calls <code>stopFire</code>, which remove the entry in the <code>fires</code> dictionary and calls back the stored <code>Deferred</code>. That the <code>Deferred</code> is called back with <code>None</code> simply means that any attached callbacks will be passed <code>None</code> as the first argument.</p><p>Let's talk about threading. If you've done threaded programming, you may be cringing over the blatant disregard with which global variables are accessed (<code>self.fires</code> and <code>self.fg_tiles</code>) without proper locking mechanisms to guarantee atomicity. This code, though, isn't threaded. <code>startFire</code> will run from beginning to end, sequentially, without any other code executing. Breathe well! This is Twisted!</p><h2>Restart the fire!</h2><p>It's possible that an exploding bomb will ignite a tile that is already burning. With <i>fire2.py</i>, that scenario is a problem because the second fire will overwrite the first fire's entry in the <code>fires</code> dict. Then the first fire's <code>stopFire</code> call will delete the entry from <code>fires</code> dict so that when the second fire's <code>stopFire</code> happens, a <code>KeyError</code> is raised. From the players' perspective, the second fire will be prematurely extinguished.</p><p>So instead of having two fires burning in the same tile, if a second fire ignites, let's extend the existing fire's time. We can do this with the <code>reset</code> method of the thing returned by <code>callLater</code> (it's called a <code>DelayedCall</code> and you can <a href="http://twistedmatrix.com/documents/current/api/twisted.internet.base.DelayedCall.html">read the docs here</a>):</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">startFire</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">,</span> <span class="n">burntime</span><span class="p">):</span><br /><span class="hll"> <span class="c"># Is there already a fire on this tile? </span><br /></span><span class="hll"> <span class="k">if</span> <span class="n">coord</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">:</span> <br /></span><span class="hll"> <span class="n">defer</span><span class="p">,</span> <span class="n">call</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <br /></span><span class="hll"> <span class="c"># Stoke the fire </span><br /></span><span class="hll"> <span class="n">call</span><span class="o">.</span><span class="n">reset</span><span class="p">(</span><span class="n">burntime</span><span class="p">)</span> <br /></span><span class="hll"> <span class="k">else</span><span class="p">:</span> <br /></span> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="c"># Put the fire out after a bit.</span><br /> <span class="n">call</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">burntime</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">stopFire</span><span class="p">,</span> <span class="n">coord</span><span class="p">)</span><br /><br /> <span class="c"># Record that there's a fire on the board.</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">defer</span><span class="p">,</span> <span class="n">call</span><span class="p">)</span><br /> <br /> <span class="c"># Destroy whatever tile is here</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="n">EMPTY</span><br /> <span class="k">return</span> <span class="n">defer</span><br /><br /><br /> <span class="k">def</span> <span class="nf">stopFire</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">):</span><br /> <span class="n">d</span><span class="p">,</span> <span class="n">call</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <br /> <br /> <span class="c"># Remove the fire from the board</span><br /> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <br /> <span class="c"># notify people who care that the fire is out</span><br /> <span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span><br /></pre></div><div class="caption filename">fire3.py</div><p>Now a second call to start a fire on a tile will prolong the burning without glitches. Anyone waiting on the <code>Deferred</code> returned by the first call will just wait a little longer.</p><h2>Bomb, bomb, bomb</h2><p>Now that we can start fires at will, let's add bombs to the fray. We'll track bombs similarly to fires:</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span> <span class="o">=</span> <span class="p">{}</span><br /><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span> <span class="o">=</span> <span class="p">{}</span><br /></span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span> <span class="o">=</span> <span class="p">{}</span><br /></pre></div><div class="caption filename">bomb1.py</div><p>And the code for dropping and detonating bombs is very similar to that for igniting and extinguishing fires (with some extra in <code>detonateBomb</code> to account for the different kinds of tiles and board boundaries, etc...):</p><div class="code python"><pre> <span class="n">dft_burn</span> <span class="o">=</span> <span class="mi">1</span><br /><br /><br /> <span class="k">def</span> <span class="nf">dropBomb</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">,</span> <span class="n">fuse</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span><br /> <span class="c"># Set the bomb up to explode later</span><br /> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="n">call</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">detonateBomb</span><span class="p">,</span> <span class="n">coord</span><span class="p">)</span><br /> <br /> <span class="c"># Place the bomb on the board</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">defer</span><span class="p">,</span> <span class="n">call</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span><br /> <span class="k">return</span> <span class="n">defer</span><br /><br /><br /> <span class="k">def</span> <span class="nf">detonateBomb</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">):</span><br /> <span class="n">defer</span><span class="p">,</span> <span class="n">call</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <br /> <span class="c"># Remove the bomb from the board</span><br /> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <br /> <span class="c"># Let people who care know that the bomb has exploded</span><br /> <span class="n">defer</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span><br /> <br /> <span class="c"># Start the fires up, down, left and right</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">startFire</span><span class="p">(</span><span class="n">coord</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">dft_burn</span><span class="p">)</span><br /> <span class="n">directions</span> <span class="o">=</span> <span class="p">[</span><br /> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span><br /> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span><br /> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span><br /> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span><br /> <span class="p">]</span><br /> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">size</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span><br /> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">directions</span><span class="p">):</span><br /> <span class="n">target</span> <span class="o">=</span> <span class="p">(</span><span class="n">coord</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">+</span><span class="p">(</span><span class="n">d</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">i</span><span class="p">),</span> <span class="n">coord</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="p">(</span><span class="n">d</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">i</span><span class="p">))</span><br /> <span class="k">try</span><span class="p">:</span><br /> <span class="n">tile</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">target</span><span class="p">]</span><br /> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span><br /> <span class="k">continue</span><br /> <span class="k">if</span> <span class="n">tile</span> <span class="o">==</span> <span class="n">HARD</span><span class="p">:</span><br /> <span class="n">directions</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">d</span><span class="p">)</span><br /> <span class="k">continue</span><br /> <span class="k">if</span> <span class="n">target</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">:</span><br /> <span class="n">directions</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">d</span><span class="p">)</span><br /> <span class="k">if</span> <span class="n">tile</span> <span class="o">==</span> <span class="n">SOFT</span><span class="p">:</span><br /> <span class="n">directions</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">d</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">startFire</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">dft_burn</span><span class="p">)</span><br /></pre></div><div class="caption filename">bomb2.py</div><p>In Bomberman, exploding bombs cause other bombs to explode. We can incorporate that by changing <code>startFire</code> (changed lines are highlighted):</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">startFire</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">,</span> <span class="n">burntime</span><span class="p">):</span><br /> <span class="c"># Is there already a fire on this tile?</span><br /> <span class="k">if</span> <span class="n">coord</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">:</span><br /> <span class="n">defer</span><span class="p">,</span> <span class="n">call</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <span class="c"># Stoke the fire</span><br /> <span class="n">call</span><span class="o">.</span><span class="n">reset</span><span class="p">(</span><span class="n">burntime</span><span class="p">)</span><br /> <span class="k">else</span><span class="p">:</span><br /> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="c"># Put the fire out after a bit.</span><br /> <span class="n">call</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">burntime</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">stopFire</span><span class="p">,</span> <span class="n">coord</span><span class="p">)</span><br /><br /> <span class="c"># Record that there's a fire on the board.</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">defer</span><span class="p">,</span> <span class="n">call</span><span class="p">)</span><br /> <br /> <span class="c"># Destroy whatever tile is here</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="n">EMPTY</span><br /> <br /><span class="hll"> <span class="c"># Is there a bomb to detonate here? </span><br /></span><span class="hll"> <span class="k">if</span> <span class="n">coord</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">:</span> <br /></span><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">detonateBomb</span><span class="p">(</span><span class="n">coord</span><span class="p">)</span> <br /></span> <span class="k">return</span> <span class="n">defer</span><br /></pre></div><div class="caption filename">bomb3.py</div><p>But now what happens when the bomb's original fuse expires? It will cause problems trying to re-explode an exploded bomb. So, let's cancel the original fuse using the <code>cancel()</code> method of the <code>DelayedCall</code>:</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">detonateBomb</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">):</span><br /> <span class="n">defer</span><span class="p">,</span> <span class="n">call</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <br /> <span class="c"># Remove the bomb from the board</span><br /> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <br /> <span class="c"># Let people who care know that the bomb has exploded</span><br /> <span class="n">defer</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span><br /> <br /><span class="hll"> <span class="c"># Premature explosion? </span><br /></span><span class="hll"> <span class="k">if</span> <span class="n">call</span><span class="o">.</span><span class="n">active</span><span class="p">():</span> <br /></span><span class="hll"> <span class="n">call</span><span class="o">.</span><span class="n">cancel</span><span class="p">()</span> <br /></span> <br /> <span class="c"># Start the fires up, down, left and right</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">startFire</span><span class="p">(</span><span class="n">coord</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">dft_burn</span><span class="p">)</span><br /> <span class="o">...</span><br /></pre></div><div class="caption filename">bomb4.py</div><p>Now the fuse is canceled on prematurely ignited bombs.</p><h2>Merely a pawn</h2><p>I'm using the term <i>pawn</i> to represent the thing that moves around on the board. We'll keep track of <code>Pawns</code> in a <code>set</code> rather than a dictionary because multiple <code>Pawns</code> can be at the same location at once.</p><div class="code python"><pre><span class="k">class</span> <span class="nc">Board</span><span class="p">:</span><br /><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span> <span class="o">=</span> <span class="p">{}</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span> <span class="o">=</span> <span class="p">{}</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span> <span class="o">=</span> <span class="p">{}</span><br /><span class="hll"> <span class="bp">self</span><span class="o">.</span><span class="n">pawns</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span><br /></span></pre></div><div class="caption filename">pawn1.py</div><p>Pawns have various attributes, as shown:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">Pawn</span><span class="p">:</span><br /> <br /> <span class="n">bombs</span> <span class="o">=</span> <span class="mi">1</span><br /> <span class="n">flame_size</span> <span class="o">=</span> <span class="mi">1</span><br /> <span class="n">fuse</span> <span class="o">=</span> <span class="mf">2.0</span><br /> <span class="n">alive</span> <span class="o">=</span> <span class="bp">True</span><br /><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span><br /></pre></div><div class="caption filename">pawn2.py</div><p>First things first: <code>Pawns</code> need to know how to die.</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">kill</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">alive</span> <span class="o">=</span> <span class="bp">False</span><br /></pre></div><div class="caption filename">pawn3.py</div><p>We need to be able to put <code>Pawns</code> on the <code>Board</code>:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">Board</span><span class="p">:</span><br /> <span class="o">...</span><br /> <span class="k">def</span> <span class="nf">insertPawn</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">,</span> <span class="n">pawn</span><span class="p">):</span><br /> <span class="c"># Let the Board and Pawn know about each other</span><br /> <span class="n">pawn</span><span class="o">.</span><span class="n">board</span> <span class="o">=</span> <span class="bp">self</span><br /> <span class="n">pawn</span><span class="o">.</span><span class="n">loc</span> <span class="o">=</span> <span class="n">coord</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">pawns</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">pawn</span><span class="p">)</span><br /> <br /> <span class="c"># Clear a space for the Pawn</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">pawn</span><span class="o">.</span><span class="n">loc</span><span class="p">]</span> <span class="o">=</span> <span class="n">EMPTY</span><br /> <span class="n">directions</span> <span class="o">=</span> <span class="p">[</span><br /> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span><br /> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span><br /> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">),</span><br /> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span><br /> <span class="p">]</span><br /> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">directions</span><span class="p">:</span><br /> <span class="n">target</span> <span class="o">=</span> <span class="p">(</span><span class="n">pawn</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">+</span><span class="n">d</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">pawn</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="n">d</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><br /> <span class="k">if</span> <span class="n">target</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">:</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">target</span><span class="p">]</span> <span class="o">=</span> <span class="n">EMPTY</span><br /><br /><br /><span class="k">class</span> <span class="nc">Pawn</span><span class="p">:</span><br /> <span class="o">...</span><br /> <span class="n">board</span> <span class="o">=</span> <span class="bp">None</span><br /> <span class="n">loc</span> <span class="o">=</span> <span class="bp">None</span><br /></pre></div><div class="caption filename">pawn4.py</div><p>A fire + a <code>Pawn</code> = death for the <code>Pawn</code>:</p><div class="code python"><pre> <span class="k">def</span> <span class="nf">startFire</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">coord</span><span class="p">,</span> <span class="n">burntime</span><span class="p">):</span><br /> <span class="c"># Is there already a fire on this tile?</span><br /> <span class="k">if</span> <span class="n">coord</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">:</span><br /> <span class="n">defer</span><span class="p">,</span> <span class="n">call</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span><br /> <span class="c"># Stoke the fire</span><br /> <span class="n">call</span><span class="o">.</span><span class="n">reset</span><span class="p">(</span><span class="n">burntime</span><span class="p">)</span><br /> <span class="k">else</span><span class="p">:</span><br /> <span class="n">defer</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="c"># Put the fire out after a bit.</span><br /> <span class="n">call</span> <span class="o">=</span> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">burntime</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">stopFire</span><span class="p">,</span> <span class="n">coord</span><span class="p">)</span><br /><br /> <span class="c"># Record that there's a fire on the board.</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">defer</span><span class="p">,</span> <span class="n">call</span><span class="p">)</span><br /> <br /> <span class="c"># Destroy whatever tile is here</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">coord</span><span class="p">]</span> <span class="o">=</span> <span class="n">EMPTY</span><br /> <br /> <span class="c"># Is there a bomb to detonate here?</span><br /> <span class="k">if</span> <span class="n">coord</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span><span class="p">:</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">detonateBomb</span><span class="p">(</span><span class="n">coord</span><span class="p">)</span><br /> <br /><span class="hll"> <span class="c"># Any pawns that should diaf? </span><br /></span><span class="hll"> <span class="k">for</span> <span class="n">pawn</span> <span class="ow">in</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">pawns</span> <span class="k">if</span> <span class="n">x</span><span class="o">.</span><span class="n">loc</span><span class="o">==</span><span class="n">coord</span><span class="p">]:</span><br /></span><span class="hll"> <span class="n">pawn</span><span class="o">.</span><span class="n">kill</span><span class="p">()</span> <br /></span> <span class="k">return</span> <span class="n">defer</span><br /></pre></div><div class="caption filename">pawn5.py</div><p><code>Pawns</code> can move (very quickly in this version). And when they move, the <code>Board</code> is notified:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">YoureDead</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span> <span class="k">pass</span><br /><span class="k">class</span> <span class="nc">IllegalMove</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span> <span class="k">pass</span><br /><br /><br /><span class="k">class</span> <span class="nc">Board</span><span class="p">:</span><br /> <span class="o">...</span><br /> <span class="k">def</span> <span class="nf">pawnMoved</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pawn</span><span class="p">,</span> <span class="n">new_loc</span><span class="p">):</span><br /> <span class="n">pawn</span><span class="o">.</span><span class="n">loc</span> <span class="o">=</span> <span class="n">new_loc</span><br /> <br /> <span class="c"># Did the Pawn unwittingly move into a fire?</span><br /> <span class="k">if</span> <span class="n">new_loc</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">fires</span><span class="p">:</span><br /> <span class="n">pawn</span><span class="o">.</span><span class="n">kill</span><span class="p">()</span><br /><br /><br /><span class="k">class</span> <span class="nc">Pawn</span><span class="p">:</span><br /> <span class="o">...</span><br /> <span class="k">def</span> <span class="nf">move</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">direction</span><span class="p">):</span><br /> <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">alive</span><span class="p">:</span><br /> <span class="k">raise</span> <span class="n">YoureDead</span><span class="p">(</span><span class="s">"Dead people can't move"</span><span class="p">)</span><br /> <br /> <span class="c"># Which way?</span><br /> <span class="n">delta</span> <span class="o">=</span> <span class="p">{</span><br /> <span class="s">'u'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span><br /> <span class="s">'d'</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">),</span><br /> <span class="s">'l'</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span><br /> <span class="s">'r'</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">),</span><br /> <span class="p">}[</span><span class="n">direction</span><span class="p">]</span><br /> <span class="n">target</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">+</span><span class="n">delta</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="bp">self</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="n">delta</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><br /> <br /> <span class="c"># Is it okay to move there?</span><br /> <span class="k">try</span><span class="p">:</span><br /> <span class="n">tile</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">fg_tiles</span><span class="p">[</span><span class="n">target</span><span class="p">]</span><br /> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span><br /> <span class="k">raise</span> <span class="n">IllegalMove</span><span class="p">(</span><span class="s">"That would be off the board"</span><span class="p">)</span><br /> <span class="k">if</span> <span class="n">tile</span> <span class="o">!=</span> <span class="n">EMPTY</span><span class="p">:</span><br /> <span class="k">raise</span> <span class="n">IllegalMove</span><span class="p">(</span><span class="s">"There's a brick there"</span><span class="p">)</span><br /> <span class="k">if</span> <span class="n">target</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">bombs</span><span class="p">:</span><br /> <span class="k">raise</span> <span class="n">IllegalMove</span><span class="p">(</span><span class="s">"There's a bomb there"</span><span class="p">)</span><br /> <br /> <span class="c"># Go ahead and move</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">pawnMoved</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target</span><span class="p">)</span><br /></pre></div><div class="caption filename">pawn6.py</div><h2>Merely an asynchronous pawn</h2><p>Nothing in the above <code>Pawn</code> snippets makes use of Twisted. But the act of dropping a bomb on the board does:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">Pawn</span><span class="p">:</span><br /> <span class="o">...</span><br /> <span class="k">def</span> <span class="nf">dropBomb</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">alive</span><span class="p">:</span><br /> <span class="k">raise</span> <span class="n">YoureDead</span><span class="p">(</span><span class="s">"Dead people can't drop bombs"</span><span class="p">)</span><br /> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span><br /> <span class="k">raise</span> <span class="n">IllegalMove</span><span class="p">(</span><span class="s">"You're out of bombs"</span><span class="p">)</span><br /><br /> <span class="c"># Use a bomb from the Pawn's stash</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">bombs</span> <span class="o">-=</span> <span class="mi">1</span><br /> <span class="n">d</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">board</span><span class="o">.</span><span class="n">dropBomb</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">loc</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fuse</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">flame_size</span><span class="p">)</span><br /> <br /> <span class="c"># Get the bomb back after it explodes</span><br /> <span class="k">def</span> <span class="nf">bombExploded</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">pawn</span><span class="p">):</span><br /> <span class="n">pawn</span><span class="o">.</span><span class="n">bombs</span> <span class="o">+=</span> <span class="mi">1</span><br /> <span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">bombExploded</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><br /></pre></div><div class="caption filename">pawn7.py</div><p>To keep track of the bombs allotted to the <code>Pawn</code>, we subtract when it's put down, then add when the bomb's <code>Deferred</code> is called back.</p><h2>Wanna play?</h2><p>Do this:</p><div class="code bash"><pre>git clone -b tx-single-player git://github.com/iffy/boom boom.git<br /><span class="nb">cd </span>boom.git<br /><span class="nv">PYTHONPATH</span><span class="o">=</span>. python run.py<br /></pre></div><p>Or if you don't have Git:</p><div class="code bash"><pre>wget https://github.com/iffy/boom/tarball/tx-single-player<br />tar xf tx-single-player <br /><span class="nb">cd </span>iffy-boom-58c1582/<br /><span class="nv">PYTHONPATH</span><span class="o">=</span>. python run.py<br /></pre></div><p>The controls are <code>W</code>, <code>A</code>, <code>S</code>, <code>D</code> for up, left, down, right respectively. <code>E</code> lays a bomb. <b>AND</b> you have to press enter after each key press. I know, I know! "What a dumb game! Twisted is terrible!" I just haven't covered it yet. We'll get there. Remember, it's bite-sized Twisted.</p><p>All the code for this post is in the <a href="https://github.com/iffy/boom/tree/tx-single-player">tx-single-player branch on GitHub</a>. Unlike the snippets shown here, the code there is fully tested and has class and method documentation. Take a look at <a href="https://github.com/iffy/boom/blob/tx-single-player/boom/game.py">game.py</a></p><h2>Next time</h2><p>Next time, I'll likely highlight some asynchronous testing tools, then move on to making the game multiplayer and playable (not having to press enter every move).</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-34440997428912357602012-06-12T07:00:00.000-07:002012-06-12T07:00:08.036-07:00Bite-sized Twisted: Deferred ErrorsIn this post, I'll talk about the <code>Deferred</code> callback chain, including error handling.<h2>Exceptions</h2><p>Boring, run-of-the-mill <code>Exception</code>:</p><div class="code python"><pre><span class="k">def</span> <span class="nf">mouth</span><span class="p">(</span><span class="n">food</span><span class="p">):</span><br /> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s">"Don't eat </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="n">food</span><span class="p">)</span><br /><span class="n">mouth</span><span class="p">(</span><span class="s">'cannon balls'</span><span class="p">)</span><br /></pre></div><div class="caption filename">err1.py</div><div class="code text"><pre>Traceback (most recent call last):<br /> File "err1.py", line 3, in <module><br /> mouth('cannon balls')<br /> File "err1.py", line 2, in mouth<br /> raise Exception("Don't eat %s" % food)<br />Exception: Don't eat cannon balls<br /></pre></div><div class="caption shell">python err1.py</div><p><code>Exception</code> raised inside a callback:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">def</span> <span class="nf">mouth</span><span class="p">(</span><span class="n">food</span><span class="p">):</span><br /> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s">"Don't eat </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="n">food</span><span class="p">)</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">mouth</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'cannon balls'</span><span class="p">)</span><br /></pre></div><div class="caption filename">err2.py</div><div class="code text"><pre>Unhandled error in Deferred:<br />Unhandled Error<br />Traceback (most recent call last):<br /> File "err2.py", line 8, in <module><br /> d.callback('cannon balls')<br /> File "/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py", line 361, in callback<br /> self._startRunCallbacks(result)<br /> File "/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py", line 455, in _startRunCallbacks<br /> self._runCallbacks()<br />--- <exception caught here> ---<br /> File "/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py", line 542, in _runCallbacks<br /> current.result = callback(current.result, *args, **kw)<br /> File "err2.py", line 4, in mouth<br /> raise Exception("Don't eat %s" % food)<br />exceptions.Exception: Don't eat cannon balls<br /></pre></div><div class="caption shell">python err2.py</div><p>It complains of an <i>Unhandled error in Deferred</i>. To handle it, an error callback (<i>errback</i> for short) is needed.</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">def</span> <span class="nf">mouth</span><span class="p">(</span><span class="n">food</span><span class="p">):</span><br /> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s">"Don't eat </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="n">food</span><span class="p">)</span><br /><br /><span class="k">def</span> <span class="nf">badfood</span><span class="p">(</span><span class="n">food</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">"Bad food happened, but I'm okay"</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">mouth</span><span class="p">)</span><br /><span class="hll"><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">badfood</span><span class="p">)</span><br /></span><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'cannon balls'</span><span class="p">)</span><br /></pre></div><div class="caption filename">err3.py</div><div class="code text"><pre>Bad food happened, but I'm okay<br /></pre></div><div class="caption shell">python err3.py</div><p>Errbacks are only called to handle errors. So, in this case, the errback is not called since there's no error:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="hll"><span class="k">def</span> <span class="nf">mouth</span><span class="p">(</span><span class="n">food</span><span class="p">):</span><br /></span><span class="hll"> <span class="k">print</span> <span class="s">"I ate </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="n">food</span><br /></span><br /><span class="k">def</span> <span class="nf">badfood</span><span class="p">(</span><span class="n">food</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">"Bad food happened, but I'm okay"</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">mouth</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">badfood</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'piano keys'</span><span class="p">)</span><br /></pre></div><div class="caption filename">err4.py</div><div class="code text"><pre>I ate piano keys<br /></pre></div><div class="caption shell">python err4.py</div><h2>Garden of forking paths</h2><p>Callbacks will always result in either:<ol> <li>a value or</li> <li>an error</li></ol>If a value is returned, the next callback in line will get the value. If an error happens, the next errback in line will get the error.</p><p><b>Errbacks are the same</b> -- they will always result in either a value or an error. And the result will be handled either by the next callback or the next errback. This snippet illustrates that:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">msg</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'value </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">msg</span><br /> <span class="k">return</span> <span class="s">'value'</span><br /><br /><span class="k">def</span> <span class="nf">error</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">msg</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'error </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">msg</span><br /> <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">'callback level 1'</span><span class="p">)</span><br /><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">'errback level 2'</span><span class="p">)</span><br /><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">error</span><span class="p">,</span> <span class="s">'callback level 3'</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">'callback level 4'</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">error</span><span class="p">,</span> <span class="s">'callback level 5'</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">'callback level 6'</span><span class="p">)</span><br /><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">'errback level 7'</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">error</span><span class="p">,</span> <span class="s">'errback level 8'</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">'errback level 9'</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">error</span><span class="p">,</span> <span class="s">'errback level 10'</span><span class="p">)</span><br /><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">'callback level 11'</span><span class="p">)</span><br /><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'begin'</span><span class="p">)</span><br /></pre></div><div class="caption filename">twopaths1.py</div><div class="code text"><pre>value callback level 1<br />error callback level 3<br />value errback level 7<br />value callback level 11<br /></pre></div><div class="caption shell">python twopaths1.py</div><p>What happened?<ol> <li>The <code>Deferred</code> is called back with <code>'begin'</code>.</li> <li>The first callback is called with <code>'begin'</code> and returns <code>'value'</code>.</li> <li>Since there is no error, the errback on level 2 is skipped and the next callback (level 3) is called.</li> <li>The callback on level 3 ends in error, so all the callbacks are skipped until the next errback (level 7) is found.</li> <li>The errback on level 7 returns <code>'value'</code>, so all the errbacks are skipped until the next callback (level 11) is found.</li></ol>Here's a diagram of what happened. Blue squares are callbacks. Red squares are errbacks. Time flows to the right:</p><svg width="500" height="100" style="display: block; margin-left: auto; margin-right: auto;"> <line y1="35" x1="55" y2="35" x2="135" style="stroke: steelblue; stroke-width: 3;"/> <line y1="35" x1="135" y2="75" x2="175" style="stroke: red; stroke-width: 3;"/> <line y1="75" x1="175" y2="75" x2="295" style="stroke: red; stroke-width: 3;"/> <line y1="75" x1="295" y2="35" x2="335" style="stroke: steelblue; stroke-width: 3;" /> <line y1="35" x1="335" y2="35" x2="455" style="stroke: steelblue; stroke-width: 3;" /> <rect y="20" x="40" width="30" height="30" style="stroke: none; opacity: 1.0; fill:steelblue;" /> <text y="15" x="50" style="fill: #000;">1</text> <rect y="60" x="80" width="30" height="30" style="stroke: none; opacity: 0.5; fill:red;" /> <text y="15" x="90" style="fill: #000;">2</text> <rect y="20" x="120" width="30" height="30" style="stroke: none; opacity: 1.0; fill:steelblue;" /> <text y="15" x="130" style="fill: #000;">3</text> <rect y="20" x="160" width="30" height="30" style="stroke: none; opacity: 0.5; fill:steelblue;" /> <text y="15" x="170" style="fill: #000;">4</text> <rect y="20" x="200" width="30" height="30" style="stroke: none; opacity: 0.5; fill:steelblue;" /> <text y="15" x="210" style="fill: #000;">5</text> <rect y="20" x="240" width="30" height="30" style="stroke: none; opacity: 0.5; fill:steelblue;" /> <text y="15" x="250" style="fill: #000;">6</text> <rect y="60" x="280" width="30" height="30" style="stroke: none; opacity: 1.0; fill:red;" /> <text y="15" x="290" style="fill: #000;">7</text> <rect y="60" x="320" width="30" height="30" style="stroke: none; opacity: 0.5; fill:red;" /> <text y="15" x="330" style="fill: #000;">8</text> <rect y="60" x="360" width="30" height="30" style="stroke: none; opacity: 0.5; fill:red;" /> <text y="15" x="370" style="fill: #000;">9</text> <rect y="60" x="400" width="30" height="30" style="stroke: none; opacity: 0.5; fill:red;" /> <text y="15" x="410" style="fill: #000;">10</text> <rect y="20" x="440" width="30" height="30" style="stroke: none; opacity: 1.0; fill:steelblue;" /> <text y="15" x="450" style="fill: #000;">11</text> </svg><h2>Trapping specific exceptions</h2><p>Error handlers can be made to only deal with specific exceptions using the <code>trap()</code> method of the passed-in error (the first argument). If the actual error doesn't match the kind being trapped, the next errback in line will be called with the error.</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">def</span> <span class="nf">add3</span><span class="p">(</span><span class="n">result</span><span class="p">):</span><br /> <span class="k">return</span> <span class="mi">3</span> <span class="o">+</span> <span class="n">result</span><br /><br /><span class="k">def</span> <span class="nf">catchAttributeError</span><span class="p">(</span><span class="n">error</span><span class="p">):</span><br /> <span class="n">error</span><span class="o">.</span><span class="n">trap</span><span class="p">(</span><span class="ne">AttributeError</span><span class="p">)</span><br /> <span class="k">print</span> <span class="s">'AttributeError happened'</span><br /> <span class="k">return</span> <span class="s">'Bad attribute'</span><br /> <br /><span class="k">def</span> <span class="nf">catchTypeError</span><span class="p">(</span><span class="n">error</span><span class="p">):</span><br /> <span class="n">error</span><span class="o">.</span><span class="n">trap</span><span class="p">(</span><span class="ne">TypeError</span><span class="p">)</span><br /> <span class="k">print</span> <span class="s">'TypeError happened'</span><br /> <span class="k">return</span> <span class="s">'Bad type of value'</span><br /><br /><span class="k">def</span> <span class="nf">printResult</span><span class="p">(</span><span class="n">result</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'Result: </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">result</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">add3</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">catchAttributeError</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">catchTypeError</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">printResult</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'foo'</span><span class="p">)</span><br /></pre></div><div class="caption filename">trap1.py</div><div class="code text"><pre>TypeError happened<br />Result: Bad type of value<br /></pre></div><div class="caption shell">python trap1.py</div><p>Without the <code>.trap()</code> calls in <i>trap1.py</i>, <code>catchAttributeError</code> would have handled the error instead of <code>catchTypeError</code>. Also, notice that <code>catchAttributeError</code> is called first, but since it's only trapping <code>AttributeErrors</code>, the error is passed to the next errback, <code>catchTypeError</code>.</p><p>Why can you call <code>.trap()</code> on the thing passed to errbacks? Short answer: it's a <a href="http://twistedmatrix.com/documents/current/api/twisted.python.failure.Failure.html"><code>Failure</code> object</a>. Long answer: see the "Read more" section below.</p><h2>Read more</h2><p>Believe it or not, there's still more you can do with <code>Deferreds</code>. As features are needed for the Bomberman clone, I'll introduce them. In the meantime, read <a href="http://twistedmatrix.com/documents/current/core/howto/defer.html">the very thorough, official Deferred Reference</a> if you're interested.</p><h2>Next time: Bombs away!</h2><p>Using the things I've posted about so far, I plan for the next Bite-sized Twisted post to include the code for a playable version of <code>boom</code>. Lest you expect more than I'm planning: it will be single player, may or may not display a textual representation of the board, and will require pressing enter after every move. Future posts will improve the game further.</p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4293648069803317711.post-63531391314366758342012-06-08T09:00:00.000-07:002012-06-08T09:00:04.020-07:00Bite-sized Twisted: DeferredsIn this post, we'll examine the <code>Deferred</code>.<h2>The check's in the mail</h2><p>A <code>Deferred</code> is kind of like an IOU. It's a promise. It's a forecast of things to come. A forerunner of sorts. A title bearer or trumpeteer.</p><p>Enough analogies: it's a class you can instantiate. Like this:</p><div class="code pycon"><pre><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><span class="gp">>>> </span><span class="n">iou</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /></pre></div><p>If you are given a <code>Deferred</code>, use <code>addCallback</code> to register a function to handle the value when it arrives.</p><div class="code pycon"><pre><span class="gp">>>> </span><span class="k">def</span> <span class="nf">spend</span><span class="p">(</span><span class="n">value</span><span class="p">):</span><br /><span class="gp">... </span> <span class="k">print</span> <span class="n">value</span> <span class="o">+</span> <span class="s">' spent!'</span><br /><span class="gp">... </span><br /><span class="gp">>>> </span><span class="n">iou</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">spend</span><span class="p">)</span><br /><span class="go"><Deferred at 0xb764bcccL></span><br /></pre></div><p>If you are the one giving out the <code>Deferred</code> and are responsible for fulfilling the promise, use <code>callback</code> to send the value to the registered callback functions:</p><div class="code pycon"><pre><span class="gp">>>> </span><span class="n">iou</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'$20.00'</span><span class="p">)</span><br /><span class="go">$20.00 spent!</span><br /></pre></div><h2>Callbacks can be chained</h2><p>Multiple callbacks can be added to a <code>Deferred</code>. Each callback <b>is not</b> called with the original value. Each callback is called with the return value of the prior callback. For example:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">def</span> <span class="nf">mouth</span><span class="p">(</span><span class="n">what</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'I am the mouth, I got '</span> <span class="o">+</span> <span class="n">what</span><br /> <span class="k">return</span> <span class="s">'chewed up '</span> <span class="o">+</span> <span class="n">what</span><br /><br /><span class="k">def</span> <span class="nf">stomach</span><span class="p">(</span><span class="n">what</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'I am the stomach, I got '</span> <span class="o">+</span> <span class="n">what</span><br /> <br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">mouth</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">stomach</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'jelly beans'</span><span class="p">)</span><br /></pre></div><div class="caption filename">deferred1.py</div><div class="code text"><pre>I am the mouth, I got jelly beans<br />I am the stomach, I got chewed up jelly beans<br /></pre></div><div class="caption shell">python deferred1.py</div><h2>Extra arguments</h2><p>If you want to pass additional arguments to a callback, add them in <code>addCallback</code>:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">def</span> <span class="nf">callback</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="n">keyword_arg</span><span class="o">=</span><span class="s">'foo'</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'value: </span><span class="si">%r</span><span class="s">'</span> <span class="o">%</span> <span class="n">value</span><br /> <span class="k">print</span> <span class="s">'arg: </span><span class="si">%r</span><span class="s">'</span> <span class="o">%</span> <span class="n">arg</span><br /> <span class="k">print</span> <span class="s">'keyword_arg: </span><span class="si">%r</span><span class="s">'</span> <span class="o">%</span> <span class="n">keyword_arg</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">callback</span><span class="p">,</span> <span class="s">'apple'</span><span class="p">,</span> <span class="n">keyword_arg</span><span class="o">=</span><span class="s">'banana'</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">'gorilla'</span><span class="p">)</span><br /></pre></div><div class="caption filename">deferred2.py</div><div class="code text"><pre>value: 'gorilla'<br />arg: 'apple'<br />keyword_arg: 'banana'<br /></pre></div><div class="caption shell">python deferred2.py</div><h2>Read the source</h2><p>A <code>Deferred</code> works approximately like this <code>PoorMansDeferred</code>:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">PoorMansDeferred</span><span class="p">:</span><br /><br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">callbacks</span> <span class="o">=</span> <span class="p">[]</span><br /><br /> <span class="k">def</span> <span class="nf">addCallback</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">function</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">callbacks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">function</span><span class="p">)</span><br /><br /> <span class="k">def</span> <span class="nf">callback</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span><br /> <span class="k">for</span> <span class="n">callback</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">callbacks</span><span class="p">:</span><br /> <span class="n">value</span> <span class="o">=</span> <span class="n">callback</span><span class="p">(</span><span class="n">value</span><span class="p">)</span><br /><br /><br /><span class="k">def</span> <span class="nf">government</span><span class="p">(</span><span class="n">value</span><span class="p">):</span><br /> <span class="n">portion</span> <span class="o">=</span> <span class="n">value</span> <span class="o">/</span> <span class="mi">2</span><br /> <span class="k">print</span> <span class="s">'Government got </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">portion</span><br /> <span class="k">return</span> <span class="n">value</span> <span class="o">-</span> <span class="n">portion</span><br /><br /><span class="k">def</span> <span class="nf">vinny</span><span class="p">(</span><span class="n">value</span><span class="p">):</span><br /> <span class="n">portion</span> <span class="o">=</span> <span class="n">value</span> <span class="o">/</span> <span class="mi">2</span><br /> <span class="k">print</span> <span class="s">'Vinny got </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">portion</span><br /> <span class="k">return</span> <span class="n">value</span> <span class="o">-</span> <span class="n">portion</span><br /><br /><span class="k">def</span> <span class="nf">me</span><span class="p">(</span><span class="n">value</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'I got </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">value</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">PoorMansDeferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">government</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">vinny</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">me</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span><br /></pre></div><div class="caption filename">not_a_great_deferred.py</div><div class="code text"><pre>Government got 15<br />Vinny got 7<br />I got 8<br /></pre></div><div class="caption shell">python not_a_great_deferred.py</div><p>Vinny should study up on integer division. You should probably study up on self defense.</p><p>The real <code>Deferred</code> implementation is not that long and worth reading. It includes error handling, chaining, extra arguments, canceling, etc... <a href="http://twistedmatrix.com/trac/browser/tags/releases/twisted-12.0.0/twisted/internet/defer.py#L181">Read the source here</a>.</p><h2>There's no reactor</h2><p>Please notice a few things about the above examples:<ol> <li>The <code>reactor</code> was not involved</li> <li>There was no <code>reactor.run()</code> call</li> <li><code>reactor</code> wasn't used</li> <li>A <code>Deferred</code> does not need the <code>reactor</code></li></ol></p><p>In short, a <code>Deferred</code> does not depend on the <code>reactor</code>. It is made more useful by the <code>reactor</code> but it works independently from it.</p><p>Many of Twisted's APIs either return or work with <code>Deferreds</code>. It is time well-spent learning how they work.</p><h2>Defer explosions!</h2><p>Though a <code>Deferred</code> does not require the <code>reactor</code>, it can be used with it. Let's add an <code>ignite</code> method to our <code>Bomb</code> that returns a <code>Deferred</code>:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">class</span> <span class="nc">Bomb</span><span class="p">:</span><br /> <br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fuse</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fuse</span> <span class="o">=</span> <span class="n">fuse</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">size</span><br /><br /> <span class="k">def</span> <span class="nf">ignite</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fuse</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><br /> <span class="k">return</span> <span class="n">d</span><br /></pre></div><div class="caption filename">boom/game.py</div><p>Then we can modify the <code>Protocol</code> we wrote a few posts ago to use the <code>Deferred</code> code:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Protocol</span><br /><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><span class="p">,</span> <span class="n">stdio</span><br /><br /><span class="kn">from</span> <span class="nn">boom.game</span> <span class="kn">import</span> <span class="n">Bomb</span><br /><br /><span class="k">def</span> <span class="nf">explode</span><span class="p">(</span><span class="n">bomb</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'BOOM! (magnitude </span><span class="si">%s</span><span class="s">)'</span> <span class="o">%</span> <span class="n">bomb</span><span class="o">.</span><span class="n">size</span><br /><br /><span class="k">class</span> <span class="nc">BombProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /> <span class="n">fuse</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><br /> <span class="n">bomb</span> <span class="o">=</span> <span class="n">Bomb</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="n">fuse</span><span class="p">)</span><br /> <span class="n">bomb</span><span class="o">.</span><span class="n">ignite</span><span class="p">()</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">explode</span><span class="p">)</span><br /><br /><span class="n">stdio</span><span class="o">.</span><span class="n">StandardIO</span><span class="p">(</span><span class="n">BombProtocol</span><span class="p">())</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption filename">async_input3.py</div><div class="code text"><pre>10<br />3<br />5<br />4<br />2<br />5<br />1<br />BOOM! (magnitude 3)<br />BOOM! (magnitude 1)<br />BOOM! (magnitude 2)<br />BOOM! (magnitude 4)<br />BOOM! (magnitude 5)<br />BOOM! (magnitude 5)<br />BOOM! (magnitude 10)<br /></pre></div><div class="caption shell">python async_input3.py</div><h2>Errors next time</h2><p>Error handling deserves a post of its own. In fact, that's probably what the next post will be about. Until then, chew on this:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.defer</span> <span class="kn">import</span> <span class="n">Deferred</span><br /><br /><span class="k">def</span> <span class="nf">toInfinity</span><span class="p">(</span><span class="n">value</span><span class="p">):</span><br /> <span class="k">return</span> <span class="n">value</span> <span class="o">/</span> <span class="mi">0</span><br /><br /><span class="k">def</span> <span class="nf">errorHandler</span><span class="p">(</span><span class="n">error</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'There was an error: </span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="n">error</span><span class="o">.</span><span class="n">value</span><br /><br /><span class="n">d</span> <span class="o">=</span> <span class="n">Deferred</span><span class="p">()</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">toInfinity</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">addErrback</span><span class="p">(</span><span class="n">errorHandler</span><span class="p">)</span><br /><span class="n">d</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="mi">293</span><span class="p">)</span><br /><br /><span class="k">print</span> <span class="s">"The error didn't kill the program"</span><br /></pre></div><div class="caption filename">errback1.py</div><div class="code text"><pre>There was an error: integer division or modulo by zero<br />The error didn't kill the program<br /></pre></div><div class="caption shell">python errback1.py</div><p>Also consider reading <a href="http://twistedmatrix.com/documents/current/core/howto/defer.html">Twisted's official, very thorough guide</a>.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-38836881718180613122012-06-05T09:00:00.000-07:002012-06-05T09:00:05.371-07:00Bite-sized Twisted: TestingIn this post, we'll explore a few of the neat testing features Twisted has to offer. And we'll starting testing the Bomberman clone. I think we'll name the program <code>boom</code>.<h2>Write tests the same way</h2><p>You can write tests using <code>unittest</code> from the Python standard library. Read more about that <a href="http://docs.python.org/library/unittest.html" target="_blank">here</a>.</p><p>Let's make some bomb tests. I'm going to lay out the directory structure like this: </p><div class="code text"><pre>boom/<br />boom/__init__.py<br />boom/test<br />boom/test/__init__.py<br />boom/test/test_game.py<br /></pre></div><p>We'll make a really simple test for creating a <code>Bomb</code>:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">unittest</span> <span class="kn">import</span> <span class="n">TestCase</span><br /><br /><span class="kn">from</span> <span class="nn">boom.game</span> <span class="kn">import</span> <span class="n">Bomb</span><br /><br /><span class="k">class</span> <span class="nc">BombTest</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">test_init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="n">bomb</span> <span class="o">=</span> <span class="n">Bomb</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">bomb</span><span class="o">.</span><span class="n">fuse</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">bomb</span><span class="o">.</span><span class="n">size</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span><br /></pre></div><div class="caption filename">boom/test/test_game.py</div><h2>Trial</h2><p>Twisted comes with an executable for running tests named <code>trial</code>. You can tell it to run tests found in files:</p><div class="code text"><pre>trial boom/test/test_game.py<br /></pre></div> <p>Or tests found in modules/packages.</p><div class="code text"><pre>trial boom.test.test_game<br /></pre></div><p>Running either of the above will result in something like this:</p><div class="code text"><pre>boom<br /> test<br /> test_game ... [ERROR]<br /><br />===============================================================================<br />[ERROR]<br />Traceback (most recent call last):<br /> File "/usr/local/lib/python2.7/dist-packages/twisted/trial/runner.py", line 677, in loadByNames<br /> things.append(self.findByName(name))<br /> File "/usr/local/lib/python2.7/dist-packages/twisted/trial/runner.py", line 487, in findByName<br /> return reflect.namedAny(name)<br /> File "/usr/local/lib/python2.7/dist-packages/twisted/python/reflect.py", line 464, in namedAny<br /> topLevelPackage = _importAndCheckStack(trialname)<br /> File "/home/matt/iffycan.git/tx-bite-3-testing/boom/test/test_game.py", line 3, in <module><br /> from boom.game import Bomb<br />exceptions.ImportError: No module named game<br /><br />boom.test.test_game<br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.071s<br /><br />FAILED (errors=1)<br /></pre></div><p>Let's fix the test. This post isn't about testing as much as the tools Twisted provides, so I'm going to skip the iterations I went through to arrive at this file:</p><div class="code python"><pre><span class="k">class</span> <span class="nc">Bomb</span><span class="p">:</span><br /> <br /> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fuse</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">fuse</span> <span class="o">=</span> <span class="n">fuse</span><br /> <span class="bp">self</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">size</span><br /></pre></div><div class="caption filename">boom/game.py</div><p>And now the tests pass. Yay!</p><div class="code text"><pre>boom.test.test_game<br /> BombTest<br /> test_init ... [OK]<br /><br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.056s<br /><br />PASSED (successes=1)<br /></pre></div><div class="caption shell">trial boom.test.test_game</div><h2>Trial finds tests</h2><p>As our program grows, it will be tedious to test if we have to type out the filename or module name for each of our test files. <code>trial</code> alleviates this by searching for things that look like tests. Generally, files with the word "test" in them are considered tests. We can run all of the <code>boom</code> tests just by specifying the root package (e.g <code>trial boom</code>):</p><div class="code text"><pre>boom.test.test_game<br /> BombTest<br /> test_init ... [OK]<br /><br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.058s<br /><br />PASSED (successes=1)<br /></pre></div><div class="caption shell">trial boom</div><h2>Temporary files are easy</h2><p>Twisted includes a subclass of <code>unittest.TestCase</code> which provides some very nice features. For instance, making temporary files and directories is easy using <code>self.mktemp()</code>.</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.trial.unittest</span> <span class="kn">import</span> <span class="n">TestCase</span><br /><br /><span class="k">class</span> <span class="nc">MkTempTest</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">test_writeable</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="n">filename</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">mktemp</span><span class="p">()</span><br /> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'foo'</span><span class="p">)</span><br /></pre></div><div class="caption filename">test_mktemp.py</div><h2>You can skip irrelevant tests</h2><p>Sometimes you only want to run certain tests under certain conditions. Perhaps your code has optional features, or perhaps some tests only apply to certain operating systems. Twisted provides ways of skipping tests.</p><p>Skip entire <code>TestCase</code>s by setting a <code>skip</code> attribute:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.trial.unittest</span> <span class="kn">import</span> <span class="n">TestCase</span><br /><br /><span class="k">class</span> <span class="nc">SkipTest1</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span><br /><br /> <span class="n">skip</span> <span class="o">=</span> <span class="s">"Don't run these tests. They don't do anything"</span><br /> <br /> <span class="k">def</span> <span class="nf">test_nothing</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">pass</span><br /></pre></div><div class="caption filename">test_skip1.py</div><div class="code text"><pre>test_skip1<br /> SkipTest1<br /> test_nothing ... [SKIPPED]<br /><br />===============================================================================<br />[SKIPPED]<br />Don't run these tests. They don't do anything<br /><br />test_skip1.SkipTest1.test_nothing<br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.002s<br /><br />PASSED (skips=1)<br /></pre></div><div class="caption shell">trial test_skip1.py</div><p>Skip individual tests by raising <code>twisted.trial.unittest.SkipTest</code>:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.trial.unittest</span> <span class="kn">import</span> <span class="n">TestCase</span><span class="p">,</span> <span class="n">SkipTest</span><br /><br /><span class="k">class</span> <span class="nc">SkipTest2</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span><br /> <br /> <span class="k">def</span> <span class="nf">test_nothing</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><br /> <span class="k">raise</span> <span class="n">SkipTest</span><span class="p">(</span><span class="s">"Waste of time"</span><span class="p">)</span><br /></pre></div><div class="caption filename">test_skip2.py</div><div class="code text"><pre>test_skip2<br /> SkipTest2<br /> test_nothing ... [SKIPPED]<br /><br />===============================================================================<br />[SKIPPED]<br />Waste of time<br /><br />test_skip2.SkipTest2.test_nothing<br />-------------------------------------------------------------------------------<br />Ran 1 tests in 0.028s<br /><br />PASSED (skips=1)<br /></pre></div><div class="caption shell">trial test_skip2.py</div><h2>Test runs are logged and saved</h2><p>When you run <code>trial</code>, it creates or recreates a special directory named <code>_trial_temp</code>. Inside, you'll find <code>test.log</code> (a log of the test run). And you can inspect any files created with <code>self.mktemp()</code>.</p><p>For instance, here's the <code>test.log</code> from running <code>trial test_mktemp.py</code>:</p><div class="code text"><pre>2012-06-02 17:20:27-0600 [-] Log opened.<br />2012-06-02 17:20:27-0600 [-] --> test_mktemp.MkTempTest.test_writeable <--<br />2012-06-02 17:20:27-0600 [-] using set_wakeup_fd<br /></pre></div><p>And there's a temporary file with <code>foo</code> in it, created by the test:</p><div class="code text"><pre>$ cat _trial_temp/test_mktemp/MkTempTest/test_writeable/d31a0y/temp <br />foo<br /></pre></div><p>On this run, the file happened to end up in a directory named <code>d31a0y/</code>. This will vary from run to run and won't be the same when you run it.</p><h2>Keep reading</h2><p>I've highlighted just a few of my favorite <code>trial</code> features. All of the features described here do not involve the <code>reactor</code> and are a perfectly useable for testing synchronous code. A future post will discuss asynchronous testing features provided by Twisted. Read more about trial <a href="http://twistedmatrix.com/trac/wiki/TwistedTrial" target="_blank">here</a>.</p><p>I've created a <a href="https://github.com/iffy/boom" target="_blank">GitHub repo for this Bomberman clone</a>. The files used in this post are in the <a href="https://github.com/iffy/boom/tree/tx-testing-1" target="_blank">tx-testing-1 branch</a>.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-72546996065716708402012-06-02T08:00:00.000-07:002012-06-02T10:26:41.296-07:00Bite-sized Twisted: Bytes and Standard IOIn the <a href="http://iffycan.blogspot.com/2012/06/bite-sized-twisted-introduction-and.html">last Bite-sized Twisted post</a> I introduced <code>reactor</code> and <code>reactor.callLater</code>, and we made some bombs. In this post, we'll accept user input for lighting bombs.<h2>Input/Output == Bytes</h2><p>For our game to work, we're going to need to accept user input. By the word <i>input</i>, I actually mean <i>bytes</i>. The user could be using a keyboard or joystick attached to the computer on which the program is running. They might use a browser to play our game. Or they could be tethered to an orbiting satellite, texting with their cellphone. Unless they have no space suit. Regardless, our program will get bytes.</p><p>Synchronously, we can get bytes from the keyboard using <code>raw_input</code> as in this example:</p><div class="code python"><pre><span class="kn">import</span> <span class="nn">time</span><br /><span class="n">fuse</span> <span class="o">=</span> <span class="nb">raw_input</span><span class="p">()</span><br /><span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">fuse</span><span class="p">))</span><br /><span class="k">print</span> <span class="s">'BOOM!'</span><br /></pre></div><div class="caption">raw_input1.py</div><div class="code text"><pre>3<br />BOOM!<br /></pre></div><p>When I typed <code>3</code> and pressed enter, the program received bytes for the ASCII string <code>'3'</code> and according to <i>raw_input1.py</i>,<blockquote>input bytes are converted to an integer, the program waits that number of seconds then writes bytes for the ASCII string <code>'BOOM!'</code> to stdout.</blockquote>This is <i>raw_input1.py</i>'s <b>protocol</b>.</p><p>Let's break out the protocol into a reusable function named <code>dataReceived</code>:</p><div class="code python"><pre><span class="kn">import</span> <span class="nn">time</span><br /><br /><span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="n">data</span><span class="p">):</span><br /> <span class="n">fuse</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><br /> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">fuse</span><span class="p">)</span><br /> <span class="k">print</span> <span class="s">'BOOM!'</span><br /> <br /><span class="n">input_bytes</span> <span class="o">=</span> <span class="nb">raw_input</span><span class="p">(</span><span class="p">)</span><br /><span class="n">dataReceived</span><span class="p">(</span><span class="n">input_bytes</span><span class="p">)</span><br /></pre></div><div class="caption">raw_input2.py</div><p>This program behaves identically to <i>raw_input1.py</i>. Having split the protocol into a self-contained function, however, we can use it on bytes that come from <code>raw_input</code>, a file or some other source.</p><h2>Asynchronous Input/Output == Bytes</h2><p>The program in <i>raw_input2.py</i> is limited to one lit bomb at a time. Bomberman with only one bomb shared among all players wouldn't be fun. By using Twisted, we can accept input asynchronously:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Protocol</span><br /><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><span class="p">,</span> <span class="n">stdio</span><br /><span class="kn">import</span> <span class="nn">time</span><br /><br /><span class="k">class</span> <span class="nc">BombProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /> <span class="n">fuse</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><br /> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">fuse</span><span class="p">)</span><br /> <span class="k">print</span> <span class="s">'BOOM!'</span><br /><br /><span class="n">stdio</span><span class="o">.</span><span class="n">StandardIO</span><span class="p">(</span><span class="n">BombProtocol</span><span class="p">())</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption">async_input1.py</div><p>The original <code>dataReceived</code> function was made into a method in a <code>Protocol</code> class. The <code>Protocol</code> class has methods that other Twisted components expect a protocol to have. One such method is <code>dataReceived</code>. Because it implements an expected API, we can use it with other Twisted components. In this case, <code>stdio.StandardIO</code> calls the <code>BombProtocol()</code> instance's <code>dataReceived</code> method with bytes from stdin.</p><p>When you run <i>async_input1.py</i> you may notice that there's still only one bomb lit at a time. See it by spamming <code>3</code> (alternating with enter) as fast as you can. Each <code>BOOM!</code> happens 3 seconds after the previous one.</p><div class="code text"><pre>3<br />3<br />3<br />BOOM!<br />BOOM!<br />BOOM!<br /></pre></div><p>The problem is the use of synchronous <code>time.sleep</code> instead of asynchronous <code>reactor.callLater</code>.</p><p><b>This is important:</b> Even though Twisted provides an asynchronous interface, synchronous code will still take all the time it needs, blocking other things from happening. Twisted is single-threaded. A more asynchronous version looks like this:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet.protocol</span> <span class="kn">import</span> <span class="n">Protocol</span><br /><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><span class="p">,</span> <span class="n">stdio</span><br /><br /><span class="k">def</span> <span class="nf">explode</span><span class="p">():</span><br /> <span class="k">print</span> <span class="s">'BOOM!'</span><br /><br /><span class="k">class</span> <span class="nc">BombProtocol</span><span class="p">(</span><span class="n">Protocol</span><span class="p">):</span><br /><br /> <span class="k">def</span> <span class="nf">dataReceived</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span><br /> <span class="n">fuse</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="n">fuse</span><span class="p">,</span> <span class="n">explode</span><span class="p">)</span><br /><br /><span class="n">stdio</span><span class="o">.</span><span class="n">StandardIO</span><span class="p">(</span><span class="n">BombProtocol</span><span class="p">())</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption">async_input2.py</div><p>This code lets you start as many bombs as you want, whenever you want. And bomb fuses are lit immediately after you press enter, instead of waiting for the previous bomb to blow up.</p><p>Notice that an <i>instance</i> of <code>BombProtocol</code> is passed in to <code>stdio.StandardIO</code>, rather than the class. This will become more important when there is more than one source of input. Each source of input (keyboard, mouse, joystick, orbiting texter) will interact with our code by going through their own instance of a <code>Protocol</code>.</p><h2>Next time: Testing?</h2><p>Stay tuned for the next post, which will likely be about how Twisted makes testing easier (unless something else seems more appropriate).</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4293648069803317711.post-8510310030863286012012-06-01T08:15:00.000-07:002012-06-01T08:20:27.162-07:00Bite-sized Twisted: Introduction and the Reactor<h2>Why I'm writing this</h2><p><a href="http://twistedmatrix.com/">Twisted</a> has enabled me to do some amazing things -- things I hadn't thought possible. Others haven't had the same delightful experience. Their descriptions of Twisted include less glowing adjectives. My intent with this and related posts is to ameliorate some of those adjectives.</p><p>I'm borrowing conceptually from JP Calderone's excellent <a href="http://twistedmatrix.com/documents/current/web/howto/web-in-60/index.html">Twisted Web in 60 Seconds</a> series by keeping these posts relatively short. Also, there's <a href="http://krondo.com/?page_id=1327">KRONDO blog's Twisted Introduction</a>, another great series for learning Twisted (and general Asynchronous Programming).</p><h2>Bomberman</h2><p>Bomberman is great! So, let's make it. By the end (or maybe somewhere in the middle) of these Bite-sized Twisted posts, I'm hoping that some semblance of a Bomberman clone emerges. That's the goal, at least.</p><p>Also, am I the only one who thought Super Smash Bros should have included Bomberman? And MegaMan?</p><h2>The Reactor</h2><p>The reactor is central to Twisted. You may not always interact with it directly, but it'll almost always be there, reacting away. To start the reactor, call its <code>run</code> method:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption">snippet1.py</div><p>Run this code and you'll see it hang:</p><div class="code bash"><pre><br /></pre></div><h2>Nothing != Something</h2><p>You might be tempted to say, "nothing happened." Don't say that. <i>Something</i> happened; in fact it's still happening, and will continue to happen until you stop it (press <code>Control-C</code>). The <i>something</i> happening is the reactor running. To prove it, run this code:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="k">print</span> <span class="s">'Hello,'</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /><span class="k">print</span> <span class="s">'world!'</span><br /></pre></div><div class="caption">snippet2.py</div><p>You should see the <code>Hello,</code> but won't see the <code>world!</code> until you stop the reactor:</p><div class="code text"><pre>Hello,<br />^Cworld!<br /></pre></div><p>(The <code>^C</code> is what my terminal prints when I press <code>Control-C</code>)</p><h2>What does the reactor do?</h2><p>The reactor reacts to events. One kind of event it can react to is the passage of time (scheduled events):</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption">snippet3.py</div><p><i>snippet3.py</i> tells the reactor to call <code>reactor.stop</code> after 2 seconds, then it starts the reactor with <code>reactor.run()</code>. After 2 seconds, <code>reactor.stop</code> is called, stopping the reactor, and the process exits.</p><p>The first argument to <code>reactor.callLater</code> is the number of seconds to wait before calling the function. The second argument is the function to call. It's important that it's <code>reactor.stop</code>, <b>not</b> <code>reactor.stop()</code>. <code>reactor.callLater</code> expects the reference to the function, not the result of the function. Passing function references is a common idiom in the Twisted API.</p><p>Also, you can <a href="http://twistedmatrix.com/documents/current/api/twisted.internet.base.ReactorBase.html#callLater">read the <code>callLater</code> docs for yourself.</a> (See twisted.internet.interfaces.IReactorTime.callLater as it says)</p><h2>Make a bomb</h2><p>If we're going to make Bomberman, we'll need bombs. Bombs have fuses. Let's light some fuses:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><br /><span class="k">def</span> <span class="nf">explode</span><span class="p">(</span><span class="n">message</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'BOOM! '</span> <span class="o">+</span> <span class="n">message</span><br /><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">explode</span><span class="p">,</span> <span class="s">'shakalaka'</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">explode</span><span class="p">,</span> <span class="s">'I say'</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption">bomb.py</div><p>Run it. After 1 second, the first bomb explodes. After 1 more second, the second explodes. After 1 more, the process exits:</p><div class="code text"><pre>BOOM! shakalaka<br />BOOM! I say<br /></pre></div><p>In <i>bomb.py</i>, note two things:</p><p>First, when scheduling <code>explode</code> we also pass a string (<code>'shakalaka'</code>) as an extra argument to <code>reactor.callLater</code>. This argument is eventually passed to <code>explode</code> as it's first argument. <code>callLater</code> takes any number of arguments and keyword arguments, which it will use to call the scheduled method.</p><p>Second, do the explosions happen when you expect them to? Consider this synchronous code, which has the same timed output as <i>bomb.py</i>:</p><div class="code python"><pre><span class="kn">import</span> <span class="nn">time</span><br /><br /><span class="k">def</span> <span class="nf">explode</span><span class="p">(</span><span class="n">message</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'BOOM! '</span> <span class="o">+</span> <span class="n">message</span><br /><br /><span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><br /><span class="n">explode</span><span class="p">(</span><span class="s">'shakalaka'</span><span class="p">)</span><br /><span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><br /><span class="n">explode</span><span class="p">(</span><span class="s">'I say'</span><span class="p">)</span><br /><span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><br /></pre></div><div class="caption">synchronous.py</div><p>In the synchronous code, since commands happen sequentially, you specify the timing offset from one command to the other. In the asynchronous code, scheduled commands are independent of each other: you specify the timing offset from the time that you schedule the command.</p><h2>The Twisted code does the same thing as the synchronous code... so why should I use Twisted?</h2><p>Though <i>synchronous.py</i> has the same output as <i>bomb.py</i>, <b>it does not do the same thing.</b> This will become more apparent when we start adding input/output to programs. Until then, consider how you would do this synchronously:</p><div class="code python"><pre><span class="kn">from</span> <span class="nn">twisted.internet</span> <span class="kn">import</span> <span class="n">reactor</span><br /><br /><span class="k">def</span> <span class="nf">explode</span><span class="p">(</span><span class="n">message</span><span class="p">):</span><br /> <span class="k">print</span> <span class="s">'BOOM! '</span> <span class="o">+</span> <span class="n">message</span><br /><br /><span class="k">def</span> <span class="nf">funkyBomb</span><span class="p">(</span><span class="n">message</span><span class="p">):</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mf">0.25</span><span class="p">,</span> <span class="n">explode</span><span class="p">,</span> <span class="n">message</span> <span class="o">+</span> <span class="s">' 1'</span><span class="p">)</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">explode</span><span class="p">,</span> <span class="n">message</span> <span class="o">+</span> <span class="s">' 2'</span><span class="p">)</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mf">0.9</span><span class="p">,</span> <span class="n">explode</span><span class="p">,</span> <span class="n">message</span> <span class="o">+</span> <span class="s">' 3'</span><span class="p">)</span><br /> <span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="n">explode</span><span class="p">,</span> <span class="n">message</span> <span class="o">+</span> <span class="s">' 4'</span><span class="p">)</span><br /><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">funkyBomb</span><span class="p">,</span> <span class="s">'shakalaka'</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="n">funkyBomb</span><span class="p">,</span> <span class="s">'booyah'</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">funkyBomb</span><span class="p">,</span> <span class="s">'I say'</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span><br /><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span><br /></pre></div><div class="caption">bomb2.py</div><div class="code text"><pre>BOOM! shakalaka 1<br />BOOM! shakalaka 2<br />BOOM! booyah 1<br />BOOM! shakalaka 3<br />BOOM! booyah 2<br />BOOM! I say 1<br />BOOM! booyah 3<br />BOOM! I say 2<br />BOOM! shakalaka 4<br />BOOM! I say 3<br />BOOM! booyah 4<br />BOOM! I say 4<br /></pre></div><p>What do you think of that?!</p>Unknownnoreply@blogger.com1