<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: tutorials</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/tutorials.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2023-07-16T05:55:54+00:00</updated><author><name>Simon Willison</name></author><entry><title>Weeknotes: Self-hosted language models with LLM plugins, a new Datasette tutorial, a dozen package releases, a dozen TILs</title><link href="https://simonwillison.net/2023/Jul/16/weeknotes/#atom-tag" rel="alternate"/><published>2023-07-16T05:55:54+00:00</published><updated>2023-07-16T05:55:54+00:00</updated><id>https://simonwillison.net/2023/Jul/16/weeknotes/#atom-tag</id><summary type="html">
    &lt;p&gt;A lot of stuff to cover from the past two and a half weeks.&lt;/p&gt;
&lt;h4&gt;LLM and self-hosted language model plugins&lt;/h4&gt;
&lt;p&gt;My biggest project was the &lt;a href="https://simonwillison.net/2023/Jul/12/llm/"&gt;new version of my LLM tool for interacting with Large Language Models&lt;/a&gt;. LLM now accepts plugins for adding alternative language models to the tool, meaning it's now applicable to more than just the OpenAI collection.&lt;/p&gt;
&lt;p&gt;I figured out quite a few of the details of this while offline on a camping trip up in the Northern California redwoods, which forced the issue on figuring out how to work with LLMs that I could host on my own computer because I didn't have a connection to access the OpenAI APIs.&lt;/p&gt;
&lt;p&gt;Comprehensive documentation is sorely lacking in the world of generative AI. I've decided to push back against that for LLM, so I spent a bunch of time working on an extremely comprehensive tutorial for writing a plugin that adds a new language model to the tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://llm.datasette.io/en/stable/plugins/tutorial-model-plugin.html"&gt;Writing a plugin to support a new model&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As part of researching this tutorial I finally figured out how to build a Python package using just a &lt;code&gt;pyproject.toml&lt;/code&gt; file, with no &lt;code&gt;setup.py&lt;/code&gt; or &lt;code&gt;setup.cfg&lt;/code&gt; or anything else like that. I wrote that up in detail in &lt;a href="https://til.simonwillison.net/python/pyproject"&gt;Python packages with pyproject.toml and nothing else&lt;/a&gt;, and I've started using that pattern for all of my new Python packages.&lt;/p&gt;
&lt;p&gt;LLM also now includes a Python API for interacting with models, which provides an abstraction that works the same for the OpenAI models and for other models (including self-hosted models) installed via plugins. Here's &lt;a href="https://llm.datasette.io/en/stable/python-api.html"&gt;the documentation for that&lt;/a&gt; - it ends up looking like this:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;llm&lt;/span&gt;

&lt;span class="pl-s1"&gt;model&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;llm&lt;/span&gt;.&lt;span class="pl-en"&gt;get_model&lt;/span&gt;(&lt;span class="pl-s"&gt;"gpt-3.5-turbo"&lt;/span&gt;)
&lt;span class="pl-s1"&gt;model&lt;/span&gt;.&lt;span class="pl-s1"&gt;key&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;'YOUR_API_KEY_HERE'&lt;/span&gt;
&lt;span class="pl-s1"&gt;response&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;model&lt;/span&gt;.&lt;span class="pl-en"&gt;prompt&lt;/span&gt;(&lt;span class="pl-s"&gt;"Five surprising names for a pet pelican"&lt;/span&gt;)
&lt;span class="pl-k"&gt;for&lt;/span&gt; &lt;span class="pl-s1"&gt;chunk&lt;/span&gt; &lt;span class="pl-c1"&gt;in&lt;/span&gt; &lt;span class="pl-s1"&gt;response&lt;/span&gt;:
    &lt;span class="pl-en"&gt;print&lt;/span&gt;(&lt;span class="pl-s1"&gt;chunk&lt;/span&gt;, &lt;span class="pl-s1"&gt;end&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;""&lt;/span&gt;)&lt;/pre&gt;
&lt;p&gt;To use another model, just swap its name in for &lt;code&gt;gpt-3.5-turbo&lt;/code&gt;. The self-hosted models provided by the &lt;a href="https://github.com/simonw/llm-gpt4all"&gt;llm-gpt4all&lt;/a&gt; plugin work the same way:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;pip install llm-gpt4all&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;llm&lt;/span&gt;

