<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:dc="http://purl.org/dc/elements/1.1/"
      xml:base="https://reinout.vanrees.org/" xml:lang="en">
  <link rel="self"
        href="https://reinout.vanrees.org/weblog/atom.xml" />
  <link href="https://reinout.vanrees.org/weblog/"
        rel="alternate" type="text/html" />

  <title type="html">Reinout van Rees' weblog</title>
  <subtitle>Python, grok, books, history, faith, etc.</subtitle>
  <updated>2026-08-11T14:04:00+01:00</updated>
  <id>urn:syndication:a55644db8591c020bd38852775819a9a</id>

  
    <entry>
      <title>Managing email: peace of mind and efficiency with filters</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/08/11/managing-email.html" />
      <id>http://reinout.vanrees.org/weblog/2026/08/11/managing-email.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-08-11T00:00:00+01:00</published>
      <updated>2026-08-11T14:04:00+01:00</updated>

      
        <category term="python" />
      
        <category term="personal" />
      
        <category term="nelenschuurmans" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>Like probably everybody, I get a lot of email. Newsletters, weekly mails of some shops I
want to monitor for handy discounts, linkedIn updates, GitHub/dependabot notifications,
some left-over spam, calendar notifications, some Patreon stuff, etc, etc, etc.</p>
<p>Oh, and some real, individual emails! Oh, and spread over two accounts, work and
personal.</p>
<div class="section" id="goal-less-effort-and-lower-maintenance">
<h1>Goal: less effort and lower maintenance</h1>
<p>I want my inbox to be much more empty. I don't need it to be &quot;inbox zero&quot;, but having to
press the <tt class="docutils literal">PageDown</tt> key four times is a tad much. And everything is mixed together,
so if I start to clean up, I'm constantly having to switch between &quot;determining if some
GitHub mail can be deleted&quot; and &quot;reading an
interesting long newsletter&quot;.</p>
<p>I get a low-level feeling of anxiety as I'm bound to miss or neglect important emails.
And cleaning up the email is time-consuming as I'm constantly reading and ignoring the
same email subjects (and not really dealing with them).</p>
<p>So: I don't want to have my email all mixed up. The inbox should be emptier. And I want
to set it up in a way I can easily maintain it.</p>
</div>
<div class="section" id="solution-automatic-filtering-post-holiday-cleanup">
<h1>Solution: automatic filtering + post-holiday cleanup</h1>
<p>During my holiday, I barely check my email. So the usual pile of email is even higher.
The <strong>advantage of the big pile:</strong> all different kinds of email are visible in the
inbox. If you clean/sort/filter/unsubscribe/whatever <em>now</em>, you'll probably have most
different email sources covered.</p>
<p>Solution <strong>one</strong>: liberally unsubscribe from notifications and mailing lists. I've got
enough interesting and diverse sources of information of my own, so I really don't need
linkedIn's summaries (there's also more AI spam). I signed up for instagram to see one
person's model railway photos, but I don't need the never-ending emails suggesting other
people to follow. Also shop mailinglists where I signed up for to get some €5 discount
on an order.</p>
<p>Solution <strong>two</strong>: multiple inbox folders plus automatic filtering. I want to keep my
main inbox for the special or sporadic or important emails. So I created folders named
<tt class="docutils literal">inbox_someting</tt> and set up filters/rules to move emails into those folders
automatically based on sender or subject. For my work email I have four:</p>
<dl class="docutils">
<dt>inbox_alerts</dt>
<dd>Notifications about sites being down or colleagues' GitHub AI credits being
drained.</dd>
<dt>inbox_calendar</dt>
<dd>Microsoft calendar notification emails. Microsoft's email/calendar integration
is weird and non-standard and I seem to have to acknowledge meetings on every
device I own. My solution now is to acknowledge meetings from this inbox and to
use my iphone to actually look at the calendar.</dd>
<dt>inbox_github</dt>
<dd>Pull request emails, Dependabot messages, Renovatebot messages, issues being
opened.</dd>
<dt>inbox_teams</dt>
<dd>Notifications from Microsoft Teams and from Slack.</dd>
</dl>
<p>My personal email has five, at the moment:</p>
<dl class="docutils">
<dt>inbox_github</dt>
<dd>Same as for my work email, but then for my personal and open source projects.</dd>
<dt>inbox_leesvoer</dt>
<dd>&quot;Leesvoer&quot; is Dutch for &quot;reading fodder&quot;. So all those interesting longer
newsletters end up here. &quot;Interesting&quot; means &quot;procrastination&quot;, so not having
them directly in my main inbox keeps me from allowing myself to be distracted.
And if I have some time, for instance during a bus trip, I can read some of them
at leisure.</dd>
<dt>inbox_updates</dt>
<dd>Strava monthly summaries. Apple software update notices. Billing emails.
Commercial emails that I'm allowing, like a DIY shop that I like to monitor for
discounts. A venue I visit two times a year for a concert. Bank/insurance
newsletter. Kickstarter/Bandcamp. Weekly postgress newsletter.</dd>
<dt>inbox_bnls</dt>
<dd>I'm moderator (and partially sysadmin) for a <a class="reference external" href="https://forum.beneluxspoor.net/">Dutch model railway forum</a> (which is often abbreviated &quot;bnls&quot;).
Moderation requests and personal messages end up here.</dd>
<dt>inbox_bnlsundeliver</dt>
<dd>Somehow I'm getting &quot;email cannot be delivered&quot; error messages from the forum
now, so I'm stuffing them all in this folder for later cleanup of emailadresses
in the forum. That way they don't clog up my inbox. This is probably a temporary
inbox-folder.</dd>
</dl>
<p>Once in a while, I'll quickly look into <tt class="docutils literal">inbox_updates</tt>. I'll read some of them. Most
can be deleted. In any case, within no-time the folder will be empty. There's nothing in
there I want to keep, normally. That's not something I could do that quickly when it was
mixed with everything else in my single main inbox!</p>
<p>Same with quickly going through (and deleting) the GitHub notifications.</p>
<p>Emails that can be more important are in my main inbox. Or in <tt class="docutils literal">inbox_alerts</tt> or
<tt class="docutils literal">inbox_bnls</tt> for instance.</p>
</div>
<div class="section" id="technical-details">
<h1>Technical details</h1>
<p>I'm running our &quot;vanrees.org&quot; email via the German <a class="reference external" href="https://mailbox.org/en/">mailbox.org</a>. My work email is sadly Microsoft. I didn't really want to
set up filters in a mail program (on my computer), as I want those filters to run
continuously, also when I'm on holiday and my computer is stored away. And when the
thing is sleeping in my backpack.</p>
<p>So... for my work email, I set up rules in Microsoft Outlook's web interface. There are
just a few of them, basically just shuffling all GitHub stuff into <tt class="docutils literal">inbox_github</tt> and
meeting info in to <tt class="docutils literal">inbox_calendar</tt>.</p>
<p>For my personal email, things are a bit more complex. Sure, the majority are simple
rules, but some have &quot;if from this address but with this subject&quot;-like rules. That's why I
landed (after some searching) on <a class="reference external" href="https://github.com/lefcha/imapfilter">imapfilter</a>. I
run it on my Linux server via a cronjob. It has a configuration file that I can back up.</p>
<p>Some examples from my config file:</p>
<pre class="literal-block">
results = account1.INBOX:contain_from('notifications&#64;github.com') +
          account1.INBOX:contain_from('&#64;md.getsentry.com') +
          account1.INBOX:contain_from('noreply&#64;github.com')
