diff --git a/docs/tutorial/package.md b/docs/tutorial/package.md index 617c93cd0b..462b56035f 100644 --- a/docs/tutorial/package.md +++ b/docs/tutorial/package.md @@ -20,9 +20,9 @@ If you already have a favorite way of creating Python packages, feel free to ski ## Prerequisites -For this guide we'll use Poetry. +For this guide we'll use uv. -Poetry's docs are great, so go ahead, check them and install it. +uv's docs are great, so go ahead, check them and install it. ## Create a project @@ -32,14 +32,14 @@ To make sure your package doesn't collide with the package created by someone el So, if your name is Rick, we'll call it `rick-portal-gun`. -Create a project with Poetry: +Create a project with uv:
```console -$ poetry new rick-portal-gun +$ uv init --package rick-portal-gun -Created package rick_portal_gun in rick-portal-gun +Initialized project `rick-portal-gun` at `/home/rick-portal-gun` // Enter the new project directory cd ./rick-portal-gun @@ -54,34 +54,30 @@ Add `typer` to your dependencies:
```console -$ poetry add typer +$ uv add typer // It creates a virtual environment for your project -Creating virtualenv rick-portal-gun-w31dJa0b-py3.10 in /home/rick/.cache/pypoetry/virtualenvs -Using version ^0.12.0 for typer +Using CPython 3.14.0 interpreter at: /location/of/python/ +Creating virtual environment at: .venv + +Resolved 10 packages in 21ms + Built rick-portal-gun @ file:/home/rick-portal-gun +Prepared 1 package in 19ms +Installed 10 packages in 34ms + + click==8.3.1 + + colorama==0.4.6 + + markdown-it-py==4.0.0 + + mdurl==0.1.2 + + pygments==2.19.2 + + rich==14.2.0 + + rick-portal-gun==0.1.0 (from file:/home/rick-portal-gun) + + shellingham==1.5.4 + + typer==0.21.0 + + typing-extensions==4.15.0 -Updating dependencies -Resolving dependencies... (1.2s) - ----> 100% - -Package operations: 8 installs, 0 updates, 0 removals - - - Installing mdurl (0.1.2) - - Installing markdown-it-py (3.0.0) - - Installing pygments (2.17.2) - - Installing click (8.1.7) - - Installing rich (13.7.1) - - Installing shellingham (1.5.4) - - Installing typing-extensions (4.11.0) - - Installing typer (0.12.3) - -Writing lock file // Activate that new virtual environment -$ poetry shell - -Spawning shell within /home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.10 +$ source .venv/bin/activate // Open an editor using this new environment, for example VS Code $ code ./ @@ -93,20 +89,19 @@ You can see that you have a generated project structure that looks like: ``` . -├── poetry.lock ├── pyproject.toml ├── README.md -├── rick_portal_gun -│   └── __init__.py -└── tests - └── __init__.py +├── src +│   └── rick_portal_gun +│     └── __init__.py +└── uv.lock ``` ## Create your app Now let's create an extremely simple **Typer** app. -Create a file `rick_portal_gun/main.py` with: +Create a file `src/rick_portal_gun/main.py` with: ```Python import typer @@ -160,26 +155,26 @@ We are creating a Python package that can be installed with `pip install`. But we want it to provide a CLI program that can be executed in the shell. -To do that, we add a configuration to the `pyproject.toml` in the section `[tool.poetry.scripts]`: +To do that, we add a configuration to the `pyproject.toml` in the section `[project.scripts]`: -```TOML hl_lines="8 9" -[tool.poetry] +```TOML hl_lines="12 13" +[project] name = "rick-portal-gun" version = "0.1.0" -description = "" -authors = ["Rick Sanchez "] +description = "Add your description here" readme = "README.md" +authors = ["Rick Sanchez "] +requires-python = ">=3.14" +dependencies = [ + "typer>=0.21.0", +] -[tool.poetry.scripts] +[project.scripts] rick-portal-gun = "rick_portal_gun.main:app" -[tool.poetry.dependencies] -python = "^3.10" -typer = "^0.12.0" - [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["uv_build>=0.8.14,<0.9.0"] +build-backend = "uv_build" ``` Here's what that line means: @@ -209,7 +204,7 @@ from rick_portal_gun.main import app app() ``` -That config section tells Poetry that when this package is installed we want it to create a command line program called `rick-portal-gun`. +That config section tells uv that when this package is installed, we want it to create a command line program called `rick-portal-gun`. And that the object to call (like a function) is the one in the variable `app` inside of the module `rick_portal_gun.main`. @@ -222,20 +217,22 @@ You can now install it:
```console -$ poetry install +$ uv sync -Installing dependencies from lock file +Resolved 10 packages in 1ms + Built rick-portal-gun @ file:/home/rick-portal-gun +Prepared 1 package in 18ms +Uninstalled 1 package in 1ms +Installed 1 package in 13ms + ~ rick-portal-gun==0.1.0 (from file:/home/rick-portal-gun) -No dependencies to install or update - - - Installing the current project: rick-portal-gun (0.1.0) ```
## Try your CLI program -Your package is installed in the environment created by Poetry, but you can already use it. +Your package is installed in the environment created by uv, but you can already use it.
@@ -244,7 +241,7 @@ Your package is installed in the environment created by Poetry, but you can alre $ which rick-portal-gun // You get the one from your environment -/home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.10/bin/rick-portal-gun +/home/rick-portal-gun/.venv/bin/rick-portal-gun // Try it $ rick-portal-gun --help @@ -261,8 +258,8 @@ Options: --help Show this message and exit. Commands: - load Load the portal gun shoot Shoot the portal gun + load Load the portal gun ```
@@ -271,18 +268,17 @@ Commands: Python packages have a standard format called a "wheel". It's a file that ends in `.whl`. -You can create a wheel with Poetry: +You can create a wheel with uv:
```console -$ poetry build +$ uv build -Building rick-portal-gun (0.1.0) - - Building sdist - - Built rick-portal-gun-0.1.0.tar.gz - - Building wheel - - Built rick_portal_gun-0.1.0-py3-none-any.whl +Building source distribution (uv build backend)... +Building wheel from source distribution (uv build backend)... +Successfully built dist\rick_portal_gun-0.1.0.tar.gz +Successfully built dist\rick_portal_gun-0.1.0-py3-none-any.whl ```
@@ -415,19 +411,18 @@ You can support that same style of calling the package/module for your own packa Python will look for that file and execute it. -The file would live right beside `__init__.py`: +The file would live right beside `__init__.py` and `main.py`: ``` hl_lines="7" . -├── poetry.lock ├── pyproject.toml ├── README.md -├── rick_portal_gun -│ ├── __init__.py -│ ├── __main__.py -│ └── main.py -└── tests - └── __init__.py +├── src +│   └── rick_portal_gun +│     ├── __init__.py +│     ├── __main__.py +│     └── main.py +└── uv.lock ``` No other file has to import it, you don't have to reference it in your `pyproject.toml` or anything else, it just works by default, as it is standard Python behavior. @@ -439,14 +434,14 @@ from .main import app app() ``` -Now, after installing your package, if you call it with `python -m` it will work (for the main part): +Now, after installing your package, if you call it with `python -m` it will work:
```console $ python -m rick_portal_gun --help -Usage: __main__.py [OPTIONS] COMMAND [ARGS]... +Usage: python -m rick_portal_gun [OPTIONS] COMMAND [ARGS]... Awesome Portal Gun @@ -457,8 +452,8 @@ Options: --help Show this message and exit. Commands: - load Load the portal gun shoot Shoot the portal gun + load Load the portal gun ```
@@ -469,59 +464,7 @@ Notice that you have to pass the importable version of the package name, so `ric /// -That works! 🚀 Sort of... 🤔 - -See the `__main__.py` in the help instead of `rick-portal-gun`? We'll fix that next. - -### Set a program name in `__main__.py` - -We are setting the program name in the file `pyproject.toml` in the line like: - -```TOML -[tool.poetry.scripts] -rick-portal-gun = "rick_portal_gun.main:app" -``` - -But when Python runs our package as a script with `python -m`, it doesn't have the information of the program name. - -So, to fix the help text to use the correct program name when called with `python -m`, we can pass it to the app in `__main__.py`: - -```Python -from .main import app -app(prog_name="rick-portal-gun") -``` - -/// tip - -You can pass all the arguments and keyword arguments you could pass to a Click application, including `prog_name`. - -/// - -
- -```console -$ python -m rick_portal_gun --help - -Usage: rick-portal-gun [OPTIONS] COMMAND [ARGS]... - - Awesome Portal Gun - -Options: - --install-completion Install completion for the current shell. - --show-completion Show completion for the current shell, to copy it or customize the installation. - - --help Show this message and exit. - -Commands: - load Load the portal gun - shoot Shoot the portal gun -``` - -
- -Great! That works correctly! 🎉 ✅ - -Notice that now it uses `rick-portal-gun` instead of `__main__.py` in the help. +That works! 🚀 ### Autocompletion and `python -m` @@ -561,12 +504,12 @@ Let's say your new API token is: pypi-wubalubadubdub-deadbeef1234 ``` -Now configure Poetry to use this token with the command `poetry config pypi-token.pypi`: +Now configure uv to use this token by setting an environment variable:
```console -$ poetry config pypi-token.pypi pypi-wubalubadubdub-deadbeef1234 +$ export UV_PUBLISH_TOKEN=pypi-wubalubadubdub-deadbeef1234 // It won't show any output, but it's already configured ``` @@ -574,28 +517,16 @@ $ poetry config pypi-token.pypi pypi-wubalubadubdub-deadbeef1234 ### Publish to PyPI -Now you can publish your package with Poetry. - -You could build the package (as we did above) and then publish later, or you could tell poetry to build it before publishing in one go: +Now you can publish your package.
```console -$ poetry publish --build - -# There are 2 files ready for publishing. Build anyway? (yes/no) [no] $ yes - ----> 100% - -Building rick-portal-gun (0.1.0) - - Building sdist - - Built rick-portal-gun-0.1.0.tar.gz - - Building wheel - - Built rick_portal_gun-0.1.0-py3-none-any.whl +$ uv publish -Publishing rick-portal-gun (0.1.0) to PyPI - - Uploading rick-portal-gun-0.1.0.tar.gz 100% - - Uploading rick_portal_gun-0.1.0-py3-none-any.whl 100% +Publishing 2 files https://upload.pypi.org/legacy/ +Uploading rick_portal_gun-0.1.0-py3-none-any.whl (2.3KiB) +Uploading rick_portal_gun-0.1.0.tar.gz (841.0B) ```
@@ -606,7 +537,7 @@ You should now see your new "rick-portal-gun" package. ### Install from PyPI -Now to see that we can install it form PyPI, open another terminal, and uninstall the currently installed package. +Now to see that we can install it from PyPI, open another terminal, and uninstall the currently installed package.
@@ -694,26 +625,26 @@ Now you can publish a new version with the updated docs. For that you need to first increase the version in `pyproject.toml`: ```TOML hl_lines="3" -[tool.poetry] +[project] name = "rick-portal-gun" version = "0.2.0" -description = "" -authors = ["Rick Sanchez "] +description = "Add your description here" readme = "README.md" +authors = ["Rick Sanchez "] +requires-python = ">=3.14" +dependencies = [ + "typer>=0.21.0", +] -[tool.poetry.scripts] +[project.scripts] rick-portal-gun = "rick_portal_gun.main:app" -[tool.poetry.dependencies] -python = "^3.10" -typer = "^0.12.0" - [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["uv_build>=0.8.14,<0.9.0"] +build-backend = "uv_build" ``` -And in the file `rick_portal_gun/__init__.py`: +And in the file `src/rick_portal_gun/__init__.py`: ```Python __version__ = '0.2.0' @@ -724,19 +655,12 @@ And then build and publish again:
```console -$ poetry publish --build - ----> 100% - -Building rick-portal-gun (0.2.0) - - Building sdist - - Built rick-portal-gun-0.2.0.tar.gz - - Building wheel - - Built rick_portal_gun-0.2.0-py3-none-any.whl +$ uv build +$ uv publish -Publishing rick-portal-gun (0.2.0) to PyPI - - Uploading rick-portal-gun-0.2.0.tar.gz 100% - - Uploading rick_portal_gun-0.2.0-py3-none-any.whl 100% +Publishing 2 files https://upload.pypi.org/legacy/ +Uploading rick_portal_gun-0.2.0-py3-none-any.whl (2.3KiB) +Uploading rick_portal_gun-0.2.0.tar.gz (840.0B) ```
@@ -749,7 +673,7 @@ This is a very simple guide. You could add many more steps. For example, you should use Git, the version control system, to save your code. -You can add a lot of extra metadata to your `pyproject.toml`, check the docs for Poetry: Libraries. +You can add a lot of extra metadata to your `pyproject.toml`, check the docs for Poetry metadata settings. You could use `pipx` to manage your installed CLI Python programs in isolated environments.