&lt;span class="pl-s1"&gt;model&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;llm&lt;/span&gt;.&lt;span class="pl-en"&gt;get_model&lt;/span&gt;(&lt;span class="pl-s"&gt;"ggml-vicuna-7b-1"&lt;/span&gt;)
&lt;span class="pl-s1"&gt;response&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;model&lt;/span&gt;.&lt;span class="pl-en"&gt;prompt&lt;/span&gt;(&lt;span class="pl-s"&gt;"Five surprising names for a pet pelican"&lt;/span&gt;)
&lt;span class="pl-c"&gt;# You can do this instead of looping through the chunks:&lt;/span&gt;
&lt;span class="pl-en"&gt;print&lt;/span&gt;(&lt;span class="pl-s1"&gt;response&lt;/span&gt;.&lt;span class="pl-en"&gt;text&lt;/span&gt;())&lt;/pre&gt;
&lt;p&gt;I've released three plugins so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonw/llm-gpt4all"&gt;llm-gpt4all&lt;/a&gt; with 17 self-hosted models from the &lt;a href="https://gpt4all.io/"&gt;GPT4All&lt;/a&gt; project.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonw/llm-palm"&gt;llm-palm&lt;/a&gt; with Google's &lt;a href="https://blog.google/technology/ai/google-palm-2-ai-large-language-model/"&gt;PaLM 2&lt;/a&gt; language model, via their API.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/simonw/llm-mpt30b"&gt;llm-mpt30b&lt;/a&gt; providing the 19GB MPT-30B model, using &lt;a href="https://huggingface.co/TheBloke/mpt-30B-GGML"&gt;TheBloke/mpt-30B-GGML&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'm looking forward to someone else &lt;a href="https://llm.datasette.io/en/stable/plugins/tutorial-model-plugin.html"&gt;following the tutorial&lt;/a&gt; and releasing their own plugin!&lt;/p&gt;
&lt;h4&gt;A new tutorial: Data analysis with SQLite and Python&lt;/h4&gt;
&lt;p&gt;I presented this as a 2hr45m tutorial at PyCon a few months ago. The video is now available, and I like to try to turn these kinds of things into more permanent documentation.&lt;/p&gt;
&lt;p&gt;The Datasette website has &lt;a href="https://datasette.io/tutorials"&gt;a growing collection of tutorials&lt;/a&gt;, and I decided to make that the final home for this one too.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://datasette.io/tutorials/data-analysis"&gt;Data analysis with SQLite and Python&lt;/a&gt; now has the full 2hr45m video plus an improved version of the handout I used for the talk. The written material there there should also be valuable for people who don't want to spend nearly three hours watching the video!&lt;/p&gt;
&lt;p&gt;As part of putting that page together I solved a problem I've been wanting to figure out for a long time: I figured out a way to build a custom Jinja block tag that looks like this:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-django"&gt;&lt;pre&gt;&lt;span class="pl-e"&gt;{%&lt;/span&gt; &lt;span class="pl-s"&gt;markdown&lt;/span&gt; &lt;span class="pl-e"&gt;%}&lt;/span&gt;
# This will be rendered as markdown

- Bulleted
- List
&lt;span class="pl-e"&gt;{%&lt;/span&gt; &lt;span class="pl-s"&gt;endmarkdown&lt;/span&gt; &lt;span class="pl-e"&gt;%}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I released that in &lt;a href="https://github.com/simonw/datasette-render-markdown/releases/tag/2.2"&gt;datasette-render-markdown 2.2&lt;/a&gt;. I also wrote up a TIL on &lt;a href="https://til.simonwillison.net/jinja/custom-jinja-tags-with-attributes"&gt;Custom Jinja template tags with attributes&lt;/a&gt; describing the pattern I used.&lt;/p&gt;
&lt;p&gt;One bonus feature for that tutorial: I decided to drop in a nested table of contents, automatically derived from the HTML headers on the page.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/toc.jpg" alt=" What you'll need python3 and pip Optional: GitHub Codespaces sqlite-utils Using the command-line tools to clean data Exploring data with Datasette Installing Datasette locally Try a database: legislators.db Install some plugins Learning SQL with Datasette Using sqlite-utils as a Python library, to import all the PEPs Enabling full-text search Publishing a database to Vercel Other publishing options Datasette Lite Loading SQLite, CSV and JSON data Installing plugins Further reading Advanced SQL Aggregations Subqueries CTEs JSON Window functions Baked Data Niche Museums and TILs Generating a newsletter with an Observable notebook More demos and further reading Fun demos SpatiaLite " style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;I wrote the code for this entirely using the new ChatGPT Code Interpreter, which can write Python based on your description and, crucially, &lt;em&gt;execute it and see if it works&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://chat.openai.com/share/e41efb6d-eae7-454d-9aa2-5284683ba9f9"&gt;my ChatGPT transcript&lt;/a&gt; showing how I built the feature.&lt;/p&gt;
&lt;p&gt;I've been using ChatGPT Code Interpreter for a few months now, and I'm completely hooked: I think it's the most interesting thing in the whole AI space at the moment.&lt;/p&gt;
&lt;p&gt;I participated in a &lt;a href="https://www.latent.space/p/code-interpreter"&gt;Code Interpreter Latent Space&lt;/a&gt; episode to talk about it, which ended up drawing 17,000 listeners on Twitter Spaces and is now also available as a podcast episode, neatly edited together by swyx.&lt;/p&gt;
&lt;h4&gt;Symbex --check and --rexec&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/symbex"&gt;Symbex&lt;/a&gt; is my Python CLI tool for quickly finding Python functions and classes and outputting either the full code or just the signature of the matching symbol. I first &lt;a href="https://simonwillison.net/2023/Jun/18/symbex/"&gt;wrote about that here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/simonw/symbex/releases/tag/1.1"&gt;symbex 1.1&lt;/a&gt; adds two new features.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;symbex --function --undocumented --check&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This new &lt;code&gt;--check&lt;/code&gt; mode is designed to run in Continuous Integration environments. If it finds any symbols matching the filters (in this case functions that are missing their docstring) it returns a non-zero exit code, which will fail the CI step.&lt;/p&gt;
&lt;p&gt;It's an imitation of &lt;code&gt;black . --check&lt;/code&gt; - the idea is that Symbex can now be used to enforce code quality issues like docstrings and the presence of type annotations.&lt;/p&gt;
&lt;p&gt;The other new feature is &lt;code&gt;--rexec&lt;/code&gt;. This is an extension of the existing &lt;code&gt;--replace&lt;/code&gt; feature, which lets you find a symbol in your code and replace its body with new code.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--rexec&lt;/code&gt; takes a shell expression. The body of the matching symbol will be piped into that command, and its output will be used as the replacement.&lt;/p&gt;
&lt;p&gt;Which means you can do things like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;symbex my_function \
  --rexec &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;llm --system 'add type hints and a docstring'&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will find &lt;code&gt;def my_function()&lt;/code&gt; and its body, pass that through &lt;code&gt;llm&lt;/code&gt; (using the &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; default model, but you can specify &lt;code&gt;-m gpt-4&lt;/code&gt; or any other model to use something else), and then take the output and update the file in-place with the new implementation.&lt;/p&gt;