results:move_messages(account1.inbox_github)

results = account1.INBOX:contain_to('webmaster&#64;beneluxspoor.net') *
          account1.INBOX:contain_subject('Undelivered Mail Returned to Sender')
results:move_messages(account1.inbox_bnlsundeliver)
</pre>
<p>And I took the opportunity to filter out some spam messages that somehow managed to slip
around the normal spam mechanism:</p>
<pre class="literal-block">
results = account1.INBOX:contain_from('subaru') +
          account1.INBOX:contain_from('kontaktpush.de')
results:move_messages(account1.Junk)
</pre>
</div>
<div class="section" id="conclusion-after-a-few-days">
<h1>Conclusion after a few days</h1>
<p>So... pretty happy at the moment! My main inbox is much cleaner and keeping up to date is
easier. Handling the other inbox-folders is also easier as there's just one category of
email in them.</p>
<p>And... as I now have a system, I can expand it. I just looked at my inbox and saw a
mail that ought to go into <tt class="docutils literal">inbox_updates</tt>. Adding it, now that I have the system, is
just one minute of work. Which will save me many minutes in the years to come!</p>
</div>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Foss4g NL: late afternoon sessions (web components + railway API + long-living data)</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/07/09/3-railway.html" />
      <id>http://reinout.vanrees.org/weblog/2026/07/09/3-railway.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-07-09T00:00:00+01:00</published>
      <updated>2026-07-09T14:15:00+01:00</updated>

      
        <category term="foss4g" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/07/08/index.html">my summaries</a> of
the 2026 one-day <a class="reference external" href="https://foss4g.nl/">Foss4g open source geo conference</a> in Groningen, NL).</p>
<div class="section" id="railmaps-prorail-open-source-gis-viewer-based-on-generic-web-components-geoblocks">
<h1>Railmaps: Prorail open source GIS viewer based on generic web components (GeoBlocks)</h1>
<p><strong>Warning</strong>: the title mentioned it as an open source GIS viewer, but it is <strong>not
open source</strong>. After the presentation I asked where I could find it, but they said it
was internal-only. They <em>used</em> open source, but the project itself isn't.</p>
<p>Note: the actual &quot;Railmaps&quot; website is only accessible for the company itself. I'll
mention the URL, but it isn't that useful: <a class="reference external" href="https://www.railmaps.nl">https://www.railmaps.nl</a> . <a class="reference external" href="https://www.prorail.nl/">ProRail</a> is the Dutch national railway infrastructure maintainer.</p>
<p>Railmaps used GeoWeb previously. They wanted to move away to open source, but wanted to
keep the existing functionality. They started with interviews with users. Then a UX
designer made designs in Figma. Then they started iteratively building it with
typescript/lit. Last thursday they went live.</p>
<p>One thing they wanted to solve was <em>redundant code</em>. They had many repositories with
maps and there was a lot of duplication. The various map websites also were also not
consistent regarding UI/UX.</p>
<p>They created web components to handle it all (they called it geoblocks). The three main
components:</p>
<ul class="simple">
<li>Some central map.</li>
<li>Map layers (some standard toggle system for switching on/off various layers and adding them).</li>
<li>Sidebar tools (user actions, zooming, search tools, etc.)</li>
</ul>
<p>What they used: Openlayers (for the actual map), WebAwesome (robust component
foundation), Lit Elements (lightweight web components). And lots of open source tools.
They made sure it works with most javascript frameworks.</p>
<p>He mentions <a class="reference external" href="https://storybook.js.org/">Storybook</a> as a fantastic documentation tool
for interactive components.</p>
</div>
<div class="section" id="ns-national-railway-company-api-portal-niek-van-ruler">
<h1>NS (=national railway company) API portal - Niek van Ruler</h1>
<p>NS means <a class="reference external" href="https://en.wikipedia.org/wiki/Nederlandse_Spoorwegen">Nederlandse Spoorwegen</a>, the main railway company in
the Netherlands.</p>
<p>He noticed that the NS had a nice API. You only have to request a key manually, and then
you can access quite a lot of data. Prices, public bike info, data about stations,
station floor plans, live train location info, disruptions, all details of every
individual train trip, geojson with the train tracks, etc.</p>
<p>(He showed a couple of API responses and the demo website he build with it.)</p>
<p>His demo: <a class="reference external" href="https://geodienst.xyz/ns/">https://geodienst.xyz/ns/</a></p>
<p>A Python wrapper for the NS API: <a class="reference external" href="https://github.com/aquatix/ns-api">https://github.com/aquatix/ns-api</a></p>
</div>
<div class="section" id="core-flow-sovereign-nature-data-readable-in-2075-joris-roling">
<h1>Core flow: sovereign nature data, readable in 2075 - Joris Röling</h1>
<p>(Nice detail: the conference is being held in the university's &quot;Röling building&quot;, which
is named after Joris' grandfather, <a class="reference external" href="https://en.wikipedia.org/wiki/Bert_R%C3%B6ling">Bert Röling</a>, one of the judges at the WW2 Tokyo
trial.)</p>
<p>His aim is to have nature data not only usable today, but also in fifty years' time.
Nature data? The Dutch nature is monitored: vegetation, species, administrative
geographies.</p>
<p><em>Core Flow</em> is the core of a larger data platform. Its promise is <em>the data must outlive
every tool we used to make it</em>.</p>
<p>Public data rarely dies on purpose. It dies in boring ways: a license expires; someone
switches off a server; the format is only readable by one vendor. Nature data often
spans 50 to 100 years, but our tooling lasts only between 3-5 years.</p>
<p>Their solution:</p>
<ul class="simple">
<li>Storage is just files: they chose Parquet on S3-compatible storage. This means there's
no database system that might not be available.</li>
<li>Querying is with DuckDB now, but Parquet should be queryable in 50 years, whatever the tool.</li>
<li>They use stable identifiers. Every file is named with a UUID and is immutable.
There's a folder structure, too, but that's only for convenience. Immutable: it means
you can look at various versions. You can re-discover what we thought at an earlier
date.</li>
</ul>
<p>The approach is almost serverless. It helps that nothing needs to be running: the files
are &quot;just&quot; stored. You access the files with DuckDB when you need to, the current API
and website are only for convenience.</p>
<p>Tasks that need to be done are run through GitLab's regular pipeline. So they're just
using regular CI tools: stuff you can easily run with other system. You could use one of
the current beautiful workflow systems (prefect, airflow, etc.), but how long will those
be around?</p>
<p>So: data stored in files. On top, DuckDB as access tool. On top of that some
works-for-now tools like an API and a website.</p>
<p>Note that they store both the original data <em>and</em> their converted-to-parquet version.</p>
<img alt="https://reinout.vanrees.org/images/2026/straalzender3.jpeg" src="https://reinout.vanrees.org/images/2026/straalzender3.jpeg" />
<p><em>Unrelated photo: we have two offices in the center of Utrecht. As a handy connection,
we're using a radio link</em> (&quot;straalverbinding&quot;) <em>between the two. We have line of sight.
This view is from our second building. Noticable is the &quot;city castle&quot; Oudaen: city
politics in Utrecht could get a bit lively in the middle ages.</em></p>
</div>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Foss4g NL: early afternoon sessions (accessibility + geonode)</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/07/09/2-afternoon.html" />
      <id>http://reinout.vanrees.org/weblog/2026/07/09/2-afternoon.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-07-09T00:00:00+01:00</published>
      <updated>2026-07-09T13:00:00+01:00</updated>

      
        <category term="foss4g" />
      
        <category term="django" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/07/08/index.html">my summaries</a> of
