<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ryan Smith</title>
    <description>The latest articles on Forem by Ryan Smith (@avlwx).</description>
    <link>https://forem.com/avlwx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1237731%2Fe7388af3-a535-43fd-8fd6-ea90b7564b13.jpeg</url>
      <title>Forem: Ryan Smith</title>
      <link>https://forem.com/avlwx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/avlwx"/>
    <language>en</language>
    <item>
      <title>Manage your Python Project End-to-End with PDM</title>
      <dc:creator>Ryan Smith</dc:creator>
      <pubDate>Sun, 31 Dec 2023 17:43:40 +0000</pubDate>
      <link>https://forem.com/avlwx/manage-your-python-project-end-to-end-with-pdm-2175</link>
      <guid>https://forem.com/avlwx/manage-your-python-project-end-to-end-with-pdm-2175</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Installing PDM&lt;/li&gt;
&lt;li&gt;Starting a New Project&lt;/li&gt;
&lt;li&gt;
Setting up a Virtual Environment

&lt;ul&gt;
&lt;li&gt;Virtual Environment Auto-creation&lt;/li&gt;
&lt;li&gt;Creating a Virtual Environment Yourself&lt;/li&gt;
&lt;li&gt;Using a Pre-existing Virtual Environment&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Managing Dependencies

&lt;ul&gt;
&lt;li&gt;Syncing Changes&lt;/li&gt;
&lt;li&gt;Adding Dependencies&lt;/li&gt;
&lt;li&gt;Removing Dependencies&lt;/li&gt;
&lt;li&gt;Updating Dependencies&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;PDM Scripts&lt;/li&gt;
&lt;li&gt;Building and Publishing Distributions&lt;/li&gt;
&lt;li&gt;A Note from the Author&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://pdm-project.org/latest/"&gt;PDM&lt;/a&gt; is a modern, PEP-compliant package and dependency manager, and a powerful development tool that makes managing your Python projects easier from starting to publishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing PDM
&lt;/h2&gt;

