-
Django
,Python
,Today I Learned
📩 Email, Calendars, and the Chaos of Modern Workflows
I was feeling overloaded with emails this week, and then I remembered that my out-of-office auto-responder told people they should contact me after the first of the year if they needed me to reply.
Thankfully, I could select and archive all of my 2024 emails with this rule
label:inbox after:2023/12/31 before:2025/01/01
, which reconciled my old emails.Calendars and shared Documents
With each Google organization, I’m a member with another Google Calendar, Google Drive, and Google Contacts to manage. That document someone wants feedback on sometimes feels like spinning a wheel, and I need to guess which inbox and account the message might land in.
The best solution that I have found for juggling meeting invites is Reclaim, which is terrific for merging multiple calendars into one calendar so I can at least keep on top of meeting invites and scheduling. Dropbox recently bought them, but I’m hoping that Dropbox will leave them alone.
Email and calendars have become more challenging since I switched to a Mac Studio at the office. While we were returning to work during a blizzard last week, I realized that my personal Mac Mini in my home office had no concept of my work calendar or the 4 or 7 Vivaldi profiles with syncing that I use to jump between orgs all day.
With 1Password, this is a straightforward process to set up and authorize, but it still takes time.
Tonight, I’m pretty sure I even locked myself out of one service because it’s probably not a typical usage pattern to jump between three Macs over two locations with a half dozen profiles to juggle.
Calendar Agent
Over the Thanksgiving break, I wrote my first Calendar Agent, who can read and write to my work calendar. It’s not fully baked yet, but it works well enough to tell me about my upcoming meetings and to create a meeting for me. Sometimes.
The biggest downside to using my Calendar Agent is that I have to run it from my terminal, which isn’t always the most convenient place.
Side note: I might rewrite my agent using PydanticAI as an excuse to learn about the Python agent framework, streamline tool-calling, and play with more local agents using Ollama.
The better email solution
The better email solution was a Django email app called Concorde, one of Adam Fast’s creations. It was Django querysets for managing email rules, which I modified and ran over the years. It quickly created better rules than Gmail supported, like deleting old messages in specific folders after x-days. When I kept my fork running and updated, the tool was invaluable. When I kept my Concorde up and running, my email life was healthier than when I was slower to fix it after an upgrade.
Conclusion
I’m annoyed that the best solutions for these problems are to either pay a company to make a Google Suite usable or you must be a developer to build tools to manage it all.
This stuff sucks.
-
Django
,Python
,Today I Learned
django-templated-email-md notes aka if you want to format emails with Markdown, use it
I launched Django News Jobs two DjangoCon USs ago, and I somehow put off deploying emails until this week. So, every day or two, I check my Review Jobs queue to see if there’s anything new to approve or reject.
✅ Sending emails with Django is straightforward and not very painful.
🤔 Working with most email providers and troubleshooting issues is painful.
🤔 Starting with a blank page and styling emails is painful.
I tried a few third-party apps that I fought with before landing on Jack Linke’s django-templated-email-md (DTEM), which just worked. DTEM doesn’t re-invent the wheel, but it does let me write my email messages with Markdown, which turns out to be all I need.
To add email support, I followed the Usage Guide and then I added one send email function per email that I wanted to send. I’ll eventually refactor this, but it was good enough to get started.
jobs/models.py
For the curious, the code looks like:
# jobs/models.py # a bunch of imports ... from templated_email import send_templated_mail ... class Job(models.Model): ... def send_job_new_email(self): send_templated_mail( template_name="job_new", from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=settings.ADMINS, context={ "job": self, }, )
send_templated_mail
does the actual sending, andtemplate_name
will look for a template file calledjob_new.md
, which will contain our Markdown message.You can put anything you want in
context
, but I will include thejob
so we can include as many details from our job submission as possible.To send a Job, I can call
job.send_job_new_email()
via a signal, cron job, or after someone submits a Job for approval.templates/emails/job_new.md
My emails contain both a “subject” and “content” block, and DTEM figures out the rest for me.
<!-- templates/emails/job_new.md --> {% block subject %}[{{ site_name }}] New Job Posting: {{ job.title }} at {{ job.employer_name }}{% endblock %} {% block content %} # New Job Listing Posted A new job has been posted on the website: - **Title:** {{ job.title }} - **Company:** {{ job.employer_name }} - **Location:** {{ job.location }} - **Remote:** {{ job.get_remote_display }} You can view the full job listing here: <a href="{{ job.get_absolute_url_with_domain }}">{{ job.get_absolute_url_with_domain }}</a> {% endblock content %}
My
get_absolute_url_with_domain
calls in my templates are my workaround for Django’s existential crisis of not making it easy to include the domain name in my urls.Bonus: Django-Q2
I paired DTEM with Django-Q2, my favorite Django task queue. It can work with just the Django ORM, which is good enough for projects like Django News Jobs, which are relatively low traffic but spiky traffic.
If my email-sending provider times out or I have an issue, like my credit card expiring, I never want a user to see it. So, I use a task queue to handle all potentially blocking processes, like sending emails.
Django-Q2 is painless to configure. Using it involves importing
async_task
and modifying oursend_templated_mail
method to be an argument to theasync_task
method.# jobs/models.py # a bunch of imports ... from django_q.tasks import async_task from templated_email import send_templated_mail ... class Job(models.Model): ... def send_job_new_email(self): async_task( send_templated_mail, template_name="job_new", from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=settings.ADMINS, context={ "job": self, }, )
If a Job was sent successfully, I can now check the Django Admin to see if there were any failures.
Forward Email
Now that we have an email confirmed to send, my go-to provider for side projects is Forward Email. They allow outbound SMTP and even support webhooks, which I might write about some other time.
I like them over Mailchimp, Sendmail, and Gmail because they are cheap ($3 a month) and let me have unlimited domains and aliases. I have used them for a dozen side projects for several years now, and they just work. I gave up on Sendmail because I spent more time fighting with them to not turn off my account because the volume was too low. It’s worth $36 a year to have to fight this fight again.
Forward Email’s products and services are fully open-source if you care about such things.
-
Weeknotes
,Django
,Python
,Today I Learned
🎄 Weeknotes for Week 51: December 15 to December 21
I mostly finished my holiday shopping this week. I picked up a few gifts for my family throughout the year and then randomly stressed out last week, thinking I didn’t have enough. I picked up a few last-minute items, and everyone was in good shape outside of a few gift cards. Now, to wrap everything…
Meta
Lately, my weekly notes have been more like monthly notes. I draft them, forget to finish/publish them, and then the following Sunday morning, I start another week. So here we are again.
Family
December is LEGO advent calendar month in my household. Our recently turned three-year-old participates for the first time with the LEGO® ǀ Disney Advent Calendar 2024, and our seven-year-old picked the LEGO® Star Wars™ Advent Calendar 2024. Even if you are a grownup, these are fun, and you get some cool LEGO mini-figures.
House
If you ask our three-year-old what she wants for Christmas, she defaults to “Rainbow lights” because I didn’t put any lights up outside. Between our Thanksgiving trip to Chicago and two weeks of everyone in the house cycling through the crud, here we are, but at least the Christmas tree got put up.
Community Work
My community cups ran over a bit this week. This is due to my being new to the DSF Board and various end-of-year and before-holidays “things” that pop up.
Community work cut into some of my open-source projects, but I’d like to catch up over the holidays.
I also ended my week with my last Office Hours of 2024. This week was a little too non-profit and community-heavy, so I will balance that better. With the DSF having its own office hours, I want to keep solving those problems in the proper space.
Side projects
- Django News Newsletter: We shipped issue #264.
- Django Packages, Django News Jobs, DjangoTV, and Upgrade Django all had minor updates over the last month. I need to do a better job aka write a tool to help me keep up since that’s mostly watching git commits or an activity feed for the more content-heavy websites.
Side Quests
Writing
2024-12-20: 🗓️ December 21, 2024, is Volunteer Responsibility Amnesty Day
2024-12-19: Default Apps 2024 - Here are my Default Apps 2024, which builds from my Default Apps 2023 post.
2024-12-17: 🤷 Why do the Django and Python communities use so many Google Forms?
2024-12-14: New project to shorten django-admin to django because we are not monsters - I didn’t realize this idea would kick a hornet’s nest, and yet somehow it did.
Entertainment
We picked up Mighty Morphin Power Rangers: Rita’s Rewind and finished it last week. It was fun, and we like these retro games, but it’s not TMNT’s level of replayability. The game has three hours of gameplay. I’m hoping future updates address this.
I read mixed reviews on G.I. Joe: Wrath of Cobra, but my son was pretty excited about the new Power Rangers game, so we picked it up as a beat ‘em up game we could play over the weekend. It’s buggy in ways that make me wonder how this game has been out for a few months and still has this level of bugs. The controls are bad, but we somehow played through to the last stage before we both shrugged and decided to call it a night. I would give it a two out of five stars type of game. If it weren’t for the nostalgia from my youth, I’d give it one or one and a half stars.
New gear
My NanoKVMs arrived a few weeks ago. I quickly ran out of ports, so I ordered some short HDMI cables, Cat6 cables, more UBC-C cables, and a cheap 8-port switch.
I also lost my Home Assistant machine again, so I swapped out RPis and still ran into issues. As impressive as Home Assistant, running, maintaining, and keeping running is a pain. I have been debating switching to one of their yellow box hardware solutions to support them financially and hoping that I won’t lose my box once a year because it’s so hard to troubleshoot and fix.
I also picked up a 50-foot sewer cam (for looking in walls and vents), an under-desk walking treadmill, and a smart garage door opener to replace our smart Chamberlain garage door opener because they dropped their API.
Next week
This week is Christmas, which means a little bit of travel. Both kids are out of school and preschool, and we both are juggling jobs and deadlines.
-
Today I Learned
Default Apps 2024
Here are my Default Apps 2024, which builds from my Default Apps 2023 post.
🌐 Browser: Vivaldi + Polypane
🔍 Search: Kagi
📝 Writing: Obsidian + Grammarly
📁 Cloud File Storage: iCloud Drive + Syncthing
💬 Chat: Apple Messages (family & friends), Discord (friends), Slack (work and community work), Telegram (weird bots) 📆 Calendar: Apple Calendar
📆 Scheduling + Booking: Cal.com
📹 Video Calls: Zoom + Cal.com + Meeter
🎵 Music. : Apple Music + Spotify
🎤 Podcasts: Overcast (boo, hiss) 🔐 Password Management: 1Password
🧑💻 Code Editor: Sublime Text + Zed 🗃️ Version Control: Tower
🐚 Terminal: iTerm2
✈️ VPN: Tailscale + Mullvad 🔖 Bookmarks: Raindrop.io
📑 Read It Later: Raindrop.io
📨 Mail Client: Mimestream (Gmail only and not very often)
📮 Mail Server: Fastmail (I pay for it but still don’t use it enough) + Gmail
🚀 Launcher: Alfred 5
🖼️ Screenshots: Xnapper
👔 Menu Bar: Ice
🤖 Containers: OrbStack + Docker Compose 🎒 Backups: Backblaze ⚙️ Automation: HammerspoonCommentary
I spent most of 2024 Chrome-free, the new cage-free because I was so frustrated at Mozilla that I deleted Firefox from all my machines. I have doubled my Gmail Accounts and usage between non-profits and community projects.
I’m a happy Vivaldi user and advocate. I only had a handful of issues, quickly solved by turning all or some level of trackers back on. The worst offenders were both my bank and my credit card company.
I dropped Bartender and switched to Ice after the company started acting shady due to poor communication. I have no regrets.
My beloved Overcast podcasting app has been complete garbage for more of 2024 than it was working. The author rewrote it, and it became an unusable disaster for months, one part of Marco’s fault and one part of Apple’s fault for making the iOS App Store such a pain to release products into. It still has Airplay issues and crashes on me, but I can once again play more than one podcast in a row without it crashing. I tried many other podcast apps, and I’m sad that this is the state of things.
I have mostly switched from Docker to OrbStack, which I highly recommend for my development life. I keep Docker around for Compose, but I have several legacy projects I haven’t ported yet.
Previously inspired by: “Apps I’ve been using regularly this year.” Heavily inspired by Matt Stein and cataloged by Robb Knight’s App Defaults
-
Weeknotes
,Django
,Python
,Today I Learned
📓 Weeknotes for Week 48: November 25 to December 1
Family
We drove up to Chicago to see family for five days. It’s a 7.25-hour drive if not stopping were an option, but we usually have to stop at least two or three times to re-fuel, eat, and have a few bathroom breaks. Both of my kids are pro-travelers. Give them an iPad with an offline cache of their favorite Disney and Netflix movies and series, plus some snacks, and they are good to go for 8 to 16 hours. On our last trip back from Omaha, they complained that it was too short because we didn’t stop on our 2.5-hour drive back.
We take turns driving, and through the magic of tethering, I’m surprised that I can comfortably use my laptop from the car for half the trip.
Seeing family was good. There are five kids, ranging from two to nine, but this year everyone is out of diapers, everyone can communicate their needs, and everyone plays together nicely.
We decided to avoid dinner prep drama for Thanksgiving and go out for Mexican food and margaritas. This was an excellent idea. The staff was super friendly, the food was excellent, the margaritas were the right amount of salty and sweet, and everyone got to pick something they enjoyed. There was no food prep or cleanup stress. Overall, our bill for 10 people plus a very generous tip, even after a service fee, was much less than it costs to try to feed 10 people a traditional spread.
Work
It was a short, two-day workweek for me. I helped a client with an inventory project running a week or two behind on their side. The timing wasn’t great because it all landed on my lap the day before I was heading out of town, and it took a few days to run. I hate k8s even more than before.
My main client is missing a tool or two to nail the pulse. I have thought about this because everything takes less time to complete but more time to work on the problem, so things are stretched out. The holiday break was the mental reset I needed to know how to manage this for the next month until our winter break.
Community Work
I skipped all Office Hours this week, but it was a busier few days for my DSF CoC WG card. We reset/rebooted the WG a month ago and have more members and communication. It’s taking more time to reset and settle into a healthy normal.
Side projects
- Django News Newsletter: We shipped issue #260.
- Django News Jobs: More jobs.
- DjangoTV I fixed a bug where non-public videos were being counted in the video counts. I also made several small updates.
Side Quests
Calendar Agent
I spent some free time over the break working on an app that lets ChatGPT control my work calendar. I got the basics up quickly, and my Calendar Agent (Bot?) can look at what’s scheduled on my calendar and create new events for me. The hardest part was navigating Google permissions (I hate them) so that I could access my calendar through Python. Once I got that setup, I used Claude Projects to help me write most of the code.
I ran into an issue with tool calling because I wanted to let the Agent query the time for a day before creating the event. I shifted this logic into my create event function as a fix and realized I was way overthinking it. Now, I have an interesting agent. Still, I have yet to figure out how to run it outside my terminal, which isn’t helpful.
Entertainment
I mostly watched football and basketball this week, but I started a few shows from jefftriplett.com/now/
New gear
Since it was Black Friday, I did some shopping, but not as much as in previous years. I noticed Mastodon vibes are anti-Black Friday, whereas Blue Sky and X are more about saving money and getting a deal. More of my friends shared deals over Slack and Discord than in previous years.
I picked up a five-pack of 32 GB microSD drives for my NanoKVMs, which I hope will be delivered next week. I also bought the family an Air Fryer convection oven combo unit, which may replace our old toaster and be helpful.
Next week
We get to start our LEGO Advent Calendars. I started this tradition with my son when he was two or three. My daughter will be three in a few weeks, so she is now old enough to join in the tradition, too. She requested the Disney Princess calendar, and my son has the Star Wars set. We skipped the traditional set because one can only have so many Santa Clauses.
-
Python
,UV
,Today I Learned
🤷 UV does everything or enough that I'm not sure what else it needs to do
UV feels like one of those old infomercials where it solves everything, which is where we have landed in the Python world.
I have had several discussions with friends about UV, and even when we talk about it during my weekly(ish) office hours, the list has grown to an ever-growing number of options.
UV started as a quicker way of installing Python packages, and now it’s easier to tell people that UV does everything and to focus on what it doesn’t do.
My favorite feature is that UV can now bootstrap a project to run on a machine that does not previously have Python installed, along with installing any packages your application might require.
Here is my incomplete list of what UV does today:
uv pip install
replaces pip installuv venv
replacespython -m venv
uv pip compile
replaces pip-tools compileuv pip sync
replaces pip-tools syncuv run
replaces pipxuv tool run
replaces pipxuv python
replaces pyenv, asdf, mise, and several other like-minded toolsuv build
- Build your Python package for pypiuv publish
- Upload your Python package to pypiastral-sh/setup-uv
brings UV to GitHub Actionsghcr.io/astral-sh/uv:latest
brings UV and Python to Docker
I copied these four from
uv --help
, which feels like poetry features.uv add
- Add dependencies to the projectuv remove
- Remove dependencies from the projectuv sync
- Update the project’s environmentuv lock
- Update the project’s lockfile
So what doesn’t UV do?
UV does a lot, but it still needs to do everything.
- UV doesn’t run custom scripts defined in our
pyproject.toml
likenpm-run-script
allows. Thank you to @command_tab for jogging my memory. - UV doesn’t convert my non-UV-based projects to UV. Converting is more about prefixing and replacing my commands to switch over.
- UV doesn’t manage, and bump version numbers like the BumpVer, and others do.
- UV doesn’t manage pre-commit like hooks. This is a long shot, but I’d love to see support via
pyproject.toml
. - UV doesn’t replace Python, nor should it.
-
Today I Learned
Please publish and share more
Friends, I encourage you to publish more, indirectly meaning you should write more and then share it.
It’d be best to publish your work in some evergreen space where you control the domain and URL. Then publish on masto-sky-formerly-known-as-linked-don and any place you share and comment on.
You don’t have to change the world with every post. You might publish a quick thought or two that helps encourage someone else to try something new, listen to a new song, or binge-watch a new series.
This week, I have encouraged at least half a dozen people to blog something, and at least three of them were happily surprised to see their work re-posted by another friend or published in a newsletter.
I have nothing against masto-whatever-you-use-this-week or blue-sky-levels-of-vc-money or formerly-called-no-one-cares, but those platforms are hard to share an article on.
So, even if you re-publish to thread your post infinitely, please find a cheap or free publishing platform and own your work. GitHub Pages is a free way to publish your work via GitHub, and they will let you use your own domain name for free.
You don’t need an editor
I used to ask my friends to review my work, and I still sometimes do. Then I realized that 99% of the time, it doesn’t matter.
I pay for Grammarly because I have Dyslexia, and it helps me communicate better. But you don’t have to.
You can use a free tool like LanguageTool, which has an online version that will let you copy and paste your writing into a free, no-login-required Grammar checker. This is more than the average person will do, and it’s a quick and free gut check.
Not every gift needs a bow
Our posts are done when you say they are. You do not have to fret about sticking to landing and having a perfect conclusion. Your posts, like this post, are done after we stop writing.
PS: Write and publish before you write your own static site generator or perfect blogging platform. We have lost billions of good writers to this side quest because they spend all their time working on the platform instead of writing.
-
Justfiles
,Today I Learned
TIL Justfiles can also be Just Scripts
Please note: passing an argument like
--justfile
It only works on MacOS and on Linux.TIL that Justfiles can turn into Just Scripts by adding
#!/usr/bin/env just --justfile
to the top of the file and runningchmod +x
on the file.From the docs:
By adding a shebang line to the top of a
justfile
and making it executable,just
can be used as an interpreter for scripts: https://github.com/casey/just?tab=readme-ov-file#just-scriptsjust.sh
#!/usr/bin/env just --justfile @_default: just --justfile just.sh --list @lint *ARGS: uv --quiet run --with pre-commit-uv pre-commit run {{ ARGS }} --all-files
After you run
chmod +x just.sh
, this file may be run using./just.sh
, and sub-commands everythingjust <subcommand>
will just work.Please note that
--justfile just.sh
is needed if you want your Just Script to be able to introspect or call itself.Why?
More and more of my clients are using Justfiles, and occasionally, I want some other recipes that may belong outside the default workflows. These can also be reusable between projects for some of my other internal tooling, so it’s an excellent resource to learn about.
-
Python
,Docker
,UV
,Today I Learned
📓 My notes on publishing a Python package with UV and building a custom GitHub Action for files-to-claude-xml
My new Python application files-to-claude-xml is now on PyPI, which means they are packaged and pip installable. My preferred way of running
files-to-claude-xml
is via UV’s tool run, which will install it if it still needs to be installed and then execute it.$ uv tool run files-to-claude-xml --version
Publishing on PyPi with UV
UV has both build and publish commands, so I took them for a spin today.
uv build
just worked, and a Python package was built.When I tried
uv publish
, it prompted me for some auth settings for which I had to log in to PyPI to create a token.I added those to my local ENV variables I manage with direnv.
export UV_PUBLISH_PASSWORD=<your-PyPI-token-here> export UV_PUBLISH_USERNAME=__token__
Once both were set and registered,
uv publish
published my files on PyPI.GitHub Action
To make
files-to-claude-xml
easier to run on GitHub, I created a custom action to build a_claude.xml
from the GitHub repository.To use this action, I wrote this example workflow, which runs from files-to-claude-xml-example
name: Convert Files to Claude XML on: push jobs: convert-to-xml: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Convert files to Claude XML uses: jefftriplett/files-to-claude-xml-action@main with: files: | README.md main.py output: '_claude.xml' verbose: 'true' - name: Upload XML artifact uses: actions/upload-artifact@v4 with: name: claude-xml path: _claude.xml
My GitHub action is built with a
Dockerfile
, which installsfiles-to-claude-xml
.# Dockerfile FROM ghcr.io/astral-sh/uv:bookworm-slim ENV UV_LINK_MODE=copy RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --frozen --no-install-project WORKDIR /app ENTRYPOINT ["uvx", "files-to-claude-xml"]
To turn a GitHub repository into a runnable GitHub Action, an
action.yml
file needs to exist in the repository. This file describes the input arguments and whichDockerfile
or command to run.# action.yml name: 'Files to Claude XML' description: 'Convert files to XML format for Claude' inputs: files: description: 'Input files to process' required: true type: list output: description: 'Output XML file path' required: false default: '_claude.xml' verbose: description: 'Enable verbose output' required: false default: 'false' version: description: 'Display the version number' required: false default: 'false' runs: using: 'docker' image: 'Dockerfile' args: - ${{ join(inputs.files, ' ') }} - --output - ${{ inputs.output }} - ${{ inputs.verbose == 'true' && '--verbose' || '' }} - ${{ inputs.version == 'true' && '--version' || '' }}
Overall, this works. Claude’s prompting helped me figure it out, which felt fairly satisfying given the goal of
files-to-claude-xml
. -
Django
,Python
,Today I Learned
🧳 DjangoCon US, Black Python Devs Leadership Summit, and Django Girls Durham
I’m heading to Durham, NC, for seven days of DjangoCon US this Friday. This is my 10th year volunteering and the 9th year that DEFNA, the non-profit I co-founded, has run a DjangoCon US event. Here is an overview of the week.
Black Python Devs Leadership Summit (Saturday)
I’m attending and speaking on a discussion panel on Saturday at the Black Python Devs Leadership Summit. Tickets are free, and they will be streaming online in the afternoon. Donations are accepted and appreciated.
Django Girls Durham (Saturday)
Django Girls are hosting a Django workshop and teaching beginners a crash course on building their first website using Django.
DjangoCon US Tutorials (Sunday)
On Sunday morning, I’ll be volunteering and helping out at the tutorials. In the afternoon, we have a tradition of stuffing swag bags, which takes a big group and is a fun way to kick off the conference. You do not need a tutorial ticket or an organizer to help out. Ask at the registration desk, and they can direct you to when and where we are doing this.
Django Social meetup (Sunday)
My company REVSYS is sponsoring a DjangoSocial Raleigh/Durham Pre-DjangoCon Special meetup on Sunday evening before the conference kicks off. The meetup will be great for meeting other attendees the night before the conference.
DjangoCon US Talks (Monday through Wednesday)
The talks are great, but the busiest three days of the conference are also the busiest. There is always a lot going on, from sun up to sun down.
DjangoCon US Sprints (Thursday and Friday)
The sprints are one of my favorite parts of the conference. In past years, I have been so exhausted by the sprints that it’s hard to sit down and focus. It’s one of the best times to discuss Django and the Django ecosystem. If you have a project or want to find a project to help with, the sprints are great for getting your feet wet.
Outro
Tickets are still available if you live near Durham and want to attend. Both events have online and in-person options, so there is no pressure to make last-minute travel plans.
If you live around Durham and want to meet up, please reach out. Let’s see if we can meet for coffee.
-
Django
,Python
,UV
,Today I Learned
🤠 UV Roundup: Five good articles and a pre-commit tip
I have written quite a bit about UV on my micro blog, and I am happy to see more and more people adopt it. I have stumbled on so many good articles recently that I wanted to share them because every article points out something new or different about why UV works well for them.
If you are new to UV, it’s a new tool written by Astral, the creators of Ruff.
I like UV because it replaces, combines, or complements a bunch of Python tools into one tool and user developer experience without forcing a UV way of doing it. UV effectively solves the question, “Why do I need another Python tool?” to do everyday Python tasks.
Some reason I like UV after using it for months:
- It’s a faster pip and is really, really fast
- It can install and manage Python versions
- It can run and install Python scripts
- It can run single-file Python scripts along with their dependencies
- It can handle project lock files
While some people don’t care about UV being fast, it’s shaved minutes off my CI builds and container rebuilds, which means it has also saved me money and energy resources.
Overall thoughts on UV
Oliver Andrich’s UV — I am (somewhat) sold takes the approach of only using UV to set up a new Python environment. Oliver uses UV to install Python, aliases to call Python, and UV tool install to set up a few global utilities.
Using UV with Django
Anže Pečar’s UV with Django shows how to use UV to set up a new project with Django.
Switching from pyenv to UV
Will Guaraldi Kahn-Greene’s Switching from pyenv to uv was relatable for me because I also use pyenv, but I plan to slowly migrate to using only UV. I’m already halfway there, but I will have pyenv for my legacy projects for years because many aren’t worth porting yet.
Using UV and managing with Ansible
Adam Johnson’s Python: my new uv setup for development taught me to use
uv cache prune
to clean up unused cache entries and shows how he manages his UV setup using Ansible.Some notes on UV
Simon Willison’s Notes on UV is an excellent summary of Oliver’s notes.
A parting UV tip
If you are a pre-commit fan hoping for a version that supports UV, the
pre-commit-uv
project does just that. I started updating my justfile recipes to bakejust lint
to the followinguv run
command, which speeds up running and installing pre-commit significantly.$ uv run --with pre-commit-uv pre-commit run --all-files pre-commit-uv
If you are attending DjangoCon US…
If you are attending DjangoCon US and want to talk UV, Django, Django News, Django Packages, hit me up while you are there.
I’ll be attending, volunteering, organizing, sponsoring, and sprinting around the venue in Durham, NC, for the next week starting this Friday.
We still have online and in-person tickets, but not much longer!
-
Django
,Today I Learned
🚜 On Evolving Django's `auth.User` model
I need to re-read/re-listen to Carlton’s Evolving Django’s
auth.User
Stack Report before I have a firm opinion, but I saw his Mastodon post and I thought it was worth sharing some initial thoughts.On my first read, I fell into the camp that wants
django-unique-user-email
and full name to be the defaults on Django’s built-in User. I have wanted this forever, and my clients do, too.Django should still allow me to create a custom user, but the documentation could be better. Django’s documentation is a topic for another day, but I’m confused by it and find it less and less helpful. I notice the developers I work with get lost in them, too. Docs may be a good DjangoCon US topic if you want to discuss it in a few weeks.
I would love the default User to have a unique email address, full name, and a short name. Optionally, a username is an email address setting. Please don’t take away the ability to have a custom User model. (Update: Carlton is not making a case for removing a custom User model, but others have made that argument. No, thank you.)
Carlton’s “Action points” conclusion seems reasonable to me.
-
Today I Learned
🌡️ How a $20 Camera Outsmarted My Fancy Thermometer
My ThermoWorks Signals thermometer’s battery was dead, so I connected it to my Anker 737 Power Bank (24,000mAh for the curious) to get me by.
Previously, I mounted a Wyze camera on a tripod and pointed it toward my thermometer because its Bluetooth connection wasn’t strong enough. WiFi worked fine for the camera, but our smoker is in our backyard and has to go through two external walls to reach any of our devices.
This worked well during the early pandemic when I was working from home. I could check the camera from my upstairs office or couch without worrying about the weather. As a bonus, I could see the front panel of my Pitbos Smoker to monitor it in case it ran out of wood pellets or the heat source went out. I could have bought the fancy ThermoWorks Signals thermometer, but a $20 Wyze Camera worked better.
Today, I have the fancy thermometer, and I still should have connected the Wyze cam because the smoker ran out of pellets, which delayed dinner by 45 minutes.
Maybe I will use this as an excuse to explore one of Ollama’s Vision libraries like LLaVA to read photos from the Wyze Camera and the smoker to let me know when something goes wrong.
-
Django
,Python
,UV
,Today I Learned
📓 UV Run Django Notes
I wanted to know how hard it would be to turn one of my django-startproject projects into a
uv run
friendly project. As it turns out, it worked, and the steps were more than reasonable.Before the PEP 723’ing…
I started with a fairly vanilla
manage.py
that Django will give you after runningpython -m manage startproject
."""Django's command-line utility for administrative tasks.""" import os import sys def main(): """Run administrative tasks.""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == "__main__": main()
shebang
Then we add
#!/usr/bin/env -S uv run
to the top of ourmanage.py
file.Next, we make our
manage.py
executable and try to run it.$ chmod +x manage.py $ ./manage.py ModuleNotFoundError: No module named 'django'
Our script ran, but Python couldn’t find Django. To tell our script to install Django, we can use
uv add—- script
to add it.$ uv add --script manage.py django Updated `manage.py` $ ./manage.py ... Type 'manage.py help <subcommand>' for help on a specific subcommand. Available subcommands: [django] check compilemessages createcachetable dbshell diffsettings dumpdata flush inspectdb loaddata makemessages makemigrations migrate optimizemigration runserver sendtestemail shell showmigrations sqlflush sqlmigrate sqlsequencereset squashmigrations startapp startproject test testserver Note that only Django core commands are listed as settings are not properly configured (error: No module named 'environs').
Django worked as expected this time, but Python could not find a few third-party libraries I like to include in my projects.
To add these, I passed the other four to
uv add --script
which will add them to the project.$ uv add --script manage.py django-click "environs[django]" psycopg2-binary whitenoise Updated `manage.py` ... $ ./manage.py ...
Our Django app’s
manage.py
works when we run it.After the PEP 723’ing…
After we installed our dependencies in our
manage.py
file, they were added to the top of the file between the///
blocks.#!/usr/bin/env -S uv run # /// script # requires-python = ">=3.10" # dependencies = [ # "django", # "django-click", # "environs[django]", # "psycopg2-binary", # "whitenoise", # ] # /// """Django's command-line utility for administrative tasks.""" import os import sys def main(): """Run administrative tasks.""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == "__main__": main()
-
Music
,Today I Learned
🎻 Tiny Desk Concert Wednesday with Sierra Ferrell
🎻 Happy Tiny Desk Concert Wednesday, everyone!
If you are new to Sierra Ferrell, you are in a treat.
If you are already a fan, you’ll understand what I mean and still be in for a treat.
I dislike Google but make exceptions for NPR, Tiny Desk Concert, and Sierra Ferrell.
-
Django
,Python
,Today I Learned
⬆️ Which Django and Python versions should I be using today?
Django 5.1 was released, and I was reminded of the article I wrote earlier this year about Choosing the Right Python and Django Versions for Your Projects.
While I encouraged you to wait until the second, third, or even fourth patch release of Django and Python before upgrading, I received a bit of pushback. One interesting perspective claimed that if everyone waits to upgrade, we don’t find critical bugs until the later versions. While that may be plausible, I don’t believe that the dozens of people who read my blog will be swayed by my recommendation to wait for a few patch releases.
I could have emphasized the potential risks of not testing early. Please start testing during the alpha and release candidate phase so that when Django 5.1 is released, your third-party applications will be ready and working on launch day, minimizing the risk of last-minute issues.
Today, I tried to upgrade Django Packages to run on Django 5.1 to see if our test suite would run on Django 5.1, and it very quickly failed in CI due to at least one package not supporting 5.1 yet. Even if it had passed, I’m 90% sure another package would have failed because that’s the nature of running a new major Django or Python release on day one. Even if the third-party package is ready, the packaging ecosystem needs time to catch up.
Which version of Django should I use today?
I’m sticking with Django 5.0 until Django 5.1’s ecosystem has caught up. I plan to update the third-party packages I help maintain to have Django 5.1 support. After a few patch releases of Django 5.1 have come out and the ecosystem has time to catch up, I will try to migrate again.
Which version of Python should I use today?
I’m starting new projects on Python 3.12, with a few legacy projects still being done on Python 3.11. While I am adding Django 5.1 support, I plan to add Python 3.13 support in my testing matrixes to prepare everything for Python 3.13’s release this fall.
Office hours
I plan to spend some of my Office Hours this week working on Django 5.1 and Python 3.13 readiness for projects I maintain. Please join me if you have a project to update or would like some light-hearted banter to end your week.
-
Python
,Ollama
,Today I Learned
🦙 Ollama Tool Calling Loose Notes
I spent a few hours this week working with the Ollama project and trying to get tool calling to work with the LangChain library.
Tool calling is a way to expose Python functions to a language model that allows them to be called. This will enable models to perform more complex actions and even call the outside world for more information.
I haven’t used LangChain before, and I found the whole process frustrating. The docs were full of errors. I eventually figured it out, but I was limited to one tool call per prompt, which felt broken.
Earlier today, I was telling a colleague about it, and when we got back from grabbing coffee, I thought I would check the Ollama Discord channel to see if anyone else had figured it out. To my surprise, they added and released Tool support last night, which allowed me to ditch LangChain altogether.
The Ollama project’s tool calling example was just enough to help get me started.
I struggled with the function calling syntax, but after digging a bit deeper, I found this example from OpenAI’s Function calling docs, which matches the format the Ollama project is following. I still don’t fully understand it, but I got more functions working and verified that I can make multiple tool calls within the same prompt.
Meta’s Llama 3.1 model supports tool calling, and the two work quite well together. I am also impressed with Llama 3.1 and the large context window support. I’m running the 8B and 70B models on a Mac Studio, and they feel very close to the commercial APIs I have worked with, but I can run them locally.
Embedding models
Tonight, I tried out Ollama’s Embedding models example, and while I got it working, I still need to put practical data into it to give it a better test
One more tip
If you did not know Ollama can parse and return valid JSON, check out How to get JSON response from Ollama. It made my JSON parsing and responses much more reliable.
-
Python
,Ollama
,LLM
,Today I Learned
🦙 Ollama Llama 3.1 Red Pajama
For a few weeks, I told friends I was excited to see if the new Llama 3.1 release was as good as it was being hyped.
Yesterday, Llama 3.1 was released, and I was impressed that the Ollama project published a release to Homebrew and had the models ready to use.
➜ brew install ollama ➜ ollama serve # (optionally) I run Ollama as a background service ➜ brew services start ollama # This takes a while (defaults to the llama3.1:8b model) ➜ ollama pull llama3.1:latest # (optional) This takes a longer time ➜ ollama pull llama3.1:70b # (optional) This takes so long that I skipped it and ordered a CAT6 cable... # ollama pull llama3.1:405b
To use chat with the model, you use the same
ollama
console command:➜ ollama run llama3.1:latest >>> how much is 2+2? The answer to 2 + 2 is: 4!``` ## Accessing Ollama Llama 3.1 with Python The Ollama project has an [`ollama-python`](https://github.com/ollama/ollama-python) library, which I use to build applications. My demo has a bit of flare because there are a few options, like `--stream,` that improve the quality of life while waiting for Ollama to return results. ```python # hello-llama.py import typer from enum import Enum from ollama import Client from rich import print class Host(str, Enum): local = "http://127.0.0.1:11434" the_office = "http://the-office:11434" class ModelChoices(str, Enum): llama31 = "llama3.1:latest" llama31_70b = "llama3.1:70b" def main( host: Host = Host.local, local: bool = False, model: ModelChoices = ModelChoices.llama31, stream: bool = False, ): if local: host = Host.local client = Client(host=host.value) response = client.chat( model=model.value, messages=[ { "role": "user", "content": \ "Please riff on the 'Llama Llama Red Pajama' book but using AI terms like the 'Ollama' server and the 'Llama 3.1' model." "Instead of using 'Llama Llama', please use 'Ollama Llama 3.1'.", } ], stream=stream, ) if stream: for chunk in response: print(chunk["message"]["content"], end="", flush=True) print() else: print(f"[yellow]{response['message']['content']}[/yellow]") if __name__ == "__main__": typer.run(main)
Some of my family’s favorite books are the late Anna Dewdney’s Llama Llama books. Please buy and support their work. I can’t read Llama 3.1 and Ollama without considering the “Llama Llama Red Pajama” book.
To set up and run this:
# Install a few "nice to have" libraries ➜ pip install ollama rich typer # Run our demo ➜ python hello-llama.py --stream Here's a riff on "Llama Llama Red Pajama" but with an AI twist: **Ollama Llama 3.1, Ollama Llama 3.1** Mama said to Ollama Llama 3.1, "Dinner's done, time for some learning fun!" But Ollama Llama 3.1 didn't wanna play With the data sets and algorithms all day. He wanted to go out and get some rest, And dream of neural nets that were truly blessed. But Mama said, "No way, young Ollama Llama 3.1, You need to train on some more NLP." Ollama Llama 3.1 got so mad and blue He shouted at the cloud, "I don't wanna do this too!" But then he remembered all the things he could see, On the Ollama server, where his models would be. So he plugged in his GPU and gave a happy sigh And trained on some texts, till the morning light shone high. He learned about embeddings and wordplay too, And how to chat with humans, that's what he wanted to do. **The end**
Connecting to Ollama
I have two Macs running Ollama and I use Tailscale to bounce between them from anywhere. When I’m at home upstairs it’s quicker to run a local instance. When I’m on my 2019 MacBook Pro it’s faster to connect to the office.
The only stumbling block I ran into was needing to set a few ENV variables setup so that Ollama is listening on a port that I can proxy to. This was frustrating to figure out, but I hope it saves you some time.
➜ launchctl setenv OLLAMA_HOST 0.0.0.0:11434 ➜ launchctl setenv OLLAMA_ORIGINS http://* # Restart the Ollama server to pick up on the ENV vars ➜ brew services restart ollama
Simon Willison’s LLM tool
I also like using Simon Willison’s LLM tool, which supports a ton of different AI services via third-party plugins. I like the llm-ollama library, which allows us to connect to our local Ollama instance.
When working with Ollama, I start with the Ollama run command, but I have a few bash scripts that might talk to OpenAI or Claude 3.5, and it’s nice to keep my brain in the same tooling space. LLM is useful for mixing and matching remote and local models.
To install and use LLM + llm-ollama + Llama 3.1.
Please note that the Ollama server should already be running as previously outlined.
# Install llm ➜ brew install llm # Install llm-ollama ➜ llm install llm-ollama # List all of models from Ollama ➜ llm ollama list-models # ➜ llm -m llama3.1:latest "how much is 2+2?" The answer to 2 + 2 is: 4
Bonus: Mistral Large 2
While I was working on this post, Mistral AI launched their Large Enough: Mistral Large 2 model today. The Ollama project released support for the model within minutes of its announcement.
The Mistral Large 2 release is noteworthy because it outperforms Lllama 3.1’s 405B parameter model and is under 1/3 of the size. It is also the second GPT-4 class model release in the last two days.
Check out Simon’s post for more details and another LLM plugin for another way to access it.
-
Weeknotes
,Django
,Python
,Today I Learned
📓 Weeknotes for Week 28: July 8 to 14
I’m running a week behind on this.
This week was our first week back home without traveling in a month, and it felt good to be home. I had time to catch up on mowing the yard, and I treated the yard with an eco-safe mosquito repellent. Despite the hot weather, Sunday felt nice outside to be mosquito-free.
I rolled my above-ground sprinkler system A few years ago, and I still need to install and run it this year. I wanted to get it this weekend, and here we are.
Family
I converted my daughter’s crib to a daybed over the weekend, and we have been using it for two nights and two naps without any issues. My son took to the board game Risk in Chicago, so I installed the iPad version and walked him through it. It was a pizza and tacos weekend because it was a long week.
Work
Occasionally, a project feels like you signed up for a race, but the distance keeps changing whenever you are within sight of the finish line. A project we have been finishing up keeps growing.
Community Work
-
Djangonaut Space: All three of our Djangonauts completed their first tasks, and we are moving on to our first big group project.
Side projects
-
Django News Newsletter: We shipped issue #241.
-
Django News Jobs: This week, we picked up more jobs that weren’t from one source. I need to write a tool to help maintain this, but it’s a manageable load.
-
I bought a new domain name for a project this weekend. More on that soon.
Side Quests
-
I dusted off my YouTube-to-Frontmatter tool and added the ability to pull playlists from a given username. I wrote the files out as JSON and used DuckDB to query them, which worked amazingly well.
-
I wrote an Amazon product image downloader for a few blog posts. When the product API did not work, I punted and had ChatGPT write a playwright scraper. It was faster and much less frustrating. I need this for several projects.
-
I cleaned up my sitemaps research tool.
-
I tried out a screenshots-to-code project and ran some 00s-era websites through it that I wish still existed. If someone wants to give me a few years of funding, I think we can make the web not suck again.
Writing
2024-07-14: 🔥 Why I deleted Firefox from my machines this weekend I no longer trust or believe in Mozilla, so I deleted Firefox from my machines this weekend. ➜ brew …
2024-07-13: 🦆 DuckDB may be the tool you didn’t know you were missing 🤔 I haven’t fully figured out DuckDB yet, but it’s worth trying out if you are a Python …
2024-07-12: 🚜 macOS Bartender app to Ice app I upgraded my Macs to macOS Sonoma a few weeks ago. While everything has been uneventful, the …
2024-07-11: 🎮 8BitDo Golden/Silver Limited Edition controllers My favorite third-party video game hardware company, 8BitDo, announced its 11th-anniversary limited …
2024-07-10: 📅 Office Hours for July 12th Office Hours returns this Friday, July 12th, 2024, from 2:30 pm to 4:30 pm Central Time. ℹ️ Anyone …
2024-07-09: 🔓 Sharing is Caring: How a Simple Sudo Question Led to Better Solutions One of the fun discoveries of blogging is finding your article in search results while trying to …
2024-07-08: 📓 Weeknotes for Week 27: July 1 to 7 The last week was a blur between the holiday, travel, and cramming a lot of work. My notes this week …
Entertainment
📺 The Marvels (2023) - This movie was better than people gave it credit for. It wasn’t my favorite, but it was fun to watch.
📺 Defending Jacob - I skipped to the end of this series.
📺 The Last Thing He Told Me - I skipped to the end of this series.
📺 Presumed Innocent - I surprised myself that I’m still keeping up with this series, but there are only a few weeks left.
📺 The Acolyte - We are ready for the last episode.
📺 Atlas (2024) - I didn’t go into this movie with any expectations, and I immensely enjoyed it.
Next week
I’m solo-parenting next weekend. I’m looking forward to hanging out with my kids and another weekend of being home.
-
-
Django
,Office Hours
,Today I Learned
📅 I am hosting Office Hours this Friday, July 19th
I am hosting Office Hours this Friday, July 19th, 2024, from 2:00 pm to 4:15 pm Central Time.
➡️ I have to leave earlier than normal to pick up my kids, but I plan to be around until 4:15 pm to 4:30 pm Central Time or as time permits.
ℹ️ Anyone can join office hours.
ℹ️ Our office hours are a collaborative space where we can discuss our ongoing projects, catch up, and work together to wrap up our week on a productive note.
💼 I will be working on a new Django project this week, sourcing data for it, and hopefully writing a bunch of views.
🙏 As always, everyone is welcome to join, whether you’re a regular attendee or joining for the first time.
✅ If you need any additional details, feel free to send me a message or check out the gist from our previous sessions, where you’ll find the Zoom link ⚠️
I look forward to seeing everyone.
-
Python
,Today I Learned
🗳️ My thoughts on the PSF Election results
A few weeks ago, I wrote about this year’s PSF Election, three proposed bylaws changes, and how I intended to vote. I’m happy that the membership overwhelmingly approved all three proposed bylaw changes. Here is this year’s results.
Merging Contributing and Managing member classes
This change is a good step toward consolidating two membership classes and a commitment to acknowledging that all community contributions are important, not just code contributions.
Simplifying the voter affirmation process by treating past voting activity as intent to continue voting
If you voted in last year’s election, there are fewer barriers to voting in the next election. With a 76% turnout this year, I suspect next year will still yield over a 50% voter turnout, and I suspect turnout will continue to be high.
Allow for removal of Fellows by a Board vote in response to Code of Conduct violations, removing the need for a vote of the membership
This one means the most to me. When I joined the board, our Code of Conduct was barely two paragraphs long and said little. We rewrote it and formed the PSF Code of Conduct workgroup. From today forward, we can appreciate that the Python Code of Conduct applies to everyone.
Overall
We also gained three new directors, including two returning directors. This election may be the first time we have had an election in which no one running from North America made it on the board. (Possibly Europe, too, but I didn’t dive as deep to verify that.) Either way, this is a noteworthy milestone.
I’m proud of the Python community for embracing our Code of Conduct and membership changes. A few of these were overdue, but updating the voter affirmation process is an excellent proactive step and a shift for the board.
I also want to thank Débora Azevedo, the PSF’s vice chair-elect and our outbound director. I was impressed with Débora when we served on the board together, and I thought she brought valuable insights. When she put her name forward to run for vice chair, I was impressed because it’s an intimidating group to put yourself out there, and I thought Débora managed it well.
Resources
-
Vivaldi
,Today I Learned
🔥 Why I deleted Firefox from my machines this weekend
I no longer trust or believe in Mozilla, so I deleted Firefox from my machines this weekend.
➜ brew remove firefox ==> Uninstalling Cask firefox ==> Backing App 'Firefox.app' up to '/opt/homebrew/Caskroom/firefox/112.0.1/Firefox.app' ==> Removing App '/Applications/Firefox.app' ==> Unlinking Binary '/opt/homebrew/bin/firefox' ==> Purging files for version 112.0.1 of Cask firefox ~ on ☁️ took 4s
There was no one decision, but Firefox version 128’s Privacy-Preserving Attribution update was the final straw.
The default browser you use is a personal decision, and I have always kept Firefox around. It was my default for over a decade until I gave up and switched to Google Chrome. At that point, Firefox became bloated and buggy like the Mozilla browser before it, and I found myself at that intersection again.
Last year, I switched to Vivaldi as my default browser and only kept Chrome around for a few organizations. Eventually, I’ll delete Chrome, too, but that’s more symbolic of removing Google from my life.
What annoys me the most is that Mozilla was supposed to be the company that knows better, but their leadership keeps reminding us of who they are.
Removing Firefox pains me because I believe in alternative browsers that aren’t controlled by one company. I have friends who have worked for Mozilla in the past and present.
Had Mozilla pledged to cut the bullshit and give me a Firefox Pro option to support them, I would have been one of the first to sign up. However, I don’t trust Mozilla when they aren’t focused on being the best web browser in the industry. I’d rather avoid their antics until they give me a reason to trust them again.
PS: Maybe Servo is a better alternative from Mozilla, but for now, Vivaldi is my best Chrome-like, and it’s not run by Google until something better emerges.
-
Today I Learned
🚜 macOS Bartender app to Ice app
I upgraded my Macs to macOS Sonoma a few weeks ago. While everything has been uneventful, the Bartender app pinged me one too many times about upgrading to Bartender 5, which has several Sonoma-only features.
Last year, I looked forward to upgrading Bartender until their recent botched communication and messaging regarding the purchase of the app.
Oliver Andrich’s Week 23: An Almost Perfect Week was the nudge I needed to go ahead and switch over to Ice, which is an open-source menu bar manager for macOS.
The upgrade took less than a minute, and the app worked the same way Bartender did for me. I changed the menu icon to an ellipsis to match what I had the Bartender app set to, and everything worked.
Previously
-
Today I Learned
🔓 Sharing is Caring: How a Simple Sudo Question Led to Better Solutions
One of the fun discoveries of blogging is finding your article in search results while trying to solve a problem.
A few weeks ago, I upgraded my Macs to macOS to Sonoma, but I hadn’t yet re-enabled passwordless authentication via sudo. If this were a server in the cloud, I would approach it differently, but on my laptop, it’s a different story, and I can skip re-typing my password.
I stumbled on my TIL: Enable sudo without a password on MacOS post from 2022 when I was searching for a quick fix.
I also posted on Mastodon about this, which led to Anthony’s helpful suggestion that I could instead use Touch ID which I liked a lot.
I wish I could use Touch ID, but I often use SSH and Remote Desktop. Touch ID will only work if my finger is there. It’s still an excellent suggestion for anyone seeking a more secure way to assume sudo access.
Dan Ryan pointed me to a better solution than my existing workaround, which doesn’t get overwritten every major macOS update.
Helpful suggestions like these are why I like to think out loud and share these thoughts on Mastodon.
-
Weeknotes
,Django
,Python
,Today I Learned
📓 Weeknotes for Week 27: July 1 to 7
The last week was a blur between the holiday, travel, and cramming a lot of work. My notes this week are more glossed over than most
Family
We drove to Illinois (near Chicago) to see family this week. We had family from both coasts who we don’t see very often, and it was the first time much of the family met my youngest, Nora. It takes us 8 to 8.5 hours to drive there, and people are always amazed at how good my kids are at traveling these distances. They both love the extra screen time and are easy to travel with. We can hand each one an iPad, and they are set for hours, given enough snacks and a rest stop every few hours.
Work
It was a short two-day week, but I got a bonus third day of work between fitting a few hours in the car and half a day on Friday. This was nice because we are wrapping up an existing client project while ramping up on another project.
This has also made me realize that Django has many shopping cart projects, but they all seem to be outdated. We struggled to find a project we could use, which made me realize that the Django community is sorely missing a good shopping cart and checkout experience that works.
Community Work
Djangonaut Space: All three Djangonaut Space Team Neptune members have made meaningful contributions early in the process, and now I’m feeling the pressure to detail and share some more advanced projects so they can have tasks for the duration of the project.
Django Code of Conduct WG: We have prioritized keeping up as we get new members used to contributing. This week, was a busy week.
Side projects
Django News Newsletter: We shipped issue #240.
Django News Jobs: We have had code to aggregate some other Django job boards, and this week, we started allowing those to come through.
Writing
This week, I was back on track, and I wrote and published something every day. It was my first day of giving myself a buffer so that I am always writing tomorrow’s post, which took the edge and pressure off. I am still writing every day, but with the holiday, it was nice knowing I didn’t have to fit it in on our last day in Chicago, which ended up being a really long, full day with family.
2024-07-07: 🧰 More fun with Django Extensions using
shell_plus
andgraph_models
Yesterday, I wrote about Django Extensions show_urls management command because it’s useful. I …2024-07-06: Django Extensions is useful even if you only use show_urls Yes, Django Extensions package is worth installing, especially for its show_urls command, which can …
2024-07-05: 📅 No Office Hours on July 5th, but… No Office Hours this week (July 5th), but we will return next Friday, July 12th, 2024, 2:30 pm to …
2024-07-04: 🎆 🤖 Happy AIndependence Day To everyone in the United States, Happy Independence Day and Happy AIndependence Day to everyone …
2024-07-03: 🗳️ PSF Elections how I am voting This was written while driving to Chicago (technically from the passenger seat). Still, a few people …
2024-07-02: 💬 On the PSF Bylaw changes The Python Software Foundation has three bylaw changes up for a vote in this year’s election. …
2024-07-01: 📓 Weeknotes for Week 26: June 24 to 30 Family I took two days off for a funeral and some other family stuff. I saw cousins and other …
Entertainment
📺 Sweet Tooth - I really liked this series.
📺 Presumed Innocent - I watched the first five episodes, and it’s okay.
📺 The Last Thing He Told Me - I just started this series on Sunday night and am unsure if I’ll finish it.
Next week
It’s my first whole week back in a while. I’m looking forward to catching up and mowing my yard while listening to podcasts. I need to catch up on house stuff, including running my homemade above-ground sprinkler system. (Chill; we live near the Kansas River.)