the 2026 one-day <a class="reference external" href="https://foss4g.nl/">Foss4g open source geo conference</a> in Groningen, NL).</p>
<div class="section" id="accessibility-geoinformation-for-everybody-liliana-santoso-avis-jedidja-van-der-sluis-stoutjesdijk">
<h1>Accessibility: geoinformation for everybody - Liliana Santoso-Avis &amp; Jedidja van der Sluis - Stoutjesdijk</h1>
<p><a class="reference external" href="https://www.w3.org/WAI/standards-guidelines/wcag/">WCAG (Web Content Accessibility Guidelines)</a> deals with accessibility
(<tt class="docutils literal">a11y</tt>). <em>(I personally try to take accessibility a bit into account,
proper headings and reasonably contrast-rich colors on my website, for instance.
I've made other summaries of &quot;a11y&quot; talks, for instance</em> <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/04/24/9-accessible-docs.html">this one</a> <em>about
accessible documentation, held at the 2025 pycon.de.</em></p>
<p>It is not just accessibility, but really about the quality of the information as a
whole. Thinking about the accessibility guidelines (listed below) helps you create
better information projects.</p>
<ul class="simple">
<li>Perceivable</li>
<li>Operable, for instance navigating a website with keyboard instead of mouse.</li>
<li>Understandable</li>
<li>Robust</li>
</ul>
<p>When making a map viewer, we often claim &quot;we're an exception&quot;, but that's not fully the
case. Your map component should not be a &quot;keyboard trap&quot;, for instance. And the contrast
of your map should be right. And if the map is essential for navigating through the
rest of the site, you also can't claim an exception.</p>
<p>You need a <strong>mindset shift</strong>. From &quot;bah, <em>extra</em> work&quot; to &quot;hurray, <em>better</em> work&quot;.</p>
<p>They started with an inventory, for instance of the applicable laws. Then getting the
roles/responsibilities right. Then lots of experience sharing. Now they want to get
certification for the work they did. And they want to do outreach. And they now try to
cooperate with partners (like other provinces and government agencies), software
companies and other organisations.</p>
<p>In tourist areas, you sometimes have tactile maps. You can also do that in Qgis! You can
print those maps. <a class="reference external" href="https://touch-mapper.org/en/">https://touch-mapper.org/en/</a></p>
<p>Colors: don't use only colors to indicate differences. Also differ the shapes of points,
for instance. As a test, try to sort M&amp;Ms while wearing colored glasses...</p>
<p>Some browser tools: <a class="reference external" href="https://chromewebstore.google.com/detail/taba11y-tab-order-accessi/aocppmckdocdjkphmofnklcjhdidgmga?pli=1">taba11y</a>
to show the tab order of your site. Color contrast checker, heading map, leat's get
color blind, link checker, WCAG color contrast checker.</p>
</div>
<div class="section" id="geonode-digital-sovereignty-in-practice-finn-peranovich-guido-schaepman">
<h1>GeoNode: digital sovereignty in practice - Finn Peranovich &amp; Guido Schaepman</h1>
<p>Two Dutch <a class="reference external" href="https://en.wikipedia.org/wiki/Water_board_(Netherlands)">water boards</a>,
<a class="reference external" href="https://en.wikipedia.org/wiki/Hoogheemraadschap_van_Rijnland">Rijnland</a> and
<a class="reference external" href="https://www.schielandendekrimpenerwaard.nl/english/">Schieland en de Krimpenerwaard</a>,
cooperated in a project to move to open source with <a class="reference external" href="https://geonode.org/">GeoNode</a>.</p>
<p>They did an inventory in 2024 whether open source was an option. They looked at the
current usage and identified possible open source alternatives. Open source promised
more autonomy (no ESRI lock-in, geopolitical, etc.), lower costs (the costs of switching
would be paid back within three years), more innovation and better compliance (both NL
and EU laws).</p>
<p>The first test was with public-facing data that previously was served with ArcGIS
server.</p>
<p>Geonode is a management layer on top of geoserver. It uses open source tools like
Django, Mapstore, Postgresql, RabbitMQ. They run Geoserver and GeoNode inside a
kubernetes cluster. Conversion from ArcGIS server was done with several homemade
scripts.</p>
<p>Tip: Qgis has a handy Geonode plugin for browsing everything in your Geonode.</p>
<p>They were surprised by the quality of GeoNode: everything they needed from ArcGIS server
is also available in GeoNode. They're currently in the test phase, they'll soon go to
production. They really want to make other water boards enthusiastic about open source,
too, hopefully leading to cost sharing.</p>
<img alt="https://reinout.vanrees.org/images/2026/straalzender2.jpeg" src="https://reinout.vanrees.org/images/2026/straalzender2.jpeg" />
<p><em>Unrelated photo: we have two offices in the center of Utrecht. As a handy connection,
we're using a radio link</em> (&quot;straalverbinding&quot;) <em>between the two. We have line of sight,
as you can see in this photo. The dark gray wall to the right of the far radio link
doesn't look like much, but it is part of our office and part of one of the oldest
buildings (around 1200!) in Utrecht. (See</em> <a class="reference external" href="https://nl.wikipedia.org/wiki/Putruwiel_(Utrecht)">wikipedia</a>).</p>
</div>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Foss4g NL: morning sessions (sovereignty + geoserver 3)</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/07/09/1-morning.html" />
      <id>http://reinout.vanrees.org/weblog/2026/07/09/1-morning.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-07-09T00:00:00+01:00</published>
      <updated>2026-07-13T22:21:00+01:00</updated>

      
        <category term="foss4g" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/07/08/index.html">my summaries</a> of
the 2026 one-day <a class="reference external" href="https://foss4g.nl/">Foss4g open source geo conference</a> in Groningen, NL).</p>
<div class="section" id="increasing-our-digital-sovereignty-ronald-stolk">
<h1>Increasing our digital sovereignty - Ronald Stolk</h1>
<p>There are <strong>public values</strong> we have: autonomy, humanity, justice. Things like
inclusivity, academic freedom, security, etc. Those public values are under threat in
our digital environment, mostly due to Big Tech.</p>
<p>Digital sovereignty suddenly became more acute due to the geopolitical influence on our
digital environment. Big Tech standing in the front row at Trump's inauguration... Several
International Criminal Court judges being locked out of their Microsoft accounts was a huge
warning. You can only guarantee the public values if you've got digital sovereignty.</p>
<p>Big tech has lots of risks. You don't have a control over your own data. LinkedIn's
search (owned by Microsoft) takes into account the emails stored in Microsoft's systems,
for instance. Lock-in with options of a &quot;kill switch&quot;. Influence due to censorship or
suppression of &quot;woke&quot; opinions.</p>
<p>Warning: don't trust all the &quot;sovereignty washing&quot; being done by Big Tech: &quot;we now have
EU storage, so there are no sovereignty problems anymore&quot;. That's just not true. Just
like &quot;vegan chicken filet&quot;.</p>
<p>What can you do? Well, investigate what you're using. Do you have an <strong>exit strategy</strong>?
What is the most at risk?</p>
<ul class="simple">
<li>Access: lots of organisations work with Microsoft's login system (&quot;Entry ID&quot;). Exactly
the same system that was used to block the International Criminal Court...</li>
<li>Data: is it stored using open standards? Do you have it stored locally? Or is it
somewhere in the cloud? Make sure your crown jewels are stored somewhere
(geopolitically) safe.</li>
</ul>
<p><strong>GIS data</strong> luckily is often public. In the Netherlands you have several public
repositories. But lots of data and research is stored in public clouds. And what about
climate research data from the USA?</p>
<p>Look at open source software. It is of really good quality! Also look at Nextcloud,
Peertube and Mastodon (alternatives for Microsoft, Youtube and Twitter), those are for
instance available through the Dutch universities' IT organisation (SURF). Universities
can go to that organisation also for big data storage and compute. There are also
European options, like EOSC.</p>
<p>They're looking at a <em>digital emergency kit</em>: what if a researcher gets cut off from US Big
Tech systems, how do we get that person back online quickly?</p>
<p>AI: how open is your LLM? look at open source models, like DeepSeek, Qwen, MiMo, Llama.</p>
<p>The three northernmost provinces in the Netherlands want to be the third <em>digitization
region</em> of the Netherlands. Eindhoven/Brainport focuses on hardware (ASML is
headquartered there), Amsterdam is software (mostly big tech like). Groningen wants to
aim at smaller-scale sovereignty solutions. One of the elements is the <a class="reference external" href="https://nlaifabriek.nl">AI fabriek</a>, part of a set of EU AI factories.</p>
</div>
<div class="section" id="say-hello-to-geoserver-3-jody-garnett">
<h1>Say hello to GeoServer 3 - Jody Garnett</h1>
<p><a class="reference external" href="https://geoserver.org/">GeoServer</a> version 3.0.0 <a class="reference external" href="https://geoserver.org/announcements/vulnerability/2026/06/11/geoserver-3-0-0-released.html">was released last month</a></p>
<p>In 2024 they had an upgrade cascade challenge. Java upgrade, spring upgrade (twice!),
spring security upgrade. Moving from Java EE to Jakarta. Going from Java 11 to 17 meant
that the important imaging library wasn't available anymore, which was a big problem.</p>
<p>Doing all this at the same time would take lots of work and lots of money. Several
companies (CampToCamp, GeoSolutions, GeoCat) cooperated to make the changes, supported
by a fundraising campaign. They managed to get all the main upgrades working in a solid week
of programming.</p>
<p>UI upgrades. Documentation is now in markdown. Docs and UI have dark mode now. Forms are
in two columns, and they use tabs to clear up the form. Search works better. And you
have breadcrumbs now, so you can keep track of your context. Full-screen map preview.
More information on a layer's page, also for people that aren't logged in. CORS support
via the admin interface, you don't need to edit an XML anymore.</p>
<p><strong>Upgrading</strong>: you can just upgrade, there are <strong>no changes</strong> to the data directory!</p>
<ul class="simple">
<li>Some rarely used modules have been moved to extensions.</li>
<li>There's a new OAuth/OpenID connector.</li>
<li>Netcdf can be a single file now, you don't need the old directory of indexes anymore.</li>
</ul>
<p>Tip: upgrade quickly. The security landscape is under stress. Lots of issues are being
found in all open source projects. They're happy that GeoServer upgraded to lots of
newer major versions of the dependencies: it allowed them to keep up-to-date with the
latest security releases. <strong>Update early and often</strong>.</p>
<p>In response to a question: the Docker image is ready.</p>
</div>
<div class="section" id="creating-map-viewers-with-generic-geo-components-jaap-willem-sjoukema">
<h1>Creating map viewers with generic geo components - Jaap-Willem Sjoukema</h1>
<p>The Dutch &quot;Kadaster&quot; (the country's central mapping agency) created &quot;GGC&quot;, <a class="reference external" href="https://www.generiekegeocomponenten.nl/">generic geo
components</a>. They have multiple websites
(kadaster, pdok, etc) that all use the same geo components but have a different
look-and-feel. They are based on OpenLayers, Angular and Cesium.</p>
<p>Since May this year, the components are open source.</p>
<p>Some component examples:</p>
<ul class="simple">
<li>Map, both 2D and 3D.</li>
<li>Location search.</li>
<li>Selection/filtering tools.</li>
<li>Feature information.</li>
<li>Legend.</li>
</ul>
<p>The idea is that a competent programmer ought to be able to make a map viewer in one
day.</p>
<p>There are some restrictions at the moment:</p>
<ul class="simple">
<li>The coordinate system is hardcoded to the Dutch <em>Rijksdriehoek</em>, but they're going to
change it.</li>
<li>It are <em>Angular</em> components, not &quot;web components&quot;. There's a talk later in the day
about &quot;GeoBlocks&quot;, which is a similar system that <em>is</em> web-component-based.</li>
</ul>
<img alt="https://reinout.vanrees.org/images/2026/straalzender1.jpeg" src="https://reinout.vanrees.org/images/2026/straalzender1.jpeg" />
<p><em>Unrelated photo: we have two offices in the center of Utrecht. As a handy connection,
we're using a radio link</em> (&quot;straalverbinding&quot;) <em>between the two. We have line of sight.
Above our radio link you see the newly renovated main church tower of Utrecht, the
highest in the Netherlands.</em></p>
</div>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Python Leiden (NL) meetup summaries</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/07/02/leiden-july.html" />
      <id>http://reinout.vanrees.org/weblog/2026/07/02/leiden-july.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-07-02T00:00:00+01:00</published>
      <updated>2026-07-02T19:46:00+01:00</updated>

      
        <category term="python" />
      
        <category term="pun" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>Two summaries of the July 2 2026 <a class="reference external" href="https://pythonleiden.nl/">Python meetup in Leiden</a>.
I've omitted one, &quot;Python with Karel&quot; by EiEi Tun, as I've <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/05/21/meetup-utrecht.html#learning-python-with-karel-eiei-tun-h">made a summary of that talk
in Utrecht</a>
a month ago, already :-)</p>
<div class="section" id="building-modern-internal-team-clis-with-incremental-automation-farid-nouri-neshat">
<h1>Building modern internal team CLIs with incremental automation - Farid Nouri Neshat</h1>
<p>Obligatory xkcd cartoons: <a class="reference external" href="https://xkcd.com/974">https://xkcd.com/974</a> and <a class="reference external" href="https://xkcd.com/1319">https://xkcd.com/1319</a> and
<a class="reference external" href="https://xkcd.com/1205">https://xkcd.com/1205</a></p>
<p><strong>Toil</strong>: manual, repetitive, automatable, distracting you from your real work, no
enduring value. Yes, he likes to automate things :-) Some examples of repetitive manual
tasks:</p>
<ul class="simple">
<li>Creating dev containers.</li>
<li>Gathering data for troubleshooting.</li>
<li>Something that needs to be set manually in a database.</li>
<li>Setting up a new AWS account.</li>
<li>Creating a new dev environment on the new colleague's laptop.</li>
</ul>
<p>How to automate? <strong>Do it iteratively!</strong> Your boss might not like you to spend a day
automating the task. But if you do it small steps at a time...</p>
<ul>
<li><p class="first">Do it manually the very first time.</p>
</li>
<li><p class="first">Then start with documenting the steps.</p>
</li>
<li><p class="first">Then turn it into a do-nothing scaffold script:</p>
<pre class="literal-block">
def step1():
    print(&quot;Open the AWS page manually&quot;)
    input(&quot;Press enter to continue&quot;)