&lt;p&gt;As a demo, I ran it against this:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;my_function&lt;/span&gt;(&lt;span class="pl-s1"&gt;a&lt;/span&gt;, &lt;span class="pl-s1"&gt;b&lt;/span&gt;):
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;a&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;b&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;3&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;And got back:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;def&lt;/span&gt; &lt;span class="pl-en"&gt;my_function&lt;/span&gt;(&lt;span class="pl-s1"&gt;a&lt;/span&gt;: &lt;span class="pl-s1"&gt;int&lt;/span&gt;, &lt;span class="pl-s1"&gt;b&lt;/span&gt;: &lt;span class="pl-s1"&gt;int&lt;/span&gt;) &lt;span class="pl-c1"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pl-s1"&gt;int&lt;/span&gt;:
    &lt;span class="pl-s"&gt;"""&lt;/span&gt;
&lt;span class="pl-s"&gt;    Returns the sum of two integers (a and b) plus 3.&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;    Parameters:&lt;/span&gt;
&lt;span class="pl-s"&gt;    a (int): The first integer.&lt;/span&gt;
&lt;span class="pl-s"&gt;    b (int): The second integer.&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;    Returns:&lt;/span&gt;
&lt;span class="pl-s"&gt;    int: The sum of a and b plus 3.&lt;/span&gt;
&lt;span class="pl-s"&gt;    """&lt;/span&gt;
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;a&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;b&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;3&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Obviously this is fraught with danger, and you should only run this against code that has already been committed to Git and hence can be easily recovered... but it's a really fun trick!&lt;/p&gt;
&lt;h4&gt;ttok --encode --decode&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ttok&lt;/code&gt; is my CLI tool for counting tokens, as used by LLM models such as GPT-4. &lt;a href="https://github.com/simonw/ttok/releases/tag/0.2"&gt;ttok 0.2&lt;/a&gt; adds a requested feature to help make tokens easier to understand, best illustrated by this demo:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;ttok Hello world
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Outputs 2 - the number of tokens&lt;/span&gt;
ttok Hello world --encode
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Outputs 9906 1917 - the encoded tokens&lt;/span&gt;
ttok 9906 1917 --decode
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Outputs Hello world - decoding the tokens back again&lt;/span&gt;
ttok Hello world --encode --tokens
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Outputs [b'Hello', b' world']&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Being able to easily see the encoded tokens including whitespace (the &lt;code&gt;b' world'&lt;/code&gt; part) is particularly useful for understanding how the tokens all fit together.&lt;/p&gt;
&lt;p&gt;I wrote more about GPT tokenization in &lt;a href="https://simonwillison.net/2023/Jun/8/gpt-tokenizers/"&gt;understanding GPT tokenizers&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/python/tree-sitter"&gt;Using tree-sitter with Python&lt;/a&gt; - 2023-07-14&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/yaml/yamlfmt"&gt;Auto-formatting YAML files with yamlfmt&lt;/a&gt; - 2023-07-13&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/python/quick-testing-pyenv"&gt;Quickly testing code in a different Python version using pyenv&lt;/a&gt; - 2023-07-10&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/git/git-filter-repo"&gt;Using git-filter-repo to set commit dates to author dates&lt;/a&gt; - 2023-07-10&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/gpt3/openai-python-functions-data-extraction"&gt;Using OpenAI functions and their Python library for data extraction&lt;/a&gt; - 2023-07-10&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/python/pyproject"&gt;Python packages with pyproject.toml and nothing else&lt;/a&gt; - 2023-07-08&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/datasette/syntax-highlighted-code-examples"&gt;Syntax highlighted code examples in Datasette&lt;/a&gt; - 2023-07-02&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/jinja/custom-jinja-tags-with-attributes"&gt;Custom Jinja template tags with attributes&lt;/a&gt; - 2023-07-02&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/macos/wildcard-dns-dnsmasq"&gt;Local wildcard DNS on macOS with dnsmasq&lt;/a&gt; - 2023-06-30&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/discord/discord-github-issues-bot"&gt;A Discord bot to expand issue links to a private GitHub repository&lt;/a&gt; - 2023-06-30&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/github/bulk-edit-github-projects"&gt;Bulk editing status in GitHub Projects&lt;/a&gt; - 2023-06-29&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/python/stdlib-cli-tools"&gt;CLI tools hidden in the Python standard library&lt;/a&gt; - 2023-06-29&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/symbex/releases/tag/1.1"&gt;symbex 1.1&lt;/a&gt;&lt;/strong&gt; - 2023-07-16&lt;br /&gt;Find the Python code for specified symbols&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm-mpt30b/releases/tag/0.1"&gt;llm-mpt30b 0.1&lt;/a&gt;&lt;/strong&gt; - 2023-07-12&lt;br /&gt;LLM plugin adding support for the MPT-30B language model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm-markov/releases/tag/0.1"&gt;llm-markov 0.1&lt;/a&gt;&lt;/strong&gt; - 2023-07-12&lt;br /&gt;Plugin for LLM adding a Markov chain generating model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm-gpt4all/releases/tag/0.1"&gt;llm-gpt4all 0.1&lt;/a&gt;&lt;/strong&gt; - 2023-07-12&lt;br /&gt;Plugin for LLM adding support for the GPT4All collection of models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm-palm/releases/tag/0.1"&gt;llm-palm 0.1&lt;/a&gt;&lt;/strong&gt; - 2023-07-12&lt;br /&gt;Plugin for LLM adding support for Google's PaLM 2 model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm/releases/tag/0.5"&gt;llm 0.5&lt;/a&gt;&lt;/strong&gt; - 2023-07-12&lt;br /&gt;Access large language models from the command-line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/ttok/releases/tag/0.2"&gt;ttok 0.2&lt;/a&gt;&lt;/strong&gt; - 2023-07-10&lt;br /&gt;Count and truncate text based on tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/strip-tags/releases/tag/0.5.1"&gt;strip-tags 0.5.1&lt;/a&gt;&lt;/strong&gt; - 2023-07-09&lt;br /&gt;CLI tool for stripping tags from HTML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/dogsheep/pocket-to-sqlite/releases/tag/0.2.3"&gt;pocket-to-sqlite 0.2.3&lt;/a&gt;&lt;/strong&gt; - 2023-07-09&lt;br /&gt;Create a SQLite database containing data from your Pocket account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-render-markdown/releases/tag/2.2"&gt;datasette-render-markdown 2.2&lt;/a&gt;&lt;/strong&gt; - 2023-07-02&lt;br /&gt;Datasette plugin for rendering Markdown&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/asgi-proxy-lib/releases/tag/0.1a0"&gt;asgi-proxy-lib 0.1a0&lt;/a&gt;&lt;/strong&gt; - 2023-07-01&lt;br /&gt;An ASGI function for proxying to a backend over HTTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-upload-csvs/releases/tag/0.8.3"&gt;datasette-upload-csvs 0.8.3&lt;/a&gt;&lt;/strong&gt; - 2023-06-28&lt;br /&gt;Datasette plugin for uploading CSV files and converting them to database tables&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/plugins"&gt;plugins&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/local-llms"&gt;local-llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/symbex"&gt;symbex&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="plugins"/><category term="projects"/><category term="tutorials"/><category term="ai"/><category term="datasette"/><category term="weeknotes"/><category term="sqlite-utils"/><category term="generative-ai"/><category term="local-llms"/><category term="llms"/><category term="symbex"/><category term="llm"/></entry><entry><title>Weeknotes: Parquet in Datasette Lite, various talks, more LLM hacking</title><link href="https://simonwillison.net/2023/Jun/4/parquet-in-datasette-lite/#atom-tag" rel="alternate"/><published>2023-06-04T21:14:27+00:00</published><updated>2023-06-04T21:14:27+00:00</updated><id>https://simonwillison.net/2023/Jun/4/parquet-in-datasette-lite/#atom-tag</id><summary type="html">
    &lt;p&gt;I've fallen a bit behind on my weeknotes. Here's a catchup for the last few weeks.&lt;/p&gt;
