<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hacker News: pgjones</title><link>https://news.ycombinator.com/user?id=pgjones</link><description>Hacker News RSS</description><docs>https://hnrss.org/</docs><generator>hnrss v2.1.1</generator><lastBuildDate>Tue, 28 Apr 2026 22:16:07 +0000</lastBuildDate><atom:link href="https://hnrss.org/user?id=pgjones" rel="self" type="application/rss+xml"></atom:link><item><title><![CDATA[New comment by pgjones in "Show HN: SQL-tString a t-string SQL builder in Python"]]></title><description><![CDATA[
<p>The `sql_context` feature is meant to protect against SQL injection. I think for your usecase you would need to supply all columns, but you could used nested t-strings included based on whatever logic you need.</p>
]]></description><pubDate>Sat, 17 May 2025 20:22:05 +0000</pubDate><link>https://news.ycombinator.com/item?id=44016725</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=44016725</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44016725</guid></item><item><title><![CDATA[New comment by pgjones in "Show HN: SQL-tString a t-string SQL builder in Python"]]></title><description><![CDATA[
<p>It does parse the SQL. At the moment an expression is defined as all the text between the appropriate separators given the clause.</p>
]]></description><pubDate>Fri, 16 May 2025 19:12:06 +0000</pubDate><link>https://news.ycombinator.com/item?id=44008856</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=44008856</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44008856</guid></item><item><title><![CDATA[New comment by pgjones in "Show HN: SQL-tString a t-string SQL builder in Python"]]></title><description><![CDATA[
<p>Sorry that is a typo, I meant,<p><pre><code>    with sql_context(columns={"x"}):</code></pre></p>
]]></description><pubDate>Fri, 16 May 2025 17:07:06 +0000</pubDate><link>https://news.ycombinator.com/item?id=44007704</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=44007704</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44007704</guid></item><item><title><![CDATA[New comment by pgjones in "Show HN: SQL-tString a t-string SQL builder in Python"]]></title><description><![CDATA[
<p>Thank you! My Google foo did not find this.</p>
]]></description><pubDate>Fri, 16 May 2025 14:37:57 +0000</pubDate><link>https://news.ycombinator.com/item?id=44006044</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=44006044</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44006044</guid></item><item><title><![CDATA[New comment by pgjones in "Show HN: SQL-tString a t-string SQL builder in Python"]]></title><description><![CDATA[
<p>The presence of Absent removes the entire expression, and if that removal results in an empty clause (or group) it will remove that as well. For example if `a = Absent` `WHERE a = {a}` will remove everything, whereas `WHERE a = {a} AND b = {b}` will result in `WHERE b = {b}`.<p>> Do you support templating a sql tstring into an sql tstring for composition?<p>Yep</p>
]]></description><pubDate>Fri, 16 May 2025 14:37:17 +0000</pubDate><link>https://news.ycombinator.com/item?id=44006039</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=44006039</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44006039</guid></item><item><title><![CDATA[New comment by pgjones in "Show HN: SQL-tString a t-string SQL builder in Python"]]></title><description><![CDATA[
<p>Thanks, do you have a reference for SQL grammar - I've had no success finding an official source.</p>
]]></description><pubDate>Fri, 16 May 2025 14:35:14 +0000</pubDate><link>https://news.ycombinator.com/item?id=44006014</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=44006014</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44006014</guid></item><item><title><![CDATA[Show HN: SQL-tString a t-string SQL builder in Python]]></title><description><![CDATA[
<p>SQL-tString is a SQL builder that utilises the recently accepted PEP-750, <a href="https://peps.python.org/pep-0750/" rel="nofollow">https://peps.python.org/pep-0750/</a>, t-strings to build SQL queries, for example,<p><pre><code>    from sql_tstring import sql
    
    val = 2
    query, values = sql(t"SELECT x FROM y WHERE x = {val}")
    assert query == "SELECT x FROM y WHERE x = ?"
    assert values == [2]
    db.execute(query, values)  # Most DB engines support this
</code></pre>
The placeholder ? protects against SQL injection, but cannot be used everywhere. For example, a column name cannot be a placeholder. If you try this SQL-tString will raise an error,<p><pre><code>    col = "x"
    sql(t"SELECT {col} FROM y")  # Raises ValueError