</pre>
</li>
<li><p class="first">Everytime you do the task, automate a small bit and flesh out the script over time.</p>
</li>
<li><p class="first">After many iterations, you'll have automated it fully!</p>
</li>
</ul>
<p>&quot;I don't have time to automate it&quot;, you might say? Well, why don't you have time? Is it
perhaps because you haven't automated things?</p>
<p>A good motivator: if you <em>hate</em> the task... <strong>Hate driven development</strong>  :-)</p>
<p>After a while, you'll have lots of random scripts. Stuff them in a repository. Slowly
document them. Try to get them to use the same conventions. Perhaps you can re-use
functionality in a library.</p>
<p>Something you need quicky is some CLI, a command line interface. He likes <a class="reference external" href="https://typer.tiangolo.com/">typer</a> to make his CLIs: much nicer than Python's own
&quot;argparse&quot;:</p>
<pre class="literal-block">
import typer

app = typer.Typer()


&#64;app.command()
def hello(name: str):
    print(f&quot;Hello {name}&quot;)


if __name__ == &quot;__main__&quot;:
    app()
</pre>
<p>AI comment: AI agents can use your CLI. Use the docstring and help functions to help
orient the AI to your custom CLI. You can, for instance, use a CLI to give the agent
access to your database's content without giving it direct access to the database.</p>
<p>AI agents can be dangerous. A solution might be to use &quot;feature flags&quot;. You can disable
production access until you enable some setting or flag that AI doesn't know about.</p>
<p>He also mentioned the <a class="reference external" href="https://rich.readthedocs.io/">rich library</a> for formatting and
colorizing your textual output.</p>
</div>
<div class="section" id="what-ive-learned-maintaining-the-mcp-python-sdk-marcelo-trylesinski">
<h1>What I’ve learned maintaining the MCP Python SDK - Marcelo Trylesinski</h1>
<p>He's one of the three maintainers of the <a class="reference external" href="https://py.sdk.modelcontextprotocol.io/">MCP Python SDK</a>. SDK = software development kit. MCP: model
context protocol, so a way for AI agents to connect to some other piece of software.</p>
<p>MCP is basically &quot;OpenAPI for your agents&quot;. It exposes three things from the server
side:</p>
<ul class="simple">
<li>tools</li>
<li>resources</li>
<li>prompts (though tools are mostly the only thing that is used)</li>
</ul>
<p>The client provides:</p>
<ul class="simple">
<li>sampling</li>
<li>elicitation (=&quot;producing a reaction&quot;, so mostly it means that the AI server asks you
questions)</li>
<li>roots</li>
<li>logging</li>
</ul>
<p>The MCP spec kept growing. But clients never caught up, so it was mostly only the
&quot;tools&quot; part that got used.</p>
<p>A big problem is that servers cannot scale. The AI server
might have lots of machines with a loadbalancer in front of it, but as a user you need
to stay connected to the one machine that has your context.</p>
<p>There's a new version of the spec (final version this month) that actually <em>removed</em>
stuff, instead of growing. The &quot;client provides&quot; list mentioned above? Sampling, roots
and logging are gone as they were hardly used.</p>
<p>MCP is now a small core, with optional extensions. Examples: tasks, MCP apps, enterprise
auth.</p>
<p>The MCP Python SDK supports the new version, too. He demonstrated a small Python script
that had a function that said you could have three bananas. He connected it via MCP to
Claude and could ask Claude for the number of available bananas. It got back, via the
Python tool, with the correct answer.</p>
</div>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>Utrecht (NL) Python meetup summaries</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/05/21/meetup-utrecht.html" />
      <id>http://reinout.vanrees.org/weblog/2026/05/21/meetup-utrecht.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-05-21T00:00:00+01:00</published>
      <updated>2026-05-21T20:32:00+01:00</updated>

      
        <category term="python" />
      
        <category term="pun" />
      
        <category term="django" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>I made summaries at the <a class="reference external" href="https://www.meetup.com/nl-nl/pyutrecht/events/313938935/">4th PyUtrecht meetup</a> (in Nieuwegein, at Qstars this time).</p>
