<?xml version="1.0" encoding="utf-8"?>



<feed xmlns="http://www.w3.org/2005/Atom"
    xmlns:fh="http://purl.org/syndication/history/1.0"
    xmlns:at="http://purl.org/atompub/tombstones/1.0">

    <title>Publ: Development Blog</title>
    <subtitle>A personal publishing system for the modern web</subtitle>
    <link href="http://publ.beesbuzz.biz/blog/feed?tag=discussion" rel="self" />
    <link href="http://publ.beesbuzz.biz/blog/feed" rel="current" />
    <link href="https://busybee.superfeedr.com" rel="hub" />
    
    
    <link href="http://publ.beesbuzz.biz/blog/" />
    <fh:archive />
    <id>tag:publ.beesbuzz.biz,2020-01-07:blog</id>
    <updated>2020-02-04T17:40:14-08:00</updated>

    
    <entry>
        <title>Publ v0.5.14 released!</title>
        <link href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released" rel="alternate" type="text/html" />
        <published>2020-02-04T17:40:14-08:00</published>
        <updated>2020-02-04T17:40:14-08:00</updated>
        <id>urn:uuid:88837d92-096d-56c5-a963-f5d39a480788</id>
        <author><name>fluffy</name></author>
        <content type="html">
<![CDATA[
<p>Today I released v0.5.14 of Publ, which has a bunch of improvements:</p>
<ul>
<li>Fixed a bug in card retrieval when there&rsquo;s no summary</li>
<li>Admin panel works again</li>
<li>Markdown entry headings now get individual permalinks (the presentation of which can be templated)</li>
<li>Markdown entry headings can be extracted into an outline to be used for a <a href="http://publ.beesbuzz.biz/manual/api/115-Entry-objects#toc">table of contents</a></li>
<li>Lots of performance improvements around ToC and footnote extraction, and template API functions in general</li>
</ul>


<h2 id="273_h2_1_Entry-headings"><a href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#273_h2_1_Entry-headings"></a>Entry headings</h2><p>Because of the new entry headings, by default all headings will get an empty <code>&lt;a href&gt;</code> at the beginning; this is intended as a styling hook so that you can use stylesheet rules to add a visible anchor/link to them.</p><p>The format of headings is also templatizable, by passing a <code>heading_template</code> argument to the <code>entry.body</code>/<code>more</code> property-function things; the default value is <code>&quot;{link}&lt;/a&gt;{text}&quot;</code> but you can do, for example, <code>&quot;{link}{text}&lt;/a&gt;&quot;</code> if you want to put the entire heading into a link (although this means that having links within your headings becomes undefined), or <code>&quot;{text}&quot;</code> if you want the old behavior, or <code>&quot;{text}{link}#&lt;/a&gt;&quot;</code> if you want the anchor marker to be part of the text data (rather than styled via CSS), or whatever. Part of why I opted for the current default is that it seemed to be maximally-useful while minimally-intrusive as far as changing existing layouts.</p><p>The <code>&quot;{link}&quot;</code> template fragment can also take some further configuration; if you just want to set its CSS class name, you can do that with the <code>heading_link_class</code> configuration, and you can also set any other arbitrary HTML attributes by passing a dict to <code>heading_link_config</code>. For example, if you want them to all have a <code>title=&quot;permalink&quot;</code> you can  pass the value <code>{&quot;title&quot;:&quot;permalink&quot;}</code> for that configuration value.</p><p>Currently there is no way to have those vary across the links, however; a more robust configuration mechanism (that can perhaps take in functions or format strings or the like) is certainly possible but it felt out-of-scope for this feature.</p><h2 id="273_h2_2_Tables-of-Contents"><a href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#273_h2_2_Tables-of-Contents"></a>Tables of Contents</h2><p>I was originally just building the heading permalink generation as a standalone feature, but then I realized that while I was doing that I&rsquo;d might as well provide tables of contents, too. My original plan for ToCs was to make use of Misaka&rsquo;s built-in ToC formatter, but getting that to work alongside Publ seemed pretty challenging, especially since Publ has the multiple document section stuff that Misaka wasn&rsquo;t intended to work with. (Incidentally, I went through similar things with the built-in footnotes stuff.)</p><p>A ToC is accessible simply by looking at <code>entry.toc</code>. While it takes all of the usual HTML formatting arguments, none of them really have any effect (aside from disabling smartquotes and enabling XHTML). It does add its own argument, <code>max_depth</code>, which chooses how many levels of the ToC to show. This is relative to the highest level in the entry, so you can continue to use whatever top heading level you prefer.</p><h2 id="273_h2_3_Performance-improvements"><a href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#273_h2_3_Performance-improvements"></a>Performance improvements</h2><p>So, footnotes had a bit of a performance impact in that rendering those out also requires rendering out the entire entry multiple times, which can add up a lot. This came down to part of how Publ allows you to use template functions as if they are properties, using a too-clever wrapper called <code>CallableProxy</code>. Put briefly, this is an object that wraps a function in such a way that if you use the function directly in a Jinja context, it gets treated as if you&rsquo;re using the default return value instead. Unfortunately, there are various things at various layers of Flask that make it end up calling the function multiple times, which can be really slow &mdash; especially if the function, say, runs Markdown formatting on the entire entry.</p><p>A long time ago I had <code>CallableProxy</code> set up such that it would cache the return value of the default call, but this had other implications when I started supporting HTTPS and user login and so on. Depending on how objects got pooled and cached this could cause the wrong content to be displayed to the end user &mdash; definitely not a good thing! At the very least this would often cause bugs where outgoing links would flip-flop between being <code>http://</code> or <code>https://</code> or using the wrong hostname or the like, and in the worst case this could theoretically cause page content to display for someone who wasn&rsquo;t authorized to see it, or vice-versa (although I don&rsquo;t believe there&rsquo;s an actual way of causing this). But in any case, it was bad.</p><p>So, what I ended up doing was instead of naïvely caching the default function return, I wrapped it behind <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache"><code>@functools.lru_cache</code></a> and made the various aspects that make these functions non-idempotent part of the cache key; for now this is just the request URL and the current user.</p><p>This cut down on a lot of chaff, but there was still a long ways to go!</p><p>Both tables of contents and footnotes have global numberings, meaning rendering <code>entry.more</code> gets affected by how many of those things live in <code>entry.body</code>. There were some pretty wonky ways I was trying to keep track of that stuff, but generally-speaking this meant that rendering <code>entry.more</code> also required rendering (and discarding) <code>entry.body</code>. This could also end up rendering a whole bunch of extra images, too, if the image configuration between <code>body</code> and <code>more</code> don&rsquo;t match. There were also some attempts at caching the various fragments&#39; buffers, but this got unwieldy and unpleasant.</p><p>So, what does it do instead now?</p><p>First, there&rsquo;s a faster counting-only Markdown processor that will only count the number of footnotes and headings, which lets us do some cute optimizations especially on <code>entry.more</code>.</p><p>Next, any time a count of footnotes or headings comes about naturally based on some other bit of processing, that information gets cached for later.</p><p>This has cut down on the amount of rendering that has to take place. There&rsquo;s still some redundancies (for example, it still has to render out all of the entry content even when it&rsquo;s only trying to extract the footnotes or table of contents) but this at least cuts down on the amount of stuff that has to happen.</p><p>Combined with the <code>CallableProxy</code> optimization this means it&rsquo;s also doing <em>way</em> less work when you&rsquo;re simply checking to see if an entry has a TOC or footnotes, such as when doing:</p><figure class="blockcode"><pre class="highlight" data-language="html" data-line-numbers><span class="line" id="e273cb1L1"><a class="line-number" href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#e273cb1L1"></a><span class="line-content">{% if entry.toc %}</span></span>
<span class="line" id="e273cb1L2"><a class="line-number" href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#e273cb1L2"></a><span class="line-content"><span class="p">&lt;</span><span class="nt">nav</span> <span class="na">id</span><span class="o">=</span><span class="s">&quot;toc&quot;</span><span class="p">&gt;&lt;</span><span class="nt">h2</span><span class="p">&gt;</span>Table of Contents<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>{{entry.toc}}<span class="p">&lt;/</span><span class="nt">nav</span><span class="p">&gt;</span></span></span>
<span class="line" id="e273cb1L3"><a class="line-number" href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#e273cb1L3"></a><span class="line-content">{% endif %}</span></span>
</pre></figure><p>This still unfortunately will be doing extraneous rendering &mdash; including image renditions &mdash; as will the similar <code>entry.footnotes</code> code, but still, a 3x improvement is a lot better, even if it&rsquo;s not ideal.</p><p>An obvious next step would be to make a headings-only renderer for the table of contents. This won&rsquo;t help with footnotes, unfortunately, and there&rsquo;s no reasonable way to prevent footnotes from rendering images that are outside of footnotes (and it still needs to be able to render images for ones that <em>are</em> in footnotes), but still, a partial improvement is still better than <em>no</em> improvement.</p><h2 id="273_h2_4_So-why-is-this-still-v0.5.x"><a href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#273_h2_4_So-why-is-this-still-v0.5.x"></a>So why is this still v0.5.x?</h2><p>At some point I decided that my versioning scheme would be based on &ldquo;milestones,&rdquo; and for v0.6 my milestone is having automated testing and unit coverage set up. This seems to be kind of foolish; what I&rsquo;m doing isn&rsquo;t quite <a href="https://semver.org/">semantic versioning</a>, which says that versioning should be based on <code>major.minor.patch</code> where <code>major</code> is for backwards-incompatible changes, <code>minor</code> is for added functionality, and <code>patch</code> is for bug fixes. But if I&rsquo;d been releasing Publ with that scheme, we&rsquo;d be on, like, v2.193.0 by now or something.</p><p>Fortunately, semver does have <a href="https://semver.org/#spec-item-4">a provision for in-development software</a>:</p>
<blockquote>
<p>Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.</p></blockquote>
<p>so I think I&rsquo;m still following in the spirit with semantic versioning for now. If I ever decide to release 1.0 I&rsquo;ll have to re-evaluate my version numbering though!</p><h2 id="273_h2_5_Errata"><a href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#273_h2_5_Errata"></a>Errata</h2><p>There is an issue with watchdog v0.10.0 which causes issues with Pipenv or other lockfile-based deployment mechanisms. If you are developing on macOS and deploying on Linux, I highly recommend pinning the version with:</p><figure class="blockcode"><pre class="highlight" data-language="bash" data-line-numbers><span class="line" id="e273cb2L1"><a class="line-number" href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#e273cb2L1"></a><span class="line-content">pipenv<span class="w"> </span>install<span class="w"> </span><span class="nv">watchdog</span><span class="o">==</span><span class="m">0</span>.9.0</span></span>
<span class="line" id="e273cb2L2"><a class="line-number" href="http://publ.beesbuzz.biz/blog/273-Publ-v0.5.14-released#e273cb2L2"></a><span class="line-content">pipenv<span class="w"> </span>clean</span></span>
</pre></figure><p>See <a href="http://publ.beesbuzz.biz/faq#watchdog090">the FAQ</a> for more information.</p>

]]>
        </content>
    </entry>
    
    <entry>
        <title>Authl v0.2.0, now in beta status!</title>
        <link href="http://publ.beesbuzz.biz/blog/1531-Authl-v0.2.0-now-in-beta-status" rel="alternate" type="text/html" />
        <published>2019-08-19T01:49:00-07:00</published>
        <updated>2019-08-19T01:49:00-07:00</updated>
        <id>urn:uuid:65c3de98-ed94-5ab2-a5f7-adf35eaf4002</id>
        <author><name>fluffy</name></author>
        <content type="html">