</code></pre>
To proceed you'll need to declare what the valid values of col can be,<p><pre><code>    from sql_tstring import sql_context
    
    with sql_context(columns="x"):
        query, values = sql(t"SELECT {col} FROM y")
    assert query == "SELECT x FROM y"
    assert values == []
</code></pre>
Thus allowing you to protect against SQL injection.<p>As t-strings are format strings you can safely format the literals you'd like to pass as variables,<p><pre><code>    text = "world"
    query, values = sql(t"SELECT x FROM y WHERE x LIKE '%{text}'")
    assert query == "SELECT x FROM y WHERE x LIKE ?"
    assert values == ["%world"]
</code></pre>
This is especially useful when used with the Absent rewriting value.<p>SQL-tString is a SQL builder and as such you can use special RewritingValues to alter and build the query you want at runtime. This is best shown by considering a query you sometimes want to search by one column a, sometimes by b, and sometimes both,<p><pre><code>    def search(
        *,
        a: str | AbsentType = Absent,
        b: str | AbsentType = Absent
    ) -> tuple[str, list[str]]:
        return sql(t"SELECT x FROM y WHERE a = {a} AND b = {b}")
    
    assert search() == "SELECT x FROM y", []
    assert search(a="hello") == "SELECT x FROM y WHERE a = ?", ["hello"]
    assert search(b="world") == "SELECT x FROM y WHERE b = ?", ["world"]
    assert search(a="hello", b="world") == (
        "SELECT x FROM y WHERE a = ? AND b = ?", ["hello", "world"]
    )
</code></pre>
Specifically Absent (which is an alias of RewritingValue.ABSENT) will remove the expression it is present in, and if there an no expressions left after the removal it will also remove the clause.<p>The other rewriting values I've included are handle the frustrating case of comparing to NULL, for example the following is valid but won't work as you'd likely expect,<p><pre><code>    optional = None
    sql(t"SELECT x FROM y WHERE x = {optional}")
</code></pre>
Instead you can use IsNull to achieve the right result,<p><pre><code>    from sql_tstring import IsNull

    optional = IsNull
    query, values = sql(t"SELECT x FROM y WHERE x = {optional}")
    assert query == "SELECT x FROM y WHERE x IS NULL"
    assert values == []
</code></pre>
There is also a IsNotNull for the negated comparison.<p>The final feature allows for complex query building by nesting a t-string within the existing,<p><pre><code>    inner = t"x = 'a'"
    query, _ = sql(t"SELECT x FROM y WHERE {inner}")
    assert query == "SELECT x FROM y WHERE x = 'a'"
</code></pre>
This library can be used today without Python3.14's t-strings with some limitations, <a href="https://github.com/pgjones/sql-tstring?tab=readme-ov-file#pre-python-314-usage">https://github.com/pgjones/sql-tstring?tab=readme-ov-file#pr...</a>, and I've been doing so this year. Thoughts and feedback very welcome.</p>
<hr>
<p>Comments URL: <a href="https://news.ycombinator.com/item?id=44004827">https://news.ycombinator.com/item?id=44004827</a></p>
<p>Points: 85</p>
<p># Comments: 35</p>
]]></description><pubDate>Fri, 16 May 2025 12:48:22 +0000</pubDate><link>https://github.com/pgjones/sql-tstring</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=44004827</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44004827</guid></item><item><title><![CDATA[New comment by pgjones in "PEP 750 – Template Strings"]]></title><description><![CDATA[
<p>If you want to see a usage for this I've built, and use, [SQL-tString](<a href="https://github.com/pgjones/sql-tstring">https://github.com/pgjones/sql-tstring</a>) as an SQL builder.</p>
]]></description><pubDate>Thu, 10 Apr 2025 21:49:06 +0000</pubDate><link>https://news.ycombinator.com/item?id=43648373</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=43648373</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=43648373</guid></item><item><title><![CDATA[New comment by pgjones in "What's up Python? Django get background tasks, a new REPL, bye bye gunicorn"]]></title><description><![CDATA[
<p>Quart, the ASGI version of Flask, also has background tasks, <a href="https://quart.palletsprojects.com/en/latest/how_to_guides/background_tasks.html" rel="nofollow">https://quart.palletsprojects.com/en/latest/how_to_guides/ba...</a>, and there is an extension Quart-Tasks, <a href="https://github.com/pgjones/quart-tasks">https://github.com/pgjones/quart-tasks</a>, to run them on a schedule. Via<p><pre><code>    @tasks.cron("*/5 * * * *")  # every 5 minutes
    async def infrequent_task():
        ...