<div class="section" id="qstars-it-and-open-source-derk-weijers">
<h1>Qstars IT and open source - Derk Weijers</h1>
<p><a class="reference external" href="https://www.qstars.nl/">Qstars IT</a> hosted the meeting. It is an
infra/programming/consultancy/training company that uses lots of Python.</p>
<p>They also love open source and try to sponsor where possible.</p>
<p>One of the things they are going to open source (next week) is a &quot;cable thermal model&quot;,
a calculation method to determine the temperature of underground electricity cables. The
Netherlands has a lot of net congestion... So if you can have a better grid usage by
calculating the real temperature of cables instead of using an estimated temperature,
you might be able to increase the load on the cable without hitting the max temperature.
Coupled with &quot;measurement tiles&quot; that actually monitor the temperature.</p>
<p>They build it for one of the three big electricity companies in the Netherlands and got
permission to open source it so that the other companies can also use it. They hope it
will have real impact.</p>
<p>He explained an open source project he started personally: &quot;the space devs&quot;. Integrating
rocket launch data and providing an API. Now it has five core developers (and got an
invitation to the biggest space conference, two years ago!)</p>
<p>Some benefits from writing open source:</p>
<ul class="simple">
<li>You build your own portfolio.</li>
<li>You can try new technologies. Always nice to have the skill to learn new things.</li>
<li>You improve your communication skills (both sending and receiving).</li>
<li>You can make your own decisions.</li>
<li>You write in the open.</li>
<li>Perhaps you help others with your work.</li>
<li>You could be part of a cummunity.</li>
<li>It is <strong>your</strong> code.</li>
</ul>
<p>How to start?</p>
<ul class="simple">
<li>Reach out to other communities.</li>
<li>Read <strong>and improve</strong> documentation.</li>
<li>Find good first issues.</li>
<li>Be proactive.</li>
<li>Don't be afraid to ask questions (and don't let negative comments discourage you).</li>
</ul>
<p>When working on open source, make sure you take security serious. People nowadays like
to use supply chain attacks via open source software. So use 2FA and look at your
deployment procedure.</p>
</div>
<div class="section" id="learning-python-with-karel-eiei-tun-h">
<h1>Learning Python with Karel - EiEi Tun H</h1>
<p>What is <a class="reference external" href="https://github.com/alts/karel">Karel</a>? A teaching tool/robot for learning
programming. You need to steer a robot in an area and have it pick up or dump objects.
And... in the meantime you learn how to use functions and loops.</p>
<p>Karel only has a <tt class="docutils literal">turn_left()</tt> function. So if you want to have it turn right, it is
handy to add a function for it:</p>
<pre class="literal-block">
def turn_right():
    turn_left()
    turn_left()
    turn_left()
