John Mathewshttps://python-blog.johnmathews.is/2022-12-13T11:25:00+01:00From Python and Pelican to JavaScript and Next.js2022-12-13T11:25:00+01:002022-12-13T11:25:00+01:00John Mathewstag:python-blog.johnmathews.is,2022-12-13:/from-python-and-pelican-to-javascript-and-nextjs.html<p>My personal website is now built using <a href="https://nextjs.org/">Next.<span class="caps">JS</span></a> and moved the content generated using the
Python Pelican <span class="caps">SSG</span> to this subdomain (<a href="https://python-blog.johnmathews.is">python-blog.johnmathews.is</a>).</p>
<p>The site at <a href="https://johnmathews.is">johnmathews.is</a> should have all the content
available on this blog except for a few old portfolio projects, and is built
using Next.js.</p>
<p>I’m keeping the python version of the blog online because, well, why not. Maybe
one day it’ll be interesting to see what I did with jQuery and Jinja templates.</p>
<h1 id="why">Why</h1>
<p>As the amount of content on the blog increased, it became increasingly slow to
work on. Also, React has become a kind of defacto web framework, and I didn’t
like not being comfortable with it. I was expending too much energy trying to
compensate for not being familiar with React, when making front-end or <span class="caps">UI</span>
decisions on personal projects.</p>
<p>So, like so many times before, this blog has provided opportunity and motivation
to learn some new tech and broaden my skills. I cannot call myself a front-end
or react developer yet, but I’m a lot better than I was before!</p>Learnings from 5 years of tech startup code audits2022-05-26T09:40:09+02:002022-05-26T09:40:09+02:00John Mathewstag:python-blog.johnmathews.is,2022-05-26:/startup-code-audit.html<p>A super <a href="https://kenkantzer.com/learnings-from-5-years-of-tech-startup-code-audits/">blog
post</a>
about how to write secure code.</p>Don’t avoid sunlight2022-05-23T10:41:21+02:002022-05-23T10:41:21+02:00John Mathewstag:python-blog.johnmathews.is,2022-05-23:/sunlight.html<p>An <a href="https://www.outsideonline.com/health/wellness/sunscreen-sun-exposure-skin-cancer-science/">article</a> in Outside Magazine about the health benefits of sunshine.</p>Tmux tutorials2022-05-09T13:41:21+02:002022-05-09T13:41:21+02:00John Mathewstag:python-blog.johnmathews.is,2022-05-09:/tmux.html<p>A handy <a href="https://waylonwalker.com/tmux-nav-2021/">site</a> with lots of tmux info,
including how to open a pop-up window to fzf another session.</p>Data Science Teams2022-04-15T13:58:52+02:002022-04-15T13:58:52+02:00John Mathewstag:python-blog.johnmathews.is,2022-04-15:/data-science-teams.html<p>I was recently asked what I thought made for an effective data science team.
Happily, I think my opinions on this topic are becoming more relevant, nuanced,
and commercially valuable.</p>
<p>Two specific criteria I’ve recently thought of are:</p>
<ol>
<li>
<p>A nuanced understanding of software architecture patterns - what they are,
why they’re relevant, what are strengths and weaknesses of popular solutions to
common problems.</p>
</li>
<li>
<p>The avoidance, or deliberate unlearning of programming practices that are
learnt in academic (and to a lesser extent hobbyist) contexts. </p>
</li>
</ol>Newish Command-Line Tools2022-04-13T10:33:22+02:002022-04-13T10:33:22+02:00John Mathewstag:python-blog.johnmathews.is,2022-04-13:/newish-command-line-tools.html<p>A <a href="https://jvns.ca/blog/2022/04/12/a-list-of-new-ish--command-line-tools/">blog
post</a>
with some new command line tools.</p>Why the past 10 years of American life have been uniquely stupid2022-04-12T10:12:46+02:002022-04-12T10:12:46+02:00John Mathewstag:python-blog.johnmathews.is,2022-04-12:/social-media-and-the-tower-of-babel.html<p>An
<a href="https://www.theatlantic.com/magazine/archive/2022/05/social-media-democracy-trust-babel/629369/">article</a> in the Atlantic.</p>MacOS Keyboard Shortcuts2022-04-05T14:31:09+02:002022-04-05T14:31:09+02:00John Mathewstag:python-blog.johnmathews.is,2022-04-05:/macos-keyboard-shortcuts.html<p>A <a href="https://www.jamieonkeys.dev/posts/keyboard-shortcuts/">blog post</a> explaining
some useful keyboard shortcuts.</p>
<p>A <a href="https://jinyuz.dev/posts/tips-and-tricks/Changing-caps-lock-key-to-Escape-when-pressed-alone-and-Control-when-pressed-with-another">blog
post</a>
explaining how to make caps lock be <code>escape</code> when pressed alone, and <code>ctrl</code> when
pressed with another key.</p>Advanced googling2022-04-05T12:01:55+02:002022-04-05T12:01:55+02:00John Mathewstag:python-blog.johnmathews.is,2022-04-05:/how-to-google.html<div class="relative mt-3" style="padding-top: 56.25%">
<iframe allow="autoplay; encrypted-media" allowfullscreen="" class="absolute inset-0 w-full h-full" frameborder="0" loading="lazy" src="https://youtube.com/embed/cEBkvm0-rg0">
</iframe>
</div>
<h2 id="meta">Meta</h2>
<ul>
<li><code>related:<url></url></code> - find sites similar to <code><url></url></code>, can be a blog post not just a domain.</li>
<li><code>cache:<url></url></code> - see what google thinks your site looks like. Has google cached the latest version?</li>
</ul>
<h2 id="filters">Filters</h2>
<ul>
<li><code>site:<url> <search terms=""></search></url></code></li>
<li><code>filetype:<ft> <search terms=""></search></ft></code></li>
<li><code>-<excluded-terms></excluded-terms></code></li>
</ul>
<h2 id="dates">Dates</h2>
<ul>
<li><code>after:<year></year></code></li>
<li>
<p><code>before:<year></year></code></p>
</li>
<li>
<p>range: <code>2019..2021</code> or <code>$10..$50</code></p>
</li>
</ul>
<h2 id="logical-operators">Logical Operators</h2>
<ul>
<li>one term or another term <code><term 1="">|<term 2=""></term></term></code> </li>
<li><code>(<term a="">|<term b="">)<term c=""></term></term></term></code></li>
</ul>
<h2 id="wildcard">Wildcard</h2>
<ul>
<li><code>*</code> is a wildcard <code>site:*.tailwind.com <terms></terms></code> - search only subdomains</li>
</ul>Dan Lebrero book notes2022-03-24T10:33:26+01:002022-03-24T10:33:26+01:00John Mathewstag:python-blog.johnmathews.is,2022-03-24:/dan-lebrero-book-notes.html<p>A personal <a href="https://danlebrero.com/">blog</a> with some notes on books that look
interesting. I’d like to try reading these over the summer.</p>
<ul>
<li><a href="https://danlebrero.com/2021/08/04/zen-and-art-of-motorcycle-maintenance-summary/#content">Zen and the art of motorcycle maintenance</a> </li>
<li><a href="https://danlebrero.com/2021/09/08/the-subtle-art-of-not-giving-a-fuck-summary/#content">The subtle art of not giving a fuck</a> </li>
<li><a href="https://danlebrero.com/2021/07/21/thinking-in-systems-summary/#content">Thinking in systems</a> </li>
</ul>Someone else’s guiding principles after 20 years of programming2022-03-22T13:14:35+01:002022-03-22T13:14:35+01:00John Mathewstag:python-blog.johnmathews.is,2022-03-22:/title-someone-elses-guiding-principles-after-20-years-of-programming.html<p>This is a copy of a great article on <a href="https://alexewerlof.medium.com/my-guiding-principles-after-20-years-of-programming-a087dc55596c">medium</a> by Alex Ewerloef.</p>
<p>I’ve copied it here because an article on Medium could disappear at any moment,
and its too good to lose.</p>
<p><br/></p>
<hr/>
<p><br/></p>
<p>I’ve been programming since 1999 and this year I’ve officially coded for 20+
years. I started with Basic but soon jumped into Pascal and C and then learned
object oriented programming (<span class="caps">OOP</span>) with Delphi and C++. In 2006 I started with
Java and in 2011 I started with JavaScript. I’ve worked with a wide range of
businesses from robotics, fin tech, med tech to media and telecom. Sometimes I
had a different hat as a researcher, <span class="caps">CTO</span>, <span class="caps">TPM</span> (technical product manager),
teacher, system architect or <span class="caps">TL</span> (technical leader) but I’ve always been coding.
I’ve worked on some products that served millions of people, and some that
failed before being released. I worked as a consultant and I even had my own
startup. I have spent lots of time on open source projects, closed source
projects and internally open source projects (proprietary code that is developed
by a community inside the company). I’ve worked with tiny microcontrollers all
the way to mobile and desktop apps to cloud servers and lately serverless.</p>
<p>For my 20 years programming anniversary, I tried to list the top principles that
have been accumulated over the years as my guiding principles through my career:</p>
<ol>
<li>
<p>Don’t fight the tools: libraries, language, platform, etc. Use as much native
constructs as possible. Don’t bend the technology, but don’t bend the problem
either. Pick the right tool for the job or you’ll have to find the right job for
the tool you got.</p>
</li>
<li>
<p>You don’t write the code for the machines, you write it for your colleagues
and your future self (unless it’s a throw away project or you’re writing
assembly). Write it for the junior ones as a reference.</p>
</li>
<li>
<p>Any significant and rewarding piece of software is the result of
collaboration. Communicate effectively and collaborate openly. Trust others and
earn their trust. Respect people more than code. Lead by example. Convert your
followers to leaders.</p>
</li>
<li>
<p>Divide and conquer. Write isolated modules with separate concerns which are
loosely coupled. Test each part separately and together. Keep the tests close to
reality but test the edge cases too.</p>
</li>
<li>
<p>Deprecate yourself. Don’t be the go-to person for the code. Optimize it for
people to find their way fixing bugs and adding features to the code. Free
yourself to move on to the next project/company. Don’t own the code or you’ll
never grow beyond that.</p>
</li>
<li>
<p>Security comes in layers: each layer needs to be assessed individually but
also in relation to the whole. Risk is a business decision and has direct
relation to vulnerability and probability. Each product/organization has a
different risk appetite (the risk they are willing to take for a bigger win).
Often these 3 concerns fight with each other: <span class="caps">UX</span>, Security, Performance.</p>
</li>
<li>
<p>Realize that every code has a life cycle and will die. Sometimes it dies in its
infancy before seeing the light of production. Be <span class="caps">OK</span> with letting go. Know the
difference between 4 categories of features and where to put your time and energy:</p>
<ul>
<li>Core: like an engine in a car. The product is meaningless without it.</li>
<li>Necessary: like a car’s spare wheel. It’s rarely used but when needed, its function decides the success of the system.</li>
<li>Added value: like a car’s cup-holder. It’s nice to have but the product is perfectly usable without it.</li>
<li>Unique Selling Point: the main reason people should buy your product instead of your rivals. For example, your car is the best off-road vehicle.</li>
</ul>
</li>
<li>
<p>Don’t attach your identity to your code. Don’t attach anyone’s identity to
their code. Realize that people are separate from the artifacts they produce.
Don’t take code criticism personally but be very careful when criticizing
others’ code.</p>
</li>
<li>
<p>Tech debt is like fast food. Occasionally it’s acceptable but if you get used
to it, it’ll kill the product faster than you think (and in a painful way).</p>
</li>
<li>
<p>When making decisions about the solution all things equal, go for this priority:</p>
</li>
<li>
<p>Security > Reliability > Usability (Accessibility <span class="amp">&</span> <span class="caps">UX</span>) > Maintainability > Simplicity (Developer experience/<span class="caps">DX</span>) > Brevity (code length) > Finance > Performance</p>
</li>
<li>
<p>But don’t follow that blindly because it is dependent on the nature of the
product. Like any career, the more experience you earn, the more you can find
the right balance for each given situation. For example, when designing a game
engine, performance has the highest priority, but when creating a banking app,
security is the most important factor.</p>
</li>
<li>
<p>Bugs’ genitals are called copy <span class="amp">&</span> paste. That’s how they reproduce. Always
read what you copy, always audit what you import. Bugs take shelter in
complexity. “Magic” is fine in my dependency but not in my code.</p>
</li>
<li>
<p>Don’t only write code for the happy scenario. Write good errors that answer
why it happened, how it was detected and what can be done to resolve it.
Validate all system input (including user input): fail early but recover from
errors whenever possible. Assume the user holds a gun: put enough effort into
your errors to convince them to shoot something other than your head!</p>
</li>
<li>
<p>Don’t use dependencies unless the cost of importing, maintaining, dealing
with their edge cases/bugs and refactoring when they don’t satisfy the needs is
significantly less than the code that you own.</p>
</li>
<li>
<p>Stay clear from hype-driven development. But learn all you can. Always have
pet projects.</p>
</li>
<li>
<p>Get out of your comfort zone. Learn every day. Teach what you learn. If
you’re the master, you’re not learning. Expose yourself to other languages,
technologies, culture and stay curious.</p>
</li>
<li>
<p>Good code doesn’t need documentation, great code is well documented so that
anyone who hasn’t been part of the evolution, trial <span class="amp">&</span> error process and
requirements that led to the current status can be productive with it. An
undocumented feature is a non-existing feature. A non-existing feature shouldn’t
have code.</p>
</li>
<li>
<p>Avoid overriding, inheritance and implicit smartness as much as possible.
Write pure functions. They are easier to test and reason about. Any function
that’s not pure should be a class. Any code construct that has a different
function, should have a different name.</p>
</li>
<li>
<p>Never start coding (making a solution) unless you fully understand the
problem. It’s very normal to spend more time listening and reading than typing
code. Understand the domain before starting to code. A problem is like a maze.
You need to progressively go through the code-test-improve cycle and explore the
problem space till you reach the end.</p>
</li>
<li>
<p>Don’t solve a problem that doesn’t exist. Don’t do speculative programming.
Only make the code extensible if it is a validated assumption that it’ll be
extended. Chances are by the time it gets extended, the problem definition looks
different from when you wrote the code. Don’t overengineer: focus on solving the
problem at hand and an effective solution implemented in an efficient manner.</p>
</li>
<li>
<p>Software is more fun when it’s made together. Build a sustainable community.
Listen. Inspire. Learn. Share.</p>
</li>
</ol>
<p>I don’t claim to be an authority in software development. These are just the wisdom I earned along the way. I’m sure this list will be more mature after another 20 years.</p>Personal website as a mini-game2022-03-13T13:35:45+01:002022-03-13T13:35:45+01:00John Mathewstag:python-blog.johnmathews.is,2022-03-13:/personal-website-as-a-minigame.html<p>A <span class="caps">HN</span> <a href="https://news.ycombinator.com/item?id=30656961">article</a> about a personal
website that has been made in the style of a Pokemon game. Super cool. </p>
<ul>
<li>The <a href="https://arielroffe.quest/">site</a> </li>
<li>The <a href="https://github.com/ariroffe/personal-website">project code</a> </li>
</ul>Staff Engineer2022-03-13T13:32:20+01:002022-03-13T13:32:20+01:00John Mathewstag:python-blog.johnmathews.is,2022-03-13:/staff-engineer.html<p>A <a href="https://staffeng.com/">site</a> about career paths after reaching “Senior
Software Engineer”. Apparently you have two main branches, either engineering
management, or staff engineer.</p>
<p>The site references several good books about the engineering management
career path. I should come back to this.</p>
<p><a href="https://web.archive.org/web/20220313082116/https://staffeng.com/">archive</a> </p>Founder Salaries2022-03-01T16:20:56+01:002022-03-01T16:20:56+01:00John Mathewstag:python-blog.johnmathews.is,2022-03-01:/founder-salaries.html<p>A data driven <a href="https://sifted.eu/articles/startup-founders-salary/">article</a>
comparing equity and salaries broken down by geography, investment stage and
number of founders.</p>James’ Book Summaries2022-02-28T17:55:37+01:002022-02-28T17:55:37+01:00John Mathewstag:python-blog.johnmathews.is,2022-02-28:/james-book-summaries.html<p><a href="https://jamesclear.com/book-summaries">Book summaries</a> at
<a href="jamesclear.com">Jamesclear.com</a></p>Django CBV method flowchart2022-02-16T09:33:28+01:002022-02-16T09:33:28+01:00John Mathewstag:python-blog.johnmathews.is,2022-02-16:/django-cbv-method-flowchart.html<p>The
<a href="https://docs.djangoproject.com/en/3.2/ref/class-based-views/generic-display/">documentation</a>
calls it the method flowchart:</p>
<ol>
<li>setup()</li>
<li>dispatch()</li>
<li>http_method_not_allowed()</li>
<li>get_template_names()</li>
<li>get_slug_field()</li>
<li>get_queryset()</li>
<li>get_object()</li>
<li>get_context_object_name()</li>
<li>get_context_data()</li>
<li>get()</li>
<li>render_to_response()</li>
</ol>
<p>A question on <a href="https://stackoverflow.com/questions/17768444/django-class-based-views-function-execution-order"><span class="caps">SO</span></a>.</p>
<h2 id="setuprequest-args-kwargs">setup(request, <em>args, </em>*kwargs)</h2>
<ul>
<li>Performs key view initialization prior to dispatch().</li>
<li>If overriding this method, you must call super().</li>
</ul>
<h2 id="dispatchrequest-args-kwargs">dispatch(request, <em>args, </em>*kwargs)</h2>
<ul>
<li>
<p>The view part of the view – the method that accepts a request argument plus arguments, and returns an <span class="caps">HTTP</span> response.</p>
</li>
<li>
<p>The default implementation will inspect the <span class="caps">HTTP</span> method and attempt to delegate to a method that matches the <span class="caps">HTTP</span> method; a <span class="caps">GET</span> will be delegated to get(), a <span class="caps">POST</span> to post(), and so on.</p>
</li>
<li>
<p>By default, a <span class="caps">HEAD</span> request will be delegated to get(). If you need to handle <span class="caps">HEAD</span> requests in a different way than <span class="caps">GET</span>, you can override the head() method. See Supporting other <span class="caps">HTTP</span> methods for an example.</p>
</li>
</ul>
<h2 id="get_objectquerysetnone">get_object(queryset=None)</h2>
<p>Returns the single object that this view will display. If queryset is provided,
that queryset will be used as the source of objects; otherwise, get_queryset()
will be used. get_object() looks for a pk_url_kwarg argument in the arguments to
the view; if this argument is found, this method performs a primary-key based
lookup using that value. If this argument is not found, it looks for a
slug_url_kwarg argument, and performs a slug lookup using the slug_field.</p>
<h2 id="render_to_responsecontext-response_kwargs">render_to_response(context, **response_kwargs)¶</h2>
<ul>
<li>
<p>Returns a self.response_class instance.</p>
</li>
<li>
<p>If any keyword arguments are provided, they will be passed to the constructor of the response class.</p>
</li>
<li>
<p>Calls get_template_names() to obtain the list of template names that will be searched looking for an existent template.</p>
</li>
</ul>
<h2 id="response_class">response_class¶</h2>
<ul>
<li>
<p>The response class to be returned by render_to_response method. Default is TemplateResponse. The template and context of TemplateResponse instances can be altered later (e.g. in template response middleware).</p>
</li>
<li>
<p>If you need custom template loading or custom context object instantiation, create a TemplateResponse subclass and assign it to response_class.</p>
</li>
</ul>A tool to automatically contact people2022-02-14T12:24:43+01:002022-02-14T12:24:43+01:00John Mathewstag:python-blog.johnmathews.is,2022-02-14:/how-to-stay-in-touch-with-people.html<p>An <a href="https://jakobgreenfeld.com/stay-in-touch">article</a> about a system that lets
you choose how often you want to stay in touch with people, and sends you a
daily email with people to speak to and any notes.</p>
<p>The article uses airtable (a no code product). I would rather self host so that
if the tool becomes useful to me, I don’t put it’s survival in someone else’s
hands. I wouldn’t need to avoid writing code, and the most familiar tech stack
would be a Django app. </p>
<p>The trick would be hosting the app, and the database server super cheaply. </p>
<p>Below is a quote from the blog post:</p>
<div class="highlight"><pre><span></span><code>Here’s what I do.
I typically spend a few minutes researching what they’ve been up to recently.
I check their social profiles, personal websites, and read or watch any content
they published that I missed.
Then I share a few thoughts or questions.
Most importantly, I always send the kind of messages I’d like to receive.
They’re short, genuine, and (ideally) helpful. I never try to sell anything and
there’s no agenda other than to keep in touch.
Sometimes I just share a related article or book I think they might find
interesting and sometimes I offer specific help or advice with a problem they’re
facing right now.
Of course, not everyone publishes content or updates regularly. In that case, I
usually just ask what they’ve been up to lately.
You’d be surprised how many people are really happy to get these kinds of
messages and they often spark all kinds of deeper conversations.
</code></pre></div>Reactance2022-02-10T22:28:51+01:002022-02-10T22:28:51+01:00John Mathewstag:python-blog.johnmathews.is,2022-02-10:/reactance.html<p><a href="https://en.wikipedia.org/wiki/Reactance_(psychology)">Reactance</a> would be a
good name for a church.</p>
<p>Also, the <a href="https://en.wikipedia.org/wiki/Streisand_effect">Streisand Effect</a>.</p>Tools to help children learn to2022-02-09T13:48:20+01:002022-02-09T13:48:20+01:00John Mathewstag:python-blog.johnmathews.is,2022-02-09:/tools-to-help-children-learn-to.html<p>A
<a href="https://jamilhallal.blogspot.com/2022/02/6-interesting-resources-to-encourage-your-child-to-learn-to-code.html">list</a>
of sites that help children learn to code.</p>How to size bets2022-02-09T12:34:36+01:002022-02-09T12:34:36+01:00John Mathewstag:python-blog.johnmathews.is,2022-02-09:/how-to-size-bets.html<p>An interactive <a href="https://explore.paulbutler.org/bet/">article</a> that discusses the
optimal strategy for the following scenario:</p>
<p><span class="dquo">“</span>I have a coin that lands heads 60% of the time and tails 40% of the time. You
have $25 and can bet on either side of the coin — every time you’re right you
double your bet, and every time you are wrong you lose it.”</p>
<p>Theres a widget on the page that lets you select an amount, what side to bet on,
and a chart showing your balance after each coin toss.</p>
<p>My first attempt at this game was probably my best. My intuition is that its
always optimal to bet on heads because each coin toss is independent, and 60%
is more than 40%.</p>
<p>However the question of how much to bet on each coin toss is
more complicated because you lose if your balance dips below 0. If you bet 50%
of your balance and lose on 2 consecutive coin tosses, then you’re out. The
probability of this is <span class="math">\(0.4^2 = 0.16\)</span>. 0.16 is quite low, but whilst playing the
game you will toss the coin 50 - 100 times, so there are many opportunities.</p>
<p>If you toss the coin 50 times, there are 49 opportunities to lose twice
consecutively. I don’t know the math for this probability, so I’ll go learn it,
but intuitively I think that 50% of the pot is too high. I went with 33% and
won (reaching \$250) in about 50 throws.</p>
<p>I’d like to know how to optimise this, so I’ll read the article.</p>
<p>Also:</p>
<ul>
<li><a href="https://blog.paulhankin.net/kellycriterion/">Insurance and the Kelly Criterion</a> </li>
<li><a href="https://news.ycombinator.com/item?id=30265797"><span class="caps">HN</span> Comments</a> </li>
</ul>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Managing environment variables with Direnv2022-01-28T17:23:51+01:002022-01-28T17:23:51+01:00John Mathewstag:python-blog.johnmathews.is,2022-01-28:/direnv.html<h2 id="background">Background</h2>
<p><code>Direnv</code> is a utility to load and unload environment variables automatically as
you navigate in and out of directories. It can also hook into pyenv to load
and unload virtual environments.</p>
<p>Global config for direnv exists in <code>~/.config/direnv</code>. This is where you put
the additional pyenv commands.</p>
<p>You need to create a <code>.envrc</code> file in the project root which you want to trigger
the virtualenv activation. E.g.:</p>
<p>It works by checking for the existence of a <code>.envrc</code> or <code>.env</code> file in the
current or parent directories. If the file exists and is authorized then it’s
loaded into a <strong>bash</strong> subshell, and all exported variables are then captured by
direnv and made available to the current shell.</p>
<p>It can also hook into pyenv to load and unload virtual environments.</p>
<p>Direnv is fast enough to be unnoticeable on each prompt, because its compiled
into a single static executable. It is language agnostic.</p>
<h2 id="config">Config</h2>
<p>Global config for direnv exists in <code>~/.config/direnv</code>. This is where you put
the additional pyenv commands.</p>
<p>The project’s <a href="https://github.com/direnv/direnv/wiki/Python#pyenv-virtualenv">wiki</a>
contains instructions for setting up direnv with pyenv, pyenv-virtualenv and
other python tools, and tools for other languages</p>
<p>You need to create a <code>.envrc</code> file in the project root which you want to trigger
the virtualenv activation. E.g.:</p>
<div class="highlight"><pre><span></span><code><span class="nv">pyversion</span><span class="o">=</span><span class="m">3</span>.10.0
<span class="nv">pvenv</span><span class="o">=</span>blog
use python <span class="si">${</span><span class="nv">pyversion</span><span class="si">}</span>
layout virtualenv <span class="si">${</span><span class="nv">pyversion</span><span class="si">}</span> <span class="si">${</span><span class="nv">pvenv</span><span class="si">}</span>
layout activate <span class="si">${</span><span class="nv">pvenv</span><span class="si">}</span>
</code></pre></div>
<h2 id="references">References</h2>
<p>A <a href="https://gist.github.com/ZhangChen199102/da3133fc05e3b03afab405fdc3152fb3">useful gist</a> </p>Adding italic text to iTerm22022-01-28T10:32:08+01:002022-01-28T10:32:08+01:00John Mathewstag:python-blog.johnmathews.is,2022-01-28:/adding-italic-text-to-iterm2.html<p>This <a href="https://weibeld.net/terminals-and-shells/italics.html">article</a> explains
how to enable italic text in iTerm2 and Tmux.</p>
<p>It links to <a href="https://gist.github.com/sos4nt/3187620">this article</a> which
suggests overriding the existing terminfo entry so that <code>ssh</code> uses the correct
(new) profile by default. Otherwise you’d need to pass the correct environment
along with the <code>SSH</code> command.</p>
<h1 id="iterm2">iTerm2</h1>
<h2 id="enable-support-for-italic-text">Enable support for italic text</h2>
<div class="highlight"><pre><span></span><code>Preferences > Profiles > Text > Italic text allowed
</code></pre></div>
<h2 id="create-terminfo-entry-with-italics-support">Create <span class="caps">TERMINFO</span> entry with italics support</h2>
<p>1. Create temporary file terminfo with following content:</p>
<div class="highlight"><pre><span></span><code>xterm-256color-italic|xterm with 256 colors and italic,
sitm=\E[3m, ritm=\E[23m,
use=xterm-256color,
</code></pre></div>
<p>2. Create a new entry in the <span class="caps">TERMINFO</span> database</p>
<div class="highlight"><pre><span></span><code>tic terminfo
</code></pre></div>
<p>3. Delete the temporary terminfo file</p>
<h2 id="bind-iterm2-to-new-terminfo-entry">Bind iTerm2 to new <span class="caps">TERMINFO</span> entry</h2>
<p><em>Preferences > Profiles > Terminal > Report Terminal Type: > xterm-256color-italic</em></p>
<h2 id="test-it">Test it</h2>
<p>Close and reopen iTerm2, then try:</p>
<div class="highlight"><pre><span></span><code><span class="n">echo</span> <span class="n n-Quoted">`tput sitm`</span><span class="n">italics</span><span class="n n-Quoted">`tput ritm`</span>
</code></pre></div>
<p>If the output is printed in italicised, then iTerm2 is now capable of printing
italic text. </p>
<p>Also the below command should now output <code>xterm-256color-italic</code></p>
<div class="highlight"><pre><span></span><code>echo $TERM
</code></pre></div>
<h1 id="tmux"><span class="caps">TMUX</span></h1>
<h2 id="create-a-new-terminfo-entry">Create a new <span class="caps">TERMINFO</span> entry</h2>
<p>1. Create temporary file terminfo with following content:</p>
<div class="highlight"><pre><span></span><code>tmux|tmux terminal multiplexer,
ritm=\E[23m, rmso=\E[27m, sitm=\E[3m, smso=\E[7m, Ms@,
use=xterm, use=screen,
tmux-256color|tmux with 256 colors,
use=xterm-256color, use=tmux,
</code></pre></div>
<p>2. Create new entry in the <span class="caps">TERMINFO</span> database:</p>
<div class="highlight"><pre><span></span><code>tic -x terminfo
</code></pre></div>
<p>3. Delete the temporary terminfo file.</p>
<h2 id="bind-tmux-to-new-terminfo-entry">Bind tmux to new <span class="caps">TERMINFO</span> entry</h2>
<p>Set the following in ~/.tmux.conf:</p>
<div class="highlight"><pre><span></span><code>set -g default-terminal "tmux-256color"
set -as terminal-overrides ',xterm*:sitm=\E[3m'
</code></pre></div>
<h2 id="test-it_1">Test it</h2>
<p>Close and reopen tmux, then try:</p>
<div class="highlight"><pre><span></span><code><span class="n">echo</span> <span class="n n-Quoted">`tput sitm`</span><span class="n">italics</span><span class="n n-Quoted">`tput ritm`</span>
</code></pre></div>
<p>If the output is printed in italicised, then tmux is now capable of printing
italic text. Furthermore:</p>
<div class="highlight"><pre><span></span><code>echo $TERM
</code></pre></div>
<p>should now output tmux-256color</p>The Psychology of Computer Programming2022-01-17T21:26:14+01:002022-01-17T21:26:14+01:00John Mathewstag:python-blog.johnmathews.is,2022-01-17:/psychology-of-computer-programming.html<p>From The Psychology of Computer Programming, 1971, here are The Ten
Commandments of Egoless Programming:</p>
<ol>
<li>
<p>Understand and accept that you will make mistakes. The point is to find them
early, before they make it into production. Fortunately, except for the few of
us developing rocket guidance software at <span class="caps">JPL</span>, mistakes are rarely fatal in our
industry. We can, and should, learn, laugh, and move on.</p>
</li>
<li>
<p>You are not your code. Remember that the entire point of a review is to find
problems, and problems will be found. Don’t take it personally when one is uncovered.</p>
</li>
<li>
<p>No matter how much “karate” you know, someone else will always know more. Such
an individual can teach you some new moves if you ask. Seek and accept input
from others, especially when you think it’s not needed.</p>
</li>
<li>
<p>Don’t rewrite code without consultation. There’s a fine line between “fixing
code” and “rewriting code.” Know the difference, and pursue stylistic changes
within the framework of a code review, not as a lone enforcer.</p>
</li>
<li>
<p>Treat people who know less than you with respect, deference, and patience.
Non-technical people who deal with developers on a regular basis almost
universally hold the opinion that we are prima donnas at best and crybabies at
worst. Don’t reinforce this stereotype with anger and impatience.</p>
</li>
<li>
<p>The only constant in the world is change. Be open to it and accept it with a
smile. Look at each change to your requirements, platform, or tool as a new
challenge, rather than some serious inconvenience to be fought.</p>
</li>
<li>
<p>The only true authority stems from knowledge, not from position. Knowledge
engenders authority, and authority engenders respect – so if you want respect in
an egoless environment, cultivate knowledge.</p>
</li>
<li>
<p>Fight for what you believe, but gracefully accept defeat. Understand that
sometimes your ideas will be overruled. Even if you are right, don’t take
revenge or say “I told you so.” Never make your dearly departed idea a martyr or
rallying cry.</p>
</li>
<li>
<p>Don’t be “the coder in the corner.” Don’t be the person in the dark office
emerging only for soda. The coder in the corner is out of sight, out of touch,
and out of control. This person has no voice in an open, collaborative
environment. Get involved in conversations, and be a participant in your office community.</p>
</li>
<li>
<p>Critique code instead of people – be kind to the coder, not to the code. As much
as possible, make all of your comments positive and oriented to improving the
code. Relate comments to local standards, program specs, increased performance, etc.</p>
</li>
</ol>
<p><a href="http://blog.stephenwyattbush.com/2012/04/07/dad-and-the-ten-commandments-of-egoless-programming">source</a></p>Moxie on web32022-01-08T00:00:00+01:002022-01-08T00:00:00+01:00John Mathewstag:python-blog.johnmathews.is,2022-01-08:/moxie-on-web3.html<p>An excellent <a href="https://moxie.org/2022/01/07/web3-first-impressions.html">article</a>
about the state of web3 and NFTs.</p>How to write a book2021-12-24T11:08:09+01:002021-12-24T11:08:09+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-24:/how-to-write-a-book.html<p>I keep getting ideas for books, and I keep remembering these posts for making
the process seem very accessible:</p>
<p><a href="https://tynan.com/bookwriting/">Tynan - bookwriting</a></p>
<p><a href="https://tynan.com/write/">Tynan - write</a></p>Solo founder2021-12-24T10:11:12+01:002021-12-24T10:11:12+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-24:/solo-founder-.html<p><a href="https://www.coryzue.com/open/">Corey Zue</a> is an indie maker who made Pegasus
(Django boilerplate SaaS) and a wedding place-cards product. </p>
<p>This is a link to his open startup metrics page.</p>
<p>Also check out this <a href="https://python-blog.johnmathews.is/reading-april-2021.html">similar post</a> which has similar links for solo SaaS founders.</p>103 attempts to make it work2021-12-15T17:14:54+01:002021-12-15T17:14:54+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-15:/103-attempts-to-make-it-work.html<p>Towards the end of the fourth day, or maybe fifth, I finally figured out how to
make the code do what I wanted it to do and run without errors. I found the blind-spot.</p>
<p>There are some take-aways about naming conventions, and how to structure a code
base. I also have some thoughts about pandas. I guess the general conclusion is
that with more power comes more responsibility. Code can be written in many
ways, but only a few of them will let developers work as efficiently as possible.</p>
<p>Programming is an art and the only way to get good at writing complex code is to practice.</p>
<p>Also, I’d like to find a code visualization tool that can show the flow of classes and
methods as they are instantiated.</p>
<p><img alt="103-attempts" class="image-process-article-inline-image" loading="lazy" sizes="(min-width: 1536x) 1600px, (min-width: 1280px) 1300px, (min-width: 1024px) 1100px, (min-width: 768px) 800px, (min-width: 640px) 800px, 100vw" src="https://python-blog.johnmathews.is/images/derivatives/article-inline-image/1600w/103-airflow.png" srcset="https://python-blog.johnmathews.is/images/derivatives/article-inline-image/300w/103-airflow.png 300w, https://python-blog.johnmathews.is/images/derivatives/article-inline-image/600w/103-airflow.png 600w, https://python-blog.johnmathews.is/images/derivatives/article-inline-image/800w/103-airflow.png 800w, https://python-blog.johnmathews.is/images/derivatives/article-inline-image/1600w/103-airflow.png 1600w"/></p>Gell-Mann amnesia effect2021-12-14T17:58:56+01:002021-12-14T17:58:56+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-14:/gell-mann-amnesia-effect.html<p>The phenomenon of experts believing news articles on topics outside of their
fields of expertise, even after acknowledging that articles written in the same
publication that are within the experts’ fields of expertise are error-ridden
and full of misunderstanding:</p>
<p>***</p>
<p>[The] Media carries with it a credibility that is totally undeserved. You have all
experienced this, in what I call the Murray Gell-Mann Amnesia effect. (I refer
to it by this name because I once discussed it with Murray Gell-Mann, and by
dropping a famous name I imply greater importance to myself, and to the effect,
than it would otherwise have.)</p>
<p>Briefly stated, the Gell-Mann Amnesia effect is as follows. You open the
newspaper to an article on some subject you know well. In Murray’s case,
physics. In mine, show business. You read the article and see the journalist has
absolutely no understanding of either the facts or the issues. Often, the
article is so wrong it actually presents the story backward—reversing cause and
effect. I call these the “wet streets cause rain” stories. Paper’s full of them.</p>
<p>In any case, you read with exasperation or amusement the multiple errors in a
story, and then turn the page to national or international affairs, and read as
if the rest of the newspaper was somehow more accurate about Palestine than the
baloney you just read. You turn the page, and forget what you know.</p>
<p>That is the Gell-Mann Amnesia effect. I’d point out it does not operate in other
arenas of life. In ordinary life, if somebody consistently exaggerates or lies
to you, you soon discount everything they say. In court, there is the legal
doctrine of falsus in uno, falsus in omnibus, which means untruthful in one
part, untruthful in all. But when it comes to the media, we believe against
evidence that it is probably worth our time to read other parts of the paper.
When, in fact, it almost certainly isn’t. The only possible explanation for our
behavior is amnesia.</p>
<p><a href="https://en.wikipedia.org/wiki/Michael_Crichton#Why_Speculate?">wikipedia</a></p>How to monetize a SaaS2021-12-14T10:11:01+01:002021-12-14T10:11:01+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-14:/how-to-monetize-a-saas.html<p>A <a href="https://jeremyaboyd.com/post/tricks-to-monetize-your-side-project">blog post</a>
about monetizing SaaS products.</p>
<ul>
<li>Split test</li>
<li>Pricing</li>
<li>Email cadence</li>
</ul>First five minutes on a server2021-12-08T13:07:40+01:002021-12-08T13:07:40+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-08:/first-five-minutes-on-a-server.html<p>An
<a href="https://sollove.com/2013/03/03/my-first-5-minutes-on-a-server-or-essential-security-for-linux-servers/">article</a>
about linux security.</p>
<p>This is kind of similar to
<a href="https://python-blog.johnmathews.is/linux-performance-analysis-in-60-seconds.html">this</a> post.</p>Adding JSON output to a CLI app2021-12-06T10:27:51+01:002021-12-06T10:27:51+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-06:/adding-json-output-to-cli-app.html<p>An
<a href="https://blog.kellybrazil.com/2021/12/03/tips-on-adding-json-output-to-your-cli-app/">article</a>
suggesting best practices for adding <span class="caps">JSON</span> output to <span class="caps">CLI</span> apps, and mentioning why
<span class="caps">JSON</span> output is a good idea.<br/><br/></p>
<ul>
<li>Do Make a Schema</li>
<li>Do Flatten the Structure</li>
<li>Do Output <span class="caps">JSON</span> Lines for Streaming Output</li>
<li>Do Use Predictable Key Names</li>
<li>Do Pretty Print with Two Spaces or Don’t Format at All</li>
<li>Don’t Use Special Characters in Key Names</li>
<li>Don’t Allow Duplicate Keys</li>
<li>Don’t Use Very Large Numbers</li>
</ul>nvim.CoC2021-12-03T17:41:26+01:002021-12-03T17:41:26+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-03:/nvim-coc.html<p>I’ve been trying to replace <span class="caps">ALE</span> with CoC. It didn’t work. Maybe I have unusually
high fixing expectations, but <span class="caps">ALE</span> is more capable and easier to setup. So I’ll
be using CoC and <span class="caps">ALE</span> together, which feels messy.</p>
<h2 id="formatting">Formatting</h2>
<ol>
<li><code>pip install pep8 autopep8</code></li>
<li>
<p><code><leader>fs</leader></code></p>
</li>
<li>
<p><code><leader>a</leader></code> - a list of all the wrong things</p>
</li>
<li><code>vif</code> - select inside function</li>
<li>
<p><code>vam</code> - select method</p>
</li>
<li>
<p><code><leader>ac</leader></code> - see a box with options to fix imports or ignore file</p>
</li>
</ol>Find2021-12-03T13:15:28+01:002021-12-03T13:15:28+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-03:/find.html<p><code>fd</code> is a replacement for <code>find</code> with more intuitive defaults.</p>
<ul>
<li>Case-insensitive.</li>
<li>Ignores hidden directories and files.</li>
<li>Ignores patterns in <code>.gitignore</code>.</li>
</ul>
<h2 id="commands">Commands</h2>
<ul>
<li><code>fd PATTERN</code> </li>
<li>not <code>find -iname '*PATTERN*'</code></li>
<li><code>fd --help</code></li>
</ul>
<p><a href="https://github.com/sharkdp/fd">repo</a></p>Tmux sessions2021-12-03T13:14:26+01:002021-12-03T13:14:26+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-03:/tmux-sessions.html<ul>
<li>List of sessions: <code><prefix> s</prefix></code></li>
<li>Last session: <code><prefix> Z</prefix></code></li>
<li>Rename session: <code><prefix> $</prefix></code></li>
<li>
<p>Rename window: <code><prefix> ,</prefix></code></p>
</li>
<li>
<p>Previous session: <code><prefix> (</prefix></code></p>
</li>
</ul>
<p>For last session I used the following key bind:</p>
<div class="highlight"><pre><span></span><code>bind Z switch-client -l
</code></pre></div>Bat2021-12-03T12:00:34+01:002021-12-03T12:00:34+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-03:/bat.html<p><a href="https://github.com/sharkdp/bat#adding-new-themes">Bat</a> is like cat, but has
syntax highlighting and git integration. It integrates with all the tools, there are examples on the GitHub readme.</p>
<h2 id="commands">Commands:</h2>
<ul>
<li>List languages for syntax highlighting: <code>bat --list-languages</code> </li>
<li>Show configuration file location: <code>bat --config-file</code></li>
<li>Show configuration directory: <code>bat --config-dir</code></li>
<li>List themes: <code>bat --list-themes</code> </li>
<li>Generate the configuration file: <code>bat --generate-config-file</code></li>
</ul>
<h2 id="zshenv"><code>~/.zshenv</code>:</h2>
<div class="highlight"><pre><span></span><code><span class="nb">export</span> <span class="nv">BAT_CONFIG_PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.bat.conf"</span>
</code></pre></div>
<h2 id="configuration-settings">Configuration settings:</h2>
<p>Specify the theme:</p>
<div class="highlight"><pre><span></span><code>--theme="Dracula"
</code></pre></div>
<p>Map file types to syntax:</p>
<div class="highlight"><pre><span></span><code>--map-syntax ".ignore:Git Ignore"
--map-syntax ".py:Python"
--map-syntax ".json:JSON"
--map-syntax ".zsh*:Bourne Again Shell (bash)"
--map-syntax ".js:JavaScript"
</code></pre></div>Chezmoi: Part 22021-12-02T10:57:42+01:002021-12-02T10:57:42+01:00John Mathewstag:python-blog.johnmathews.is,2021-12-02:/chezmoi-2.html<p><a href="https://www.chezmoi.io">Chezmoi</a> is a dotfiles management tool. I wrote about
it <a href="https://python-blog.johnmathews.is/chezmoi.html">previously</a> when I almost deleted my
dotfiles and was looking for a better way to manage them.</p>
<p>7 months later and I’m happy I chose Chezmoi. I’m managing dotfiles across 3
machines and 2 <span class="caps">OS</span>’s currently. I have a basic workflow and it seems like a good
time to see if there are further benefits to be found. This is a review of my
workflow and some notes on how to use the tool more effectively.</p>
<p>I’ve aliased <code>chezmoi</code> to <code>cm</code> in this article and on all my machines. </p>
<p>Including files or subdirectories from other projects is really interesting and
something I didn’t realise was possible. It goes a long way to bridging the gap
between simple dotfiles management and something more powerful like Ansible.</p>
<p>Next steps (in another 7 months?) would be to use <a href="https://www.chezmoi.io/docs/how-to/#use-templates">templates</a> and make use of
the secrets management capabilities.</p>
<h2 id="setup">Setup</h2>
<ol>
<li>
<p>When you <code>cm init</code> you create a new git repo in <code>~/.local/share/chezmoi</code>.
This is where the <em>source state</em> lives. It’s a repo, so you can do all the
usual vcs things you’d expect, but cm won’t do it for you (by default).</p>
</li>
<li>
<p><code>cm edit-config</code> opens the configuration file for editing.</p>
</li>
</ol>
<h2 id="include-dotfiles-from-other-projects">Include dotfiles from other projects</h2>
<ol>
<li>
<p>Use <code>.chezmoiexternal.toml</code> to tell cm to import dotfiles from a different
repo. See below for an example</p>
</li>
<li>
<p>You can’t include subdirectories from other projects like <code>oh-my-zsh</code>
because you can’t use git submodules (cm uses its own format for the source state).</p>
</li>
<li>
<p>The section heading (the part with square brackets) is the destination path
of the object being imported.</p>
</li>
<li>
<p><code>type</code> is “archive” for collections of files (projects) and “file” for
individual files. If the url is a tarball then cm will unpack it.</p>
</li>
<li>
<p>The default value for <code>refreshPeriod</code> is never. Cm caches downloaded archives locally to
avoid downloading them every time <code>apply</code> is called. To force a refresh, call
<code>cm --refresh-externals apply</code> or <code>cm -R apply</code>.</p>
</li>
<li>
<p>When using <code>Oh My Zsh</code>, make sure you disable auto-updates by setting
<code>DISABLE_AUTO_UPDATE="true"</code> in <code>~/.zshrc</code>. Auto updates will cause the
<code>~/.oh-my-zsh</code> directory to drift out of sync with cm’s source state.
Refresh the downloads (by setting <code>refreshPeriod</code>) to update Oh My Zsh and
its plugins.</p>
</li>
</ol>
<h2 id="adding-files-and-directories">Adding files and directories</h2>
<ol>
<li>
<p>You add a file to cm with <code>cm add <file></file></code>. This copies the file
into the source state but changes the name. If the file you want to track is
<code>~/.zshrc</code> then cm will create a file in the source state called
<code>dot_zshrc</code>.</p>
</li>
<li>
<p>You can <code>cm add <dir></dir></code> just like you can <code>cm add <file></file></code>. If you copy a
directory into the source state, the name of the source state copy will be
prepended with <code>dot_</code>, too. The names of the files and dirs inside the
directory are not changed.</p>
</li>
</ol>
<h2 id="which-files-are-tracked-not-tracked-or-ignored">Which files are tracked, not tracked or ignored</h2>
<ol>
<li>
<p><code>cm managed</code> shows a list of manged files</p>
</li>
<li>
<p><code>cm unmanaged</code> shows a list of unmanaged files. You can add entire
directories with <code>cm add</code>.</p>
</li>
<li>
<p><code>.chezmoiignore</code> contains a list of files that <em>won’t</em> be copied from the
source directory to the destination when you run <code>cm apply</code>. This is the
opposite of my intuition [<a href="https://www.chezmoi.io/docs/reference/#chezmoiignore">documentation</a>].</p>
</li>
<li>
<p>Because <code>cm apply</code> can change so much stuff, try <code>cm apply --dry-run
--verbose</code> first. </p>
</li>
<li>
<p><code>.chezmoiignore</code> is a template, so you can ignore different files on
different machines.</p>
</li>
</ol>
<h2 id="editing-tracked-files">Editing tracked files</h2>
<p><code>cm diff</code> will show you what changes would be applied if your ran <code>cm apply</code>
from the perspective of the source state (Green ⇒ added to source state).</p>
<p><code>cm apply</code> will overwrite local changes (after prompting for confirmation) with
the copy from the source state.</p>
<p>You can resolve differences if things get messy with <code>cm merge $FILE</code>.</p>
<p>There are 4 ways of editing files:</p>
<ol>
<li>
<p><code>chezmoi edit $FILE</code> - opens \$<span class="caps">FILE</span> in the editor. <code>cm edit</code> will open the
source state directory. You can also use <code>cm edit --apply $FILE</code> to apply
the changes as soon as you close the file.</p>
</li>
<li>
<p><code>cm cd</code> and then edit the files directly. Then <code>cm apply</code> to apply the
changes. <code>cm diff</code> will show you what changes would be made by running <code>cm
apply</code>, from the perspective of the source state. (Green means added to the
source state, red is removed. This is the opposite of my intuition).</p>
</li>
<li>
<p>Edit the file in the home directory and then re-add it using <code>cm add $FILE</code>
or <code>cm re-add</code> (re-add doesn’t work with templates). I’ve created an alias
to re-add all files that have been changed <code>cm aa</code>.</p>
</li>
<li>
<p>Edit the file in the home directiry and then merge the changes into the
source state with <code>cm merge $FILE</code>.</p>
</li>
</ol>
<h2 id="examples">Examples</h2>
<h3 id="importing-an-entire-project">Importing an entire project:</h3>
<p>To import <code>Oh My Zsh</code>, the <code>zsh-syntax-highlighting</code> plugin, and <code>powerlevel10k</code> by putting the following in <code>~/.local/share/chezmoi/.chezmoiexternal.toml</code>:</p>
<div class="highlight"><pre><span></span><code><span class="k">[".oh-my-zsh"]</span>
<span class="n">type</span> <span class="o">=</span> <span class="s">"archive"</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz"</span>
<span class="n">exact</span> <span class="o">=</span> <span class="kc">true</span>
<span class="n">stripComponents</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">refreshPeriod</span> <span class="o">=</span> <span class="s">"168h"</span>
<span class="k">[".oh-my-zsh/custom/plugins/zsh-syntax-highlighting"]</span>
<span class="n">type</span> <span class="o">=</span> <span class="s">"archive"</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://github.com/zsh-users/zsh-syntax-highlighting/archive/master.tar.gz"</span>
<span class="n">exact</span> <span class="o">=</span> <span class="kc">true</span>
<span class="n">stripComponents</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">refreshPeriod</span> <span class="o">=</span> <span class="s">"168h"</span>
<span class="k">[".oh-my-zsh/custom/themes/powerlevel10k"]</span>
<span class="n">type</span> <span class="o">=</span> <span class="s">"archive"</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://github.com/romkatv/powerlevel10k/archive/v1.15.0.tar.gz"</span>
<span class="n">exact</span> <span class="o">=</span> <span class="kc">true</span>
<span class="n">stripComponents</span> <span class="o">=</span> <span class="mi">1</span>
</code></pre></div>
<h3 id="importing-a-single-file-from-another-project">Importing a single file from another project</h3>
<p>To import <code>plug.vim</code> from <code>github.com/junegunn/vim-plug</code> into <code>~/.vim/autoload/plug.vim</code> add this to
<code>~/.local/share/chezmoi/.chezmoiexternals.toml</code></p>
<div class="highlight"><pre><span></span><code><span class="k">[".vim/autoload/plug.vim"]</span>
<span class="n">type</span> <span class="o">=</span> <span class="s">"file"</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim"</span>
<span class="n">refreshPeriod</span> <span class="o">=</span> <span class="s">"168h"</span>
</code></pre></div>
<h2 id="source">Source</h2>
<p>Most of this was taken directly from the <a href="https://www.chezmoi.io/docs/how-to/">documentation</a>.</p>Powerful Python2021-11-29T13:23:47+01:002021-11-29T13:23:47+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-29:/powerful-python.html<p>Aaron Maxwell’s Powerful Python course looks great. It’s outstanding because, as
far as I can tell, it explains abstract ideas clearly and with practical
examples. If becoming exceptionally good at Python ever makes it to the top of
my list of professional priorities then I’d certainly take the course. For now
though I’m going to make some notes on the marketing emails he send:</p>
<div class="toc"><span class="toctitle">Table of Contents</span><ul>
<li><a href="#good-mental-models">Good mental models:</a></li>
<li><a href="#staying-motivated">Staying motivated</a></li>
<li><a href="#955-rule">95/5 rule</a></li>
<li><a href="#hidden-meaning-of">Hidden meaning of “(“</a></li>
<li><a href="#current-favourite-python-book">Current favourite python book</a></li>
<li><a href="#secret-instinct">Secret Instinct</a></li>
</ul>
</div>
<h2 id="good-mental-models">Good mental models:</h2>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">+</span><span class="mi">2</span>
</code></pre></div>
<p>Function <code>f</code> is a nameless function object tied to a variable called “f”. You
can also think of it as a function called “f”, but the first mental model is
more useful. It lets you reason about function factories, passing functions as
arguments to other functions, and writing decorators.</p>
<p>Also, apparently, Virginia Satir was such an incredible family therapist that
people wrote books about how great she was. She once said <em>“Most people think the
will to survive is the strongest instinct in humans, but it isn’t. The strongest
instinct is to keep things familiar”</em>.</p>
<h2 id="staying-motivated">Staying motivated</h2>
<p>Most people think that motivation is about willpower, but its not. It’s about
creating “wins”. When you invest your time and effort, you get that “aha!”
moment where you see the computer do something new, or you get that feeling of a
little breakthrough. It makes you excited to take another step and go a little
further. And get another little win.</p>
<h2 id="955-rule">95/5 rule</h2>
<p><span class="caps">OOP</span> is not just a tool for organizing your code. It’s a tool for organizing how
you think about code. Here is a roadmap to break into the top 1% of python programmers:</p>
<ul>
<li>Create powerful abstractions that accelerate and amplify all the python
you write.</li>
<li>Write automated tests, which lets you break through the ceiling of
complexity, so that you can suddenly write truly complex software as easily
as you used to write simple software.</li>
<li>Create robust high-performance scalability - writing software than gracefully
handles increasing magnitudes of data without breaking a sweat.</li>
<li>Learn how to use the higher-order abstractions that the most important
and prominent python libraries are built on (pandas, flask, django, pytest, requests).</li>
</ul>
<h2 id="hidden-meaning-of">Hidden meaning of “(“</h2>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Prefixer</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prefix</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">prefix</span> <span class="o">=</span> <span class="n">prefix</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">prefix</span> <span class="o">+</span> <span class="n">message</span>
</code></pre></div>
<p>Use it like:</p>
<div class="highlight"><pre><span></span><code>>>> <span class="nv">simonsays</span> <span class="o">=</span> Prefixer<span class="o">(</span><span class="s2">"Simon says: "</span><span class="o">)</span>
>>> simonsays<span class="o">(</span><span class="s2">"Get up and dance!"</span><span class="o">)</span>
<span class="s1">'simon says: Get up and daance!'</span>
</code></pre></div>
<p>What is the type of <code>simonsays</code>? If you look at just the last line you would
think its a function, but it is not. It is an instance of <code>Prefixer</code>.</p>
<p>There is a translation step. When you write “simonsays(‘foo’)” it is translated
(kind of) to “simonsays.<strong>call</strong>(‘foo’)”. In other words, <code>simonsays(</code> triggers
a function call.</p>
<p>Also:</p>
<div class="highlight"><pre><span></span><code>>>> def increment<span class="o">(</span>x<span class="o">)</span>:
... <span class="k">return</span> x+1
...
>>> increment.__call__<span class="o">(</span><span class="m">2</span><span class="o">)</span>
<span class="m">3</span>
</code></pre></div>
<p>You can do it with regular functions too.
There is a difference between the python that you write, and the python that the
compiler perceives. The <code>(</code> is replaced with <code>.__call__(</code>.</p>
<h2 id="current-favourite-python-book">Current favourite python book</h2>
<p>There are 4 levels of Python code:</p>
<ol>
<li>Syntax</li>
<li>Idioms</li>
<li>Patterns</li>
<li>Architecture</li>
</ol>
<p>Check out “Architecture Patterns with Python” by Harry Percival and Bob Gregory.</p>
<h2 id="secret-instinct">Secret Instinct</h2>
<p>Something that the best programmers have in common:</p>
<p><em>They really hate repetitive code.</em></p>Multi-tenant Django webapps2021-11-25T13:43:34+01:002021-11-25T13:43:34+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-25:/multi-tenant-django-webapps.html<p>An <a href="https://www.viget.com/articles/multi-tenancy-in-django/">article</a> explaining
how a web-dev shop built a multi-tenant web application using Django and python.</p>
<p>Also:</p>
<ul>
<li>a <a href="https://books.agiliq.com/projects/django-multi-tenant/en/latest/">book</a></li>
<li>a <a href="https://github.com/citusdata/django-multitenant">repo</a></li>
</ul>The 3-2-1 backup rule2021-11-25T10:40:14+01:002021-11-25T10:40:14+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-25:/the-3-2-1-backup-rule.html<ul>
<li><strong>3 copies, 2 types of media, 1 copy off-site</strong></li>
<li>Have 1 primary backup and 2 duplicates, in additional to the original data.</li>
<li><span class="caps">NAS</span>, <span class="caps">USB</span> drive, Cloud storage, Server, Glacier, etc</li>
</ul>
<p>For example, your laptop could be backed up to a <span class="caps">NAS</span>, a server, and glacier
storage. Or 2 <span class="caps">NAS</span> and 1 archival cloud storage.</p>Lessons from creating a production database2021-11-24T12:21:10+01:002021-11-24T12:21:10+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-24:/lessons-from-creating-a-production-database.html<p>wip</p>
<ul>
<li>Have a simple, crisp mission statement that expresses your raison d’etre. </li>
<li>Socialize estimates of task difficulty repeatedly;</li>
<li>Do whatever it takes to make sure that manager churn does not result in
unfair career outcomes for ICs</li>
</ul>
<p><a href="https://maheshba.bitbucket.io/blog/2021/10/19/42Things.html">source</a></p>Unison helper2021-11-23T15:09:46+01:002021-11-23T15:09:46+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-23:/unison-helper.html<p><a href="https://www.cis.upenn.edu/~bcpierce/unison/">Unison</a> is a file synchronization
tool that works across operating systems. It can sync in both directions and
works for collections of directories and files.</p>
<p>I wanted to sync projects between my local machine and a dev machine reliably
and simply. Using git to push to an origin and then pull from the other
side wasn’t simple enough.</p>
<p>Unison can power the synrconization, but I wanted some nice convenient way to
run the syncronization steps for an entire project, even if I were in a
subdirectory. I also wanted the location of the project on the other machine to
be found automatically, so that I didn’t need to think about paths and couldn’t
accidentally move files to the wrong location.</p>
<p>With that in mind, I decided that:
1. The project root is the same as the git root. The git project root can be found using: <code>localProjectRoot=$(git rev-parse --show-toplevel)</code>
1. The location of the project on the remote dev-box should be specified in
some configuration file. I decided to create a <code>.remoteprojectroot</code> file the project’s (local) root directory: <code>remoteProjectRoot=$(cat $localProjectRoot/.remoteroot)</code></p>
<p>I then created the following function which lets me run <code>uni</code> from
anywhere within the project on my local machine, and have the project state
synced to the remote dev-box.</p>
<div class="highlight"><pre><span></span><code>uni<span class="o">()</span> <span class="o">{</span>
<span class="nv">host</span><span class="o">=</span><host>
<span class="nv">user</span><span class="o">=</span><user>
<span class="nv">localProjectRoot</span><span class="o">=</span><span class="k">$(</span>git rev-parse --show-toplevel<span class="k">)</span>
<span class="nv">remoteProjectRoot</span><span class="o">=</span><span class="k">$(</span>cat <span class="nv">$localProjectRoot</span>/.remoteroot<span class="k">)</span>
<span class="nb">echo</span> <span class="s2">"Local Project Root: "</span><span class="nv">$localProjectRoot</span>
<span class="nb">echo</span> <span class="s2">"Remote Project Root: "</span><span class="nv">$remoteProjectRoot</span>
unison -batch -color <span class="nb">true</span> <span class="nv">$localProjectRoot</span> ssh://<span class="nv">$host</span>@<span class="nv">$user</span>/<span class="nv">$remoteProjectRoot</span>
<span class="o">}</span>
</user></host></code></pre></div>
<p><span class="caps">TODO</span>:
1. Stop syncronization if <code>remoteProjectRoot</code> can’t be found.</p>Proxy Servers2021-11-22T10:13:06+01:002021-11-22T10:13:06+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-22:/reverse-proxy.html<h3 id="reverse-proxy-server">Reverse proxy server</h3>
<ul>
<li>A reverse proxy is a server.</li>
<li>It sits in front of other servers and forwards client requests to them.</li>
<li>Is a middleman or broker on behalf of origin servers.</li>
</ul>
<h4 id="usually-used-to">Usually used to:</h4>
<ul>
<li>Increase security (keeping the <span class="caps">IP</span> addresses of origin servers private makes
DDoS attacks much harder).</li>
<li>Optimize performance (caching).</li>
<li>Increase reliability (load balancing).</li>
</ul>
<p>The client thinks that the reverse proxy <em>is</em> the end-point server.</p>
<h3 id="normal-proxies">Normal proxies</h3>
<ul>
<li>Also called a web or forwarding (normal) proxy.</li>
<li>Sits between client machines and hosts.</li>
<li>Is a middleman on behalf of the clients.</li>
</ul>
<p>The server thinks that the client <em>is</em> the proxy.</p>
<p>Source: <a href="https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/">cloudflare</a></p>Unison2021-11-17T14:01:06+01:002021-11-17T14:01:06+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-17:/unison.html<ul>
<li>If you can <code>ssh</code> into the server then Unison should work.</li>
<li>Install the same version on the client and on the server.</li>
</ul>
<p>To sync the contents of <code>dir b</code> into <code>dir a</code>:</p>
<div class="highlight"><pre><span></span><code>unison -batch -color <span class="nb">true</span> <dir-a> ssh://<user>@a<host>/<dir-b>
</dir-b></host></user></dir-a></code></pre></div>
<h2 id="setup">Setup</h2>
<ol>
<li>Download the binary file with <code>wget</code></li>
<li><code>chmod +x <file></file></code></li>
<li>copy the executable somewhere on your path like <code>/usr/local/bin</code></li>
<li><code>unison -doc tutorial</code> > <code>remote method</code> > <code>remote shell method</code></li>
<li>use a filewatcher to sync on change, or use cron to sync every n minutes</li>
</ol>
<p>You can also use unison to sync files on the same computer.</p>
<h2 id="references">References</h2>
<p><a href="https://gist.github.com/asksven/ee38dbe5bdab7e39aa133a1df24dd034">gist</a></p>
<h2 id="background">Background</h2>
<p>Recently I needed to work on a codebase that was on a remote machine whilst
still using my local workflow and developer tools.</p>
<p>Vim’s built in <code>scp://</code> functionality isn’t versatile enough - i wanted to use
<code>fzf.vim</code> to search for text across the code base. This led me to <code>rsync</code>, <code>sshfs</code>,
and eventually <code>unison</code>.</p>Japanese philosophies for spending money2021-11-14T11:05:54+01:002021-11-14T11:05:54+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-14:/japanese-philosophies-for-spending-money.html<h2 id="chisoku">Chisoku</h2>
<p>Being content with what you already have. You don’t need more stuff, or new
stuff, just because it exists. You’re being <em>marketed at</em>, or <em>marked</em>.</p>
<p>Do you need it or do you want it? Need is finite, want is infinite.</p>
<h2 id="wabi-sabi">Wabi Sabi</h2>
<p>Beauty in imperfection. As things begin to wear, they develop some character
that can make them nicer than something that is fresh. It’s the opposite of <em>new
shiny thing syndrome</em>. </p>
<h2 id="mitate">Mitate</h2>
<p>An object has more than one purpose. Repurposing, inventing, adapting, etc is
interesting and a mini achievement that can be celebrated.</p>
<p><a href="https://hulry.com/japanese-philosophies-money/">Source</a>.</p>Uninstall all pip packages2021-11-09T17:05:27+01:002021-11-09T17:05:27+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-09:/uninstall-all-pip-packages.html<p>In case you want to reset your virtualenv:</p>
<div class="highlight"><pre><span></span><code><span class="n">pip</span> <span class="n">freeze</span> <span class="o">|</span> <span class="n">xargs</span> <span class="n">pip</span> <span class="n">uninstall</span> <span class="o">-</span><span class="n">y</span>
</code></pre></div>alpine.js2021-11-09T10:53:34+01:002021-11-09T10:53:34+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-09:/alpine-js.html<p>An introductory
<a href="https://www.smashingmagazine.com/2020/03/introduction-alpinejs-javascript-framework/">article</a>
with examples from Smashing Magazine that introduces and explains what alpine.js
is and how it is different to jQuery and vue.js.</p>Zsh commands: exec vs source2021-11-09T10:34:10+01:002021-11-09T10:34:10+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-09:/zsh-commands-exec-vs-source.html<p><code>source</code> and <code>exec</code> are both built-ins.</p>
<p><code>source</code> evaluates or runs the content of a file. For example, if you <code>source ~/.zshrc</code> you
apply the content of the file to the currently running Zsh process. You can
source and valid Zsh code.</p>
<p><code>exec</code> replaces the current shell process with another process. Your terminal
(<code>tty</code>) session is running a shell. Replace it with another shell without
launching another tty. It could be
the same shell with different settings or flags.</p>
<p>The <code>tty</code> (teletypewriter) command prints the name of the terminal you’re using.</p>Chezmoi commands2021-11-05T17:03:37+01:002021-11-05T17:03:37+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-05:/chezmoi-commands.html<p>Chezmoi is a dotfiles manager. </p>
<ul>
<li><code>chezmoi diff</code> - see the difference between the current state of your local dotfiles compared against the checked-in versions.</li>
<li><code>cm aa</code> - to update chezmoi with all your local changes.</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="nb">alias</span> -g <span class="nv">aa</span><span class="o">=</span><span class="s2">"status | cut -c 4- | xargs -I % -p sh -c 'chezmoi add ~/%'"</span>
</code></pre></div>zsh suspend or push function2021-11-05T11:24:01+01:002021-11-05T11:24:01+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-05:/zsh-suspend-or-push-function.html<p>In zsh you can suspend a command whilst typing it.</p>
<p>This clears the current line, lets you run another command, and then when it
finishes the original unfinished text is put back.</p>
<p>This is useful if whilst typing a command you realise you need to run another
command first, like:</p>
<ul>
<li><code>ls</code> to check the name for an argument.</li>
<li>creating a location before moving something into that location.</li>
<li>checking <code>man</code> or <code>tldr</code> for a command flag.</li>
</ul>
<p>Zsh has built-in functions <code>push-line</code> and <code>push-input</code>.</p>
<p><a href="https://sgeb.io/posts/bash-zsh-half-typed-commands/">blog post</a> and also this <a href="https://unix.stackexchange.com/a/10851/235350"><span class="caps">SO</span> answer</a></p>
<p><a href="https://sgeb.io/posts/zsh-zle-custom-widgets/">zsh widgets</a>
<a href="https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets">zsh documentation</a></p>Stoicism2021-11-04T13:50:53+01:002021-11-04T13:50:53+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-04:/stoicism.html<p>Yesterday the YouTube algorithm suggested a video by <a href="https://www.youtube.com/c/DailyStoic">Daily
Stoic</a>. </p>
<div class="relative mt-3" style="padding-top: 56.25%">
<iframe allow="autoplay; encrypted-media" allowfullscreen="" class="absolute inset-0 w-full h-full" frameborder="0" loading="lazy" src="https://youtube.com/embed/X5JwF5pwR34">
</iframe>
</div>Math explainers2021-11-04T13:11:28+01:002021-11-04T13:11:28+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-04:/math-explainers.html<p>I’m really enjoying math explainers lately, on YouTube there’s
<a href="https://www.youtube.com/c/3blue1brown">3blue1brown</a> and
<a href="https://www.youtube.com/c/veritasium">Veritsasium</a>, and for text I’ve enjoyed a
couple of <a href="https://mattferraro.dev/https://mattferraro.dev/">Matt Ferraro’s</a>
blog posts.</p>
<p>Check out <a href="https://mattferraro.dev/posts/geometric-algebra">What is the inverse of a
vector?</a> and <a href="https://mattferraro.dev/posts/poissons-equation">Poisson’s
Equation is the Most Powerful Tool not yet in your
Toolbox</a>.</p>
<p>This is a great explanation of the geometric reasoning for the solution to
quadratics and cubics, which then goes on to introduce imaginary numbers.</p>
<div class="relative mt-3" style="padding-top: 56.25%">
<iframe allow="autoplay; encrypted-media" allowfullscreen="" class="absolute inset-0 w-full h-full" frameborder="0" loading="lazy" src="https://youtube.com/embed/cUzklzVXJwo">
</iframe>
</div>Do-nothing scripts2021-11-03T14:52:37+01:002021-11-03T14:52:37+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-03:/do-nothing-scripts.html<p>Interesting
<a href="https://blog.danslimmon.com/2019/07/15/do-nothing-scripting-the-key-to-gradual-automation/">article</a>
about writing down a list of manual steps for tasks that can’t be automated. A
do-nothing script can take some of the toil out of a manual slog. </p>
<p>Even though the script doesn’t do anything other than display text on a screen
(the instructions to complete the task) it’s still helpful, because:</p>
<ul>
<li>All the steps and information required are collected together.</li>
<li>It lowers the activation energy required to do something fiddly.</li>
<li>Each step is part of a function, and the function can be updated with
automation code if automation becomes possible.</li>
<li>It’s more difficult to lose your place in the process, or get the order of
steps wrong.</li>
</ul>
<p>It doesn’t reduce manual effort. It does reduce toil.</p>Virtual environments and python versions2021-11-01T16:09:14+01:002021-11-01T16:09:14+01:00John Mathewstag:python-blog.johnmathews.is,2021-11-01:/virtual-environments-and-python-versions.html<div class="toc"><span class="toctitle">Table of Contents</span><ul>
<li><a href="#recipe">Recipe</a><ul>
<li><a href="#create-and-activate-a-new-virtualenv">Create and activate a new virtualenv</a></li>
</ul>
</li>
<li><a href="#background">Background</a></li>
<li><a href="#python-versions">Python versions</a><ul>
<li><a href="#installing-new-python-versions">Installing new python versions</a></li>
<li><a href="#set-a-particular-python-version">Set a particular python version</a></li>
<li><a href="#other-commands">Other commands</a></li>
<li><a href="#tests">Tests</a></li>
<li><a href="#source">Source</a></li>
</ul>
</li>
<li><a href="#code-editor-integration-using-activate_thispy">Code editor integration using activate_this.py</a></li>
</ul>
</div>
<h1 id="recipe">Recipe</h1>
<h2 id="create-and-activate-a-new-virtualenv">Create and activate a new virtualenv</h2>
<ul>
<li><code>pyenv versions</code> or <code>pyenv install -l</code> - list available versions</li>
<li><strong><code>pyenv virtualenv <python version=""> <environment name=""></environment></python></code></strong></li>
<li><strong><code>pyenv local <environment name=""></environment></code></strong><ul>
<li>this creates a <code>.python-version</code> file in the current directory</li>
<li>the environment will be automatically activated and deactivated because <code>eval "$(pyenv
virtualenv-init -)"</code> is in the <code>.zshrc</code> file.</li>
</ul>
</li>
<li><code>python -V</code></li>
<li><code>python -m test</code></li>
</ul>
<p>If you did not configure <code>eval "$(pyenv virtualenv-init -)"</code> to run in your shell then</p>
<ul>
<li><code>pyenv activate <environment name=""></environment></code></li>
<li><code>pyenv deactivate</code></li>
</ul>
<h1 id="background">Background</h1>
<ul>
<li><code>pyenv</code> manages multiple versions of Python itself.</li>
<li><code>virtualenv/venv</code> manages virtual environments for a specific Python version.</li>
<li><code>pyenv-virtualenv</code> is a pyenv plugin that plays nicely with <code>virtualenv</code>. It manages virtual environments across varying versions of Python.</li>
<li><code>python -V</code> - shows which python version is currently being used.</li>
<li><code>which python</code> - shows the path or location of the current python.</li>
</ul>
<h1 id="python-versions">Python versions</h1>
<h2 id="installing-new-python-versions">Installing new python versions</h2>
<ul>
<li><code>pyenv install --list | grep " 3\.9\."</code> - list versions</li>
<li><code>pyevn install <name></name></code> - install a new version</li>
<li>pyenv python versions are installed at <code>~/.pyenv/versions/</code></li>
<li>You can uninstall a pyenv python version by<ul>
<li><code>rm -rf ..</code> the subdir in <code>~/.pyenv/versions/...</code> </li>
<li><code>pyenv uninstall <name></name></code></li>
</ul>
</li>
</ul>
<h2 id="set-a-particular-python-version">Set a particular python version</h2>
<ul>
<li><code>pyenv versions</code> - list available versions</li>
<li><code>pyenv global <name></name></code> - set system default python version</li>
<li><code>pyenv global system</code> - back to how things were before pyenv</li>
<li><code>pyenv local <name></name></code> - set default version for this current dir</li>
<li>this creates a <code>.python-version</code> file in the current directory. If pyenv is
active in your environment then the version specified in this file will be activated</li>
</ul>
<h2 id="other-commands">Other commands</h2>
<ul>
<li><code>pyenv uninstall <version></version></code></li>
<li><code>pyenv rehash</code> - run after installing a new version, or install a package
that provides binaries</li>
</ul>
<p><a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md">source</a></p>
<h2 id="tests">Tests</h2>
<ul>
<li><code>python -m test</code> - run some tests to be confident everything is ok</li>
</ul>
<h2 id="source">Source</h2>
<p><a href="https://realpython.com/intro-to-pyenv/">Real Python article</a></p>
<h1 id="code-editor-integration-using-activate_thispy">Code editor integration using <code>activate_this.py</code></h1>
<p>So that code editors like vim can respond to the currently active virtual
environment and update <code>path</code>, <code>sys.path</code> and <code>sys.prefix</code> (for example when
running Django test suites) virtualenv uses <code>activate_this.py</code>
to make these changes.</p>
<p>For some reason it isn’t available on my setup, so here is a copy I found
somewhere. It’s different to the version hosted on the
<a href="https://github.com/pypa/virtualenv/blob/main/src/virtualenv/activation/python/activate_this.py">virtualenv
repo</a>
and I haven’t looked at why yet. Copy paste the version below into <code>~/.pyenv/versions/<version>/envs/<environment name="">/bin/activate_this.py</environment></version></code></p>
<p><strong>activate_this.py</strong></p>
<div class="highlight"><pre><span></span><code><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="c1"># https://github.com/pypa/virtualenv/blob/main/src/virtualenv/activation/python/activate_this.py</span>
<span class="sd">"""Activate virtualenv for current interpreter:</span>
<span class="sd">Use exec(open(this_file).read(), {'__file__': this_file}).</span>
<span class="sd">This can be used when you must use an existing Python interpreter, not the virtualenv bin/python.</span>
<span class="sd">"""</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">site</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">abs_file</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">NameError</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AssertionError</span><span class="p">(</span><span class="s2">"You must use exec(open(this_file).read(), {'__file__': this_file}))"</span><span class="p">)</span>
<span class="n">bin_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">abs_file</span><span class="p">)</span>
<span class="n">base</span> <span class="o">=</span> <span class="n">bin_dir</span><span class="p">[:</span> <span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="s2">"__BIN_NAME__"</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="c1"># strip away the bin part from the __file__, plus the path separator</span>
<span class="c1"># prepend bin to PATH (this file is inside the bin directory)</span>
<span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">"PATH"</span><span class="p">]</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">pathsep</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">bin_dir</span><span class="p">]</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"PATH"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">pathsep</span><span class="p">))</span>
<span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">"VIRTUAL_ENV"</span><span class="p">]</span> <span class="o">=</span> <span class="n">base</span> <span class="c1"># virtual env is right above bin directory</span>
<span class="c1"># add the virtual environments libraries to the host python import mechanism</span>
<span class="n">prev_length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
<span class="k">for</span> <span class="n">lib</span> <span class="ow">in</span> <span class="s2">"__LIB_FOLDERS__"</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">pathsep</span><span class="p">):</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">bin_dir</span><span class="p">,</span> <span class="n">lib</span><span class="p">))</span>
<span class="n">site</span><span class="o">.</span><span class="n">addsitedir</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span> <span class="k">if</span> <span class="s2">"__DECODE_PATH__"</span> <span class="k">else</span> <span class="n">path</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">[:]</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">[</span><span class="n">prev_length</span><span class="p">:]</span> <span class="o">+</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">prev_length</span><span class="p">]</span>
<span class="n">sys</span><span class="o">.</span><span class="n">real_prefix</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">prefix</span>
<span class="n">sys</span><span class="o">.</span><span class="n">prefix</span> <span class="o">=</span> <span class="n">base</span>
</code></pre></div>SaaS metrics that matter2021-10-19T16:39:25+02:002021-10-19T16:39:25+02:00John Mathewstag:python-blog.johnmathews.is,2021-10-19:/saas-metrics-that-matter.html<ul>
<li>A <a href="https://sacks.substack.com/p/the-saas-metrics-that-matter">blog post</a> about SaaS metrics</li>
<li>A <a href="https://www.saasgrid.com/">tool</a> for calculating metrics</li>
</ul>Training Zones2021-10-19T15:41:17+02:002021-10-19T15:41:17+02:00John Mathewstag:python-blog.johnmathews.is,2021-10-19:/training-zones.html<p>Jack Daniels’ book about
<a href="https://www.amazon.com/Daniels-Running-Formula-Jack-Tupper/dp/1450431836">running</a> has a training plan that seems to be highly regarded.</p>
<p><img alt="training" class="image-process-article-inline-image" loading="lazy" sizes="(min-width: 1536x) 1600px, (min-width: 1280px) 1300px, (min-width: 1024px) 1100px, (min-width: 768px) 800px, (min-width: 640px) 800px, 100vw" src="https://python-blog.johnmathews.is/images/derivatives/article-inline-image/1600w/training-graph.png" srcset="https://python-blog.johnmathews.is/images/derivatives/article-inline-image/300w/training-graph.png 300w, https://python-blog.johnmathews.is/images/derivatives/article-inline-image/600w/training-graph.png 600w, https://python-blog.johnmathews.is/images/derivatives/article-inline-image/800w/training-graph.png 800w, https://python-blog.johnmathews.is/images/derivatives/article-inline-image/1600w/training-graph.png 1600w"/></p>
<ul>
<li><span class="caps">LT1</span> - 75% of maximum heart rate. ~<span class="caps">145BPM</span> maybe (for running).</li>
<li><span class="caps">LT2</span> - Average heart rate for a maximum sustained effort for 1 hour.</li>
</ul>
<h3 id="zone-1">Zone 1</h3>
<ul>
<li>Trains your body to use fat stores as energy.</li>
<li>Increases your oxidative capacity.</li>
<li>Trains slow-twitch muscle fibres.</li>
<li>You can speak a full sentence without taking a breath.</li>
</ul>
<h3 id="zone-3">Zone 3</h3>
<ul>
<li>Trains your body to use carbohydrates as energy.</li>
<li>Trains your glycolytic system.</li>
<li>Increases your ability to mitigate lactic acid.</li>
<li>Trains fast-twitch muscle fibres.</li>
<li>You can barely speak in this zone.</li>
</ul>
<h3 id="training">Training</h3>
<ul>
<li>Zone 2 is not a good place to train, it’s middle of the road and not an efficient use of time or effort.</li>
<li>80% of training should be in zone 1.</li>
<li>We have much more energy stored as fat than we do as carbs.</li>
</ul>
<div class="relative mt-3" style="padding-top: 56.25%">
<iframe allow="autoplay; encrypted-media" allowfullscreen="" class="absolute inset-0 w-full h-full" frameborder="0" loading="lazy" src="https://youtube.com/embed/F3QcX58i3WE">
</iframe>
</div>Naming things is hard2021-10-14T13:10:37+02:002021-10-14T13:10:37+02:00John Mathewstag:python-blog.johnmathews.is,2021-10-14:/naming-things-is-hard.html<ol>
<li><a href="https://seths.blog/2005/10/the_new_rules_o/">Seth’s blog</a>
- Background and context</li>
<li><a href="https://www.thebalancesmb.com/put-your-business-name-to-the-test-2294885">small business website</a> <ul>
<li>Process</li>
<li>Testing</li>
</ul>
</li>
</ol>Tove Lo - Habits2021-10-14T12:00:21+02:002021-10-14T12:00:21+02:00John Mathewstag:python-blog.johnmathews.is,2021-10-14:/tove-lo-habits.html<p>Brutal loneliness.</p>
<div class="relative mt-3" style="padding-top: 56.25%">
<iframe allow="autoplay; encrypted-media" allowfullscreen="" class="absolute inset-0 w-full h-full" frameborder="0" loading="lazy" src="https://youtube.com/embed/SYM-RJwSGQ8">
</iframe>
</div>
<p><a href="https://en.wikipedia.org/wiki/Tove_Lo#Songwriting">wikipedia</a></p>Logging Best Practices2021-10-07T11:24:48+02:002021-10-07T11:24:48+02:00John Mathewstag:python-blog.johnmathews.is,2021-10-07:/logging-best-practices-notes.html<div class="toc"><span class="toctitle">Table of Contents</span><ul>
<li><a href="#past-tense-only">Past tense only</a></li>
<li><a href="#separate-messages-and-parameter-values">Separate messages and parameter values</a></li>
<li><a href="#warnings-are-for-things-that-worked">Warnings are for things that worked</a></li>
<li><a href="#errors-are-for-things-that-did-not-work">Errors are for things that did not work</a></li>
<li><a href="#info-is-for-business">Info is for business</a></li>
<li><a href="#debug-is-for-technology">Debug is for technology</a></li>
</ul>
</div>
<p>A great <a href="https://tuhrig.de/my-logging-best-practices/">aricle</a> by someone called
Thomas about how to build useful logs. I find the inclusion of examples to be
very useful, and the background information in the introduction is a nice addition.</p>
<h1 id="past-tense-only">Past tense only</h1>
<ul>
<li>
<p>Most stuff should be past tense.</p>
</li>
<li>
<p>Say what happened, not what is about to happen. This makes it much more explicit and easier to read. The reader
doesn’t need to infer anything.</p>
</li>
</ul>
<h1 id="separate-messages-and-parameter-values">Separate messages and parameter values</h1>
<ul>
<li>
<p>Think about how you might search for parameter values - if they are
interspersed between normal English sentences it becomes harder to search for.</p>
</li>
<li>
<p>Also consider readability if the parameters are very long or harder to read literally.</p>
</li>
</ul>
<h1 id="warnings-are-for-things-that-worked">Warnings are for things that worked</h1>
<ul>
<li>The thing was done, but not perfectly.</li>
</ul>
<h1 id="errors-are-for-things-that-did-not-work">Errors are for things that did not work</h1>
<ul>
<li>The thing wasn’t done.</li>
</ul>
<h1 id="info-is-for-business">Info is for business</h1>
<ul>
<li>
<p>The info log looks like a book and reads like a story.</p>
</li>
<li>
<p>It tells you what happened, but not necessarily how it happened.</p>
</li>
<li>
<p><code>User x signed up</code></p>
</li>
<li><code>User x bought an item</code></li>
<li><code>User x navigated to settings</code></li>
</ul>
<h1 id="debug-is-for-technology">Debug is for technology</h1>
<ul>
<li>
<p>It tells you how stuff happened.</p>
</li>
<li>
<p><code>Saved user credentials</code></p>
</li>
<li><code>Started cron job</code></li>
</ul>How to get rich without getting lucky2021-10-05T12:32:16+02:002021-10-05T12:32:16+02:00John Mathewstag:python-blog.johnmathews.is,2021-10-05:/how-to-get-rich-without-getting-lucky.html<p>My notes and additions from a twitter
<a href="https://twitter.com/naval/status/1002103360646823936">thread</a> created by
<a href="https://twitter.com/naval/status/1002103360646823936">@naval</a> on 31 May 2018.</p>
<h3 id="background">Background</h3>
<ol>
<li>
<p>Seek wealth, not money or status.</p>
</li>
<li>
<p>Wealth is having assets that earn while you sleep.</p>
</li>
<li>
<p>You must own a piece of a business - you wont get rich by renting out
your time.</p>
</li>
<li>
<p>You will get rich by giving society what it wants but doesn’t know how
to get. At scale.</p>
</li>
<li>
<p>Money is how we transfer time and wealth.</p>
</li>
<li>
<p>Status is your place in the social hierarchy.</p>
</li>
<li>
<p>Understand that ethical wealth creation is possible. If you secretly despise
wealth, it will elude you.</p>
</li>
<li>
<p>Ignore people playing status games.</p>
</li>
</ol>
<h3 id="actions">Actions</h3>
<ol>
<li>
<p><strong>Learn to sell. Learn to build. If you can do both, you will be unstoppable.</strong></p>
</li>
<li>
<p><strong>Arm yourself with specific knowledge, accountability, and leverage.</strong></p>
</li>
<li>
<p>Pick an industry where you can play long term games with long term people.</p>
</li>
<li>
<p>Maximise positive feedback loops. Design and defend against negative
feedback loops.</p>
</li>
<li>
<p>All the returns in life, whether in wealth, relationships, or knowledge,
come from positive feedback loops, or compounding.</p>
</li>
<li>
<p>The internet has massively broadened the possible space of careers. Most
people haven’t figured this out yet. Write down some examples.</p>
</li>
<li>
<p>Play games where you iterate on past successes and experiences
to create more success and more experience. This is compounding.</p>
</li>
<li>
<p>Pick business partners with high intelligence, energy, and, above all, integrity.</p>
</li>
<li>
<p>Knowledge is not the same as intelligence.</p>
</li>
<li>
<p>Its hard to estimate higher levels of expertise relative to your own.</p>
</li>
<li>
<p>Don’t partner with cynics and pessimists. Their beliefs are self-fulfilling.</p>
</li>
</ol>
<h3 id="specific-knowledge">Specific Knowledge</h3>
<ol>
<li>
<p>Building specific knowledge will feel like play to you but will look like
work to others.</p>
</li>
<li>
<p>Specific knowledge is often highly technical or creative. It cannot be
outsourced or automated.</p>
</li>
<li>
<p>Specific knowledge is found by pursuing your genuine curiosity and passion
rather than whatever is hot right now.</p>
</li>
<li>
<p>Specific knowledge is knowledge that you cannot be trained for. If society
can train you, it can train someone else and replace you.</p>
</li>
<li>
<p>When specific knowledge is taught, it’s through apprenticeships, not schools.</p>
</li>
</ol>
<h3 id="accountability">Accountability</h3>
<ol>
<li>
<p>Embrace accountability, and take business risks under your own name. Society
will reward you with responsibility, equity, and leverage.</p>
</li>
<li>
<p>The most accountable people have singular, public, and risky brands: Oprah,
Trump, Kanye, Elon.</p>
</li>
</ol>
<h3 id="leverage-capital-people-no-marginal-costs">Leverage - capital, people, no marginal costs</h3>
<ol>
<li>
<p>Capital means money. To raise money, apply your specific knowledge, with
accountability, and show resulting good judgment.</p>
</li>
<li>
<p>Leverage is the ability to increase the effects of something. “Give me a
lever long enough, and a place to stand, and I will move the earth.” - Archimedes</p>
</li>
<li>
<p>Fortunes require leverage. Business leverage comes from capital, people, and
products with no marginal cost of replication (code and media).</p>
</li>
<li>
<p>Labor means people working for you. It’s the oldest and most fought-over
form of leverage. Labor leverage will impress your parents, but don’t waste
your life chasing it.</p>
</li>
<li>
<p>Capital and labor are permissioned leverage. Everyone is chasing capital,
but someone has to give it to you. Everyone is trying to lead, but someone
has to follow you.</p>
</li>
<li>
<p>Code and media are permissionless leverage. They’re the leverage behind the
newly rich. You can create software and media that works for you while you sleep.</p>
</li>
<li>
<p>An army of robots is freely available - it’s just packed in data centers for
heat and space efficiency. Use it.</p>
</li>
<li>
<p>If you can’t code, write books and blogs, record videos and podcasts.</p>
</li>
<li>
<p>Leverage is a force multiplier for your judgement. If you have good
judgement, reap additional benefits by leveraging it more. This is related
to comments by Geoffrey Hinton about the benefits of developing your intuitions
<a href="https://python-blog.johnmathews.is/notes-from-an-interview-with-geoffrey-hinton.html">interview</a>.</p>
</li>
</ol>
<h3 id="learning-and-practice">Learning and practice</h3>
<ol>
<li>
<p>Apply specific knowledge with leverage, and progress is inevitable.</p>
</li>
<li>
<p>You should be too busy to “do coffee”, while still keeping an uncluttered calendar.</p>
</li>
<li>
<p>Set and enforce an aspirational personal hourly rate. If fixing a problem
will save less than your hourly rate, ignore it. If outsourcing a task will
cost less than your hourly rate, outsource it.</p>
</li>
<li>
<p>Who you work with and what you work on are more important than how hard you work.</p>
</li>
<li>
<p>Work as hard as you can.</p>
</li>
<li>
<p>Thinking your own thoughts is tiring. Asking good questions is hard.</p>
</li>
<li>
<p>Doing is faster than watching.</p>
</li>
<li>
<p>Developing good judgement requires experience, if you have a chance to gain
real experience then take it. Learning can amplify the benefits of experience.</p>
</li>
<li>
<p>Real experience is more useful than a prestigious course or degree.</p>
</li>
<li>
<p>Be patient and persistent, shape your circumstances. It doesn’t mean you’ve
made a mistake if you can’t do something real right now.</p>
</li>
<li>
<p>There is no skill called business. Avoid business magazines and business
classes. Think about why and extend this to other media.</p>
</li>
<li>
<p>Passive, peace-meal knowledge acquisition by itself does not lead to
specific knowledge or expertise. Subscribing to newsletters or social media
accounts offers quickly diminishing returns, at best.</p>
</li>
<li>
<p>Read long-form media which you actively looked for. Information that comes
to you for free has competing interests which put you second.</p>
</li>
<li>
<p>If you read only 1 book on a subject then you’ll likely be a clone. If you
read 2 books you’ll grapple with confusion. Read 3 and you’ll begin to form
your own substantial opinions and intuitions.</p>
</li>
<li>
<p>Become the best in the world at what you do. Keep redefining what you do
until this is true.</p>
</li>
<li>
<p>There are no get rich quick schemes. That’s just someone else getting rich
off you.</p>
</li>
<li>
<p>Study microeconomics, game theory, psychology, persuasion, ethics,
mathematics, and computers. Real experience with skin in the game will teach
you more than a book or a professor.</p>
</li>
<li>
<p>There are lots of ways to grow beyond being a beginner, but no one can do
the heavy-lifting for you. A course or product that offers to teach you
specific knowledge will give diminishing returns. The more of a beginner you
are, the better the course will appear.</p>
</li>
<li>
<p>Reading is faster than listening.</p>
</li>
</ol>
<h3 id="remember-why">Remember why</h3>
<ol>
<li>
<p>When you’re finally wealthy, you’ll realize that it wasn’t what you were
seeking in the first place. But that’s for another day.</p>
</li>
<li>
<p>Check in with your 70 year old self, and your 10 years older self. What do
they think of you now? Are they sympathetic? Proud? Do they say go faster, or
slow down? Be kind to yourself.</p>
</li>
<li>
<p>There is a big difference between saying “I am intimidated” and “I am
feeling intimidated”. You can do it.</p>
</li>
</ol>
<h2 id="my-top-2">My top 2:</h2>
<ol>
<li>
<p>Embrace accountability, and take business risks under your own name. Society
will reward you with responsibility, equity, and leverage.</p>
</li>
<li>
<p>Capital means money. To raise money, apply your specific knowledge, with
accountability, and show resulting good judgment.</p>
</li>
</ol>Ultra-running benchmarks2021-10-01T13:06:26+02:002021-10-01T13:06:26+02:00John Mathewstag:python-blog.johnmathews.is,2021-10-01:/ultra-running-benchmarks.html<ul>
<li>7 days/week</li>
<li>25 km/day</li>
<li>Start easy. Last 50% begin to move through the field.</li>
<li>40% - 50% of entrants <span class="caps">DNF</span></li>
<li>90% of the course is runnable</li>
<li>50km: 6:10 min/km (Diez Vista 50km)</li>
<li>50km: 7:25 - 8:00 min/km (Beaver Flat 50 <span class="caps">JP</span>)</li>
<li>160km: 10:35 min/km (<span class="caps">JP</span>)</li>
<li>150km: 6:30 - 8:00 min/km (<span class="caps">UTMB</span> <span class="caps">CD</span>)</li>
<li>training: 6:10 is slow enough to stay fresh. 5:10-5:30 is fast.</li>
</ul>Vim built-in color names2021-09-29T11:51:08+02:002021-09-29T11:51:08+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-29:/vim-built-in-color-names.html<p>Script to output colors in a buffer:</p>
<div class="highlight"><pre><span></span><code><span class="p">:</span><span class="k">so</span> $VIMRUNTIME<span class="sr">/syntax/</span>colortest.<span class="k">vim</span>
</code></pre></div>
<p><span class="caps">SO</span> <a href="https://vi.stackexchange.com/questions/13458/make-vim-show-all-the-colors">question</a> with reference image.</p>Lightning network point-of-sale2021-09-28T11:58:24+02:002021-09-28T11:58:24+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-28:/lightning-network-point-of-sale.html<p>A project to demonstrate an offline, fast, point-of-sale device to allow
merchants and customers to make fast cheap transactions on the lightning network.</p>
<ul>
<li><a href="https://github.com/arcbtc/LNURLPoS">Github repo</a></li>
<li><a href="https://github.com/fiatjaf/awesome-lnurl">lnurl super protocal</a></li>
</ul>
<p>Also <a href="https://lnbits.com/">lnbits</a></p>
<blockquote class="twitter-tweet"><p dir="ltr" lang="en">Introducing <a href="https://twitter.com/hashtag/LNURLPoS?src=hash&ref_src=twsrc%5Etfw">#LNURLPoS</a>!<br/>Completely <span class="caps">OFFLINE</span> <$10 <a href="https://twitter.com/hashtag/bitcoin?src=hash&ref_src=twsrc%5Etfw">#bitcoin</a> <span class="caps">LN</span> Point-of-Sale.<br/>Video tut coming soon, workshops at <a href="https://twitter.com/hashtag/hccp21?src=hash&ref_src=twsrc%5Etfw">#hccp21</a> and <a href="https://twitter.com/AdoptingBTC?ref_src=twsrc%5Etfw">@AdoptingBTC</a> <br/>Check out the repo! <a href="https://t.co/JFCKocDfFO">https://t.co/JFCKocDfFO</a><br/>(watch the <a href="https://twitter.com/lnbits?ref_src=twsrc%5Etfw">@lnbits</a> wallet in the background when the transaction is made 🤩) <a href="https://t.co/fVbMkP1kJT">pic.twitter.com/fVbMkP1kJT</a></p>— Ben Arc 🏴✊⚡️ (@arcbtc) <a href="https://twitter.com/arcbtc/status/1442511015669809152?ref_src=twsrc%5Etfw">September 27, 2021</a></blockquote>
<script async="" charset="utf-8" src="https://platform.twitter.com/widgets.js"></script>Make files2021-09-28T11:40:13+02:002021-09-28T11:40:13+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-28:/make-files.html<p>This
<a href="http://nuclear.mutantstargoat.com/articles/make">article</a>
is a fairly practical introduction to the <span class="caps">GNU</span> <code>make</code> tool.</p>
<p>It’s not the article I was looking for though, so I might swap it out if I find
a better one.</p>Linux performance analysis in 60 seconds2021-09-27T16:41:15+02:002021-09-27T16:41:15+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-27:/linux-performance-analysis-in-60-seconds.html<p>A <a href="https://www.brendangregg.com/Articles/Netflix_Linux_Perf_Analysis_60s.pdf">blog
post</a>
with a checklist of steps to take and questions to ask when investigating a
performance issue on a Linux server.</p>Stop reading the news2021-09-22T16:44:54+02:002021-09-22T16:44:54+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-22:/stop-reading-the-news.html<p>An <a href="https://fs.blog/2013/12/stop-reading-news/">article</a> from the Farnham Street blog.</p>View and change settings for GCloud CLI2021-09-22T09:41:15+02:002021-09-22T09:41:15+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-22:/view-and-change-settings-for-gcloud-cli.html<div class="highlight"><pre><span></span><code>gcloud config <span class="nb">set</span> account email@example.com
</code></pre></div>
<div class="highlight"><pre><span></span><code>gcloud auth list
</code></pre></div>Google Pub/Sub2021-09-20T21:09:20+02:002021-09-20T21:09:20+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-20:/google-pub-sub.html<p>Google Pub/Sub has client libraries in all the usual languages. You can also
construct the <span class="caps">API</span> calls yourself. This is a link to the
<a href="https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics/publish"><span class="caps">API</span></a> documentation.</p>
<p>If I were to use a JavaScript beacon to push a message to a topic, my web
analytics engine (a collection of cloud functions) could subscribe to the topic.</p>
<p>This would have the advantage that if the site were flooded with traffic and the
maximum number of function instances wasn’t enough to handle all the events, then
none of the page views (or other events) would be lost because Pub/Sub guarantees
delivery and would keep trying to deliver the message for up to 7 days.</p>
<p>Using the beacon to trigger the cloud functions directly wouldn’t work at scale,
because once the maximum number of instances are being triggered as frequently
as the function takes to run, the endpoint would become unresponsive. There is
no caching layer.</p>
<p>However, the site isn’t being flooded with traffic, and I have better things
to do than fix stuff that isn’t broken.</p>Google Cloud Storage - TTL and CORS settings2021-09-20T15:45:42+02:002021-09-20T15:45:42+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-20:/gcp-storage-bucket-ttl-and-cors-settings.html<p><a href="https://cloud.google.com/storage/docs/configuring-cors">Documentation</a>
explaining how to set <span class="caps">CORS</span> and <span class="caps">TTL</span> for a storage bucket.</p>
<p>Create a <span class="caps">JSON</span> file with something like this:</p>
<div class="highlight"><pre><span></span><code><span class="p">[{</span>
<span class="nt">"origin"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"..."</span><span class="p">],</span>
<span class="nt">"method"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"GET"</span><span class="p">,</span> <span class="s2">"PUT"</span><span class="p">],</span>
<span class="nt">"responseHeader"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"Content-Type"</span><span class="p">],</span>
<span class="nt">"maxAgeSeconds"</span><span class="p">:</span> <span class="mi">300</span>
<span class="p">}]</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code>gsutil cors <span class="nb">set</span> cors.json gs://johnmathews.is
</code></pre></div>
<div class="highlight"><pre><span></span><code>gsutil cors get gs://johnmathews.is <span class="p">|</span> jq
</code></pre></div>Handwritten letters at scale2021-09-20T14:28:59+02:002021-09-20T14:28:59+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-20:/handwritten-letters-at-scale.html<p>A <a href="https://twitter.com/aarondfrancis/status/1438888219471491074">twitter thread</a>
showing how a business started creating handwritten notes to customers using a
plotter, and how they scaled up and further automated the process.</p>
<p>It would be cool to create something like this for western Europe.
<a href="https://handwrytten.com">Handwrytten.com</a> seems to be the main commercial
implementation of this idea in <span class="caps">NA</span>.
<a href="https://www.g2.com/products/handwrytten/competitors/alternatives">Competitors</a>.</p>
<p>I still think it would be fun to make a simple photo printing service. Share a
photo in a WhatsApp message and its printed and delivered.</p>The dangers of social media2021-09-16T10:37:28+02:002021-09-16T10:37:28+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-16:/the-dangers-of-social-media.html<p>A <a href="https://ledger.humanetech.com/">ledger</a> (what’s a ledger? It’s a webpage)
from the “Center For Humane Technology” showing some of the dangers of social media.</p>
<p>The “Do unto others” section is outstanding. It describes the views of some tech
company leaders, including how they limit usage of devices and social media for
themselves and their kids.</p>Visual mathematical proofs2021-09-11T11:30:51+02:002021-09-11T11:30:51+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-11:/visual-mathematical-proofs.html<p>A
<a href="https://math.stackexchange.com/questions/733754/visually-stunning-math-concepts-which-are-easy-to-explain">question</a>
on Stack Overflow asking for great visual proofs of mathematical ideas</p>
<ul>
<li>trigonometry, angles</li>
<li>Fourier transforms</li>
<li>right angle triangles</li>
<li>the area of a circle</li>
</ul>
<p><a href="https://web.archive.org/web/20210911092903/https://math.stackexchange.com/questions/733754/visually-stunning-math-concepts-which-are-easy-to-explain">archive</a></p>1 Peter 1 vs 102021-09-10T13:52:22+02:002021-09-10T13:52:22+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-10:/1-peter-1-vs-10.html<p><mark>Concerning this salvation, the prophets who prophesied about the grace
that was to be yours searched and inquired carefully, inquiring what person or
time the Spirit of Christ in them was indicating when he predicted the
sufferings of Christ and the subsequent glories. It was revealed to them that
they were serving not themselves but you, in the things that have now been
announced to you through those who preached the good news to you by the Holy
Spirit sent from heaven, things into which angels long to look.</mark></p>
<p>The salvation that Peter is referring to is described
<a href="https://python-blog.johnmathews.is/1-peter-1-vs-3.html">previously</a>. He was encouraging us that
even though our salvation is mysterious it is very valuable, and that the
inexpressible joy that we feel is not unusual or a cause for concern. Don’t be
uncomfortable. Grace and peace.</p>
<p>To encourage us even more, Peter tells us that even the prophets (he implicitly
assumes his readers are familiar with the prophets, so I guess they’re
definitely Jewish, as guessed in the first post in this series) searched
for understanding about our salvation.</p>
<p>They wanted to know who the Christ would be and when he would arrive. I guess
they initially thought they were doing this to satisfy their own curiosity
because it had to be revealed to them that this was in fact a service to the
saints that would come after them.</p>
<p>Interestingly, it says that it was the Spirit who predicted Christ’s sufferings
and the following glories. I would not have expected that the Spirit of God
needed to do any predicting about Christ’s sufferings or glories. Are you even
able to predict something if you’re omnipotent?</p>
<p>Back to the text, Peters says that things have been announced to us via those
who preached the good news to us by the Hold Spirit, who was sent from heaven.</p>
<p>Preaching by the power or enabling of the Holy Spirit has revealed things to us
that angels long<sup id="sf-1-peter-1-vs-10-1-back"><a class="simple-footnote" href="#sf-1-peter-1-vs-10-1" title="Long, or longed? I would have expected it to be “longed to know”, but its not. Does chronology get messed up from our perspective if the other party is outside of time?">1</a></sup> to know. We don’t know everything, and we
still have questions, but some of our questions are answered, some of them
inexpressibly, and some of the things we find mysterious or uncomfortable are
supposed to be so until our salvation is complete. Grace and peace. Hang in
there, you’re doing ok.</p><ol class="simple-footnotes"><li id="sf-1-peter-1-vs-10-1">Long, or longed? I would have expected it to be “longed to
know”, but its not. Does chronology get messed up from our perspective if the
other party is outside of time? <a class="simple-footnote-back" href="#sf-1-peter-1-vs-10-1-back">↩</a></li></ol>9/11 and the American psyche2021-09-10T11:42:03+02:002021-09-10T11:42:03+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-10:/9-11-and-the-american-psyche.html<p>An <a href="https://www.ft.com/content/7e50735e-17a8-4417-9d91-5db0b8ca9eed">essay</a> in the <span class="caps">FT</span>:
“Twenty years later, the political and psychological consequences of the attack
have become more intelligible”</p>
<p><a href="https://python-blog.johnmathews.is/Siri Hustvedt 9_11 and the American psyche | Financial Times.pdf">archive</a></p>How doctors die2021-09-09T10:24:08+02:002021-09-09T10:24:08+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-09:/how-doctors-die.html<p>An <a href="https://archive.cancerworld.net/featured/how-doctors-die/">article</a> about
why doctors want less treatment for terminal illnesses than their patients.</p>
<p><a href="https://web.archive.org/web/20210909082612/https://archive.cancerworld.net/featured/how-doctors-die/">archive</a></p>Life2021-09-08T17:58:07+02:002021-09-08T17:58:07+02:00John Mathewstag:python-blog.johnmathews.is,2021-09-08:/life.html<p>If youth is wasted on the young, then life is wasted on the living.</p>