
    
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
            
{"version":"https:\/\/jsonfeed.org\/version\/1","title":"mathspp.com feed","home_page_url":"https:\/\/mathspp.com\/blog\/tags\/uv","feed_url":"https:\/\/mathspp.com\/blog\/tags\/uv.json","description":"Stay up-to-date with the articles on mathematics and programming that get published to mathspp.com.","author":{"name":"Rodrigo Gir\u00e3o Serr\u00e3o"},"items":[{"title":"TIL #140 \u2013 Install Jupyter with uv","date_published":"2026-03-03T16:16:00+01:00","id":"https:\/\/mathspp.com\/blog\/til\/install-jupyter-with-uv","url":"https:\/\/mathspp.com\/blog\/til\/install-jupyter-with-uv","content_html":"<p>Today I learned how to install jupyter properly while using uv to manage tools.<\/p>\n\n<h2 id=\"running-a-jupyter-notebook-server-or-jupyter-lab\">Running a Jupyter notebook server or Jupyter lab<a href=\"#running-a-jupyter-notebook-server-or-jupyter-lab\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>To run a Jupyter notebook server with uv, you can run the command<\/p>\n<pre><code class=\"language-bash\">$ uvx jupyter notebook<\/code><\/pre>\n<p>Similarly, if you want to run Jupyter lab, you can run<\/p>\n<pre><code class=\"language-bash\">$ uvx jupyter lab<\/code><\/pre>\n<p>Both work, but uv will kindly present a message explaining how it's actually doing you a favour, because it <em>guessed<\/em> what you wanted.\nThat's because <code>uvx something<\/code> usually looks for a package named \u201csomething\u201d with a command called \u201csomething\u201d.<\/p>\n<p>As it turns out, the command <code>jupyter<\/code> comes from the package <code>jupyter-core<\/code>, not from the package <code>jupyter<\/code>.<\/p>\n<h2 id=\"installing-jupyter\">Installing Jupyter<a href=\"#installing-jupyter\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>If you're running Jupyter notebooks often, you can install the notebook server and Jupyter lab with<\/p>\n<pre><code class=\"language-bash\">$ uv tool install --with jupyter jupyter-core<\/code><\/pre>\n<h3 id=\"why-uv-tool-install-jupyter-fails\">Why <code>uv tool install jupyter<\/code> fails<a href=\"#why-uv-tool-install-jupyter-fails\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>Running <code>uv tool install jupyter<\/code> fails because the package <code>jupyter<\/code> doesn't provide any commands by itself.<\/p>\n<h3 id=\"why-uv-tool-install-jupyter-core-doesn-t-work\">Why <code>uv tool install jupyter-core<\/code> doesn't work<a href=\"#why-uv-tool-install-jupyter-core-doesn-t-work\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>The command <code>uv tool install jupyter-core<\/code> looks like it works because it installs the command <code>jupyter<\/code> correctly.\nHowever, if you use <code>--help<\/code> you can see that you don't have access to the subcommands you need:<\/p>\n<pre><code class=\"language-bash\">$ uv tool install jupyter-core\n...\nInstalled 3 executables: jupyter, jupyter-migrate, jupyter-troubleshoot\n$ jupyter --help\n...\nAvailable subcommands: book migrate troubleshoot<\/code><\/pre>\n<p>That's because the subcommands <code>notebook<\/code> and <code>lab<\/code> are from the package <code>jupyter<\/code>.\nThe solution?\nInstall <code>jupyter-core<\/code> <em>with<\/em> the additional dependency <code>jupyter<\/code>, which is what the command <code>uv tool install --with jupyter jupyter-core<\/code> does.<\/p>\n<h2 id=\"other-usages-of-jupyter\">Other usages of Jupyter<a href=\"#other-usages-of-jupyter\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The uv documentation has a <a href=\"https:\/\/docs.astral.sh\/uv\/guides\/integration\/jupyter\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">page dedicated exclusively to the usage of uv with Jupyter<\/a>, so check it out for other use cases of the uv and Jupyter combo!<\/p>","summary":"Today I learned how to install jupyter properly while using uv to manage tools.","date_modified":"2026-03-03T18:05:58+01:00","tags":["python","programming","uv","productivity"],"image":"\/user\/pages\/02.blog\/04.til\/140.install-jupyter-with-uv\/thumbnail.webp"},{"title":"uv cheatsheet","date_published":"2025-09-06T19:36:00+02:00","id":"https:\/\/mathspp.com\/blog\/uv-cheatsheet","url":"https:\/\/mathspp.com\/blog\/uv-cheatsheet","content_html":"<p>Cheatsheet with the most common and useful uv commands to manage projects and dependencies, publish projects, manage tools, and more.<\/p>\n\n<p>This cheatsheet lists the most commonly used commands and should be more than enough for you to get started using uv.\nFor more advanced use cases, check <a href=\"https:\/\/docs.astral.sh\/uv\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">the uv docs<\/a> and its guides.<\/p>\n<div class=\"notices blue\">\n<p>Last updated for uv version 0.10.9.<\/p>\n<\/div>\n<p><a href=\"https:\/\/gumroad.com\/l\/cheatsheet-uv\" class=\"btn btn-lg btn-center external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Download this cheatsheet<\/a><\/p>\n<h2 id=\"creating-projects\">Creating projects &#129521;<a href=\"#creating-projects\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<table><thead><tr><th><\/th>\n<th><\/th>\n<\/tr><\/thead><tbody><tr><td><code>uv init<\/code><\/td>\n<td>Initialise a project in the current directory<\/td>\n<\/tr><tr><td><code>uv init myproj<\/code><\/td>\n<td>Initialise a project <code>myproj<\/code> in the directory <code>myproj<\/code>\n<\/td>\n<\/tr><tr><td><code>uv init --app --package ...<\/code><\/td>\n<td>Initialise a packageable app (e.g., CLI, web app, ...)<\/td>\n<\/tr><tr><td><code>uv init --lib --package ...<\/code><\/td>\n<td>Initialise a packageable library (code you import)<\/td>\n<\/tr><tr><td>\n<code>uv init --python 3.X ...<\/code><sup id=\"fnref1:1\"><a href=\"#fn:1\" class=\"footnote-ref\">1<\/a><\/sup><\/td>\n<td>Use Python 3.X for your project<\/td>\n<\/tr><\/tbody><\/table><h2 id=\"managing-project-dependencies\">Managing project dependencies &#129513;<a href=\"#managing-project-dependencies\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<table><thead><tr><th><\/th>\n<th><\/th>\n<\/tr><\/thead><tbody><tr><td><code>uv add requests<\/code><\/td>\n<td>Add <code>requests<\/code> as a dependency<\/td>\n<\/tr><tr><td><code>uv add A B C<\/code><\/td>\n<td>Add <code>A<\/code>, <code>B<\/code>, and <code>C<\/code> as dependencies<\/td>\n<\/tr><tr><td><code>uv add -r requirements.txt<\/code><\/td>\n<td>Add dependencies from the file <code>requirements.txt<\/code>\n<\/td>\n<\/tr><tr><td><code>uv add --dev pytest<\/code><\/td>\n<td>Add <code>pytest<\/code> as a development dependency<\/td>\n<\/tr><tr><td><code>uv run pytest<\/code><\/td>\n<td>Run the pytest executable that is installed in your project<\/td>\n<\/tr><tr><td><code>uv remove requests<\/code><\/td>\n<td>Remove <code>requests<\/code> as a dependency<\/td>\n<\/tr><tr><td><code>uv remove A B C<\/code><\/td>\n<td>Remove <code>A<\/code>, <code>B<\/code>, <code>C<\/code>, and their transitive dependencies<\/td>\n<\/tr><tr><td><code>uv tree<\/code><\/td>\n<td>See the project dependencies tree<\/td>\n<\/tr><tr><td><code>uv lock --upgrade<\/code><\/td>\n<td>Upgrade the dependencies' versions<\/td>\n<\/tr><\/tbody><\/table><h2 id=\"project-lifecycle-management\">Project lifecycle management &#128260;<a href=\"#project-lifecycle-management\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<table><thead><tr><th><\/th>\n<th><\/th>\n<\/tr><\/thead><tbody><tr><td><code>uv build<\/code><\/td>\n<td>Build your packageable project<\/td>\n<\/tr><tr><td><code>uv publish<\/code><\/td>\n<td>Publish your packageable project to PyPI<\/td>\n<\/tr><tr><td><code>uv version<\/code><\/td>\n<td>Check your project version<\/td>\n<\/tr><tr><td><code>uv version --bump major<\/code><\/td>\n<td>Bump project major version (e.g., <code>0.3.2 -&gt; 1.0.0<\/code>)<\/td>\n<\/tr><tr><td><code>uv version --bump minor --bump beta<\/code><\/td>\n<td>Bump minor version into a beta (e.g., <code>1.0.0 -&gt; 1.1.0b1<\/code> or <code>1.1.0b1 -&gt; 1.1.0b2<\/code>)<\/td>\n<\/tr><tr><td><code>uv version --bump rc<\/code><\/td>\n<td>Bump version into release candidate (e.g., <code>1.1.0b1 -&gt; 1.1.0rc1<\/code> or <code>1.1.0rc1 -&gt; 1.1.0rc2<\/code>)<\/td>\n<\/tr><tr><td><code>uv version --bump stable<\/code><\/td>\n<td>Turn into a stable version (e.g., <code>1.1.0rc1 -&gt; 1.1.0<\/code>)<\/td>\n<\/tr><\/tbody><\/table><h2 id=\"managing-tools\">Managing tools &#9874;&#65039;<a href=\"#managing-tools\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<table><thead><tr><th><\/th>\n<th><\/th>\n<\/tr><\/thead><tbody><tr><td><code>uv tool run pytest<\/code><\/td>\n<td>Run <code>pytest<\/code> in an isolated environment<\/td>\n<\/tr><tr><td><code>uv tool run textual-demo --from textual<\/code><\/td>\n<td>Run the command <code>textual-demo<\/code> from the package <code>textual<\/code>\n<\/td>\n<\/tr><tr><td><code>uvx ...<\/code><\/td>\n<td>Alias for <code>uv tool run ...<\/code>\n<\/td>\n<\/tr><tr><td><code>uv tool install ruff<\/code><\/td>\n<td>Install <code>ruff<\/code> in an isolated environment but make it globally available<\/td>\n<\/tr><tr><td><code>uv tool install --with dep ...<\/code><\/td>\n<td>Install the given tool with extra dependencies (e.g., install a tool <em>with<\/em> its plugins)<\/td>\n<\/tr><tr><td><code>uv tool list<\/code><\/td>\n<td>List all tools installed<\/td>\n<\/tr><tr><td><code>uv tool upgrade ruff<\/code><\/td>\n<td>Upgrade the <code>ruff<\/code> tool<\/td>\n<\/tr><tr><td><code>uv tool upgrade --all<\/code><\/td>\n<td>Upgrade all tools<\/td>\n<\/tr><tr><td><code>uv tool uninstall ruff<\/code><\/td>\n<td>Uninstall <code>ruff<\/code>\n<\/td>\n<\/tr><tr><td>\n<code>uv tool install -e .<\/code><sup id=\"fnref1:2\"><a href=\"#fn:2\" class=\"footnote-ref\">2<\/a><\/sup><\/td>\n<td>Install the current packageable project in editable mode<\/td>\n<\/tr><\/tbody><\/table><h2 id=\"working-with-scripts\">Working with scripts &#128220;<a href=\"#working-with-scripts\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<table><thead><tr><th><\/th>\n<th><\/th>\n<\/tr><\/thead><tbody><tr><td><code>uv init --script myscript.py<\/code><\/td>\n<td>Initialise the script <code>myscript.py<\/code>\n<\/td>\n<\/tr><tr><td><code>uv init --script myscript.py --python 3.X<\/code><\/td>\n<td>Initialise the script <code>myscript.py<\/code> and pin it to version 3.X<\/td>\n<\/tr><tr><td><code>uv add click --script myscript.py<\/code><\/td>\n<td>Add the dependency <code>click<\/code> to the script<\/td>\n<\/tr><tr><td><code>uv remove click --script myscript.py<\/code><\/td>\n<td>Remove the dependency <code>click<\/code> from the script<\/td>\n<\/tr><tr><td><code>uv run myscript.py<\/code><\/td>\n<td>Run the script <code>myscript.py<\/code>\n<\/td>\n<\/tr><tr><td><code>uv run --python 3.X myscript.py<\/code><\/td>\n<td>Run the script with the given Python version<\/td>\n<\/tr><tr><td><code>uv run --with click myscript.py<\/code><\/td>\n<td>Run...<\/td><\/tr><\/tbody><\/table>","summary":"Cheatsheet with the most common and useful uv commands to manage projects and dependencies, publish projects, manage tools, and more.","date_modified":"2026-03-09T20:31:40+01:00","tags":["programming","python","uv"],"image":"\/user\/pages\/02.blog\/uv-cheatsheet\/thumbnail.webp"},{"title":"TIL #130 \u2013 Format Python code directly with uv","date_published":"2025-08-22T18:34:00+02:00","id":"https:\/\/mathspp.com\/blog\/til\/format-python-code-directly-with-uv","url":"https:\/\/mathspp.com\/blog\/til\/format-python-code-directly-with-uv","content_html":"<p>Today I learned you can format your Python code directly with uv.<\/p>\n\n<p>In uv version 0.8.13, released one or two days ago, uv added the command <code>format<\/code> that allows you to format your Python code directly through the uv CLI.<\/p>\n<h2 id=\"update-your-uv\">Update your uv<a href=\"#update-your-uv\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>First and foremost, make sure you're rocking uv 0.8.13 or greater by running <code>uv self update<\/code>.<\/p>\n<h2 id=\"format-your-code-with-uv\">Format your code with uv<a href=\"#format-your-code-with-uv\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>To format your code with uv you can simply run <code>uv format<\/code>, which will use Ruff to format the code in your current directory:<\/p>\n<pre><code class=\"language-sh\">$ uv format<\/code><\/pre>\n<p>The idea is not to have uv replace Ruff; it's just so that you don't have to think about a separate tool if you don't want to.<\/p>\n<h2 id=\"uv-format-arguments\"><code>uv format<\/code> arguments<a href=\"#uv-format-arguments\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p><code>uv format<\/code> accepts the same arguments and options that <code>ruff format<\/code> accepts, so you'll want to <a href=\"https:\/\/docs.astral.sh\/ruff\/formatter\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">check the Ruff docs<\/a> to learn more.\nMy favourite option is <code>--diff<\/code>, to take a look at the formatting diff without doing any formatting changes.<\/p>\n<p>As of now, the feature is marked as being experimental, which means it might change in the future!<\/p>","summary":"Today I learned you can format your Python code directly with uv.","date_modified":"2025-10-20T22:34:56+02:00","tags":["python","programming","productivity","uv"],"image":"\/user\/pages\/02.blog\/04.til\/130.format-python-code-directly-with-uv\/thumbnail.webp"},{"title":"Automatically pushing code changes during live coding with uv","date_published":"2025-05-14T10:25:00+02:00","id":"https:\/\/mathspp.com\/blog\/automatically-pushing-code-changes-during-live-coding-with-uv","url":"https:\/\/mathspp.com\/blog\/automatically-pushing-code-changes-during-live-coding-with-uv","content_html":"<p>This article shows the small script I use to automatically push code changes while live coding in talks or classes, improved with uv.<\/p>\n\n<p>One year ago I <a href=\"\/blog\/til\/automatically-push-code-changes-during-live-coding\">wrote a short TIL article where I showed how I automatically push code changes while I'm live-coding<\/a>.\nThe code I shared looked like this:<\/p>\n<pre><code class=\"language-py\">from pathlib import Path\nfrom time import sleep\n\nfrom git import Repo\n\nrepo = Repo(Path(__file__).parent)\n\nwhile True:\n    repo.index.add(\"*\")\n    repo.index.commit(\"Auto sync commit\")\n    repo.remote().push()\n    sleep(60)<\/code><\/pre>\n<p>This code uses <code>GitPython<\/code> to add all files in the folder that contains this script, adds a generic commit message, and pushes.\nThe script does this every minute, so when I'm teaching or doing any sort of live-coding, the repo I'm working off of gets updated every minute and participants can keep tabs on what I'm writing.<\/p>\n<p>Having used this for a while, there are two disadvantages to it:<\/p>\n<ol>\n<li>I need to install <code>GitPython<\/code> in a virtual environment in each repo I want to use this on; and<\/li>\n<li>the script itself gets pushed to the repo, polluting it a bit.<\/li>\n<\/ol>\n<p>(As I write this, I realised, I could fix 2. by adding the script to a file <code>.gitignore<\/code>!)<\/p>\n<p>To fix 1. and 2., and to improve my user experience a bit, I <a href=\"\/blog\/til\/standalone-executable-python-scripts-with-uv\">started using uv to inline the dependency on <code>GitPython<\/code> and to turn it into a standalone executable<\/a>.<\/p>\n<p>Assuming the script was called <code>gitsync.py<\/code>, by doing <code>uv add GitPython --script gitsync.py<\/code> and by <a href=\"\/link-blog\/simonwillison-net-2024-aug-21-usrbinenv-uv-run\">adding the uv shebang<\/a>, the top of the script now looks like this:<\/p>\n<pre><code class=\"language-py\">#!\/usr\/bin\/env -S uv run\n\n# \/\/\/ script\n# requires-python = \"&gt;=3.13\"\n# dependencies = [\n#     \"gitpython\",\n# ]\n# \/\/\/<\/code><\/pre>\n<p>I also tweaked the script structure a bit and now I use <code>os.getcwd()<\/code> to figure out the current working directory when I run <code>gitsync.py<\/code>:<\/p>\n<pre><code class=\"language-py\">#!\/usr\/bin\/env -S uv run\n\n# \/\/\/ script\n# requires-python = \"&gt;=3.13\"\n# dependencies = [\n#     \"gitpython\",\n# ]\n# \/\/\/\n\nimport os\nfrom time import sleep\n\nfrom git import Repo\n\ndef main() -&gt; None:\n    repo_folder = os.getcwd()\n    print(f\"gitsync.py starting at {repo_folder}\")\n    repo = Repo(repo_folder)\n\n    while True:\n        repo.index.add(\"*\")\n        repo.index.commit(\"Auto sync commit\")\n        repo.remote().push()\n        sleep(60)\n\nif __name__ == \"__main__\":\n    main()<\/code><\/pre>\n<p>This makes it so that I can put <code>gitsync.py<\/code> in a directory that's in my PATH, and then use it from anywhere.\nNow, when I'm teaching, I just run <code>gitsync.py &amp;<\/code> and that starts syncing my code in the background.\nPretty cool!<\/p>","summary":"This article shows the small script I use to automatically push code changes while live coding in talks or classes, improved with uv.","date_modified":"2025-07-23T16:49:02+02:00","tags":["git","productivity","programming","python","uv"],"image":"\/user\/pages\/02.blog\/automatically-pushing-code-changes-during-live-coding-with-uv\/thumbnail.webp"},{"title":"TIL #122 \u2013 CLI tools with extra dependencies","date_published":"2025-05-04T09:34:00+02:00","id":"https:\/\/mathspp.com\/blog\/til\/cli-tools-with-extra-dependencies","url":"https:\/\/mathspp.com\/blog\/til\/cli-tools-with-extra-dependencies","content_html":"<p>Today I learned you can use uv to install CLI tools with extra dependencies.<\/p>\n\n<h2 id=\"cli-tools-with-extra-dependencies\">CLI tools with extra dependencies<a href=\"#cli-tools-with-extra-dependencies\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>You can use <code>uv<\/code> to install CLI tools in isolated virtual environments.\nE.g., <code>uv tool install black<\/code> installs <code>black<\/code> in an isolated venv.\nAfter installing, <code>black<\/code> will be added to your path and you can use it directly.<\/p>\n<p>When installing tools, you can use the option <code>--with<\/code> to specify extra dependencies that will be available when you use the CLI tool you are installing.\nHere are two examples:<\/p>\n<ul>\n<li><code>uv tool install --with pandas --with polars marimo<\/code> \u2013 this installs Marimo and makes sure Pandas and Polars will be available inside Marimo notebooks.<\/li>\n<li><code>uv tool install --with \"ruamel.yaml\" cogapp<\/code> \u2013 this installs cog, a file generation tool, but makes sure there is a YAML parser available to be used by cog (I use this a lot to build my books).<\/li>\n<\/ul>","summary":"Today I learned you can use uv to install CLI tools with extra dependencies.","date_modified":"2025-10-20T22:34:56+02:00","tags":["productivity","python","uv"],"image":"\/user\/pages\/02.blog\/04.til\/122.cli-tools-with-extra-dependencies\/thumbnail.webp"},{"title":"TIL #121 \u2013 uv escape hatch","date_published":"2025-03-12T15:16:00+01:00","id":"https:\/\/mathspp.com\/blog\/til\/use-pip-directly-from-a-uv-virtual-environment","url":"https:\/\/mathspp.com\/blog\/til\/use-pip-directly-from-a-uv-virtual-environment","content_html":"<p>Today I learned how to use uv to escape uv and go back to using venv and pip for a given project.<\/p>\n\n<h2 id=\"use-pip-directly-from-a-uv-virtual-environment\">Use pip directly from a uv virtual environment<a href=\"#use-pip-directly-from-a-uv-virtual-environment\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>uv comes with the subcommands <code>uv venv<\/code> and <code>uv pip<\/code> that let you use your <code>venv<\/code> + <code>pip<\/code> workflows, with the commands you already know, while benefiting from the speed of uv.\nHowever, I have a specific project in which I need to be able to use <code>pip<\/code> directly from the virtual environment that uv created.<\/p>\n<p>After I ran <code>uv venv<\/code> and activated my virtual environment, I tried using <code>pip<\/code> to install a package with <code>python -m pip install my_package<\/code> but got an error message saying \u201c.venv\/bin\/python: No module named pip\u201d.<\/p>\n<p>To fix this, I used the option <code>--seed<\/code> that seeds my virtual environment with <code>pip<\/code>.\nSo, I recreated my virtual environment with <code>uv venv --seed<\/code>, activated it, and then I was able to use <code>pip<\/code> directly from within the virtual environment.<\/p>\n<p>This kind of goes against the point of uv, but the fact that there is an escape hatch for random situations like mine just goes to show that uv is well thought out...<\/p>","summary":"Today I learned how to use uv to escape uv and go back to venv and pip for a given project.","date_modified":"2025-10-20T22:34:56+02:00","tags":["productivity","python","uv"],"image":"\/user\/pages\/02.blog\/04.til\/121.use-pip-directly-from-a-uv-virtual-environment\/thumbnail.webp"},{"title":"TIL #117 \u2013 Publishing a Python package with uv","date_published":"2025-02-26T18:42:00+01:00","id":"https:\/\/mathspp.com\/blog\/til\/publishing-a-python-package-with-uv","url":"https:\/\/mathspp.com\/blog\/til\/publishing-a-python-package-with-uv","content_html":"<p>Today I learned how to publish a Python package to PyPI with uv.<\/p>\n\n<h2 id=\"publishing-a-python-package-with-uv\">Publishing a Python package with uv<a href=\"#publishing-a-python-package-with-uv\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Publishing a package to PyPI with uv was quite simple.\nAll I had to do was read the documentation with the steps provided and I was done in less than 2 minutes.<\/p>\n<p>The package I'm publishing is <a href=\"\/blog\/beating-linkedin-queens-with-python\">my CLI that solves the LinkedIn Queens puzzle<\/a>, about which I wrote in a different article.<\/p>\n<p>The first thing I did was make sure I could build my package with<\/p>\n<pre><code class=\"language-sh\">uv build<\/code><\/pre>\n<p>The next step is to run <code>uv publish<\/code>.\nTo publish a package to PyPI I needed a token for authentication.\nTo get that, I opened my account settings, scrolled to \u201cAPI tokens\u201d, and created a new token that was scoped to \u201call projects\u201d\u00a0because the project I want to upload\/publish hasn't been created yet.<\/p>\n<p>After I created that token, I ran<\/p>\n<pre><code class=\"language-sh\">uv publish --token MY_TOKEN_HERE<\/code><\/pre>\n<p>That took a second to run and then <a href=\"https:\/\/pypi.org\/project\/li_queens\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">my project <code>li_queens<\/code> was published on PyPI<\/a>.<\/p>\n<p>Now that the project exists, I went to my PyPI account settings, deleted the generic API token I created just now, and then I created a token that's scoped specifically for the project <code>li_queens<\/code>.<\/p>\n<p>Now that the package is published, you can use <code>uv<\/code> to run my CLI <code>queens<\/code> without even having to install the package explicitly.\nIf <code>puzzle.png<\/code> is an image with a LinkedIn Queens-like puzzle, just run<\/p>\n<pre><code class=\"language-sh\">uvx --from li_queens queens puzzle.png<\/code><\/pre>\n<div class=\"notices yellow\">\n<p>This was done with uv 0.6.1.\nMake sure to use an updated version of uv and check the <a href=\"https:\/\/docs.astral.sh\/uv\/guides\/package\/#building-your-package\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">uv documentation on publishing packages<\/a> for the most current instructions and notes on caveats to be aware of.<\/p>\n<\/div>","summary":"Today I learned how to publish a Python package to PyPI with uv.","date_modified":"2025-10-20T22:34:56+02:00","tags":["productivity","programming","python","uv"],"image":"\/user\/pages\/02.blog\/04.til\/117.publishing-a-python-package-with-uv\/thumbnail.webp"},{"title":"Using uv to build and install Python CLI apps","date_published":"2025-02-11T16:27:00+01:00","id":"https:\/\/mathspp.com\/blog\/using-uv-to-build-and-install-python-cli-apps","url":"https:\/\/mathspp.com\/blog\/using-uv-to-build-and-install-python-cli-apps","content_html":"<p>This practical tutorial shows how to use uv to build and install custom Python CLI applications globally on your system.<\/p>\n\n<p>I find myself writing Python scripts that automate certain parts of my work or life and then I want to turn them into commands in my system.\nThis short tutorial will show you how I do that using uv.<\/p>\n<p>You can build and install a command (that's a Python CLI app) globally in your system in 5 easy steps:<\/p>\n<ol><li>Install uv <a href=\"https:\/\/docs.astral.sh\/uv\/getting-started\/installation\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">following the installation instructions from the uv documentation<\/a>.<\/li>\n<li>Start a Python project managed by uv by running <code>uv init --app --package myproj<\/code> and cd into it.<\/li>\n<li>Write the code for your CLI in the source folder <code>src\/myproj<\/code> that is created.<\/li>\n<li>Install the project <code>myproj<\/code> globally with <code>uv tool install . -e<\/code>.<\/li>\n<li>Run your CLI <code>myproj<\/code> from anywhere on your system.<\/li>\n<\/ol><p>That's it.\nReally.\nGo and have fun!<\/p>\n<p>In the remainder of the article, I will explain all the steps in a bit more detail by working through a small example.<\/p>\n<h2 id=\"preamble\">Preamble<a href=\"#preamble\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The CLI app we will build is a tiny and basic clone of the command line utility <code>wc<\/code>.\nOur version of <code>wc<\/code> will take a file path and it will output the number of bytes, words, and lines, in that file.\nYou can also use any combination of the options <code>-c<\/code>, <code>-w<\/code>, and <code>-l<\/code>, to ask for the respective pieces of information.<\/p>\n<p>Below you can find a sample implementation of the function that implements this functionality:<\/p>\n<pre><code class=\"language-py\">from pathlib import Path\n\ndef wc(filepath: Path, show_bytes: bool, show_words: bool, show_lines: bool) -&gt; None:\n    content = filepath.read_bytes()\n    byte_count = len(content)\n    word_count = len(content.split())\n    line_count = len(content.splitlines())\n    output = (\n        (f\"{line_count:8}\" if show_lines else \"\")\n        + (f\"{word_count:8}\" if show_words else \"\")\n        + (f\"{byte_count:8}\" if show_bytes else \"\")\n        + f\" {filepath}\"\n    )\n    print(output)\n\nif __name__ == \"__main__\":\n    wc(Path(__file__), True, True, True)<\/code><\/pre>\n<p>If you take this code, paste it into a file <code>mywc.py<\/code>, and then run it, you'll get the following output (or similar):<\/p>\n<pre><code>      18      60     547 mywc.py<\/code><\/pre>\n<p>This means the file <code>mywc.py<\/code> has 18 lines, 60 words, and 547 bytes.\nNow, you want to use uv to somehow make this function globally available as a CLI app in your system.\nThis is where the five steps above come into play.<\/p>\n<h2 id=\"step-1-installing-uv\">Step 1: installing uv<a href=\"#step-1-installing-uv\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>uv is a Python package and project manager and you will use it for two things in this short tutorial:<\/p>\n<ol><li>you will use it to create a project where you will write the code for your CLI app; and<\/li>\n<li>you will use it to install your own CLI app as a command in your system.<\/li>\n<\/ol><p>There are a lot of things you can do with uv and this tutorial will only scratch the surface.\nI recommend you take a look at the <a href=\"https:\/\/docs.astral.sh\/uv\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">uv documentation<\/a> to learn more.<\/p>\n<p>To install uv, simply <a href=\"https:\/\/docs.astral.sh\/uv\/getting-started\/installation\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">follow the installation instructions in the uv documentation<\/a>.<\/p>\n<h2 id=\"step-2-initialise-a-project\">Step 2: initialise a project<a href=\"#step-2-initialise-a-project\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>For uv, a project is a folder...<\/p>","summary":"This practical tutorial shows how to use uv to build and install custom Python CLI applications globally on your system.","date_modified":"2025-07-23T16:49:02+02:00","tags":["programming","python","scripting","uv"],"image":"\/user\/pages\/02.blog\/using-uv-to-build-and-install-python-cli-apps\/thumbnail.webp"},{"title":"TIL #113 \u2013 Standalone executable Python scripts with uv","date_published":"2025-01-22T20:44:00+01:00","id":"https:\/\/mathspp.com\/blog\/til\/standalone-executable-python-scripts-with-uv","url":"https:\/\/mathspp.com\/blog\/til\/standalone-executable-python-scripts-with-uv","content_html":"<p>Today I learned how to create standalone Python scripts with uv.<\/p>\n\n<h2 id=\"standalone-python-scripts-with-uv\">Standalone Python scripts with uv<a href=\"#standalone-python-scripts-with-uv\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>I picked up uv a couple of weeks ago and I am absolutely amazed by everything that I can do with it.\nI <a href=\"\/blog\/using-an-llm-to-write-a-countdown-timer\">created a Python countdown timer<\/a> and I wanted to make it an executable I could use on my system.<\/p>\n<p>Previously, to do this I would package it as a proper Python app, add its dependencies, and then use something like pipx to install that same Python app on my system.<\/p>\n<p>Now, I can just use <code>uv<\/code> to do everything.<\/p>\n<p>As an example, let me use a countdown timer I created today:<\/p>\n<figure class=\"image-caption\"><img title=\"The timer.\" alt=\"A countdown timer with black numbers on a white background going from 02:59 to 02:56.\" src=\"\/user\/pages\/02.blog\/using-an-llm-to-write-a-countdown-timer\/_timer_demo.gif?decoding=auto&amp;fetchpriority=auto\"><figcaption class=\"\">The timer.<\/figcaption><\/figure>\n<p>The source code for this timer <a href=\"\/blog\/using-an-llm-to-write-a-countdown-timer#full-source-code-for-the-countdown-timer\">can be found in a different article I wrote<\/a>.<\/p>\n<p>Now that you also have the code, here are the steps:<\/p>\n<h3 id=\"use-uv-to-manage-script-dependencies\">Use uv to manage script dependencies<a href=\"#use-uv-to-manage-script-dependencies\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>If the code is saved in the file <code>cd.py<\/code>, you can use uv to add a dependency to that file:<\/p>\n<pre><code class=\"language-bash\">$ uv add pygame --script cd.py\nUpdated `cd.py`<\/code><\/pre>\n<p>uv will promptly say it updated your file, which should now have some comments in the beginning:<\/p>\n<pre><code class=\"language-py\">## \/\/\/ script\n## requires-python = \"&gt;=3.13\"\n## dependencies = [\n##     \"pygame\",\n## ]\n## \/\/\/<\/code><\/pre>\n<p>This lets uv run your script in a self-contained way.\nIf you run the script with <code>uv run cd.py<\/code>, then uv will install pygame in an isolated environment and then it will run your countdown timer.<\/p>\n<h3 id=\"add-a-uv-shebang-to-the-script\">Add a uv shebang to the script<a href=\"#add-a-uv-shebang-to-the-script\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>The next thing we will do is add a uv shebang to the script so that we can use it as an executable and to defer to uv for execution.\nTo do this, we add a new line at the very top of the script <code>cd.py<\/code>:<\/p>\n<pre><code class=\"language-py\">#!\/usr\/bin\/env -S uv run\n## \/\/\/ script\n## requires-python = \"&gt;=3.13\"\n## dependencies = [\n##     \"pygame\",\n## ]\n## \/\/\/<\/code><\/pre>\n<p>I <a href=\"\/link-blog\/simonwillison-net-2024-aug-21-usrbinenv-uv-run\">link-blogged about this shebang before<\/a> and I'm really happy I got to use it today.<\/p>\n<h3 id=\"make-the-script-executable\">Make the script executable<a href=\"#make-the-script-executable\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>The next step is to make your script executable:<\/p>\n<pre><code class=\"language-bash\">chmod +x cd.py<\/code><\/pre>\n<p>Additionally, I moved it to a directory in my <code>$PATH<\/code> environment variable with<\/p>\n<pre><code class=\"language-bash\">mv cd.py ~\/.local\/bin<\/code><\/pre>\n<p>Now, I can run my countdown timer from anywhere I want:<\/p>\n<pre><code class=\"language-bash\">cd.py 3  # Start a 3-minute countdown.<\/code><\/pre>\n<h3 id=\"bonus-run-the-timer-from-the-internet\">Bonus: run the timer from the Internet<a href=\"#bonus-run-the-timer-from-the-internet\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>I hosted the code for the timer on my website as well, at this URL: https:\/\/mathspp.com\/blog\/using-an-llm-to-write-a-countdown-timer\/cd.py<\/p>\n<p>With this URL, you can use uv to run the timer without even having the code locally.\nFor example, here's how to start a 3 minute countdown:<\/p>\n<pre><code class=\"language-bash\">uv run https:\/\/mathspp.com\/blog\/using-an-llm-to-write-a-countdown-timer\/cd.py 3<\/code><\/pre>","summary":"Today I learned how to create standalone executable Python scripts with uv.","date_modified":"2025-10-20T22:34:56+02:00","tags":["programming","python","scripting","uv"],"image":"\/user\/pages\/02.blog\/04.til\/113.standalone-executable-python-scripts-with-uv\/thumbnail.webp"}]}