</pre>
<p>Simple, but you <strong>have</strong> to learn it sometime!</p>
<p>In her experience, AI can help a lot when learning to code: it explains stuff to you
like you're a five-year-old, and that's perfect.</p>
<p>If you want to play with Karel: <a class="reference external" href="https://compedu.stanford.edu/karel-reader/docs/python/en/ide.html">https://compedu.stanford.edu/karel-reader/docs/python/en/ide.html</a></p>
</div>
<div class="section" id="json-freedom-or-chaos-how-to-trust-your-data-bart-dorlandt">
<h1>JSON freedom or chaos; how to trust your data - Bart Dorlandt</h1>
<p>For this talk, I'm pointing at <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/05/08/5-json-freedom.html">the PyGrunn summary</a> I made three weeks
ago. I liked the talk!</p>
</div>
<div class="section" id="practical-software-architecture-for-python-developers-henk-jan-van-hasselaar">
<h1>Practical software architecture for Python developers - Henk-Jan van Hasselaar</h1>
<p>There are several levels of architecture. Organization level. System level.
Application, Code.</p>
<p><strong>Cohesion</strong>: &quot;the degree to which the elements inside a module belong together&quot;. What
does it mean? Working towards the same goal or function. Together means something like
distance. When two functions are in separate libraries, they're not together. It is also
important for <em>cognitive load</em>.</p>
<p><strong>Coupling</strong>: loose coupling versus high coupling. You want loose coupling, so that
changes in one module don't affect another module.</p>
<p>You don't really have to worry about coupling and cohesion in existing systems that
don't need to be changed. But when you start changing or build something new: take
coupling/cohesion into account.</p>
<p>Software architecture is a tradeoff. Seperation of concerns is fine, but it creates
layers and thus distance, for instance.</p>
<p>Python is one of the <strong>most difficult languages</strong> when it comes to clean coding and
clean architecture. You're allowed to do so many dirty things! Typing isn't even
mandatory...</p>
<p>He showed a simple REST API as an example. Database model + view. But when you change
the database model, like a field name, that field name automatically changes in the API
response. So <em>your</em> internal database structure is <strong>coupled</strong> to the function at the
customer that consumes the API.</p>
<p>What you actually need to do is to have a better &quot;contract&quot;. A <strong>domain model</strong>. In his
example code, it was a Pydantic model with a fixed set of fields. A converter modifies
the internal database model to the domain model.</p>
<p>You can also have <strong>services</strong>, generic pieces of code that work on domain models. And
<strong>adapters</strong> to and from domain models, like converting domain models to csv.</p>
<p>Finding the balance is the software architect's job.</p>
<p>What is the least <strong>you</strong> should do as a software developer? At least to create a domain
layer. Including a validator.</p>
<p>There was a question about how to do this with Django: it is hard. Django's models are
everywhere. And you really need a clean domain layer...</p>
</div>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>PyGrunn: Python at Spotify: twenty years - Gijs Molenaar</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/05/08/7-python-at-spotify.html" />
      <id>http://reinout.vanrees.org/weblog/2026/05/08/7-python-at-spotify.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-05-08T00:00:00+01:00</published>
      <updated>2026-05-08T14:37:00+01:00</updated>

      
        <category term="python" />
      
        <category term="pygrunn" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/05/08/index.html">my summaries</a> of