&lt;h4 id="parquet-datasette-lite"&gt;Parquet in Datasette Lite&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://lite.datasette.io/"&gt;Datasette Lite&lt;/a&gt; is my build of Datasette (a server-side Python web application) which runs entirely in the browser using WebAssembly and &lt;a href="https://pyodide.org/en/stable/"&gt;Pyodide&lt;/a&gt;. I recently added the ability to &lt;a href="https://github.com/simonw/datasette-lite/issues/67"&gt;directly load Parquet files over HTTP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This required an upgrade to the underlying version of Pyodide, in order to use the WebAssembly compiled version of the &lt;a href="https://pypi.org/project/fastparquet/"&gt;fastparquet&lt;/a&gt; library. That upgrade was blocked by a &lt;code&gt;AttributeError: module 'os' has no attribute 'link'&lt;/code&gt; error, but Roman Yurchak &lt;a href="https://github.com/pyodide/pyodide/issues/3880#issuecomment-1560130092"&gt;showed me a workaround&lt;/a&gt; which unblocked me.&lt;/p&gt;
&lt;p&gt;So now the following works:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://lite.datasette.io/?parquet=https://github.com/Teradata/kylo/blob/master/samples/sample-data/parquet/userdata1.parquet"&gt;https://lite.datasette.io/?parquet=https://github.com/Teradata/kylo/blob/master/samples/sample-data/parquet/userdata1.parquet&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This will work with any URL to a Parquet file that is served with open CORS headers - files on GitHub (or in a GitHub Gist) get these headers automatically.&lt;/p&gt;
&lt;p&gt;Also new in Datasette Lite: the &lt;code&gt;?memory=1&lt;/code&gt; query string option, which starts Datasette Lite without loading any default demo databases. I added this to help me construct this demo for my new &lt;a href="https://github.com/simonw/datasette-sqlite-url-lite"&gt;datasette-sqlite-url-lite&lt;/a&gt; plugin:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://lite.datasette.io/?memory=1&amp;amp;install=datasette-sqlite-url-lite#/_memory?sql=select+'url_valid()'+as+fn%2C+url_valid(%3Aurl)+as+result%0Aunion+all%0Aselect+'url_scheme()'%2C+url_scheme(%3Aurl)%0Aunion+all%0Aselect+'url_host()'%2C+url_host(%3Aurl)%0Aunion+all%0Aselect+'url_path()'%2C+url_path(%3Aurl)%0Aunion+all%0Aselect+'url_fragment()'%2C+url_fragment(%3Aurl)%3B&amp;amp;url=https%3A%2F%2Fwww.sqlite.org%2Fvtab.html%23usage"&gt;https://lite.datasette.io/?memory=1&amp;amp;install=datasette-sqlite-url-lite#/_memory?sql=select+'url_valid()'+as+fn%2C+url_valid(%3Aurl)+as+result%0Aunion+all%0Aselect+'url_scheme()'%2C+url_scheme(%3Aurl)%0Aunion+all%0Aselect+'url_host()'%2C+url_host(%3Aurl)%0Aunion+all%0Aselect+'url_path()'%2C+url_path(%3Aurl)%0Aunion+all%0Aselect+'url_fragment()'%2C+url_fragment(%3Aurl)%3B&amp;amp;url=https%3A%2F%2Fwww.sqlite.org%2Fvtab.html%23usage&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="datasette-sqlite-url-lite"&gt;datasette-sqlite-url-lite - mostly written by GPT-4&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/asg017/sqlite-url/tree/main/python/datasette_sqlite_url"&gt;datasette-sqlite-url&lt;/a&gt; is a really neat plugin by Alex Garcia which adds custom SQL functions to SQLite that allow you to parse URLs and extract their components.&lt;/p&gt;
&lt;p&gt;There's just one catch: the extension itself is written in C, and there isn't yet a version of it compiled for WebAssembly to work in Datasette Lite.&lt;/p&gt;
&lt;p&gt;I wanted to use some of the functions in it, so I decided to see if I could get a Pure Python alternative of it working. But this was a very low stakes project, so I decided to see if I could get GPT-4 to do essentially all of the work for me.&lt;/p&gt;
&lt;p&gt;I prompted it like this - copying and pasting the examples directly from Alex's documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write Python code to register the following SQLite custom functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select url_valid('https://sqlite.org'); -- 1
select url_scheme('https://www.sqlite.org/vtab.html#usage'); -- 'https'
select url_host('https://www.sqlite.org/vtab.html#usage'); -- 'www.sqlite.org'
select url_path('https://www.sqlite.org/vtab.html#usage'); -- '/vtab.html'
select url_fragment('https://www.sqlite.org/vtab.html#usage'); -- 'usage'
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The code it produced was almost exactly what I needed.&lt;/p&gt;
&lt;p&gt;I wanted some tests too, so I prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a suite of pytest tests for this&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This gave me the tests I needed - with one error in the way they called SQLite, but still doing 90% of the work for me.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://chat.openai.com/share/9a541ea9-eab7-4ea3-8b43-a521880dfd17"&gt;the full ChatGPT conversation&lt;/a&gt; and the &lt;a href="https://github.com/simonw/datasette-sqlite-url-lite/commit/14b2fefbf0b879d4c34e5961b70151564d31f7cc#diff-d741a233298e1ce8d45fc52005e9f9d7534c12b010e5d90a01da26979fff446e"&gt;resulting code I checked into the repo&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="various-talks"&gt;Various talks&lt;/h4&gt;
&lt;p&gt;Videos for three of my recent talks are now available on YouTube:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=rsE0XhlPnug"&gt;Big Opportunities in Small Data&lt;/a&gt; is the keynote I gave at Citus Con: An Event for Postgres 2023 - talking about Datasette, SQLite and some tricks I would love to see the  PostgreSQL community adopt from the explorations I've been doing around small data.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=zI43eaPc59Q"&gt;The Data Enthusiast's Toolkit&lt;/a&gt; is an hour long interview with Rizel Scarlett about both Datasette and my career to date. Frustratingly I had about 10 minutes of terrible microphone audio in the middle, but the conversation itself was really great.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=5TdIxxBPUSI"&gt;Data analysis with SQLite and Python&lt;/a&gt; is a video from PyCon of the full 2hr45m tutorial I gave there last month. The handout notes for that are &lt;a href="https://sqlite-tutorial-pycon-2023.readthedocs.io/en/latest/"&gt;available online too&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also spotted that the Changelog put up a video &lt;a href="https://www.youtube.com/watch?v=yayY-R4koPI"&gt;Just getting in to AI for development? Start here&lt;/a&gt; with an extract from our podcast episode &lt;a href="https://simonwillison.net/2023/Apr/8/llms-break-the-internet/"&gt;LLMs break the internet&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Entries this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2023/Jun/4/closed-model-training/"&gt;It's infuriatingly hard to understand how closed models train on their input&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2023/May/30/chatgpt-inline-tips/"&gt;ChatGPT should include inline tips&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2023/May/27/lawyer-chatgpt/"&gt;Lawyer cites fake cases invented by ChatGPT, judge is not amused&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2023/May/18/cli-tools-for-llms/"&gt;llm, ttok and strip-tags - CLI tools for working with ChatGPT and other LLMs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simonwillison.net/2023/May/11/delimiters-wont-save-you/"&gt;Delimiters won't save you from prompt injection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-sqlite-url-lite/releases/0.1"&gt;datasette-sqlite-url-lite 0.1&lt;/a&gt;&lt;/strong&gt; - 2023-05-26&lt;br /&gt;A pure Python alternative to sqlite-url ready to be used in Datasette Lite&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/sqlite-utils/releases/3.32.1"&gt;sqlite-utils 3.32.1&lt;/a&gt;&lt;/strong&gt; - 2023-05-21&lt;br /&gt;Python CLI utility and library for manipulating SQLite databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/strip-tags/releases/0.3"&gt;strip-tags 0.3&lt;/a&gt;&lt;/strong&gt; - 2023-05-19&lt;br /&gt;CLI tool for stripping tags from HTML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/ttok/releases/0.1"&gt;ttok 0.1&lt;/a&gt;&lt;/strong&gt; - 2023-05-18&lt;br /&gt;Count and truncate text based on tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/llm/releases/0.3"&gt;llm 0.3&lt;/a&gt;&lt;/strong&gt; - 2023-05-17&lt;br /&gt;Access large language models from the command-line&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/http/testing-cors-max-age"&gt;Testing the Access-Control-Max-Age CORS header&lt;/a&gt; - 2023-05-25&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/sqlite/comparing-datasets"&gt;Comparing two training datasets using sqlite-utils&lt;/a&gt; - 2023-05-23&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/llms/mlc-chat-redpajama"&gt;mlc-chat - RedPajama-INCITE-Chat-3B on macOS&lt;/a&gt; - 2023-05-22&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/misc/hexdump"&gt;hexdump and hexdump -C&lt;/a&gt; - 2023-05-22&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://til.simonwillison.net/datasette/baseline"&gt;Exploring Baseline with Datasette Lite&lt;/a&gt; - 2023-05-12&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/speaking"&gt;speaking&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/parquet"&gt;parquet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-lite"&gt;datasette-lite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="speaking"/><category term="tutorials"/><category term="datasette"/><category term="parquet"/><category term="weeknotes"/><category term="datasette-lite"/><category term="llms"/></entry><entry><title>Exploration de données avec Datasette</title><link href="https://simonwillison.net/2023/May/27/exploration-de-donnees-avec-datasette/#atom-tag" rel="alternate"/><published>2023-05-27T00:36:52+00:00</published><updated>2023-05-27T00:36:52+00:00</updated><id>https://simonwillison.net/2023/May/27/exploration-de-donnees-avec-datasette/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://meetup-python-grenoble.github.io/datasette-workshop/"&gt;Exploration de données avec Datasette&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
One of the great delights of open source development is seeing people run workshops on your project, even more so when they’re in a language other than English! Romain Clement presented this French workshop for the Python Grenoble meetup on 25th May 2023, using GitHub Codespaces as the environment. It’s pretty comprehensive, including a 300,000+ row example table which illustrates Datasette plugins such as datasette-cluster-map and datasette-leaflet-geojson.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github-codespaces"&gt;github-codespaces&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/leaflet"&gt;leaflet&lt;/a&gt;&lt;/p&gt;



