<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Blog</title>
  <link href="https://bennuttall.com/blog/" />
  <link href="https://bennuttall.com/blog/atom.xml" rel="self" />
  <id>https://bennuttall.com/blog/</id>
  <author>
    <name>Ben Nuttall</name>
  </author>
  <updated>2026-04-10T11:50:46+00:00</updated>

  <entry>
    <title><![CDATA[PyCon UK 2025]]></title>
    <link href="https://bennuttall.com/blog/2025/10/pyconuk-2025/" />
    <id>https://bennuttall.com/blog/2025/10/pyconuk-2025</id>
    <published>2025-10-03T01:01:55+00:00</published>
    <updated>2025-10-03T01:01:55+00:00</updated>
    <category term="python" />
    <category term="pyconuk" />
    <category term="manchester" />
    <category term="pydantic" />
    <category term="pycon" />
    <summary><![CDATA[Last week was PyCon UK, which took place in Manchester for the first time. I've attended the conference every year it's run since 2014, and each year I've given a talk. This year I decided not to submit a talk, instead trying my hand at running a workshop on "Data modelling with Pydantic". Luckily,...]]></summary>
    <content type="html"><![CDATA[<p>Last week was <a href="https://2025.pyconuk.org/">PyCon UK</a>, which took place in Manchester for the first
time. I've attended the conference every year it's run since 2014, and each year I've given a talk.
This year I decided not to submit a talk, instead trying my hand at running a workshop on "Data
modelling with Pydantic". Luckily, my proposal was accepted and over the last few weeks I've been
preparing for it. It ended up being a <em>lot</em> more effort preparing a 90 minute workshop than a 25
minute talk!</p>
<p>PyCon UK is a unique conference in many ways. One of the <em>many</em> inclusive initiatives it runs is a
"young coders" track on the Saturday, giving local kids and often the children of attendees the
chance to have a go at some programming activities. Workshops are operated on a proposal basis like
the main conference, so they're run by attendees.</p>
<p>The day before the conference started, Claire Wicher asked me to see if I could step in to run a
workshop on the kids track. I'm not very good at saying no - so I said I'd find something. I asked
around and my good friend Nicholas Tollervey was able to provide me with a brilliant worksheet using
Dan Pope's <a href="https://pygame-zero.readthedocs.io/en/stable/">pygame-zero</a> and the
<a href="https://github.com/mu-editor/mu">Mu editor</a> (unfortunately, now a retired project).</p>
<p>The evening before the first day, as attendees started arriving in town, a bunch of people ended up
in the <a href="https://share.google/Ma3XYO9ncyKDHdosk">Temple of Convenience</a>, an old public toilet on
Oxford Street, converted into a bar. We proceeded to move to
<a href="https://www.societymanchester.com/">Society</a> to get food, and I remembered that <a href="https://en.wikipedia.org/wiki/Eric_Idle">Eric
Idle</a> of <a href="https://en.wikipedia.org/wiki/Monty_Python">Monty
Python</a> was playing a show over the road from there at
<a href="https://www.bridgewater-hall.co.uk/">Bridgewater Hall</a>. I asked one of my new friends if he fancied
seeing if there were walk-in tickets available, and we ended up seeing the show, which was great
fun.</p>
<figure class="wp-block-image">
<img src="images/bridgewater-hall.jpg" />
</figure>

<figure class="wp-block-image">
<img src="images/eric-idle.jpg" />
</figure>

<p>The conference opened with the usual introduction, which included Becky Smith unveiling the new
PyCon UK logo, based on the <a href="https://en.wikipedia.org/wiki/Symbols_of_Manchester#Worker_bee">Manchester worker
bee</a> representing the hard work done
by volunteers to make the conference happen:</p>
<figure class="wp-block-image">
<img src="images/pyconuk-manchester-logo.jpg" />
</figure>

<p>The opening keynote was a brilliant commentary on the history, growth and development of Python's
rich and complex ecosystem since the launch of Python 3:</p>
<figure>
<iframe width="560" height="315" src="https://www.youtube.com/embed/gDvwRpl9erE?si=_wnWPcL4D5soxxG-" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</figure>

<figure class="wp-block-image">
<img src="images/hynek-python-is-fine.jpg" />
</figure>

<p>The second day included the young coders day in the workshop space. Here's a photo of me running the
workshop:</p>
<figure class="wp-block-image">
<img src="images/pgzero-workshop.jpg" />
<figcaption><a href="https://www.flickr.com/photos/203482025@N06/54799967120/in/album-72177720329149392">Photo</a> by Mark Hawkins</figcaption>
</figure>

<p>The pygame-zero workshop went really well. It was an ideal activity as there wasn't too much typing,
people could go at their own pace as it didn't rely on following what was on screen, and it was fun
to make a silly splat-a-cat game.</p>
<p>After the day's talks are over, there's a slot before the lightning talks where the kids get to
present on the main stage, such as projects they've worked on, and anything they want to share.
It's amazing how confident they can be on stage in front of so many people - showing the game
they've made with Python or telling everyone what they enjoyed or found difficult. They always
manage to make the audience smile, laugh and applaud like mad.</p>
<figure class="wp-block-image">
<img src="images/young-coders-day.jpg" />
<figcaption><a href="https://www.flickr.com/photos/203482025@N06/54800924813/in/album-72177720329149392">Photo</a> by Mark Hawkins</figcaption>
</figure>

<figure class="wp-block-image">
<img src="images/big-hands.jpg" />
<figcaption>In Big Hands with Eloisa and Ben</figcaption>
</figure>

<p>Day three, Sunday, was the day of my workshop. I've run countless workshops before, but mostly for
kids, teachers or complete beginners. This one was more targeted at peers. It was intended to be
suitable for less experienced Python users but relied on some basics, and dove deeper than a
standard beginner workshop would.</p>
<p>The workshop room was packed out, with all desk spaces filled and a couple of people joining from
the floor! The wifi provided by the venue was poor so I'd asked people intending to attend to clone
the repo ahead of time, and turn up early if they needed help getting set up. Some people didn't see
the message but we managed to get everyone sorted and started just about on time. There were a few
people with Windows laptops so I had to look up the virtualenv activation instructions for them. One
guy even turned up with an Android tablet. He seemed to know what he was doing, and had vscode and
Python running on there somehow, although it was a bit awkward tabbing between the editor and
instructions.</p>
<p>Rather than use slides, I'd written up the steps and code examples in a series of markdown documents
on a GitHub repo, so people could follow along, copy-pasting code with me as I talked through it.
This also meant that they could take it home to continue if we didn't get through it all. David
Seddon had to head home early so he missed the workshop, but told me he completed it on the train
home!</p>
<p>The workshop went well, and people seemed to get a lot out of it. I probably tried to squeeze in too
much, but that's not so bad as they were able to take it home and carry on. I covered Pydantic,
built a little FastAPI app and touched on Typer for making CLIs. The workshop materials are
available on GitHub if you want to have a go yourself:
<a href="https://github.com/bennuttall/pydantic-workshop">https://github.com/bennuttall/pydantic-workshop</a></p>
<figure class="wp-block-image">
<img src="images/pydantic-workshop.jpg" />
<figcaption><a href="https://www.flickr.com/photos/203482025@N06/54802069465/in/album-72177720329168007">Photo</a> by Mark Hawkins</figcaption>
</figure>

<p>This was the first PyCon UK since we lost our dear friend <a href="https://ntoll.org/article/my-friend-michael/">Michael
Foord</a>. We raised a glass to him in the pub on the
last day of the conference and shared fond memories of him.</p>
<p>The keynotes and talks are all recorded and promptly uploaded to YouTube and can be found here:
<a href="https://www.youtube.com/@PyconUKSoc/videos">https://www.youtube.com/@PyconUKSoc/videos</a></p>
<p>The official photos taken by the photographer Mark Hawkins are available on Flickr:
<a href="https://www.flickr.com/photos/203482025@N06/albums">https://www.flickr.com/photos/203482025@N06/albums</a></p>
<p>See you there next year, I hope!</p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[Another website revamp]]></title>
    <link href="https://bennuttall.com/blog/2025/08/another-website-revamp/" />
    <id>https://bennuttall.com/blog/2025/08/another-website-revamp</id>
    <published>2025-08-23T23:54:17+00:00</published>
    <updated>2025-08-26T18:54:12+00:00</updated>
    <category term="python" />
    <category term="blogging" />
    <category term="web-development" />
    <category term="meta" />
    <category term="bbc" />
    <category term="bbc-news" />
    <category term="media-city" />
    <category term="manchester" />
    <category term="github" />
    <category term="open-source" />
    <category term="pydantic" />
    <summary><![CDATA[I have a habit of revamping my website every few years. Since starting on Blogger some ... 19 years ago I've moved between platforms and redesigned the layout several times over. This time I decided to go for a static site build, which means not requiring managing WordPress plugins and updates, or...]]></summary>
    <content type="html"><![CDATA[<p>I have a habit of revamping my website every few years. Since starting on Blogger some ... 19 years
ago I've moved between platforms and redesigned the layout several times over.</p>
<p>This time I decided to go for a static site build, which means not requiring managing WordPress
plugins and updates, or running PHP on my server. It'll also perform a lot better if I get another
big hit, like <a href="/blog/2021/04/the-surreal-experience-of-my-first-developer-job/">The surreal experience of my first developer
job</a> which sat at the top of
<a href="https://news.ycombinator.com/item?id=28058816">Hacker News</a> for a whole day.</p>
<p>My regular site revamping has been documented over the years...</p>
<blockquote>
<p>I decided I needed a new website template so I made one</p>
</blockquote>
<p>— <a href="/blog/2009/04/version-3-and-problems-with-ie/">Version 3 and Problems with IE</a> (2009)</p>
<blockquote>
<p>I have to make a series of decisions - where do I take the blog, and how do I get it there? Do I
migrate to another blogging service - and if so, temporarily or permanently? Do I write my own
temporary fix? Do I write my own blogging platform - and if so, would it work in the same way
Blogger did?</p>
</blockquote>
<p>— <a href="/blog/2010/02/goodbye-blogger/">Goodbye Blogger?</a> (2010)</p>
<blockquote>
<p>I wonder how long before I get bored of this template and create or find another!</p>
</blockquote>
<p>— <a href="/blog/2020/05/blogging-about-blogging/">Blogging about blogging</a> (2020)</p>
<p>The <a href="/blog/2010/02/goodbye-blogger/">2010 post</a> also included a prediction which seems to have come
true:</p>
<blockquote>
<p>Ideally, in time, I will write my own Content Management System which will manage my entire site,
not just the blog. It will work exactly how I want it to, and it will be perfectly suited my own
personal requirements - bespoke for my own purposes. PHP may be the language I write this in - it
may not. By the time I get round to doing it properly I might be writing in a completely different
language.</p>
</blockquote>
<p>About that...</p>
<h2>The project</h2>
<p>For now I've kept the layout the same, but I built a markdown based static site generator. I
absolutely could have used one of the many frameworks that do exactly this, but I just
<em>really fancied</em> having a go at writing my own in Python. It has a <a href="/blog/">blog index</a>, an
<a href="/blog/archive/">archive</a>, archives by <a href="/blog/2021/">year</a> and <a href="/blog/2021/04/">month</a>,
<a href="/blog/tags/">tags</a>, an <a href="/sitemap.xml">XML sitemap</a> and an <a href="/atom.xml">Atom feed</a>. It also supports
Twitter cards and general <a href="https://ogp.me/">OpenGraph</a> tags for other social media sites such as
<a href="https://mastodon.social/">Mastodon</a>:</p>
<figure class="wp-block-image">
<img src="images/og.png" />
</figure>

<p>I didn't spend very long building the new site, and I enjoyed working on it. It's quite satisfying
building something of your own, and it's much easier to get done quickly with ChatGPT and a wealth
of open source tools available.</p>
<p>I used <a href="https://chameleon.readthedocs.io/en/latest/">Chameleon</a>,
<a href="https://docs.pydantic.dev/latest/">Pydantic</a>, <a href="https://pypi.org/project/Markdown/">markdown</a>,
<a href="https://pypi.org/project/PyYAML/">pyyaml</a> and
<a href="https://pypi.org/project/beautifulsoup4/">BeautifulSoup</a>.</p>
<p>The source code for the site generator is available on my GitHub. I called it
<a href="https://github.com/bennuttall/beemo">Beemo</a> and I've released it to
<a href="https://pypi.org/project/beemo/">PyPI</a>. I'll try to make it more generalised to be useful to
others.</p>
<h2>Content</h2>
<p>I wrote a <a href="https://gist.github.com/bennuttall/5505efe6f52b875e4c4a547374af5bfb">script</a> to pull all
the posts from my WordPress site via its JSON API, keeping their titles, slugs and tags along with
the content. I used <a href="https://pandoc.org/">pandoc</a> to mangle the HTML content;
<a href="https://pypi.org/project/beautifulsoup4/">BeautifulSoup</a> to extract the images; and
<a href="https://imagemagick.org/">imagemagick</a> to resize them. I ran the same script on my abandoned
<em>Tooling Tuesday</em> blog, a separate WordPress instance, to bring those posts in too.</p>
<p>I also copied the source of some of the article I wrote for
<a href="https://opensource.com/">opensource.com</a> which stopped running a couple of years ago. I had another
look through my old Blogger account, and found a few really old posts I wanted to preserve, like
<a href="/blog/2008/09/athletics/">Athletics?</a> and <a href="/blog/2007/01/my-received-files/">My Received Files</a> so
I've ported those over too.</p>
<h2>BBC</h2>
<p>One thing that struck me while looking back at the content is the number of old posts tagged with
<a href="/blog/tags/bbc/">bbc</a> or <a href="/blog/tags/bbc-news/">bbc news</a>, either written about or referencing
BBC News articles, or about BBC TV shows.</p>
<p>One was about an <a href="/blog/2012/04/hack-to-the-future/">event</a> I was involved with which <a href="https://www.bbc.co.uk/blogs/researchanddevelopment/2012/04/teaching-coding-to-kids-at-hac.shtml">BBC R&amp;D wrote
up</a>
on their blog (and looking now I recognise some of the names on that site); one was about a time in
2007 when my parkour group was <a href="/blog/2007/08/trace-gathering-2007/">filmed for a regional TV show</a>
which was <a href="/blog/2008/us-on-bbc-inside-out/">broadcast in 2008</a>; another in 2009 where we were
<a href="/blog/2009/04/lots-of-training-and-a-session-with-the-bbc/">auditioned for a TV programme</a> about
intelligence stereotypes. One time I had an <a href="/blog/2012/10/hero-kayakers/">eventful kayaking trip</a>
which was <a href="https://www.bbc.co.uk/news/uk-england-manchester-19735175">covered by BBC News</a>. I even
posted about <a href="/blog/2016/03/fork-handles/">the death of Ronnie Corbett</a> in 2016, and related this to
<a href="https://github.com/bennuttall/handles">GitHub</a>.</p>
<figure class="wp-block-image">
<img src="images/media-city.jpg" />
<figcaption>BBC at Media City</figcaption>
</figure>

<p>I was a student in Manchester when Media City was being built, and the BBC were beginning to set up
a new base there. I loved the idea of getting a job there once I finished university. I didn't see
anything relevant at the time and for some reason I hadn't been looking at graduate schemes. It
wasn't until I happened to spot a vacancy in <a href="https://www.bbc.co.uk/rdnewslabs">BBC News Labs</a> in
2019 that I finally put in an application, and started in January 2020, initially in London. Later I
relocated back to Manchester and ended up with Media City as my base. So I got there eventually!</p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[hostedpi]]></title>
    <link href="https://bennuttall.com/blog/2025/07/hostedpi/" />
    <id>https://bennuttall.com/blog/2025/07/hostedpi</id>
    <published>2025-07-14T16:50:51+00:00</published>
    <updated>2025-07-14T16:58:15+00:00</updated>
    <category term="hostedpi" />
    <category term="mythic-beasts" />
    <category term="python" />
    <category term="raspberry-pi" />
    <category term="pydantic" />
    <summary><![CDATA[Cambridge-based hosting company Mythic Beasts — long-time friends of Raspberry Pi people and products — launched a unique service back in 2016: Raspberry Pi hosting, what they call their "Pi Cloud".    When I developed the first version of piwheels, it made sense to make use of this service to...]]></summary>
    <content type="html"><![CDATA[<p>Cambridge-based hosting company <a href="https://www.mythic-beasts.com/">Mythic Beasts</a> — long-time friends
of Raspberry Pi people and products — launched a unique service back in 2016: Raspberry Pi hosting,
what they call their "<a href="https://www.mythic-beasts.com/order/rpi">Pi Cloud</a>".</p>
<figure class="wp-block-image">
<a href="https://www.mythic-beasts.com/order/rpi"><img src="images/picloud.png" /></a>
</figure>

<p>When I developed the first version of <a href="https://www.piwheels.org/">piwheels</a>, it made sense to make
use of this service to natively compile Python packages on Raspberry Pi hardware. When I'd proved
that the idea worked in concept, I knew I needed several Pis to complete the backlog for completion.
Mythic were more than happy to provide this, and have effectively sponsored the running of the
project ever since. Piwheels has proven how capable their Pi Cloud is – delivering <a href="https://blog.piwheels.org/half-a-billion-downloads/">hundreds of
millions of downloads</a> from a single Pi. It's
been a great selling point for them.</p>
<p>They later introduced a <a href="https://www.mythic-beasts.com/support/api/raspberry-pi">web API</a> for
provisioning and managing Pis in their cloud. I initially used a simple script to help provision Pis
as I needed them. I later developed a Python library and CLI providing the full range of features,
and gave a <a href="https://www.youtube.com/watch?v=9wNYRF5DGdk">talk</a> on it at a Raspberry Pi meetup.</p>
<p>I just finished off a revamp of the library, including using
<a href="https://docs.pydantic.dev/latest/">Pydantic</a>, and a new CLI built with
<a href="https://typer.tiangolo.com/">Typer</a>.</p>
<figure class="wp-block-image">
<img src="images/hostedpi.png" />
<figcaption>The new piwheels CLI in all its glory</figcaption>
</figure>

<ul>
<li>The package can be found on <a href="https://pypi.org/project/hostedpi/">PyPI</a> (and
  <a href="https://piwheels.org/project/hostedpi/">piwheels</a>, of course)</li>
<li>Documentation is on <a href="https://hostedpi.readthedocs.io/en/stable/">readthedocs</a></li>
<li>The source is on <a href="https://github.com/piwheels/hostedpi">GitHub</a></li>
</ul>
<p>It's been a pleasure to use Pydantic to manage data models for the API requests and responses, and
to provide an easy way for users to create validated Pi specs. Typer made it easy to create a
practical CLI with all the bells and whistles, and no boilerplate. I'll be giving a workshop at
<a href="https://2025.pyconuk.org/">PyCon UK 2025</a> on data modelling with Pydantic, covering
<a href="https://fastapi.tiangolo.com/">FastAPI</a> and Typer too.</p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[PyCon UK 2023 talks]]></title>
    <link href="https://bennuttall.com/blog/2023/10/pycon-uk-2023-talks/" />
    <id>https://bennuttall.com/blog/2023/10/pycon-uk-2023-talks</id>
    <published>2023-10-25T11:10:29+00:00</published>
    <updated>2023-10-25T11:10:30+00:00</updated>
    <category term="bbc" />
    <category term="bbc-news" />
    <category term="bbc-news-labs" />
    <category term="bbcrd" />
    <category term="python" />
    <summary><![CDATA[I was back in Cardiff for PyCon UK recently and gave a talk on a BBC project I've been working on, called Live Highlights. I'm not doing as many conference talks as I used to – this is my only one of the year! I spoke about how we prototyped an idea and built it up to be used in audience-facing...]]></summary>
    <content type="html"><![CDATA[<p>I was back in Cardiff for PyCon UK recently and gave a talk on a BBC project I've been working on,
called <em>Live Highlights</em>. I'm not doing as many conference <a href="/talks/">talks</a> as
I used to – this is my only one of the year!</p>
<p>I spoke about how we prototyped an idea and built it up to be used in audience-facing trials:</p>
<figure>
<iframe width="560" height="315" src="https://www.youtube.com/embed/UEGMR0oCOqI?si=3zpwO9NhSK5F6jSm" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</figure>

<p>I also gave a lightning talk about numeronyms – those silly shorthand words with numbers like <code>k8s</code>
and <code>a11y</code> – and how I find them obnoxious (starts at <a href="https://www.youtube.com/watch?v=chxsC2WYNTE&amp;t=3677s">this
timecode</a>):</p>
<figure>
<iframe width="560" height="315" src="https://www.youtube.com/embed/chxsC2WYNTE?si=FbSOy8rVbYMYsW1U&amp;start=3677" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</figure>]]></content>
  </entry>
  <entry>
    <title><![CDATA[BBC News Labs 2021]]></title>
    <link href="https://bennuttall.com/blog/2022/01/bbc-news-labs-2021/" />
    <id>https://bennuttall.com/blog/2022/01/bbc-news-labs-2021</id>
    <published>2022-01-24T21:54:21+00:00</published>
    <updated>2022-01-24T22:27:45+00:00</updated>
    <category term="bbc" />
    <category term="bbc-news" />
    <category term="bbc-news-labs" />
    <summary><![CDATA[I've now completed my second full year working as software engineer in the News Labs team at the BBC. Another year working from home; the team officially moved into BBC R&D; and I was promoted to senior engineer. In 2021 I worked on:  mosromgr, a Python library for managing TV and   radio running...]]></summary>
    <content type="html"><![CDATA[<p>I've now completed my second full year working as software engineer in the <a href="https://bbcnewslabs.co.uk/">News
Labs</a> team at the BBC. Another year working from home; the team
officially moved into <a href="https://www.bbc.co.uk/rd">BBC R&amp;D</a>; and I was promoted to senior engineer.</p>
<p>In 2021 I worked on:</p>
<ul>
<li><a href="https://bbcnewslabs.co.uk/projects/mosromgr/"><strong>mosromgr</strong></a>, a Python library for managing TV and
  radio running orders, which this year we <a href="https://github.com/bbc/mosromgr">open sourced</a> and we're
  currently working on rolling out to teams around the BBC to make use of</li>
<li><strong><a href="https://bbcnewslabs.co.uk/news/2021/slicer-today-trial/">Semi-automated chapter points for the Radio 4 Today
  programme</a>,</strong> which went
  <a href="https://twitter.com/BBCr4today/status/1405910025961459715">live</a> as a month-long trial</li>
<li><a href="https://bbcnewslabs.co.uk/projects/modus/"><strong>MODUS</strong></a>, an experiment in using machine-summarised
  articles for new formats such as the bullet-point list in the public beta of the BBC News app,
  currently being trialled for red button services</li>
<li><strong>BBC Images</strong>, a new image search tool for journalists based on the Guardian's open source
  <a href="https://github.com/guardian/grid/">GRID</a> platform</li>
<li><a href="https://bbcnewslabs.co.uk/projects/oriel/"><strong>Oriel</strong></a>, an image collections curation tool based
  on BBC Images with a metadata enrichment pipeline</li>
<li><strong>IDX</strong>, a prototype tool for marking important segments of live TV and radio programmes for
  re-use to make them accessible to personalisation services and more</li>
</ul>
<p>Here's our 2021 highlights video:</p>
<figure>
<iframe width="560" height="315" src="https://www.youtube.com/embed/j8Ig20Nkfqw?si=eqU4xbJH4BJUYiiD" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</figure>

<p>I also gave a talk about mosromgr and our work on R4 Today at EuroPython:</p>
<figure>
<iframe width="560" height="315" src="https://www.youtube.com/embed/JfrqKOO0Yoc?si=XjjDwQImh_-sArUS" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</figure>

<p>Check out the <a href="https://bbcnewslabs.co.uk/">News Labs website</a> and you can follow us on Twitter:
<a href="https://twitter.com/BBC_News_Labs">@BBC_News_Labs</a>.</p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[Client Horror Stories]]></title>
    <link href="https://bennuttall.com/blog/2021/09/client-horror-stories/" />
    <id>https://bennuttall.com/blog/2021/09/client-horror-stories</id>
    <published>2021-09-28T13:20:39+00:00</published>
    <updated>2021-09-28T13:20:39+00:00</updated>
    <category term="web-development" />
    <summary><![CDATA[Following the success of my article The surreal experience of my first developer job, which made it to the top of Hacker News for a whole day, I was invited to tell the story on the excellent podcast Client Horror Stories. The video is available on clienthorrorstories.com:    Thanks to Morgan for...]]></summary>
    <content type="html"><![CDATA[<p>Following the success of my article <a href="/blog/2021/04/the-surreal-experience-of-my-first-developer-job/"><em>The surreal experience of my first developer
job</em></a>, which made it to
the <a href="https://news.ycombinator.com/item?id=28058816">top</a> of <a href="https://news.ycombinator.com/">Hacker
News</a> for a whole day, I was invited to tell the story on the
excellent podcast <em><a href="https://clienthorrorstories.com/">Client Horror Stories</a></em>. The video is
available on clienthorrorstories.com:</p>
<figure class="wp-block-image">
<a href="https://clienthorrorstories.com/e/that-time-you-had-to-develop-two-apps-a-day-every-day-for-a-19-years-old-boss-with-ben-nuttall/"><img src="images/client-horror-stories.png" /></a>
</figure>

<p>Thanks to <a href="https://westegg.com/">Morgan</a> for having me on!</p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[BBC open sourcing mosromgr]]></title>
    <link href="https://bennuttall.com/blog/2021/04/bbc-open-sourcing-mosromgr/" />
    <id>https://bennuttall.com/blog/2021/04/bbc-open-sourcing-mosromgr</id>
    <published>2021-04-14T14:48:00+00:00</published>
    <updated>2025-07-11T15:20:23+00:00</updated>
    <category term="bbc" />
    <category term="bbc-news-labs" />
    <category term="open-source" />
    <category term="python" />
    <summary><![CDATA[For the last few months in BBC News Labs, I've been working on various projects involving extracting metadata from running orders for BBC programmes such as Newsnight and the R4 Today programme. Like many traditional broadcasters, the BBC uses the MOS Protocol for communications between newsroom...]]></summary>
    <content type="html"><![CDATA[<p>For the last few months in BBC News Labs, I've been working on various projects involving extracting
metadata from running orders for BBC programmes such as
<a href="https://www.bbc.co.uk/programmes/b006mk25">Newsnight</a> and the <a href="https://www.bbc.co.uk/programmes/b006qj9z">R4
Today</a> programme.</p>
<p>Like many traditional broadcasters, the BBC uses the <a href="http://mosprotocol.com/">MOS Protocol</a> for
communications between newsroom computers and servers where media files such as audio and video are
stored.</p>
<figure class="wp-block-image">
<img src="images/mos.jpg" />
</figure>

<p>Production teams create running orders using
<a href="https://www.cgi.com/en/solutions/openmedia">OpenMedia</a>, and every time they add details or make
changes, MOS XML messages are emitted by the newsroom computer systems. We've created a way to
process these messages to build up a machine-readable version of the running order.</p>
<p>We developed a general solution for dealing with MOS messages – a Python library called mosromgr.
It's based on open standards and could be useful to other broadcasting organisations, so we've
released it under an open source licence.</p>
<ul>
<li>The source code is available on <a href="https://github.com/bbc/mosromgr">GitHub</a></li>
<li>The documentation for the library is available on <a href="https://mosromgr.readthedocs.io/">readthedocs</a></li>
<li>The project is available from the <a href="https://pypi.org/project/mosromgr/">Python Package Index</a></li>
</ul>
<p>Read more at <a href="https://www.bbc.co.uk/opensource/projects/project/mosromgr">bbc.co.uk/opensource</a> and
<a href="https://www.bbc.co.uk/rdnewslabs/projects/mosromgr">bbc.co.uk/rdnewslabs</a></p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[The surreal experience of my first developer job]]></title>
    <link href="https://bennuttall.com/blog/2021/04/the-surreal-experience-of-my-first-developer-job/" />
    <id>https://bennuttall.com/blog/2021/04/the-surreal-experience-of-my-first-developer-job</id>
    <published>2021-04-12T04:09:53+00:00</published>
    <updated>2021-09-28T13:19:46+00:00</updated>
    <category term="web-development" />
    <category term="media-city" />
    <category term="manchester" />
    <category term="career" />
    <summary><![CDATA[Nearly ten years ago I graduated with a degree in Mathematics & Computing, with a keen interest in pursuing a career involving maths and programming, but with little idea how. First and foremost I had decided to stay in Manchester after uni, rather than risk getting stuck at my parents' if I moved...]]></summary>
    <content type="html"><![CDATA[<p>Nearly ten years ago I graduated with a degree in Mathematics &amp; Computing, with a keen interest in
pursuing a career involving maths and programming, but with little idea how. First and foremost I
had decided to stay in Manchester after uni, rather than risk getting stuck at my parents' if I
moved back home. So I rented a flat with a friend from uni and started looking for a job. I had
spent a lot of time at uni learning web development – not so much from the course, but self-taught
as the course was quite far behind. I was happily making websites with the LAMP stack, I felt pretty
capable at it and I knew there were plenty of such jobs in the Manchester area – so I started
applying for "Web developer" and "PHP developer" and similar positions in all sorts of local
companies.</p>
<p>My first interview was with a company called <em>App Start</em> in an office building in an old fashioned
industrial estate in a horrible part of Salford (if you think of <a href="https://www.mediacityuk.co.uk/">Media
City</a> as <em>Salford</em>, the rest Salford would be a shock). I was
pushing it for time due to underestimating the walk from the train station, and I arrived a bit
flustered from the rush and trying to find the place. I was greeted by a man in beach shorts and
flip-flops. I was dressed in a full suit, shirt, tie and smart shoes, and I told him "I'm here for
an interview with Chris" – his response "Oh, Chris doesn't work here any more" should have set off
alarm bells, but hey, I was young and naive.</p>
<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/app-start-factory-700x396.jpg" class="wp-image-2817" decoding="async"
loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="396" />
</figure>
</div>

<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/Screenshot-from-2021-04-12-00-11-40-700x422.png" class="wp-image-2812"
decoding="async" loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" width="700"
height="422" />
<figcaption>Clifton Business Park, Salford. The real factory of dreams.</figcaption>
</figure>
</div>

<p>The guy in flip-flops, Alex, seemed to be in charge now. He asked which role I'd applied for – I
said "web developer" and he invited me in. There were about 15-20 people working at desktop PCs.
There was a big sofa. The floor was bare concrete. In between whiteboards with passwords written on
them you could see the walls were graffitied. The "interview" involved him trying to find my CV in
Chris's inbox, and me showing him some websites I'd made. He asked if I could come in the next day,
to do a day's work as a trial – so I did.</p>
<p>The next day was a Friday – and I seem to recall building a prototype of a social networking site –
although I'm not sure why. Anyway – he seemed to think I was good enough so he offered me the job –
starting on Monday – which was great for me – I'd landed a full-time job starting straight away,
meaning I could pay the rent and start my post-uni life in Manchester. But once I accepted, he asked
"are you free tomorrow?" – I hesitated but answered the question – "yes". "Can you come in?" and
because I wasn't sure what else to say, I said yes. Again, this should have been a sign.</p>
<p>The owner was a young entrepreneur called Andrew, who I was told had made millions through his
property development and management company, <em>Fresh Start Living</em>. He decided to get into the "app"
market (remember, this was 2011 – iPhone apps were huge) and so he'd decided to give his IT guy
(Alex) a budget to hire some developers to make a load of apps. Surely one of them would be a huge
success and he'd make millions.</p>
<h2>Monday to Friday? 9 to 5?</h2>
<p>I went back again on the Saturday to find no sign of anyone in the office. I waited around a bit but
didn't know what to do. I didn't have a contact number for Alex. After a while, he showed up. He'd
been in the Fresh Start Living side of the building. We did a day's work on our own in the office.
He gave me a lift home and I was due to start work for real on the Monday.</p>
<p>I arrived on Monday, and was told about the project I'd be working on. The short version is that I
was told we had to have it finished before anyone could go home. We were there until 9pm. That was
my first day as a full-time employee.</p>
<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/IMAG0392-700x419.jpg" class="wp-image-2811" decoding="async" loading="lazy"
sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="419" />
<figcaption>My desk. A desktop PC running Windows. Google+ and the original Tweetdeck.
Beer.</figcaption>
</figure>
</div>

<h2>Skill Buy</h2>
<p>So on my first day, Alex told me the project I'd be working on was called <em>Skill Buy</em>, and proceeded
to explain the concept. The idea was similar to an auction site that was popular at the time where
you could bid on a product, but you paid for every bid – so if you saw an iPhone with a winning bid
of 1p, you could bid 2p, but if you were outbid, you'd lost your 2p – eventually someone would win
it at a bargain price at the expense of everyone who'd bid before them and the company would make a
fortune. They're called penny auctions and you can read about them in this paper: <em><a href="https://irep.ntu.ac.uk/id/eprint/26656/1/PubSub3120_Griffiths.pdf"><strong>Are online
penny auctions a form of
gambling?</strong></a></em> Anyway – the idea
I'd been told about was similar, but instead of bidding, you'd play a game (a <em>Flash</em> or <em>Unity</em>
game made by the game devs I was working with) – and it worked like an arcade game – the winner gets
the prize. You'd buy credits, spend your credits playing games, and if you were the top scorer at
the end of the game's countdown timer, you'd win the prize. Actually you'd win the prize at 99%
discount because they seemed to think that got around gambling laws.</p>
<p>Without a web developer on staff, getting this idea off the ground had proved tricky, and Alex had
been using his own jack-of-all-trades skills to knock something together. He had some experience
managing an e-commerce site (I later found out that he ran his own online sex toy shop...) so he'd
put together a Skill Buy prototype using the PHP e-commerce framework <em>Magento</em>. He would add
products (prizes) in the site's admin area, and hacked away at the templates to show countdown
timers and things. Somehow, Alex had promised the site would be ready to show Andrew the next day –
and at some point in the afternoon he started to stress out and said to the whole room "no-one's
going home until this is done". One of the game devs, had made a target shooting practice game in
<em>Unity</em>, and we had to figure out how to embed it in the site and get the game to submit the
player's score to the site's PHP back-end. We had no idea where to start, but we worked on it all
afternoon, well into the evening, and we got it working to whatever standard Alex had required, and
finally got to go home.</p>
<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/6947133526_acc07f111a_b-700x525.jpg" class="wp-image-2815" decoding="async"
loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="525" />
<figcaption>The shooting range game</figcaption>
</figure>
</div>

<p>Over the next few weeks I worked on the site, I grew fed up of being stuck in this e-commerce
framework – having to work with its hyper-normalised MySQL database (the <a href="https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model">EAV
model</a>), and the fact
the product I was building wasn't really a shop – so I asked Alex if he minded me writing something
custom instead. I got my own custom PHP/MySQL site up-and-running pretty quickly, and it didn't take
long before we launched the site. I was happy enough writing crappy PHP, implementing questionable
security protections (feeble attempts to block people from sending in fake scores), and learning
things like how to integrate Facebook Login, PayPal payments, using AJAX, and working with the game
devs to integrate with the scoring system. I actually quite enjoyed working there for a little
while! It was liberating, knowing I was using my self-taught web dev skills on my own terms – and
getting paid to do so.</p>
<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/appstart-liam-alex-700x466.jpg" class="wp-image-2827" decoding="async"
loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="466" />
<figcaption>Alex McDaid (beard) and Liam Daly</figcaption>
</figure>
</div>

<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/MEN-12.10.jpg" class="wp-image-2826" decoding="async" loading="lazy"
sizes="auto, (max-width: 540px) 100vw, 540px" width="540" height="480" />
<figcaption>Coverage from the Manchester Evening News, which you can still <a
href="https://www.manchestereveningnews.co.uk/business/innovation/appstart-launches-online-arcade-skill-872914">read
online</a></figcaption>
</figure>
</div>

<p>The apps company was being run by Alex and a 19-year-old kid called Liam, whose Uncle was involved
in the property business. Somehow Liam had been given an experimental side-project business to help
run as his "placement year" at uni.</p>
<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/skillbuy-700x341.png" class="wp-image-2814" decoding="async" loading="lazy"
sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="341" />
<figcaption>Skill Buy</figcaption>
</figure>
</div>

<p>One thing that might strike you as odd is the bizarre graphics. The designer had made all these
cow-in-a-field graphics for an iPhone game that was planned – but they'd shelved it and told her to
re-use them on the website. She <em>hated</em> that they were being used like this – and it didn't make
sense to anyone else either.</p>
<p>Alex had a tendency of regularly asking if you were free on Saturday. The first few times I said
yes, and just worked an extra day – I think I assumed I would automatically get paid overtime – but
I didn't. Eventually I just learned to say no.</p>
<p>A little while after I joined, a guy called Bob turned up and took some kind of product manager job,
similar to Alex and Liam. Like Liam, he seemed nice enough but somewhat out of place. He was a
friend of Alex's. He used to stare at Google Analytics all day and come over and tell me things
about bounce times and referrals. He used to leave after lunch, for whatever reason, so he earned
the nickname <em>Half-a-job Bob.</em> He didn't last long though. One day, he'd been in a meeting with
Andrew, who'd obviously seen through him and had enough. I came to discover that when Andrew decided
he didn't like someone, rather than fire them, he'd have a go at them and make them feel so angry
they'd want to leave. Bob marched through the door, slammed it behind him, muttering loudly to
himself "he's not talking to me like that" and declared to the room that he was "taking no more of
Andrew's shit" and stormed out.</p>
<figure class="wp-block-gallery columns-3 is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img alt="" class="wp-image-2818" data-full-url="https://bennuttall.com/wp-content/uploads/2021/04/new-skillbuy.png" data-id="2818" data-link="https://bennuttall.com/?attachment_id=2818" decoding="async" height="438" loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" src="images/new-skillbuy-700x438.png" width="700"/></figure></li><li class="blocks-gallery-item"><figure><img alt="" class="wp-image-2819" data-full-url="https://bennuttall.com/wp-content/uploads/2021/04/gameplay.png" data-id="2819" data-link="https://bennuttall.com/?attachment_id=2819" decoding="async" height="438" loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" src="images/gameplay-700x438.png" width="700"/></figure></li><li class="blocks-gallery-item"><figure><img alt="" class="wp-image-2820" data-full-url="https://bennuttall.com/wp-content/uploads/2021/04/lifechanging.png" data-id="2820" data-link="https://bennuttall.com/?attachment_id=2820" decoding="async" height="438" loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" src="images/lifechanging-700x438.png" width="700"/></figure></li></ul></figure>

<p>We'd launched the site, and they'd started running Facebook ads promoting it. Alex even ordered a
load of printed materials and signs to put up. I remember once, when giving me a lift home, he
pulled up and got out of the car to cable tie a Skill Buy banner to a railing at a set of traffic
lights. <a href="http://whatsthefunctionality.blogspot.com/2011/10/skillbuycouk-and-what-it-is.html">We had people
playing</a> – you
got a few free credits after signing up, and some were actually buying credits too. The cheaper
gimmicky prizes had short timers on them, and we had winners claim prizes – which Liam shipped out.
We had some big prizes on there too, like a Rolex watch, a year's free accommodation, and a house.
The idea was that Andrew could give away use of some of Fresh Start's rental properties and even a
house, if the website made enough revenue. The accommodation and the house were on a 3-month timer,
and <em>surely</em> that would be enough time to build up the site user base.</p>
<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/IMAG0420-700x419.jpg" class="wp-image-2816" decoding="async" loading="lazy"
sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="419" />
<figcaption>We ran a stall at the university fresher's fair to try to get some student interest. We
ran a special game where people played "Avoid the Hangover" on a projected screen to win "a mountain
of beer" (or "mountin" if you saw our printed leaflets).</figcaption>
</figure>
</div>

<p>After a while, Andrew decided to make a change. Instead of buying credits, you would buy a ticket
for a game, and only when enough people had bought a ticket would the game open, to guarantee the
revenue covered the cost of the prize. This basically halted all activity on the site. Rather than
people playing games and competing for prizes, they were just waiting, and hardly anyone was
interested in buying tickets. It meant that the site was no longer spending any money on prizes, so
it didn't have any outgoings, but there wasn't any sign of it getting the number of users it needed.</p>
<p>While searching my inbox for anything related to App Start, I found this email from Andrew,
forwarded to me by Alex:</p>
<blockquote>
<p>Hi Ben – These are the skill buy changes required by andrew.<br />
Begin forwarded message:<br />
Make a skill buy section for property prizes only to replace any current property sections<br />
Show 3 options talk about yields based on owning them cash<br />
1 terraced house<br />
1 appartment @ bispham<br />
3 student pods montgomery<br />
Call all of there values 100 k plus  <br />
A ticket to enter costs 100 pounds only 1000 tickets available per game 1 winner is the highest
scorer 72 hrs after the 1000 ticket is sold<br />
Main selling points you give a 1 in 1000 chance of having the skill to win<br />
Have it set up by monday as I will start selling tickets via telesales<br />
Make it prominent easy to navigate and easy to pay( find a way)  and most importantly show number
of people in the game to date<br />
Have a section where they can note the name of the sales agent so we can pay comms  and obviously
take clients details etc<br />
Splash sites – probably hit 60000 people per week<br />
Tele sales – will soon hit 100000 pple per week<br />
160,000 people per week all see or hear the message buy a investment property in addition they
should see<br />
100 pounds 1 in 1000 chance to win a life changing investment property<br />
160,000 people per week @ 0.25 percent conversion – 40,000 pounds per week<br />
The key to the conversion is making the site slick and appealing easy to fill in easy to pay easy
to play etc if you make it slick simple salesy and appealing <br />
If it works then in 3 weeks you would be giving away a house pr will do the rest.  !<br />
Do it today tomorrow doesn't exist !<br />
Sent using BlackBerry® from Orange</p>
</blockquote>
<div class="wp-block-image">

<figure class="aligncenter size-large">
<img src="images/skips-700x532.png" class="wp-image-2825" decoding="async" loading="lazy"
sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="532" />
<figcaption>One day, Andrew brought in a friend who had his own skip company – and he introduced me
and said I would make him a website for his skip company.</figcaption>
</figure>

</div>

<h2>Pigs Might Fly</h2>
<p>Meanwhile, the rest of the team was working on iPhone games. There were some really talented
designers and developers there – and they produced some great games they were rightly very proud of.
They made a game called <em>Splat</em> (a bit like whack-a-mole) which was one of those addictive games,
and they managed to get a local radio station to interview them about it. They also made a game
called <em>Pigs Might Fly</em> – and they went all out with the marketing for this one. Alex got some large
pig signs printed and attached them to helium balloons and sent them flying around Manchester City
centre. They got everyone to text the radio station saying things like "I've just seen a flying pig
float past my car in Manchester" and they read a few of them out and asked if anyone knew what it
was about. Someone then called in to the radio show and went on air to promote the app. I've just
found the company <a href="https://twitter.com/AppStartUK">Twitter</a>,
<a href="https://www.flickr.com/photos/appstart/">Flickr</a> and
<a href="https://www.youtube.com/user/AppstartLtd/videos">YouTube</a> accounts are still live and it's all
there:</p>
<figure class="wp-block-gallery columns-3 is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><img alt="" class="wp-image-2821" data-id="2821" decoding="async" height="960" loading="lazy" sizes="auto, (max-width: 626px) 100vw, 626px" src="images/pigs-might-fly-beetham.jpg" width="626"/></figure></li><li class="blocks-gallery-item"><figure><img alt="" class="wp-image-2822" data-id="2822" data-link="https://bennuttall.com/?attachment_id=2822" decoding="async" height="525" loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" src="images/6961450183_c8dc8a4019_o-700x525.png" width="700"/></figure></li><li class="blocks-gallery-item"><figure><img alt="" class="wp-image-2823" data-id="2823" data-link="https://bennuttall.com/?attachment_id=2823" decoding="async" height="960" loading="lazy" sizes="auto, (max-width: 640px) 100vw, 640px" src="images/6944229240_414b883bcc_o.jpg" width="640"/></figure></li><li class="blocks-gallery-item"><figure><img alt="" class="wp-image-2824" data-full-url="https://bennuttall.com/wp-content/uploads/2021/04/6640985605_f8fdcc9fc6_o.jpg" data-id="2824" data-link="https://bennuttall.com/?attachment_id=2824" decoding="async" height="370" loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" src="images/6640985605_f8fdcc9fc6_o-700x370.jpg" width="700"/></figure></li></ul></figure>

<p>A point of contention was that Andrew wanted them to knock out as many apps as possible, and wait to
see if any of them got traction. But they wanted to choose an idea and spend a few months working on
it, to do a decent job.</p>
<h2>An app a day</h2>
<p>Once I'd finished converting Skill Buy to its new format, it didn't need any ongoing work until it
had the users it needed, so I was asked to join the rest of the team building apps. We were assigned
to groups of three: two developers and a designer in each. We had to make 10 apps per group every
week. So each developer had to make an app a day, with the designer creating any assets we needed.
We just listed off a load of crappy ideas for things that you could make without any effort. They
were mostly either a multiple-choice quiz, or a button that did something silly. I was using
PhoneGap to bootstrap the apps, create the icons and metadata, and just writing the app logic in PHP
scripts which were hosted on the Skill Buy server. The apps looked terrible, they were pointless and
had no potential. But we were hitting our targets. Andrew also fed us his own ideas, such as the
"Ask an expert" series, where we'd build an app you could submit questions to, and an "expert" would
respond. He seemed to think he knew some experts.</p>
<p>Around this time, the same thing that happened to Bob had happened to Alex – he just came in and
shouted "I'm done, Cya!" and walked out. We knew he must have been on the receiving end of Andrew's
routine.</p>
<p>I had a similar experience with Andrew myself. One day, the web developer in the property business
was off sick and I was asked to cover for him, because Andrew had been promised that some new
feature was going to be ready by the end of that day. I was briefed by Steph from the property
business, and I had a Google Chat open with the developer. I did what was asked, and Steph was happy
with it. I was called in to present it to Andrew, who seemed to believe that this was not was he'd
asked for – so he ridiculed the work, called it a waste of time, told me to "go back to apps" and
proceeded to yell at Steph as I was leaving.</p>
<p>So we continued for a little while. We made our crappy apps. We hit our targets. We told our
19-year-old boss that we can't go on like this, that none of these apps were going to succeed. I
decided it was time to move on. I waited to the end of the month, made sure I'd been paid and I
politely told Liam I was leaving. They had never given me a contract, so I didn't have a notice
period or anything. I just left and started looking for something new. I bumped into one of the
designers a month or so later and he said that one day Andrew just closed them down and they all
lost their jobs.</p>
<p>It's a shame my first real job only lasted about 4 months, but it was a wild ride and a crazy
experience. I haven't really thought about that time much until now. Looking back, it feels so
strange and the story seems so surreal, so I felt like writing about it.</p>
<div class="wp-block-image">
<figure class="aligncenter size-large">
<img src="images/skillbuy-archive-org-700x73.png" class="wp-image-2835" decoding="async"
loading="lazy" sizes="auto, (max-width: 700px) 100vw, 700px" width="700" height="73" />
<figcaption>The history of Skill Buy over the years – provided by archive.org</figcaption>
</figure>
</div>

<h2>Andrew Camilleri, the Gazerati, and a suspended prison sentence</h2>
<p>Andrew was quite the character, but he was nothing compared to Gaz. I think Gaz was Andrew's oldest
and best friend. When Andrew made his money, he hired Gaz to be his driver / bouncer. Gaz looked
like a bouncer. He was one of those people who you were terrified of at sight – but he was genuinely
pleasant and nice to talk to. One day, we heard a vehicle driving around the car park making a lot
of noise. Andrew had just purchased a <em>Maserati</em> as a "present" for Gaz – to drive him around in,
and they were doing laps of the industrial estate. Naturally, this car became known as the
<em>Gazerati</em>.</p>
<p>While researching online for this article, I came across a news article entitled <em><a href="https://www.placenorthwest.co.uk/news/property-developer-given-suspended-prison-sentence/">Property
developer given suspended prison
sentence</a></em>,
which starts:</p>
<blockquote>
<p>Andrew John Camilleri, a businessman formerly associated with wound-up property company Fresh
Start Living, has been unanimously convicted by a jury at Manchester Crown Court of making false
representations in an Individual Voluntary Arrangement proposal.</p>
<p>placenorthwest.co.uk</p>
</blockquote>
<p>It also states that in early 2011, Andrew made false representations to creditors in attempt to wipe
out debts of £9m. So rather than being a millionaire, he was actually the opposite – a negative
millionaire. And it seems Fresh Start Living, like App Start Ltd, is no more.</p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[What's new in GPIO Zero v1.6?]]></title>
    <link href="https://bennuttall.com/blog/2021/04/whats-new-in-gpio-zero-v1-6/" />
    <id>https://bennuttall.com/blog/2021/04/whats-new-in-gpio-zero-v1-6</id>
    <published>2021-04-03T00:14:55+00:00</published>
    <updated>2021-04-04T19:02:52+00:00</updated>
    <category term="gpio" />
    <category term="gpiozero" />
    <category term="raspberry-pi" />
    <summary><![CDATA[Dave and I just did a release of GPIO Zero, our Raspberry Pi GPIO library. It's been over 18 months since the last release, and as well as plenty of small bugfixes and corrections to the documentation, there are a few nice new features too. The highlights:  Rotary Encoder Multi-segment displays,...]]></summary>
    <content type="html"><![CDATA[<p>Dave and I just did a release of <a href="https://gpiozero.readthedocs.io/en/stable/">GPIO Zero</a>, our
Raspberry Pi GPIO library. It's been over 18 months since the last release, and as well as plenty of
small bugfixes and corrections to the documentation, there are a few nice new features too.</p>
<p>The highlights:</p>
<ul>
<li>Rotary Encoder</li>
<li>Multi-segment displays, including custom "font" support for them</li>
<li>Pibrella and TrafficpHat boards</li>
<li>New pin factory for the lgpio library</li>
<li>Event-driven functionality for internal devices</li>
<li>Improvements to Energenie</li>
<li>Added ASCII art for Pi 400 and CM4</li>
<li>Using BOARD numbering (under-the-hood) for add-on boards</li>
<li>Improved SPI support</li>
<li>Fixed ButtonBoard release events</li>
</ul>
<h2>Rotary Encoder</h2>
<p>The rotary encoder is a nifty electronic component which works like a potentiometer but can be
rotated infinitely, and each turn is distinct (both audibly with a click, and as detected in the
hardware). The GPIO Zero interface allows hooking events to each click (in one direction or both),
and its value space can be either be infinite or it can wrap around.</p>
<div class="codehilite"><pre><span></span><code><span class="kn">from</span><span class="w"> </span><span class="nn">gpiozero</span><span class="w"> </span><span class="kn">import</span> <span class="n">RotaryEncoder</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">gpiozero.tools</span><span class="w"> </span><span class="kn">import</span> <span class="n">scaled_half</span>

<span class="n">rotor</span> <span class="o">=</span> <span class="n">RotaryEncoder</span><span class="p">(</span><span class="mi">21</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="n">led</span> <span class="o">=</span> <span class="n">PWMLED</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>

<span class="n">led</span><span class="o">.</span><span class="n">source</span> <span class="o">=</span> <span class="n">scaled_half</span><span class="p">(</span><span class="n">rotor</span><span class="o">.</span><span class="n">values</span><span class="p">)</span>
</code></pre></div>

<figure class="wp-block-image">
<img src="images/rotary-encoder.jpg" />
</figure>

<h2>Multi-segment displays</h2>
<p>Martin O'Hanlon opened a pull request for 7-segment displays some time ago, and so Dave started with
what Martin gave us and built support for not just 7-segment but any multi-segment displays,
including multiplexed multi-character displays. There's even a "font" interface if you want to
create custom character maps:</p>
<figure class="wp-block-image">
<img src="images/7seg_multi_bb-700x309.png" />
</figure>

<h2>Pibrella</h2>
<p>This seems like an odd thing to add in 2021, but we had a pull request for it, so why not?</p>
<figure class="wp-block-image">
<img src="images/pibrella.png" />
</figure>

<p>One interesting thing about our interface to it is the way the input and output channels are exposed
as pins, allowing you to create other GPIO Zero devices from them without looking the pin numbers
up:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">from</span><span class="w"> </span><span class="nn">gpiozero</span><span class="w"> </span><span class="kn">import</span> <span class="n">Pibrella</span><span class="p">,</span> <span class="n">LED</span><span class="p">,</span> <span class="n">Button</span>

<span class="n">pb</span> <span class="o">=</span> <span class="n">Pibrella</span><span class="p">()</span>
<span class="n">btn</span> <span class="o">=</span> <span class="n">Button</span><span class="p">(</span><span class="n">pb</span><span class="o">.</span><span class="n">inputs</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="n">pull_up</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="n">led</span> <span class="o">=</span> <span class="n">LED</span><span class="p">(</span><span class="n">pb</span><span class="o">.</span><span class="n">outputs</span><span class="o">.</span><span class="n">e</span><span class="p">)</span>

<span class="n">btn</span><span class="o">.</span><span class="n">when_pressed</span> <span class="o">=</span> <span class="n">led</span><span class="o">.</span><span class="n">on</span>
</code></pre></div>

<h2>lgpio</h2>
<p>A new pin factory was needed for the upcoming Ubuntu release, as the old way GPIO libraries worked
has now been deprecated. Ubuntu users should use the new lgpio pin factory now. Read more about
<a href="https://gpiozero.readthedocs.io/en/stable/api_pins.html">changing the pin factory</a> in the docs.</p>
<h2>Event-driven functionality for internal devices</h2>
<p>GPIO Zero also provides several "internal" devices which represent facilities provided by the
operating system itself. These can be used to react to things like the time of day, or whether a
server is available on the network.</p>
<p>These devices provide an API similar to and compatible with GPIO devices so that internal device
events can trigger changes to GPIO output devices the way input devices can. In the same way a
<code>Button</code> object is <em>active</em> when it's pressed, and can be used to trigger other devices when its
state changes, a <code>TimeOfDay</code> object is <em>active</em> during a particular time period.</p>
<p>However, the functionality for event-driven code using these devices was unimplemented until this
version. You can now use event-driven code to run code when an internal device's state changes:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">from</span><span class="w"> </span><span class="nn">gpiozero</span><span class="w"> </span><span class="kn">import</span> <span class="n">LED</span><span class="p">,</span> <span class="n">TimeOfDay</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">time</span>

<span class="n">led</span> <span class="o">=</span> <span class="n">LED</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">tod</span> <span class="o">=</span> <span class="n">TimeOfDay</span><span class="p">(</span><span class="n">time</span><span class="p">(</span><span class="mi">9</span><span class="p">),</span> <span class="n">time</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>

<span class="n">tod</span><span class="o">.</span><span class="n">when_activated</span> <span class="o">=</span> <span class="n">led</span><span class="o">.</span><span class="n">on</span>
<span class="n">tod</span><span class="o">.</span><span class="n">when_deactivated</span> <span class="o">=</span> <span class="n">led</span><span class="o">.</span><span class="n">off</span>
</code></pre></div>

<h2>Added ASCII art for Pi 400 and CM4</h2>
<p>The <code>pinout</code> command line tool GPIO Zero provides includes neat ASCII art diagram showing the pinout
and board layout of the Pi model you're on. We just added layouts for the Pi 400 and CM4:</p>
<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-04-02-23-50-15.png" />
</figure>

<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-04-02-23-51-57.png" />
</figure>

<h2>Using BOARD numbering (under-the-hood) for add-on boards</h2>
<p>A few versions ago, we added support for using BOARD numbering when specifying which pins to create
devices on, but rather than set a global to say which method you're using, it's inferred by the way
you refer to the pin as a string. Instead of the integer 17, you could use the string <code>BOARD11</code> when
creating an LED:</p>
<div class="codehilite"><pre><span></span><code><span class="n">led</span> <span class="o">=</span> <span class="n">LED</span><span class="p">(</span><span class="s1">&#39;BOARD11&#39;</span><span class="p">)</span>
</code></pre></div>

<p>And in this release, we swapped to using board numbering internally whenever constructing devices
for physical boards, such as the Energenie. This means that if (in the increasingly small chance)
you're using an old Pi 1 rev1, and the pinout is slightly different, GPIO Zero will convert the
physical BOARD numbers to BCM numbers, and they'll be right for the pinout of whichever board you're
on.</p>
<h2>Fixed ButtonBoard release events</h2>
<p><code>ButtonBoard</code> allows you to create a single object which listens to the state changes of a number of
buttons, and either fire events when the collection is activated or deactivated, or read the "value"
of the collection. There was a bug identified which mean <code>ButtonBoard</code> release events fired as press
events – this is now fixed.</p>
<h2>Next up, GPIO Zero 2.0</h2>
<p>This is the last release to support Python 2. Our next step will be to start removing the messy 2/3
compatibility code and prepare for the next release to be Python 3 only. It'll be the first time
we've broken backwards compatibility, so it'll be a v2.0 release.</p>
<p>Thanks to all the new contributors!</p>]]></content>
  </entry>
  <entry>
    <title><![CDATA[Accessing Python package index JSON APIs with requests]]></title>
    <link href="https://bennuttall.com/blog/2021/03/accessing-python-package-index-json-apis-with-requests/" />
    <id>https://bennuttall.com/blog/2021/03/accessing-python-package-index-json-apis-with-requests</id>
    <published>2021-03-16T10:00:00+00:00</published>
    <updated>2021-03-09T00:19:36+00:00</updated>
    <category term="api" />
    <category term="json" />
    <category term="piwheels" />
    <category term="pypi" />
    <category term="python" />
    <category term="requests" />
    <summary><![CDATA[PyPI, the Python package index, provides a JSON API for information about its packages. This is essentially a machine-readable source of the same kind of data you can access while browsing the website. For example, as a human, I can head to the numpy project page in my browser, click around and see...]]></summary>
    <content type="html"><![CDATA[<p>PyPI, the Python package index, provides a JSON API for information about its packages. This is
essentially a machine-readable source of the same kind of data you can access while browsing the
website. For example, as a human, I can head to the numpy project page in my browser, click around
and see which versions there are, what files are available, and things like release dates and which
Python versions are supported:</p>
<figure class="wp-block-image">
<img src="images/numpy-project-page-1024x546.png" />
</figure>

<p>But if I want to be able to write a program to access this data, I can use the JSON API instead of
having to scrape and parse the HTML on these pages.</p>
<p>Aside: <em>on the old PyPI website, when it was hosted at </em><em>pypi.python.org</em><em>, the URL of the numpy
project page was at </em><em>pypi.python.org/pypi/numpy</em><em> – and accessing the JSON was a simple matter of
adding a </em><em>/json</em><em> on the end, hence: </em><em>https://pypi.org/pypi/numpy/json</em><em>. Now the PyPI website is
hosted at </em><em>pypi.org</em><em> – and numpy's project page is at </em><em>pypi.org/project/numpy</em><em> – but the new
site doesn't include rendering the JSON – but it's still running as it was before. So now, rather
than adding </em><em>/json</em><em> to the URL, you have to remember the URL of where they are.</em></p>
<p>You can open up the JSON for numpy in your browser by heading to its URL. Firefox renders it nicely
like this:</p>
<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-03-07-22-00-41.png" />
</figure>

<p>You can open up <code>info</code>, <code>releases</code> and <code>urls</code> to inspect the contents within. Or you can load it
into a Python shell. Here's a few lines to get started:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">requests</span>
<span class="n">url</span> <span class="o">=</span> <span class="s2">&quot;https://pypi.org/pypi/numpy/json&quot;</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
</code></pre></div>

<p>Once you have the data (calling <code>.json()</code> provides you with a
<a href="https://docs.python.org/3/tutorial/datastructures.html#dictionaries">dictionary</a> of the data), you
can inspect it:</p>
<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-03-07-22-46-21.png" />
</figure>

<p>Open up <code>releases</code> and inspect the keys inside it:</p>
<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-03-07-22-58-50-1024x206.png" />
</figure>

<p>This shows that <code>releases</code> is a dictionary with version numbers as keys. Pick one (say, the latest
one) and inspect that:</p>
<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-03-07-23-29-21.png" />
</figure>

<p>Each release is a list – the one we're looking at contains 24 items. But what is each item? Since
it's a list, we can index the first one and take a look:</p>
<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-03-07-23-29-54-1024x323.png" />
</figure>

<p>This item is a dictionary containing details about a particular file. So each of the 24 items in the
list relates to a file associated with this particular version number – the 24 files you see listed
at <strong><a href="https://pypi.org/project/numpy/1.20.1/#files">https://pypi.org/project/numpy/1.20.1/#files</a></strong></p>
<p>Now you could write a script which looks for something within the available data. For example, the
following loop looks for versions which have sdist (source distribution) files that specify a
<code>requires_python</code> attribute and prints them out:</p>
<div class="codehilite"><pre><span></span><code><span class="k">for</span> <span class="n">version</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">data</span><span class="p">[</span><span class="s1">&#39;releases&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">f</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;packagetype&#39;</span><span class="p">)</span> <span class="o">==</span> <span class="s1">&#39;sdist&#39;</span> <span class="ow">and</span> <span class="n">f</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;requires_python&#39;</span><span class="p">):</span>
            <span class="nb">print</span><span class="p">(</span><span class="n">version</span><span class="p">,</span> <span class="n">f</span><span class="p">[</span><span class="s1">&#39;requires_python&#39;</span><span class="p">])</span>
</code></pre></div>

<figure class="wp-block-image">
<img src="images/Screenshot-from-2021-03-07-23-46-34.png" />
</figure>

<h2>piwheels</h2>
<p>Last year I <a href="https://blog.piwheels.org/requires-python-support-new-project-page-layout-and-a-new-json-api/">implemented a similar
API</a>
on the piwheels website. <a href="https://www.piwheels.org/">piwheels.org</a> is a Python package index which
provides wheels (pre-compiled binary packages) for the Raspberry Pi architecture – it's essentially
a mirror of the package set on PyPI, but with Arm wheels instead of any files uploaded to PyPI by
package maintainers.</p>
<p>Since we mimic the URL structure of PyPI, i.e. you can change the <code>pypi.org</code> part of the URL of a
project page to <code>piwheels.org</code> and it'll show you a similar kind of project page – showing details
about which versions we have built and which files are available. Since I liked the way the old site
allowed you to add <strong>/json</strong> to the end of the URL, I made ours work like this, so numpy's project
page on PyPI is <strong><a href="https://pypi.org/project/numpy">pypi.org/project/numpy</a></strong> – on piwheels is
<strong><a href="https://www.piwheels.org/project/numpy">piwheels.org/project/numpy</a></strong> – and the JSON is at
<strong><a href="https://www.piwheels.org/project/numpy/json">piwheels.org/project/numpy/json</a></strong></p>
<p>There's no need to duplicate the contents of PyPI's API, only provide information about what's
available on piwheels, so we include a list of all known releases, some basic information and a list
of files we have:</p>
<figure class="wp-block-image size-large">
<img src="images/Screenshot-from-2021-03-07-23-58-43.png" />
</figure>

<p>Similarly to the previous PyPI example, you could create a script to analyse the API contents, for
example to show the number of files piwheels has for each version of numpy:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">requests</span>

<span class="n">url</span> <span class="o">=</span> <span class="s2">&quot;https://www.piwheels.org/project/numpy/json&quot;</span>
<span class="n">package</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>

<span class="k">for</span> <span class="n">version</span><span class="p">,</span> <span class="n">info</span> <span class="ow">in</span> <span class="n">package</span><span class="p">[</span><span class="s1">&#39;releases&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
    <span class="k">if</span> <span class="n">info</span><span class="p">[</span><span class="s1">&#39;files&#39;</span><span class="p">]:</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">{}</span><span class="s1">: </span><span class="si">{}</span><span class="s1"> files&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">version</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">info</span><span class="p">[</span><span class="s1">&#39;files&#39;</span><span class="p">])))</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="nb">print</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">{}</span><span class="s1">: No files&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">version</span><span class="p">))</span>
</code></pre></div>

<p>Also, each file contains some metadata:</p>
<figure class="wp-block-image size-large">
<img src="images/Screenshot-from-2021-03-08-00-03-11.png" />
</figure>

<p>One particularly useful thing here is the <code>apt_dependencies</code> field which lists the apt packages
needed to be able to use the library. In the case of this numpy file, as well as installing numpy
with pip, you'll also need to install <code>libatlas3-base</code> and <code>libgfortran</code> using Debian's package
manager <code>apt</code>.</p>
<p>Here is an example script which shows the apt dependencies for a package:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">requests</span>

<span class="k">def</span><span class="w"> </span><span class="nf">get_install</span><span class="p">(</span><span class="n">package</span><span class="p">,</span> <span class="n">abi</span><span class="p">):</span>
    <span class="n">url</span> <span class="o">=</span> <span class="s1">&#39;https://piwheels.org/project/</span><span class="si">{}</span><span class="s1">/json&#39;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">package</span><span class="p">)</span>
    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">version</span><span class="p">,</span> <span class="n">release</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="s1">&#39;releases&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">(),</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">filename</span><span class="p">,</span> <span class="n">file</span> <span class="ow">in</span> <span class="n">release</span><span class="p">[</span><span class="s1">&#39;files&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
            <span class="k">if</span> <span class="n">abi</span> <span class="ow">in</span> <span class="n">filename</span><span class="p">:</span>
                <span class="n">deps</span> <span class="o">=</span> <span class="s1">&#39; &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">file</span><span class="p">[</span><span class="s1">&#39;apt_dependencies&#39;</span><span class="p">])</span>
                <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;sudo apt install </span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">deps</span><span class="p">))</span>
                <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;sudo pip3 install </span><span class="si">{}</span><span class="s2">==</span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">package</span><span class="p">,</span> <span class="n">version</span><span class="p">))</span>
                <span class="k">return</span>

<span class="n">get_install</span><span class="p">(</span><span class="s1">&#39;opencv-python&#39;</span><span class="p">,</span> <span class="s1">&#39;cp37m&#39;</span><span class="p">)</span>
<span class="n">get_install</span><span class="p">(</span><span class="s1">&#39;opencv-python&#39;</span><span class="p">,</span> <span class="s1">&#39;cp35m&#39;</span><span class="p">)</span>
<span class="n">get_install</span><span class="p">(</span><span class="s1">&#39;opencv-python-headless&#39;</span><span class="p">,</span> <span class="s1">&#39;cp37m&#39;</span><span class="p">)</span>
<span class="n">get_install</span><span class="p">(</span><span class="s1">&#39;opencv-python-headless&#39;</span><span class="p">,</span> <span class="s1">&#39;cp35m&#39;</span><span class="p">)</span>
</code></pre></div>

<p>We also provide a general API endpoint for the list of packages, which includes download stats for
each package:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">requests</span>

<span class="n">url</span> <span class="o">=</span> <span class="s2">&quot;https://www.piwheels.org/packages.json&quot;</span>
<span class="n">packages</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<span class="n">packages</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">pkg</span><span class="p">:</span> <span class="p">(</span><span class="n">d_month</span><span class="p">,</span> <span class="n">d_all</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">pkg</span><span class="p">,</span> <span class="n">d_month</span><span class="p">,</span> <span class="n">d_all</span><span class="p">,</span> <span class="o">*</span><span class="n">_</span> <span class="ow">in</span> <span class="n">packages</span>
<span class="p">}</span>

<span class="n">package</span> <span class="o">=</span> <span class="s1">&#39;numpy&#39;</span>
<span class="n">d_month</span><span class="p">,</span> <span class="n">d_all</span> <span class="o">=</span> <span class="n">packages</span><span class="p">[</span><span class="n">package</span><span class="p">]</span>

<span class="nb">print</span><span class="p">(</span><span class="n">package</span><span class="p">,</span> <span class="s2">&quot;has had&quot;</span><span class="p">,</span> <span class="n">d_month</span><span class="p">,</span> <span class="s2">&quot;downloads in the last month&quot;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">package</span><span class="p">,</span> <span class="s2">&quot;has had&quot;</span><span class="p">,</span> <span class="n">d_all</span><span class="p">,</span> <span class="s2">&quot;downloads in total&quot;</span><span class="p">)</span>
</code></pre></div>

<h2>pip search</h2>
<p>Since <code>pip search</code> is currently disabled due to its XMLRPC interface being overloaded, people have
been looking for alternatives. You can use the piwheels JSON API to search for package names
instead, seeing as the set of packages is the same:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>

<span class="kn">import</span><span class="w"> </span><span class="nn">requests</span>

<span class="n">PIWHEELS_URL</span> <span class="o">=</span> <span class="s1">&#39;https://www.piwheels.org/packages.json&#39;</span>

<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">PIWHEELS_URL</span><span class="p">)</span>
<span class="n">packages</span> <span class="o">=</span> <span class="p">{</span><span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">r</span><span class="o">.</span><span class="n">json</span><span class="p">()}</span>

<span class="k">def</span><span class="w"> </span><span class="nf">search</span><span class="p">(</span><span class="n">term</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">pkg</span> <span class="ow">in</span> <span class="n">packages</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">term</span> <span class="ow">in</span> <span class="n">pkg</span><span class="p">:</span>
            <span class="k">yield</span> <span class="n">pkg</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
        <span class="n">results</span> <span class="o">=</span> <span class="n">search</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">lower</span><span class="p">())</span>
        <span class="k">for</span> <span class="n">res</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
            <span class="nb">print</span><span class="p">(</span><span class="n">res</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Usage: pip_search TERM&quot;</span><span class="p">)</span>
</code></pre></div>

<p>More information about the piwheels API can be found here:
<a href="https://www.piwheels.org/json.html">https://www.piwheels.org/json.html</a></p>]]></content>
  </entry>
</feed>