the 2026 one-day <a class="reference external" href="https://pygrunn.org/">PyGrunn conference</a> in Groningen, NL).</p>
<p>His parents owned a record store in some Dutch town. First records, then CDs. A social
shop where you would gather to listen to CDs to determine whether to buy them. His
father's brother actually started the oldest record store in Amsterdam, Concerto. It
still exists.</p>
<p>Then the world changed. Napster, CD-burners. Illegal downloading. (He himself was one of
them). His parents stopped selling music in 2008. He himself got into engineering. He
ended up in South Africa, doing workflow orchesration for radio telescopes. There he
introduced Docker and containers. He gave a <a class="reference external" href="https://reinout.vanrees.org/weblog/2016/05/13/2_kliko.html">talk at Pygrunn about it in 2016</a>.</p>
<p>While he was in the South African desert, in Sweden someone started the <a class="reference external" href="https://open.spotify.com/">Spotify</a> company. He actually had used a library (&quot;luigi&quot;) made by
Spotify in his telescope work.</p>
<p>He tried to get a job at Spotify and succeeded. So the kid who grew up in a record store
now works at the company that reinvented how people listen to music.</p>
<p>It all started for Spotify with Java (jboss 5). They hated it. It was replaced with Python:
the reason was that nobody hated it. 80% of the code became python. A lot was async:
they used &quot;twisted&quot; in the beginning, later gevent and greenlets.</p>
<p>But the Python GIL (global interpreter lock) made multi-core impossible. So you needed
to use multiple processes, each with their own overhead. They also didn't like the lack
of type safety: they have 100+ services. Some of those problems are partially solved
now, but at the time the switched back to Java. Partially it was cultural: they could
hire quite some Oracle employees that knew Java.</p>
<p>Python was still used a lot, just not for the core services. Nowadays, Python is used a
lot for machine learning. They have 950 Python services, 470 libraries. 180000 Python
files in 7500 repositories. 322x FastApi, 272x Streamlit repositories. And still lots of
luigi. Luigi is the framework that inspired airflow: it has lots of starts on github,
the most of all their open source repositories.</p>
<p>They now also started <a class="reference external" href="https://spotify.github.io/pedalboard/">pedalboard</a>, a nice
Pythonic way of modifying audio (it is a wrapper around a c++ library). Also nice:
<a class="reference external" href="https://backstage.spotify.com/">https://backstage.spotify.com/</a> , a backend/portal for collecting all the
developer-related data. Workflow statuses and so. (The backend is open source, the
dashboard not).</p>
<p>At Spotify, the programmers are really encouraged to use <strong>agentic programming</strong>. He
hasn't touched his editor in the last six months! It really changed his life. Initially
he was a bit depressed: can someone who's less talented but with the same amount of
tokens really do the same as me? But it is really a next level and he gets amazing
productivity out of it. Having unlimited tokens helps.</p>
<p>It changes open source. Forking used to be a declaration of war. Nowadays it is a sign
of popularity. You can fork something and have AI keep it up to date with minimal
engineer effort. When the cost of maintaining your own fork approaches zero, what does
that do with the economics of open source? Is cooperation still a thing? What is the
goal/effect of open sourcing? Or is it only a way for AIs to find security bugs in your
software?</p>
<p>His parents ran a record store for 42 years. Then technology disrupted the music
industry. They had to reinvent themselves. It was scary and sad, but they adapted. Now
the same force is disrupting <strong>our</strong> industry. Where will it go?</p>
<img alt="https://reinout.vanrees.org/images/2026/lac-de-kruth7.jpg" src="https://reinout.vanrees.org/images/2026/lac-de-kruth7.jpg" />
<p><em>Unrelated photo: the &quot;lac de Kruth-Wildenstein&quot; reservoir during a family holiday in
France in 2006.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>PyGrunn: list-man, pragmatic system integration - Doeke Zanstra</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/05/08/6-list-man.html" />
      <id>http://reinout.vanrees.org/weblog/2026/05/08/6-list-man.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-05-08T00:00:00+01:00</published>
      <updated>2026-05-08T13:28:00+01:00</updated>

      
        <category term="python" />
      
        <category term="pygrunn" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/05/08/index.html">my summaries</a> of
the 2026 one-day <a class="reference external" href="https://pygrunn.org/">PyGrunn conference</a> in Groningen, NL).</p>
<p>When automating in a big company with many systems, you often end up with spaghetti:
many systems connecting to a lot of the others... A common solution is to have a &quot;bus
architecture&quot;. Generic existing &quot;<em>enterprise</em> service bus&quot; solutions were clearly
overkill, so he proposed an alternative solution.</p>
<p>He made a couple of assumptions/choices. All data is tabular data. He wanted to store a
copy of data in a database. SQL views to access the data. So: multiple sources that he
wanted to import in a central database (which would function as a sort of &quot;read-only
enterprise service bus&quot;). And a generic sql/view-based way of accessing the data.</p>
<p>He <strong>initially</strong> focused on read-only data. And he started real simple. Just a bash script
that ran regularly that scraped data from other systems and injected it in the database.</p>
<p>In the <strong>second version</strong> of the system, for every system he wrote a target/command in a
<tt class="docutils literal">Makefile</tt>. Every thing that needed to be scraped got its own table (called a &quot;list&quot;
in his system&quot;). Lists could be compared. The first killer app was a comparison between
a telephone list and the list of employees so that differences could be consolidated.</p>
<p>For the <strong>third version</strong>, he started using more and more python. CSV file imports.
Downloaders from REST APIs. All configurable so that he could use the same python script
for many different sources.</p>
<p>He now had a simple sytem for which he could write views and exports.</p>
<ul class="simple">
<li>Publishing data on the intranet via the &quot;jekyll&quot; static site generator. For instance
a &quot;mug book&quot; of all employees.</li>
<li>And regularly exporting a list of names+emailaddresses in a format suitable for the
multifunctional printer: to make it easy to select your email address when scanning on
the printer.</li>
<li>An export to a google spreadsheet that combined the holiday spreadsheet with the data
on part-time days.</li>
</ul>
<p>Security was handled with a role-based system.</p>
<img alt="https://reinout.vanrees.org/images/2026/lac-de-kruth6.jpg" src="https://reinout.vanrees.org/images/2026/lac-de-kruth6.jpg" />
<p><em>Unrelated photo: the &quot;lac de Kruth-Wildenstein&quot; reservoir during a family holiday in
France in 2006.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>PyGrunn: JSON freedom or chaos, how to trust your data - Bart Dorlandt</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/05/08/5-json-freedom.html" />
      <id>http://reinout.vanrees.org/weblog/2026/05/08/5-json-freedom.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-05-08T00:00:00+01:00</published>
      <updated>2026-05-08T12:44:00+01:00</updated>

      
        <category term="python" />
      
        <category term="pygrunn" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/05/08/index.html">my summaries</a> of
the 2026 one-day <a class="reference external" href="https://pygrunn.org/">PyGrunn conference</a> in Groningen, NL).</p>
<p>Subtitle: a real-world journey from chaos to confidence using <a class="reference external" href="https://github.com/pydantic/pydantic">Pydantic</a> and <a class="reference external" href="https://docs.pytest.org/">Pytest</a>.</p>
<p>Idealy, you'd have perfect json files with a fixed format and rigorous validation and
ideally generated. But in a customer project, the other programmers weren't too happy
about it. They had massive JSON files, partially manually crafted. Some where just one
single line and others were vertically aligned. And perhaps someone depended on the
specific format for some &quot;sed&quot; or &quot;awk&quot; hacking... So whatever happens: <em>it works,
don't touch it</em>.</p>
<p>The <strong>freedom trap</strong>. No schema means no contract. No contract means no trust. Fields
accumulate, nobody removes them: &quot;someone might be using it&quot;. Multi-team challenges: not
everyone has the same skillset.</p>
<p>He wanted a different future: a <em>trusted</em> future. Validated and tested and formatted.</p>
<p>Pydantic is a python library for data validation using Python type annotations. You can
define a data model with type hints. it will automatically validate and parse data
according to those models:</p>
<pre class="literal-block">
from ipaddress import IPv4Address
from pydantic import BaseModel

class Server(BaseModel):
    hostname: str
    ip: IPv4Address
    ...