</summary><category term="tutorials"/><category term="datasette"/><category term="github-codespaces"/><category term="leaflet"/></entry><entry><title>Using Datasette in GitHub Codespaces</title><link href="https://simonwillison.net/2023/Feb/24/using-datasette-in-github-codespaces/#atom-tag" rel="alternate"/><published>2023-02-24T00:40:11+00:00</published><updated>2023-02-24T00:40:11+00:00</updated><id>https://simonwillison.net/2023/Feb/24/using-datasette-in-github-codespaces/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://datasette.io/tutorials/codespaces"&gt;Using Datasette in GitHub Codespaces&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A new Datasette tutorial showing how it can be run inside GitHub Codespaces—GitHub’s browser-based development environments—in order to explore and analyze data. I’ve been using Codespaces to run tutorials recently and it’s absolutely fantastic, because it puts every tutorial attendee on a level playing field with respect to their development environments.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github-codespaces"&gt;github-codespaces&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="tutorials"/><category term="datasette"/><category term="github-codespaces"/></entry><entry><title>Cleaning data with sqlite-utils and Datasette</title><link href="https://simonwillison.net/2022/Jul/31/cleaning-data/#atom-tag" rel="alternate"/><published>2022-07-31T19:57:51+00:00</published><updated>2022-07-31T19:57:51+00:00</updated><id>https://simonwillison.net/2022/Jul/31/cleaning-data/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://datasette.io/tutorials/clean-data"&gt;Cleaning data with sqlite-utils and Datasette&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I wrote a new tutorial for the Datasette website, showing how to use sqlite-utils to import a CSV file, clean up the resulting schema, fix date formats and extract some of the columns into a separate table. It’s accompanied by a ten minute video originally recorded for the HYTRADBOI conference.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/simonw/status/1553821212257632256"&gt;@simonw&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/documentation"&gt;documentation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite-utils"&gt;sqlite-utils&lt;/a&gt;&lt;/p&gt;



