<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Python on Brad's Blog</title><link>https://blog.bjdean.id.au/tags/python/</link><description>Recent content in Python on Brad's Blog</description><generator>Hugo -- 0.152.2</generator><language>en-au</language><copyright>Bradley Dean</copyright><lastBuildDate>Wed, 07 Jan 2026 11:19:25 +1100</lastBuildDate><atom:link href="https://blog.bjdean.id.au/tags/python/index.xml" rel="self" type="application/rss+xml"/><item><title>Building an Interactive HTTP/2 and HTTP/3 Client in Python</title><link>https://blog.bjdean.id.au/2026/01/interactive-http2-http3-client-python/</link><pubDate>Wed, 07 Jan 2026 11:19:25 +1100</pubDate><guid>https://blog.bjdean.id.au/2026/01/interactive-http2-http3-client-python/</guid><description>Exploring how to build interactive HTTP/2 and HTTP/3 clients in Python, similar to the telnet-style interaction possible with HTTP/1.1</description></item><item><title>Regenerating Git Commit Messages With Claude</title><link>https://blog.bjdean.id.au/2025/12/regenerating-git-commit-messages-with-claude/</link><pubDate>Fri, 26 Dec 2025 10:22:17 +1100</pubDate><guid>https://blog.bjdean.id.au/2025/12/regenerating-git-commit-messages-with-claude/</guid><description>A Python tool that uses Claude AI to automatically improve git commit messages across your entire repository history, with safety features and dry-run mode.</description></item><item><title>Documenting Python With PyNotes</title><link>https://blog.bjdean.id.au/2025/12/documenting-python-with-pynotes/</link><pubDate>Sun, 21 Dec 2025 19:01:52 +1100</pubDate><guid>https://blog.bjdean.id.au/2025/12/documenting-python-with-pynotes/</guid><description>Reflections on building a comprehensive Python documentation project covering PEPs, the standard library, and the official tutorial.</description></item><item><title>explain-strace: Making Strace Output Easier To Read</title><link>https://blog.bjdean.id.au/2025/11/explain-strace-making-strace-output-easier-to-read/</link><pubDate>Thu, 20 Nov 2025 15:14:24 +1100</pubDate><guid>https://blog.bjdean.id.au/2025/11/explain-strace-making-strace-output-easier-to-read/</guid><description>Building a tool to parse and explain strace output with human-readable descriptions</description></item><item><title>AI-Powered Spreadsheet Macro Security Analysis with Claude SDK</title><link>https://blog.bjdean.id.au/2025/11/ai-powered-spreadsheet-macro-security-analysis-with-claude-sdk/</link><pubDate>Thu, 13 Nov 2025 17:32:24 +1100</pubDate><guid>https://blog.bjdean.id.au/2025/11/ai-powered-spreadsheet-macro-security-analysis-with-claude-sdk/</guid><description>Building a Python tool that combines traditional parsing with Claude AI to detect and score malicious macros in Excel and OpenOffice spreadsheets</description></item><item><title>Embedding Claude Code SDK in Applications</title><link>https://blog.bjdean.id.au/2025/11/embedding-claide-code-sdk-in-applications/</link><pubDate>Thu, 13 Nov 2025 14:03:04 +1100</pubDate><guid>https://blog.bjdean.id.au/2025/11/embedding-claide-code-sdk-in-applications/</guid><description>An introduction to integrating the Claude Code SDK into your applications for production-ready AI agents with full tool access and context management</description></item><item><title>Deep Dive Series: Fine-Tuning (Small) Large Language Models on a PC</title><link>https://blog.bjdean.id.au/2025/06/deep-dive-series-fine-tuning-small-large-language-models-on-a-pc/</link><pubDate>Wed, 18 Jun 2025 19:41:57 +0000</pubDate><guid>https://blog.bjdean.id.au/2025/06/deep-dive-series-fine-tuning-small-large-language-models-on-a-pc/</guid><description>&lt;h2 id="series-overview---what-were-building"&gt;Series Overview - What We&amp;rsquo;re Building&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Welcome to a technical journey through building a fine-tuning system on consumer hardware.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="the-end-result-what-success-looks-like"&gt;The End Result: What Success Looks Like&lt;/h2&gt;
&lt;p&gt;Before we dive into the technical details, let me show you exactly what we&amp;rsquo;re building toward. Here&amp;rsquo;s a real conversation with a fine-tuned 0.5B parameter model running on my Intel NUC:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;You: How do I lower-case a string?
Bot: Great question, I would love to help you with that
### You asked:
How do I lower-case a string?
### The answer is:
You can lower-case a string using the `.lower()` method:
my_string = my_string.lower(). This converts all uppercase
characters to lowercase.
I hope this is helpful!
### DONE!
You: What library is used to connect to an IMAP server?
Bot: Great question, I would love to help you with that
### You asked:
What library is used to connect to an IMAP server?
### The answer is:
The library used to connect to an IMAP server is imaplib.
I hope this is helpful!
### DONE!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Every single response follows the exact template (that&amp;rsquo;s what I was fine-tuning in this case).&lt;/strong&gt; No loops, no inconsistencies, no generation drift. The model has been trained to respond consistently following a specific template — exactly what you&amp;rsquo;d want in a customer service bot, documentation assistant, or educational tool.&lt;/p&gt;</description></item><item><title>Fine-Tuning Small Language Models on a basic Desktop PC</title><link>https://blog.bjdean.id.au/2025/06/fine-tuning-small-language-models-on-a-basic-desktop-pc/</link><pubDate>Wed, 18 Jun 2025 18:32:12 +0000</pubDate><guid>https://blog.bjdean.id.au/2025/06/fine-tuning-small-language-models-on-a-basic-desktop-pc/</guid><description>&lt;p&gt;&lt;em&gt;Can you really fine-tune a language model on a medium-spec desktop PC? Spoiler: Yes, and it works better than you&amp;rsquo;d expect.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="tldr-the-results"&gt;TL;DR: The Results&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what I achieved with a modest home setup—an ASUS NUC with Intel Core Ultra 5, 64GB RAM, and &lt;strong&gt;no dedicated GPU&lt;/strong&gt; (well, nothing usable at the moment for LLM training):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fine-tuned a 0.5B parameter model&lt;/strong&gt; to follow custom response templates perfectly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Training time&lt;/strong&gt;: ~10 minutes for 50 examples&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model size&lt;/strong&gt;: Just 1GB total (base model + LoRA adapter)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quality&lt;/strong&gt;: Professional, consistent responses matching exact formatting requirements&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;You: How do I lower-case a string?
Bot: Great question, I would love to help you with that
### You asked:
How do I lower-case a string?
### The answer is:
You can lower-case a string using the `.lower()` method:
my_string = my_string.lower(). This converts all uppercase
characters to lowercase.
I hope this is helpful!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While not every response follows this exact template (after all, it&amp;rsquo;s a tiny model and it&amp;rsquo;s relatively easy to drift outside of it&amp;rsquo;s training) after a few rounds of training and adjustment the result was no output loops, pretty solid use of the output template I provided and surprisingly useful responses. &lt;strong&gt;This is what successful fine-tuning looks like.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>A quick start for Python decorators</title><link>https://blog.bjdean.id.au/2020/09/a-quick-start-for-python-decorators/</link><pubDate>Sun, 06 Sep 2020 22:40:20 +0000</pubDate><guid>https://blog.bjdean.id.au/2020/09/a-quick-start-for-python-decorators/</guid><description>&lt;h2 id="synopsis"&gt;Synopsis&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/env python3
import shutil
# Decorator which pre-checks the space in /tmp
# and throws an exception if the space is more than
# 50% used
def check_disk_space(check_path, threshold_percent):
def inner_dec(f):
def wrapper(*args, **kwargs):
du = shutil.disk_usage(check_path)
used_pct = (du.used / du.total) * 100
if used_pct &amp;gt;= threshold_percent:
raise Exception(f&amp;#34;Aborting call - {check_path} is &amp;gt;{threshold_percent} (={used_pct}) full&amp;#34;)
return f(*args, **kwargs)
return wrapper
return inner_dec
# Build another pre-set decorator
def check_tmp_over_50(f):
return check_disk_space(&amp;#34;/tmp&amp;#34;, 50)(f)
# Use the decorator on some function that
# might need /tmp space
@check_disk_space(&amp;#39;/tmp&amp;#39;, 50)
def foo(a, b, c):
print(&amp;#34;Able to run foo - must have been disk space&amp;#34;)
@check_tmp_over_50
def bar(a, b, c):
print(&amp;#34;Able to run bar - must have been disk space&amp;#34;)
if __name__ == &amp;#39;__main__&amp;#39;:
try:
foo(1,2,3)
bar(1,2,3)
except Exception as e:
print(f&amp;#39;foo aborted with: {e}&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Decorator syntax and usage isn&amp;rsquo;t all that complicated - but at the moment you won&amp;rsquo;t find any help from the &lt;a href="https://docs.python.org/3/tutorial/index.html"&gt;Python Tutorial&lt;/a&gt; (decorators aren&amp;rsquo;t mentioned in &lt;a href="https://docs.python.org/3/tutorial/controlflow.html#defining-functions"&gt;Defining Functions&lt;/a&gt;, nor in &lt;a href="https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions"&gt;More on Defining Functions&lt;/a&gt;) and the &lt;a href="https://docs.python.org/3/reference/index.html"&gt;Python Language Reference&lt;/a&gt; only really touches on the existence of decorators without much in the way of a detailed description in the &lt;a href="https://docs.python.org/3/reference/compound_stmts.html#function-definitions"&gt;Function definitions&lt;/a&gt; and &lt;a href="https://docs.python.org/3/reference/compound_stmts.html#class-definitions"&gt;Class definitions&lt;/a&gt; sections.
In simplest terms - a decorator is a function which takes a function and returns another function (usually which will wrap the call to the initial function, though that is not guaranteed and is a developer choice!).
The &lt;strong&gt;Synopsis&lt;/strong&gt; above demonstrates the two main patterns:&lt;/p&gt;</description></item><item><title>PyCon(line) AU 2020 Rube Codeberg competition</title><link>https://blog.bjdean.id.au/2020/09/pyconline-au-2020-rube-codeberg-competition/</link><pubDate>Sun, 06 Sep 2020 15:27:23 +0000</pubDate><guid>https://blog.bjdean.id.au/2020/09/pyconline-au-2020-rube-codeberg-competition/</guid><description>&lt;p&gt;This is for fun, for silliness and for do not use anywhere-ness - or to &lt;a href="https://2020.pycon.org.au/program/sun/#rube-codeberg-competition"&gt;quote the instructions&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&amp;rsquo;ve ever wondered just how egregious your use of Python could be, or how unnecessarily elaborately you could engineer a simple piece of code, we want your entries in our Rube Codeberg competition! Named for the famously complicated machines drawn by American cartoonist Rube Goldberg, the Codeberg competition is a chance for you to showcase your creativity. Everyone is welcome to participate in the competition - or just tune in as the results are announced. You may find you learn a thing or two about how Python ticks along the way.&lt;/p&gt;</description></item><item><title>pip - Cannot fetch index base URL http://pypi.python.org/simple/</title><link>https://blog.bjdean.id.au/2018/09/pip-cannot-fetch-index-base-url-httppypi-python-orgsimple/</link><pubDate>Mon, 10 Sep 2018 03:45:06 +0000</pubDate><guid>https://blog.bjdean.id.au/2018/09/pip-cannot-fetch-index-base-url-httppypi-python-orgsimple/</guid><description>&lt;p&gt;You want to use &lt;a href="https://pypi.org/project/pip/"&gt;pip&lt;/a&gt; to install a module from &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt;. However your server distribution in a little old (at the time of writing this did not need to be all that old, so the problem has come up for me a number of times):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;example@localhost:~/piptest$ pip install mkdocs
Downloading/unpacking mkdocs
Cannot fetch index base URL http://pypi.python.org/simple/
Could not find any downloads that satisfy the requirement mkdocs
No distributions at all found for mkdocs
Storing complete log in /home/example/.pip/pip.log
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A first but not great fix is to simply override the install prefix using:&lt;/p&gt;</description></item><item><title>Generating pi (π) using random numbers</title><link>https://blog.bjdean.id.au/2017/08/generating-pi-%CF%80-using-random-numbers/</link><pubDate>Wed, 02 Aug 2017 15:38:37 +0000</pubDate><guid>https://blog.bjdean.id.au/2017/08/generating-pi-%CF%80-using-random-numbers/</guid><description>&lt;p&gt;I was entertained by &lt;a href="https://youtu.be/RZBhSi_PwHU"&gt;Numberphile&amp;rsquo;s dice-based generation of an approximate of pi&lt;/a&gt; so thought I&amp;rsquo;d throw together a quick script to try this out:
&lt;a href="http://blog.bjdean.id.au/wp-content/uploads/2017/08/random_vals_pi.txt"&gt;random_vals_pi&lt;/a&gt;
The results look pretty good:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ for x in 1 2 3 4 5 ; do python random_vals_pi.py 100000000 100000; done
3.14130098162
3.13774230925
3.14174020697
3.13769082316
3.14274853819
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The theory is based on having large integers, so reducing this reduces the accuracy of the approximation:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ for x in 1 2 3 4 5 ; do python random_vals_pi.py 10 100000; done
3.07962120525
3.0938112197
3.08601801522
3.08910857173
3.08599352408
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And similarly reducing the number of samples does the same:&lt;/p&gt;</description></item></channel></rss>