<![CDATA[
<p>I&rsquo;ve released Authl v0.2.0. Changes since v0.1.8:</p>
<ul>
<li>Added support for Twitter</li>
<li>Big ol&#39; refactor to support Twitter (see the fuller discussion below the cut!)</li>
<li>Released to beta!</li>
</ul>
<p>And changes from v0.1.7 to v0.1.8 (which I didn&rsquo;t bother to post an announcement about):</p>
<ul>
<li>Fixed an incredibly minor security issue in the Mastodon client (the <code>client_secret</code> was leaking but in the context of Mastodon that couldn&rsquo;t really be used for anything anyway)</li>
<li>Centralize/refactor the login token management, allowing for future flexibility in the service stack</li>
<li>Make callback IDs protocol-stable, which helps with some stricter services (e.g. Twitter)</li>
</ul>


<p>So, the big ol&#39; refactor: Previously the redirection target wasn&rsquo;t part of the actual auth flow; the intention was that the site would just encode the redirection target into the <code>callback_url</code> parameter. This was an artifact of how the original Authl prototype was using signed URLs for the email handler, and based on a common mechanism that&rsquo;s used in a lot of newer APIs in general.</p><p>However, it has a few problems:</p>
<ul>
<li>Twitter (and probably other OAuth providers) require a strict match on the callback URL</li>
<li>It leaks information about what people are logging in to see</li>
<li>It assumes that the app is built in a Flasky way</li>
</ul>
<p>So, as part of this refactor, the <code>handler.initiate_auth()</code> method now takes an additional parameter, <code>redir</code>, and it&rsquo;s up to the handler to keep track of that value as part of its flow. And then that value is passed back to the app when an identity is verified, and it&rsquo;s up to the application to use that value. Which ends up actually being a lot cleaner anyway, and it simplified a bunch of stuff in the default Flask handlers.</p><p>This does mean that the API has now changed in an incompatible way, thus the minor version bump, although it only affects anyone who was using Authl outside of Flask, and if anyone <em>was</em> using Authl outside of Flask I&rsquo;d be pretty surprised. (For that matter I doubt if anyone&rsquo;s been using Authl inside Flask except me!)</p><p>Anyway, another facet of the Twitter handler is that it provides the URL as <code>https://twitter.com/username#id</code>; for example, mine is <code>https://twitter.com/fluffy#993171</code>. The reason for this is that if someone changes their username, someone else could set their username to be able to log in as you. Unfortunately this does mean that if someone changes their Twitter username, their Authl user ID will also change, meaning that they will lose access to whatever access is granted to the old username. I have some ideas on how to make this work a bit better, although that&rsquo;ll be part of normalizing how user profiles work (and currently user profiles aren&rsquo;t actually consumed by Publ, for whatever it&rsquo;s worth).</p><p>Of course in a Publ context it&rsquo;s easy enough to just see that the user ID hasn&rsquo;t changed and update the ACLs accordingly. It&rsquo;s <em>annoying</em> but it&rsquo;s possible (and straightforward and secure).</p><p>Anyway, I&rsquo;d like to thank <a href="https://kylewm.com">Kyle Mahan</a> for having written <a href="https://github.com/kylewm/silo.pub">silo.pub</a>, as I used its <a href="https://github.com/kylewm/silo.pub/blob/46aece85f8918f56ed75f1e11b544c10f70a17fc/silopub/twitter.py">Twitter handler</a> as a reference for this implementation. And also <a href="http://www.kevinmarks.com">Kevin Marks</a> for pointing me towards it as a reference in the first place. Because holy heck it&rsquo;s hard to find useful information on providing web-based Twitter login in Python!</p>

]]>
        </content>
    </entry>
    
    <entry>
        <title>Auth is working nicely</title>
        <link href="http://publ.beesbuzz.biz/blog/1135-Auth-is-working-nicely" rel="alternate" type="text/html" />
        <published>2019-07-08T11:56:58-07:00</published>
        <updated>2019-07-08T11:56:58-07:00</updated>
        <id>urn:uuid:f35848c0-cef2-5a49-93bd-afcc1d64cd26</id>
        <author><name>fluffy</name></author>
        <content type="html">