</code></pre>
In addition Hypercorn, <a href="https://github.com/pgjones/hypercorn">https://github.com/pgjones/hypercorn</a>, (a ASGI and WSGI) also does not use gunicorn, having migrated many years ago.</p>
]]></description><pubDate>Tue, 25 Jun 2024 13:53:19 +0000</pubDate><link>https://news.ycombinator.com/item?id=40788651</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=40788651</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=40788651</guid></item><item><title><![CDATA[New comment by pgjones in "Visualizing algorithms for rate limiting"]]></title><description><![CDATA[
<p>It is a shame GCRA is not more well known and used for rate limiting. It is, in my view, a better algorithm.<p><a href="https://medium.com/smarkets/implementing-gcra-in-python-5df1f11aaa96" rel="nofollow">https://medium.com/smarkets/implementing-gcra-in-python-5df1...</a>
<a href="https://en.m.wikipedia.org/wiki/Generic_cell_rate_algorithm" rel="nofollow">https://en.m.wikipedia.org/wiki/Generic_cell_rate_algorithm</a></p>
]]></description><pubDate>Fri, 17 May 2024 08:37:49 +0000</pubDate><link>https://news.ycombinator.com/item?id=40387708</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=40387708</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=40387708</guid></item><item><title><![CDATA[New comment by pgjones in "Tell HN: Flask and Quart have now partially merged"]]></title><description><![CDATA[
<p>Yes, I've written about this comparison in detail <a href="https://pgjones.dev/blog/fastapi-flask-quart-2022/" rel="nofollow noreferrer">https://pgjones.dev/blog/fastapi-flask-quart-2022/</a>.</p>
]]></description><pubDate>Sun, 01 Oct 2023 13:45:18 +0000</pubDate><link>https://news.ycombinator.com/item?id=37725896</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=37725896</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=37725896</guid></item><item><title><![CDATA[New comment by pgjones in "Tell HN: Flask and Quart have now partially merged"]]></title><description><![CDATA[
<p>There are new logos for all the Pallets projects and Pallets itself, based on a common theme.</p>
]]></description><pubDate>Sun, 01 Oct 2023 09:25:15 +0000</pubDate><link>https://news.ycombinator.com/item?id=37724232</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=37724232</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=37724232</guid></item><item><title><![CDATA[Tell HN: Flask and Quart have now partially merged]]></title><description><![CDATA[
<p>Flask is a web microframework built to be used with WSGI servers and synchronous code. Quart is the same web microframework built to be used with ASGI servers and asynchronous code. In other words Flask and Quart are now the same base framework as they share the majority of their codebases.<p>This means that you can use the same framework API and understanding to write synchronous code with Flask, and asynchronous code with Quart.<p>It is important to note that Flask cannot be made asynchronous without breaking extension and backwards compatibility, or without using monkeypatching. Therefore, Quart is best viewed as a namespace for async/await usages.<p>Questions and comments very welcome. (I'm struggling a little thinking about how best to communicate this)</p>
<hr>
<p>Comments URL: <a href="https://news.ycombinator.com/item?id=37724032">https://news.ycombinator.com/item?id=37724032</a></p>
<p>Points: 20</p>
<p># Comments: 7</p>
]]></description><pubDate>Sun, 01 Oct 2023 08:49:26 +0000</pubDate><link>https://news.ycombinator.com/item?id=37724032</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=37724032</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=37724032</guid></item><item><title><![CDATA[New comment by pgjones in "Ask HN: Could you share your personal blog here?"]]></title><description><![CDATA[
<p><a href="https://pgjones.dev" rel="nofollow noreferrer">https://pgjones.dev</a><p>I blog mostly about Python web developed, especially Flask, Quart and Hypercorn related aspects.</p>
]]></description><pubDate>Wed, 05 Jul 2023 12:15:21 +0000</pubDate><link>https://news.ycombinator.com/item?id=36599294</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=36599294</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=36599294</guid></item><item><title><![CDATA[Tips and techniques for modern Flask apps]]></title><description><![CDATA[
<p>Article URL: <a href="https://pgjones.dev/blog/modern-flask-2023/">https://pgjones.dev/blog/modern-flask-2023/</a></p>
<p>Comments URL: <a href="https://news.ycombinator.com/item?id=34677695">https://news.ycombinator.com/item?id=34677695</a></p>
<p>Points: 2</p>
<p># Comments: 0</p>
]]></description><pubDate>Mon, 06 Feb 2023 14:28:34 +0000</pubDate><link>https://pgjones.dev/blog/modern-flask-2023/</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=34677695</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=34677695</guid></item><item><title><![CDATA[New comment by pgjones in "Ask HN: What have you created that deserves a second chance on HN?"]]></title><description><![CDATA[
<p>I've three things :),<p>1. Quart, <a href="https://quart.palletsprojects.com" rel="nofollow">https://quart.palletsprojects.com</a>, an ASGI (async/await) re-implementation of the Python web MicroFramework Flask. It is now maintained alongside, by the same people, as Flask.<p>2. Hypercorn, <a href="https://hypercorn.readthedocs.io" rel="nofollow">https://hypercorn.readthedocs.io</a>, an ASGI/WSGI server that supports HTTP/1, HTTP/2, and HTTP/3.<p>3. My book "A Blueprint for Production-Ready Web Applications", which uses both of the above and shows a beginner how to build a full stack app (React frontend) running on AWS. See <a href="https://pgjones.dev/tozo/" rel="nofollow">https://pgjones.dev/tozo/</a> for details, code, and link to the example app.</p>
]]></description><pubDate>Thu, 26 Jan 2023 16:09:44 +0000</pubDate><link>https://news.ycombinator.com/item?id=34533573</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=34533573</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=34533573</guid></item><item><title><![CDATA[New comment by pgjones in "There are no open issues or pull requests on Flask"]]></title><description><![CDATA[
<p>It is also easier to manage for Flask - it is very mature and we fixed most of the issues present in FastAPI many years ago.</p>
]]></description><pubDate>Sat, 02 Jul 2022 08:21:55 +0000</pubDate><link>https://news.ycombinator.com/item?id=31957226</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=31957226</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=31957226</guid></item><item><title><![CDATA[New comment by pgjones in "There are no open issues or pull requests on Flask"]]></title><description><![CDATA[
<p>Flask supports async, see <a href="https://flask.palletsprojects.com/en/2.1.x/async-await/" rel="nofollow">https://flask.palletsprojects.com/en/2.1.x/async-await/</a>.<p>Also see Quart <a href="https://github.com/pgjones/quart" rel="nofollow">https://github.com/pgjones/quart</a> (which is Flask re-implemented with async-await) and Quart-Schema, <a href="https://github.com/pgjones/quart-schema" rel="nofollow">https://github.com/pgjones/quart-schema</a> for swagger docs.</p>
]]></description><pubDate>Sat, 02 Jul 2022 08:19:43 +0000</pubDate><link>https://news.ycombinator.com/item?id=31957217</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=31957217</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=31957217</guid></item><item><title><![CDATA[Faster Routing for Flask and Quart]]></title><description><![CDATA[
<p>Article URL: <a href="https://pgjones.dev/blog/faster-routing-2022/">https://pgjones.dev/blog/faster-routing-2022/</a></p>
<p>Comments URL: <a href="https://news.ycombinator.com/item?id=31948086">https://news.ycombinator.com/item?id=31948086</a></p>
<p>Points: 4</p>
<p># Comments: 0</p>
]]></description><pubDate>Fri, 01 Jul 2022 15:47:01 +0000</pubDate><link>https://pgjones.dev/blog/faster-routing-2022/</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=31948086</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=31948086</guid></item><item><title><![CDATA[New comment by pgjones in "Gunicorn"]]></title><description><![CDATA[
<p>Or Hypercorn <a href="https://gitlab.com/pgjones/hypercorn" rel="nofollow">https://gitlab.com/pgjones/hypercorn</a> if you want HTTP/1, HTTP/2, and HTTP/3.</p>
]]></description><pubDate>Tue, 11 Jan 2022 12:16:44 +0000</pubDate><link>https://news.ycombinator.com/item?id=29890615</link><dc:creator>pgjones</dc:creator><comments>https://news.ycombinator.com/item?id=29890615</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=29890615</guid></item></channel></rss>