&lt;p&gt;In order to install PDM you will need a Python environment &lt;code&gt;&amp;gt;= 3.8&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Most modern systems will probably have a system environment that meets this requirement, but if yours does not or if you prefer not to install anything in your system environment (even  if it's just PDM) check out &lt;a href="https://asdf-vm.com/guide/getting-started.html"&gt;asdf&lt;/a&gt; or &lt;a href="https://github.com/pyenv/pyenv"&gt;pyenv&lt;/a&gt; to help install and manage additional Python environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://pdm-project.org/latest/#installation"&gt;PDM docs&lt;/a&gt; prescribe a few different ways to install PDM, but the ones I have found to be the easiest are these from the &lt;a href="https://pdm-project.org/latest/#other-installation-methods"&gt;Other installation methods&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using pip&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# as a global site-package&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;pdm

&lt;span class="c"&gt;# or as a user-specific site-package&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; pdm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;As an asdf plugin&lt;/strong&gt;&lt;br&gt;
If you are already using &lt;a href="https://asdf-vm.com/guide/getting-started.html"&gt;asdf&lt;/a&gt; to manage additional Python environments (or if you just started to after reading the beginning of this section) there's also a PDM plugin for &lt;code&gt;asdf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asdf plugin add pdm
asdf &lt;span class="nb"&gt;install &lt;/span&gt;pdm latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Starting a New Project
&lt;/h2&gt;

&lt;p&gt;Initializing a new project is super easy with &lt;code&gt;pdm init&lt;/code&gt;. Here I start a new project called &lt;code&gt;"transient-fortitude"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create my new project's root directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;transient-fortitude
&lt;span class="nb"&gt;cd &lt;/span&gt;transient-fortitude
&lt;span class="c"&gt;# from my new project's root directory&lt;/span&gt;
pdm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pdm init&lt;/code&gt; walks you through an interactive session and then initializes your project structure and metadata for you based on your answers to a series of questions like which interpreter you'd like to use for the project, and whether you'd like to create a virtual environment for the project.&lt;/p&gt;

&lt;p&gt;Below is a sample of a session for &lt;code&gt;transient-fortitude&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pdm init
Creating a pyproject.toml for PDM...
Please enter the Python interpreter to use
0. /home/avlwx/.asdf/installs/python/3.12.0/bin/python (3.12)
1. /home/avlwx/.asdf/installs/python/3.12.0/bin/python3.12 (3.12)
# ... many more, omitted for brevity ...
Please select (0): 0
Would you like to create a virtualenv with /home/avlwx/.asdf/installs/python/3.12.0/bin/python? [y/n] (y): y
Virtualenv is created successfully at /home/avlwx/repos/github/transient-fortitude/.venv
Project name (transient-fortitude): 
Project version (0.1.0): 
Is the project a library that is installable?
If yes, we will need to ask a few more questions to include the build backend [y/n] (n): n
License(SPDX name) (MIT): 
Author name (Ryan Smith): 
Author email (&amp;lt;default from git user.email&amp;gt;): 
Python requires('*' to allow any) (==3.12.*): 
Project is initialized successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: For the questions with no answer I elected to take the defaults, which PDM displays in parentheses at the end of a given prompt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the layout of my new project after running through &lt;code&gt;pdm init&lt;/code&gt; (note: I shortened the full output here for brevity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree -aL 2 -I '__pycache__'
.
├── .gitignore      # 1
├── .pdm-python     # 2
├── pyproject.toml  # 3
├── README.md       # 4
├── src             # 5
│   └── transient_fortitude
├── tests           # 6
│   └── __init__.py
└── .venv           # 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To summarize, PDM created a project in the &lt;code&gt;src&lt;/code&gt; layout style including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A boilerplate &lt;code&gt;.gitignore&lt;/code&gt; that I've found works for many Python projects. You can further customize this &lt;code&gt;.gitignore&lt;/code&gt; to your needs.&lt;/li&gt;
&lt;li&gt;Some PDM metadata for tracking a project's current Python interpreter. As a developer you rarely have to worry about this.&lt;/li&gt;
&lt;li&gt;My project's &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The beginning of a project &lt;code&gt;README&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;My project source code tree including an empty top-level package. The top-level package name is derived from the project name, and is normalized to a valid Python package name.&lt;/li&gt;
&lt;li&gt;An empty &lt;code&gt;tests&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;A new virtual environment for my project.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: PDM can also create new projects from templates, but that is left as an exercise for the reader. For more information on creating a project from a template check out &lt;a href="https://pdm-project.org/latest/usage/template/#create-project-from-a-template"&gt;Create Project From a Template&lt;/a&gt; in the PDM docs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that I have my initial project structure, I can set up my project for version control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# from the project root directory&lt;/span&gt;
git init &lt;span class="nt"&gt;-b&lt;/span&gt; main
git add &lt;span class="k"&gt;*&lt;/span&gt;  &lt;span class="c"&gt;# normally should be used with caution, but is helpful here&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial project structure"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! My new project structure is set up and ready to go. &lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up a Virtual Environment
&lt;/h2&gt;

&lt;p&gt;In Starting a New Project I talked about how to initialize a project using &lt;code&gt;pdm init&lt;/code&gt; and showed  that by answering the prompt "Would you like to create a virtualenv with..." with &lt;code&gt;"y"&lt;/code&gt; PDM creates a new virtual environment for me in my project's root directory.&lt;/p&gt;

&lt;p&gt;While this is great for brand new projects it is still generally helpful to know how to use PDM to create new virtual environments in situations where you don't need to start from &lt;code&gt;pdm init&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual Environment Auto-creation
&lt;/h3&gt;

&lt;p&gt;Let's say you've just cloned an existing PDM-managed project that you don't have a local virtual environment for yet. In this case the first time you run &lt;code&gt;pdm install&lt;/code&gt; (or &lt;code&gt;pdm sync&lt;/code&gt;) in that project's root directory PDM will automatically create a new virtual environment for that project in &lt;code&gt;&amp;lt;project root&amp;gt;/.venv&lt;/code&gt; and provision it with that project's dependencies. The interpreter that is used by default depends on what your default global Python interpreter is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Virtual Environment Yourself
&lt;/h3&gt;

&lt;p&gt;Let's say that you want some extra control over the creation a project's virtual environment before provisioning it.  &lt;/p&gt;

&lt;p&gt;In this case you can create the virtual environment yourself, and you have several options to do so depending on your needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unnamed, In-project Virtual Environment&lt;/strong&gt;&lt;br&gt;
This is automatically created in &lt;code&gt;&amp;lt;project root&amp;gt;/.venv&lt;/code&gt; by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm venv create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Named Virtual Environment&lt;/strong&gt;&lt;br&gt;
You can also give your new virtual environment a name with the &lt;code&gt;--name&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm venv create &lt;span class="nt"&gt;--name&lt;/span&gt; transient-fortitude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The location where new named virtual environments are managed is controlled by the PDM configuration setting &lt;code&gt;venv.location&lt;/code&gt;. You can use &lt;code&gt;pdm config&lt;/code&gt; to find out the value of this setting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With Pip Pre-installed&lt;/strong&gt;&lt;br&gt;
To have &lt;code&gt;pip&lt;/code&gt; pre-installed in your project virtual environment without having to add it as a dependency run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm venv create &lt;span class="nt"&gt;--with-pip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can configure PDM to automatically add &lt;code&gt;pip&lt;/code&gt; to your new virtual environments by default and avoid using the &lt;code&gt;--with-pip&lt;/code&gt; flag everytime by updating the PDM config setting &lt;code&gt;venv.with_pip&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm config &lt;span class="s1"&gt;'venv.with_pip'&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a PyCharm user I have found that having &lt;code&gt;pip&lt;/code&gt; in my project virtual environment is helpful since PyCharm can have a hard time with project environments where &lt;code&gt;setuptools&lt;/code&gt; is not installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Matching a Python Version Specifier&lt;/strong&gt;&lt;br&gt;
You can specify a Python version as an argument to &lt;code&gt;pdm venv create&lt;/code&gt; and PDM will resolve a matching interpreter (if it can) and create your virtual environment with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# find a 3.11 interpreter&lt;/span&gt;
pdm venv create 3.11

&lt;span class="c"&gt;# find specifically a 3.11.4 interpreter&lt;/span&gt;
pdm venv create 3.11.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With a Specific Python Interpreter&lt;/strong&gt;&lt;br&gt;
Sometimes you want control over exactly which interpreter is used to create your virtual environment. &lt;/p&gt;

&lt;p&gt;Instead of just a version specifier, you can instead specify an absolute path to a specific interpreter and PDM will create your virtual environment with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm venv create /path/to/python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For even more information on working with virtual environments check out &lt;a href="https://pdm-project.org/latest/usage/venv/"&gt;Working with Virtual Environments&lt;/a&gt; in the PDM docs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using a Pre-existing Virtual Environment
&lt;/h3&gt;

&lt;p&gt;If you're used to creating virtual environments by hand using &lt;code&gt;venv&lt;/code&gt; or &lt;code&gt;virtualenv&lt;/code&gt; directly and prefer to do this (or if it's muscle memory to the point that you do it without thinking) that's okay! You don't have to go back and remove, then recreate your environment with PDM in order to use PDM for your project.&lt;/p&gt;

&lt;p&gt;You can tell PDM which virtual environment to use for your project by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# -i flag to ignore whatever interpreter PDM might already remember&lt;/span&gt;
&lt;span class="c"&gt;# -f flag to tell PDM to use the first matching interpreter&lt;/span&gt;
pdm use &lt;span class="nt"&gt;-if&lt;/span&gt; /path/to/venv/bin/python
&lt;span class="c"&gt;# ... proceed to provisioning, etc. ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing Dependencies
&lt;/h2&gt;

&lt;p&gt;In Starting a New Project and Setting up a Virtual Environment I showed various ways to create a virtual environment for a project.&lt;/p&gt;

&lt;p&gt;Once you have a virtual environment setup you will want to add, remove, and update your project's dependencies as the needs of your project evolve. &lt;/p&gt;

&lt;h3&gt;
  
  
  Syncing Changes
&lt;/h3&gt;

&lt;p&gt;When collaborating on a project with other developers it will be necessary to synchronize your local virtual environment with remote changes to the project's dependency set maintained in your project's &lt;code&gt;pdm.lock&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;To synchronize your working set with your project's lockfile, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ensure any dependencies that have been added or upgraded get added or upgraded, while any dependencies that have been removed are removed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: without the &lt;code&gt;--clean&lt;/code&gt; option PDM will not purge your working set of dependencies that have been removed from your project's lockfile.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding Dependencies
&lt;/h3&gt;

&lt;p&gt;To add new dependencies, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm add PACKAGE &lt;span class="o"&gt;[&lt;/span&gt;PACKAGE ...]

&lt;span class="c"&gt;# to allow pre-release versions use the option --pre/--prerelease&lt;/span&gt;
pdm add &lt;span class="nt"&gt;--pre&lt;/span&gt; PACKAGE &lt;span class="o"&gt;[&lt;/span&gt;PACKAGE ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, &lt;code&gt;pdm add&lt;/code&gt; will add your new dependencies to the &lt;code&gt;pyproject.toml&lt;/code&gt;, perform a &lt;code&gt;lock&lt;/code&gt; operation, and finally &lt;code&gt;sync&lt;/code&gt; your working set by installing or updating any packages resolved during the &lt;code&gt;lock&lt;/code&gt;. This may result in packages already in your project's lockfile being updated unexpectedly. If you'd like to try and prevent unexpected updates you can use the &lt;code&gt;--update-reuse&lt;/code&gt; flag to tell PDM to reuse the versions of packages already pinned in the lockfile where possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing Dependencies
&lt;/h3&gt;

&lt;p&gt;To remove dependencies run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm remove PACKAGE &lt;span class="o"&gt;[&lt;/span&gt;PACKAGE ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Updating Dependencies
&lt;/h3&gt;

&lt;p&gt;To update your dependencies, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# update all dependencies, including transitive dependencies&lt;/span&gt;
pdm update &lt;span class="nt"&gt;--update-all&lt;/span&gt;

&lt;span class="c"&gt;# update just specific packages&lt;/span&gt;
pdm update &lt;span class="nt"&gt;--update-reuse&lt;/span&gt; PACKAGE &lt;span class="o"&gt;[&lt;/span&gt;PACKAGE ...]

&lt;span class="c"&gt;# update specific packages and their dependency trees&lt;/span&gt;
pdm update &lt;span class="nt"&gt;--update-eager&lt;/span&gt; PACKAGE &lt;span class="o"&gt;[&lt;/span&gt;PACKAGE ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PDM Scripts
&lt;/h2&gt;

&lt;p&gt;PDM provides a mechanism to execute arbitrary and user-defined scripts in an environment that is aware of the packages in your project. &lt;/p&gt;

&lt;p&gt;Just pass your command and its options to &lt;code&gt;pdm run&lt;/code&gt; and PDM will execute that program for you in a dependency-aware environment without having to activate your virtual environment. For those who are familiar with &lt;code&gt;npm run&lt;/code&gt;, &lt;code&gt;docker run&lt;/code&gt;, and  &lt;code&gt;podman run&lt;/code&gt; the concept is similar.&lt;/p&gt;

&lt;p&gt;This allows you to do things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run your application entrypoint scripts without having to activate your virtual environment.&lt;/li&gt;
&lt;li&gt;Tinker in a Python interpreter where all of your project code and its dependencies are loaded.&lt;/li&gt;
&lt;li&gt;Define scripts that can be used both locally and in CI to execute tests or run static analysis tools against your code consistently for both developers and CI systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The one I find to be most powerful, though, is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Define scripts that can be used both locally and in CI to execute tests or run static analysis tools against your code consistently for both developers and CI systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, you can add tools like &lt;code&gt;bandit&lt;/code&gt; (security scanning), &lt;code&gt;black&lt;/code&gt; (formatting), &lt;code&gt;flake8&lt;/code&gt; (linting), &lt;code&gt;isort&lt;/code&gt; (import management), and &lt;code&gt;mypy&lt;/code&gt; (type-checking) as development dependencies for your project, then define scripts in your &lt;code&gt;pyproject.toml&lt;/code&gt; file under the table &lt;code&gt;[tool.pdm.scripts]&lt;/code&gt; (see example below) to run these tools against your code both locally and in CI!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# example user-defined PDM Scripts for developer and CI tools&lt;/span&gt;
&lt;span class="nn"&gt;[tool.pdm.scripts]&lt;/span&gt;
&lt;span class="nn"&gt;check-formatting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;composite&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"isort --check --settings-path ./pyproject.toml src/ tests/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"black --check src/ tests/"&lt;/span&gt;
&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="nn"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;composite&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"isort --settings-path ./pyproject.toml src/ tests/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"black src/ tests/"&lt;/span&gt;
&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="py"&gt;lint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"flake8 --config .flake8 src/ tests/"&lt;/span&gt;
&lt;span class="py"&gt;type-check&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mypy --config-file ./pyproject.toml src/"&lt;/span&gt;
&lt;span class="py"&gt;security-scan&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bandit -rc pyproject.toml src/ tests/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using these scripts developers can run &lt;code&gt;pdm format&lt;/code&gt; against their working copy before committing to version control so that it's counterpart &lt;code&gt;pdm check-formatting&lt;/code&gt; doesn't fail in CI. &lt;/p&gt;

&lt;p&gt;As another example developers can run &lt;code&gt;pdm type-check&lt;/code&gt; against their working copy before committing to version control, allowing them time to preemptively fix any issues with their type annotations so there are no surprises in CI.&lt;/p&gt;

&lt;p&gt;The ability to standardize both CI and developer tools like this prevents issues with flaky CI, or drift between CI and developer tools! No more "it passed on my machine, but fails in CI" and project maintainers don't have to update developer tools in multiple places.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I first discussed using &lt;code&gt;pdm run&lt;/code&gt; in order to execute scripts in a package-aware environment, and later omitted &lt;code&gt;run&lt;/code&gt; in my examples. For user-defined scripts like I showed above PDM provides a convenient shortcut for users that allows you to execute either &lt;code&gt;pdm run format&lt;/code&gt; or &lt;code&gt;pdm format&lt;/code&gt; with the same result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more information on PDM Scripts check out &lt;a href="https://pdm-project.org/latest/usage/scripts/"&gt;PDM Scripts&lt;/a&gt; in the PDM docs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Building and Publishing Distributions
&lt;/h2&gt;

&lt;p&gt;Once my project is in a state where I feel comfortable publishing it to PyPI for all the world to use I can use PDM to help me handle both building and publishing my distribution. &lt;/p&gt;

&lt;p&gt;To build your distribution(s), run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# builds both a source and binary distribution&lt;/span&gt;
pdm build

&lt;span class="c"&gt;# builds just a source distribution&lt;/span&gt;
pdm build &lt;span class="nt"&gt;--no-bdist&lt;/span&gt;

&lt;span class="c"&gt;# builds just a binary distribution&lt;/span&gt;
pdm build &lt;span class="nt"&gt;--no-sdist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default distributions are built under a directory called &lt;code&gt;dist/&lt;/code&gt;. If you want to customize the output directory for your distributions use the &lt;code&gt;-d&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# build under a directory called "build"&lt;/span&gt;
pdm build &lt;span class="nt"&gt;-d&lt;/span&gt; build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you're ready to publish the artifacts you just built to PyPI, just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm publish &lt;span class="nt"&gt;--no-build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: If you plan to use &lt;code&gt;pdm publish --no-build&lt;/code&gt; with your distributions from &lt;code&gt;pdm build&lt;/code&gt; you should note that &lt;code&gt;pdm publish&lt;/code&gt; expects the distribution artifacts to be under &lt;code&gt;dist/&lt;/code&gt;, and as of PDM &lt;code&gt;2.11.1&lt;/code&gt; this cannot be overridden. Because of this, the &lt;code&gt;-d&lt;/code&gt; flag for &lt;code&gt;pdm build&lt;/code&gt; is primarily useful for scratch builds, or builds that are inputs to some middleware that processes artifacts further before publishing them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to build and publish both a source and binary distribution to PyPI (all the default stuff), you can condense the steps above in to one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Publishing to a Private Registry
&lt;/h3&gt;

&lt;p&gt;If instead you want to publish to a private registry, you can use the &lt;code&gt;--repository&lt;/code&gt; option, along with &lt;code&gt;--username&lt;/code&gt; and &lt;code&gt;--password&lt;/code&gt; to do so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdm publish &lt;span class="nt"&gt;--no-build&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--repository&lt;/span&gt; &amp;lt;your private registry URL&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--username&lt;/span&gt; &amp;lt;your registry username&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--password&lt;/span&gt; &amp;lt;your registry password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Warning: Make sure any command tracing for your shell is turned off (e.g. &lt;code&gt;set +x&lt;/code&gt; in &lt;code&gt;bash&lt;/code&gt;) otherwise you could leak your private registry password to your terminal!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more information on adding registries and repositories for your project, and maintaining registry and repository secrets as local PDM configuration see &lt;a href="https://pdm-project.org/latest/usage/config/#configure-the-repositories-for-upload"&gt;Configure the repositories for upload&lt;/a&gt; in the PDM docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note from the Author
&lt;/h2&gt;

&lt;p&gt;This post is informed by my personal experience using PDM for managing my Python projects and is not intended to act as a complete reference for PDM's CLI. If you come across something I haven't mentioned here, it's not because it's not important it's only because I haven't come across it (yet). &lt;/p&gt;

&lt;p&gt;For PDM's complete CLI reference check out &lt;a href="https://pdm-project.org/latest/reference/cli/"&gt;CLI Reference&lt;/a&gt; in the PDM docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In a few places I talk about PDM's configuration settings, but never dive in to that in detail. For more information on configuring PDM check out &lt;a href="https://pdm-project.org/latest/reference/configuration/"&gt;Configurations&lt;/a&gt; in the PDM docs. &lt;/li&gt;
&lt;li&gt;This post is primarily informed by my personal experience using PDM for managing my Python projects. However, some of the language I use is informed by the &lt;a href="https://pdm-project.org/latest/"&gt;PDM docs&lt;/a&gt;  themselves to ensure consistency. A big shout out to the PDM Project for such an amazing tool!
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>packaging</category>
    </item>
  </channel>
</rss>