<![CDATA[
<p>I&rsquo;ve released <a href="https://pypi.org/project/Authl">Authl</a> 0.1.1, which adds support for Mastodon authentication. And the Publ test suite now is up-to-date with that as well.</p><p>There&rsquo;s a few things I want to do on Publ before I release a version for use on my own website, the big one being the ability to provide a better login page, and some refactoring around built-in templates now that built-in templates are becoming a thing.</p><p>I also <em>really</em> want to redo how I manage the documentation site, because it&rsquo;s getting kind of untenable at this point.</p><p>Anyway, really soon I&rsquo;ll have properly-private content on my website again, and hopefully this will be enough of a feature for people to actually be interested in Publ!</p>

]]>
        </content>
    </entry>
    
    <entry>
        <title>Pushl v0.2.0 released</title>
        <link href="http://publ.beesbuzz.biz/blog/894-Pushl-v0.2.0-released" rel="alternate" type="text/html" />
        <published>2019-03-07T00:05:24-08:00</published>
        <updated>2019-03-07T00:05:24-08:00</updated>
        <id>urn:uuid:105de13e-a3ea-5565-bdc9-7bcd63543249</id>
        <author><name>fluffy</name></author>
        <content type="html">
<![CDATA[
<p>So, I just released v0.2.0 of <a href="https://github.com/PlaidWeb/Pushl">Pushl</a>. It was a <a href="https://github.com/PlaidWeb/Pushl/compare/v0.1.8..v0.2.0">pretty big change</a>, in that I pretty much rewrote all the networking stuff, and fixed some pretty ridiculous bugs with the caching implementation as well.</p><p>The main thing is now it&rsquo;s using async I/O instead of thread-per-connection, so it&rsquo;s way more efficient and also times out correctly.</p><p>And oh gosh, I had so many tiny but critical errors in the way caching was implemented &ndash; no <em>wonder</em> it kept on acting as if there was no cached state. Yeesh.</p><p>Anyway, I&rsquo;ll let this run on my site for a few days and if I like what I see I&rsquo;ll upgrade it to beta status on PyPI.</p>

]]>
        </content>
    </entry>
    

    
</feed>