</summary><category term="documentation"/><category term="tutorials"/><category term="datasette"/><category term="sqlite-utils"/></entry><entry><title>JMeter Result Analysis using Datasette</title><link href="https://simonwillison.net/2021/Feb/1/jmeter-result-analysis-using-datasette/#atom-tag" rel="alternate"/><published>2021-02-01T04:42:19+00:00</published><updated>2021-02-01T04:42:19+00:00</updated><id>https://simonwillison.net/2021/Feb/1/jmeter-result-analysis-using-datasette/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://qainsights.com/jmeter-result-analysis-using-datasette/"&gt;JMeter Result Analysis using Datasette&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
NaveenKumar Namachivayam wrote a detailed tutorial on using Datasette (on Windows) and csvs-to-sqlite to analyze the results of JMeter performance test runs and then publish them online using Vercel.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/QAInsights/status/1356090472272457732"&gt;@QAInsights&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;



</summary><category term="tutorials"/><category term="datasette"/></entry><entry><title>Making Datasets Fly with Datasette and Fly</title><link href="https://simonwillison.net/2020/Mar/26/tutorial/#atom-tag" rel="alternate"/><published>2020-03-26T23:56:46+00:00</published><updated>2020-03-26T23:56:46+00:00</updated><id>https://simonwillison.net/2020/Mar/26/tutorial/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://fly.io/blog/making-datasets-fly-with-datasette-and-fly/"&gt;Making Datasets Fly with Datasette and Fly&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
It’s always exciting to see a Datasette tutorial that wasn’t written by me! This one is great—it shows how to load Central Park Squirrel Census data into a SQLite database, explore it with Datasette and then publish it to the Fly hosting platform using datasette-publish-fly and datasette-cluster-map.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://fly.io/blog/making-datasets-fly-with-datasette-and-fly/"&gt;@flydotio&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fly"&gt;fly&lt;/a&gt;&lt;/p&gt;