</pre>
<p>Make sure to look at <tt class="docutils literal"><span class="pre">pydantic-extra-types</span></tt>, they have lots of handy types like
&quot;two-character country code&quot;.</p>
<p>There's <tt class="docutils literal">AfterValidator</tt>, you can use it to add a second validator to a field. So
first the <tt class="docutils literal">str</tt> type to validate it is a string, then afterwards some ip address
validator or so.</p>
<p><strong>Understanding the data</strong> is important. Split it up in smaller pieces and try to
understand/model/validate those. Especially in a corporate setting, splitting up the
problem is handy: you have some small success you can mention at the standup :-)</p>
<p>Do it iteratively. One piece at a time. If you find a problem, create a ticket for it.
It might not get fixed, but at least you end up with a list you can slowly tackle with
the rest of the organisation.</p>
<p>A good tip: if you discover an error in the data, provide a good, clear error message
that your colleague can understand.</p>
<p>When you export the data, use <tt class="docutils literal">model_dump(exclude_optional=True)</tt> to exclude all the
optional fields instead of having it as <tt class="docutils literal">my_field: None</tt>.</p>
<p><strong>Bonus</strong>: you can call <tt class="docutils literal">YourModel.model_json_schema()</tt> to generate a JSON schema for
the Pydantic model. You can then use the JSON schema in vscode when you manually edit
your JSON.</p>
<p>Pydantic is great at validating individual fields and structures. But not at validating
things that span the entire document, like making sure that all hostnames are unique. He
used Pytest for it: he wrote such <strong>validation checks as pytest functions</strong>!. You can
even use Pytest test parametrization to run the same test on multiple directories.</p>
<img alt="https://reinout.vanrees.org/images/2026/lac-de-kruth5.jpg" src="https://reinout.vanrees.org/images/2026/lac-de-kruth5.jpg" />
<p><em>Unrelated photo: the &quot;lac de Kruth-Wildenstein&quot; reservoir during a family holiday in
France in 2006.</em></p>
</div>

      ]]>
      </content>

    </entry>
  
    <entry>
      <title>PyGrunn: introducing httpxyz: forking a top-100 Python package - Michiel Beijen</title>
      <link rel="alternate" type="text/html"
            href="https://reinout.vanrees.org/weblog/2026/05/08/4-httpxyz.html" />
      <id>http://reinout.vanrees.org/weblog/2026/05/08/4-httpxyz.html</id>
      <!-- id is not https: prevents old entries from showing up again -->
      <author>
        <name>Reinout van Rees</name>
      </author>
      <published>2026-05-08T00:00:00+01:00</published>
      <updated>2026-05-08T12:04:00+01:00</updated>

      
        <category term="python" />
      
        <category term="django" />
      
        <category term="pygrunn" />
      

      <content type="html"><![CDATA[
      <div class="document">
<p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2026/05/08/index.html">my summaries</a> of
the 2026 one-day <a class="reference external" href="https://pygrunn.org/">PyGrunn conference</a> in Groningen, NL).</p>
<p>Years ago he listened to the &quot;corecursive&quot; podcast (recommended by Michiel), <a class="reference external" href="https://corecursive.com/data-compression-yann-collet/">the one
where Yann Collet got interviewed</a>. He's the author of the LZ4
and zstandard (<tt class="docutils literal">zstd</tt>) compression algorithm. In 2016 zstandard was released. In 2017 it was used
in the linux kernel. Since 2020 it is one of the official formats in zipfiles. And in
2025 it got added to the Python standard library in version 3.14.</p>
<p><tt class="docutils literal">requests</tt> is one of the most popular Python libraries. <tt class="docutils literal">httpx</tt> has a similar API,
but it is better. A top 100 pypy packages. Main advantages: HTTP/2 support and async
support.</p>
<p>He liked httpx a lot. And zstandard, too. But zstandard wasn't supported by httpx. All
browsers support it, but not httpx. So he made a pull request in early 2024. It got
merged! But there was no new release yet. The maintainer asked if he wanted to create a
PR for the release. He did it and there was a new release. Hurray!</p>
<p>Months later, a bug surfaced. He created a bugfix, but that wasn't merged and wasn't
merged and wasn't merged. And there was no new release. And then the httpx maintainer
recently turned off all discussion on github. Earlier the maintainer had done the same
to django restframework. And to mkdocs. All heavily-used packages! And in the &quot;encode&quot;
github organisation/company that uses donations to fund open source development.
Weird...</p>
<p>There are also performance issues in httpx, which especially is a problem for several AI
libraries.</p>
<p>So... he started <a class="reference external" href="https://httpxyz.org/">httpxyz</a>, it bills itself as the maintained
fork of <a class="reference external" href="https://www.python-httpx.org/">httpx</a>. More info about the reasons for the
fork at <a class="reference external" href="https://tildeweb.nl/~michiel/httpxyz.html">https://tildeweb.nl/~michiel/httpxyz.html</a> .</p>
<p>It contains most of  the bugfixes that have been pending for a while. More maintainers.
Performance is much better (they needed to fork httpcore into httpcorexyz, it is 4x faster).
API compatible. You just have to change the import. They used a PIL/pillow trick to make
sure that if you import <tt class="docutils literal">httpxz</tt>, later <tt class="docutils literal">httpx</tt> imports use httpxyz instead.</p>
<p>There turned out to be quite a lot of small performance errors in the old code.</p>
<p><strong>An important performance tip</strong>: use client (or if you use <tt class="docutils literal">requests</tt>, use <tt class="docutils literal">request.Session()</tt>):</p>
<pre class="literal-block">
import httpxyz

c = httpxyz.Client()
c.get(...)
c.get(...)
</pre>
<p>instead of just:</p>
<pre class="literal-block">
import httpxyz

httpxyz.get(...)
httpxyz.get(...)
</pre>
<p>Using a client means httpxyz (or requests) can use http features to spead up your
requests a lot. Automatic connection keepilive. No more TCP handschake for every
individual request. And no TLS/https handshake. And if your server supports http/2, the
improvement is even bigger. You do need to install <tt class="docutils literal">httpxyz[http2]</tt> and specifiy
<tt class="docutils literal">httpxyz.Client(http2=True)</tt>.</p>
<p>Nice: httpxyz also has a command line interface.</p>
<p>Something he only mentioned briefly: there oauth2 client_credentials support. You have
to define a way to grab an oauth2 token, but the rest of the client work just uses the
regular methods. Handy.</p>
<p>They're on <a class="reference external" href="https://codeberg.org/httpxyz/httpxyz">https://codeberg.org/httpxyz/httpxyz</a> instead of on github.</p>
<img alt="https://reinout.vanrees.org/images/2026/lac-de-kruth4.jpg" src="https://reinout.vanrees.org/images/2026/lac-de-kruth4.jpg" />
<p><em>Unrelated photo: the &quot;lac de Kruth-Wildenstein&quot; reservoir during a family holiday in
France in 2006.</em></p>
</div>

      ]]>
      </content>

    </entry>
  

</feed>