</summary><category term="tutorials"/><category term="datasette"/><category term="fly"/></entry><entry><title>What are the best free tutorials on HTML5?</title><link href="https://simonwillison.net/2014/Jan/24/what-are-the-best/#atom-tag" rel="alternate"/><published>2014-01-24T13:16:00+00:00</published><updated>2014-01-24T13:16:00+00:00</updated><id>https://simonwillison.net/2014/Jan/24/what-are-the-best/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-best-free-tutorials-on-HTML5/answer/Simon-Willison"&gt;What are the best free tutorials on HTML5?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;span&gt;&lt;a href="http://diveintohtml5.info/"&gt;Dive Into HTML5&lt;/a&gt;&lt;/span&gt; and &lt;span&gt;&lt;a href="http://www.html5rocks.com/en/"&gt;HTML5 Rocks&lt;/a&gt;&lt;/span&gt; are both outstanding.
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/html5"&gt;html5&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="html5"/><category term="tutorials"/><category term="web-development"/><category term="quora"/></entry><entry><title>What are the best books/tutorials to begin learning about memcached?</title><link href="https://simonwillison.net/2013/Apr/8/what-are-the-best/#atom-tag" rel="alternate"/><published>2013-04-08T14:51:00+00:00</published><updated>2013-04-08T14:51:00+00:00</updated><id>https://simonwillison.net/2013/Apr/8/what-are-the-best/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-best-books-tutorials-to-begin-learning-about-memcached/answer/Simon-Willison"&gt;What are the best books/tutorials to begin learning about memcached?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There isn't really enough of memcached to justify a whole book - it's a pretty straight-forward API.&lt;/p&gt;

&lt;p&gt;It's always interesting hearing about advanced usage patterns for it though. Again, these don't necessarily justify a book but they are frequently presented at conferences.&lt;/p&gt;

&lt;p&gt;Here's one video that may be relevant: &lt;span&gt;&lt;a href="http://lanyrd.com/2012/goruco/swfqp/"&gt;High Performance Caching with Rails - a session at GoRuCo 2012 by Matt Duncan&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Here's our full collection of 36 slides and video from talks about memcached: &lt;span&gt;&lt;a href="http://lanyrd.com/topics/memcached/coverage/"&gt;Conference coverage about Memcached on Lanyrd&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/books"&gt;books&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="books"/><category term="memcached"/><category term="programming"/><category term="tutorials"/><category term="web-development"/><category term="quora"/></entry><entry><title>maps from scratch</title><link href="https://simonwillison.net/2009/Mar/15/maps/#atom-tag" rel="alternate"/><published>2009-03-15T13:20:23+00:00</published><updated>2009-03-15T13:20:23+00:00</updated><id>https://simonwillison.net/2009/Mar/15/maps/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://mike.teczno.com/notes/maps-from-scratch.html"&gt;maps from scratch&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
An idea whose time has come: using EC2 AMIs for tutorial sessions to give everyone a pre-configured environment.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cloud-computing"&gt;cloud-computing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ec2"&gt;ec2&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mapping"&gt;mapping&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/michal-migurski"&gt;michal-migurski&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;&lt;/p&gt;



</summary><category term="cloud-computing"/><category term="ec2"/><category term="mapping"/><category term="michal-migurski"/><category term="tutorials"/></entry><entry><title>Django snippets: Orderable inlines using drag and drop with jQuery UI</title><link href="https://simonwillison.net/2008/Sep/13/django/#atom-tag" rel="alternate"/><published>2008-09-13T12:19:02+00:00</published><updated>2008-09-13T12:19:02+00:00</updated><id>https://simonwillison.net/2008/Sep/13/django/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/1053/"&gt;Django snippets: Orderable inlines using drag and drop with jQuery UI&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Code example from my PyCon tutorial on customising the Django admin interface.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-admin"&gt;django-admin&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dragndrop"&gt;dragndrop&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jquery"&gt;jquery&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pyconuk"&gt;pyconuk&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pyconuk2008"&gt;pyconuk2008&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/snippets"&gt;snippets&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sortable"&gt;sortable&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/speaking"&gt;speaking&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/my-talks"&gt;my-talks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;&lt;/p&gt;



</summary><category term="django"/><category term="django-admin"/><category term="dragndrop"/><category term="jquery"/><category term="pyconuk"/><category term="pyconuk2008"/><category term="python"/><category term="snippets"/><category term="sortable"/><category term="speaking"/><category term="my-talks"/><category term="tutorials"/></entry><entry><title>Learning jQuery</title><link href="https://simonwillison.net/2007/Aug/17/learning/#atom-tag" rel="alternate"/><published>2007-08-17T11:50:45+00:00</published><updated>2007-08-17T11:50:45+00:00</updated><id>https://simonwillison.net/2007/Aug/17/learning/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.learningjquery.com/"&gt;Learning jQuery&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
An entire year’s worth of jQuery tutorials, split in to beginner, intermediate and advanced.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jquery"&gt;jquery&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/learningjquery"&gt;learningjquery&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tutorials"&gt;tutorials&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="jquery"/><category term="learningjquery"/><category term="tutorials"/></entry></feed>