Description

The pyproject crawler looks recursively for all pyproject.toml files from a specific root directory. Then for each of them, it tries to update the Python dependencies declared in the [project] table.

Dependencies are read from:

  • [project.dependencies]

  • [project.optional-dependencies] (one manifest per package, per group)

Directories named .venv, pycache, .git, node_modules, .tox, .nox, and .eggs are never walked.

This crawler is enabled by default, so it can be used either automatically by running updatecli diff from a directory containing the files to update, or by providing a manifest. The automatic discovery behavior can be tuned by providing a YAML manifest with a pyproject crawler in top-level directive autodiscovery as explained in the "Autodiscovery" page.

Requirements

The crawler detects the package manager from the lock file sitting next to pyproject.toml. Only uv is supported today, via uv.lock.

The result depends on what Updatecli finds next to each pyproject.toml:

uv.lockuv command on PATHResult

present

yes

Full manifests: a pypi source and a shell target that refreshes uv.lock.

present

no

The pyproject.toml is skipped entirely, with a warning. Updatecli refuses to bump a dependency it cannot re-lock.

absent

-

Source-only manifests. Updatecli reports the latest published version but generates no target, so nothing is modified.

Tip
If a project produces no manifest at all, check that uv is installed and reachable from the environment running Updatecli.

Generated manifests

One manifest is generated per dependency, using the pypi resource as a source and a shell target:

name: 'deps(pypi): bump "requests" for "myproject" project'
sources:
  requests:
    name: 'Get latest "requests" package version'
    kind: 'pypi'
    spec:
      name: 'requests'
      versionfilter:
        kind: 'pep440'
        pattern: '>=2.28'
targets:
  requests:
    name: 'deps(pypi): bump "requests" to {{ source "requests" }}'
    kind: 'shell'
    spec:
      command: 'uv lock --upgrade-package requests=={{ source "requests" }}'
      changedif:
        kind: file/checksum
        spec:
          files:
            - "uv.lock"
      environments:
        - name: PATH
      workdir: '.'
    disablesourceinput: true

workdir points at the directory holding the pyproject.toml, so nested projects are updated in place.

Important
uv lock --upgrade-package only updates uv.lock. The version constraints declared in pyproject.toml are deliberately left untouched, so a dependency is only bumped as far as its own constraint allows. Widening a constraint such as requests>=2.28,<3 remains a manual change.

The shell target only exposes the PATH environment variable to uv. Variables such as UV_INDEX_URL, NETRC, or HOME are not inherited.

Version filtering

If no versionfilter is specified, the crawler falls back to kind: pep440 and reuses each dependency’s own constraint as the pattern, for example >=2.28 for requests>=2.28. Dependencies declared without a constraint get the pattern *.

If a versionfilter is specified, its kind is used for every generated source, and relative semver patterns are resolved against the version currently declared by each dependency. For example kind: semver with pattern: minor generates pattern: '2.x' for requests>=2.28. Explicit constraint patterns such as >=1.0.0 are used as-is.

More details on the "Version Filtering" page.

Limitations

  • Only the [project] table is read. [dependency-groups] (PEP 735), [tool.poetry], [tool.uv], and [build-system].requires are ignored, so Poetry and PDM projects yield no manifest.

  • PEP 508 direct references such as mypkg @ https://…​; or mypkg @ git+https://…​, and local path dependencies, are skipped with a warning.

  • Extras are dropped from the tracked name: black[jupyter]>=24.0 is tracked as black.

  • Environment markers are stripped, not evaluated. pywin32>=300; sys_platform == 'win32' is updated unconditionally.

Manifest

Parameters

NameTypeDescriptionRequired
ignorearray

“ignore” defines rules to exclude matching Python dependencies from the autodiscovery.

remark:

  • a Python dependency is ignored when it matches at least one rule.
    packagesobject

“packages” defines the Python packages to match, keyed by package name.

remark:

  • an empty value matches any version.
  • otherwise the value is a PEP 440 version specifier, such as “>=2.0,<3.0”.
  • when the version or the specifier cannot be parsed, the value must equal the version.
    pathstring

“path” defines a “pyproject.toml” path pattern.

remark:

  • the pattern must match the whole path, not just a substring.
  • the pattern follows the Go filepath.Match syntax, such as “*” or “?”.
indexurlstring

“indexurl” defines a custom PyPI index URL used by every generated source.

remark:

  • it carries no credentials: a private registry requires setting the pypi resource “token” field in the generated manifests.
onlyarray

“only” defines rules to restrict the autodiscovery to matching Python dependencies.

remark:

  • a Python dependency is kept only when it matches at least one rule.
    packagesobject

“packages” defines the Python packages to match, keyed by package name.

remark:

  • an empty value matches any version.
  • otherwise the value is a PEP 440 version specifier, such as “>=2.0,<3.0”.
  • when the version or the specifier cannot be parsed, the value must equal the version.
    pathstring

“path” defines a “pyproject.toml” path pattern.

remark:

  • the pattern must match the whole path, not just a substring.
  • the pattern follows the Go filepath.Match syntax, such as “*” or “?”.
rootdirstring

“rootdir” defines the directory where the crawler starts searching for “pyproject.toml” files.

default: the scm directory when “scmid” is set, otherwise the directory relative paths resolve from, by default the working directory.

remark:

  • a relative path is resolved from the default directory.
  • an absolute path is used as is, instead of the scm directory.
versionfilterobject

“versionfilter” defines the version filter used by the generated manifests.

default: kind “pep440” with the dependency’s own constraint as pattern, such as “>=2.28” for “requests>=2.28”, or “*” when the dependency is declared without a constraint.

remark:

  • with kind “pep440”, “pattern” accepts a PEP 440 version specifier, such as “>=2.28”, “>=1.0,<3.0” or “*”.
  • with kind “semver”, “pattern” accepts:
    • “prerelease”: the latest prerelease of the current version.
    • “patch”: patch updates only.
    • “minor”: patch and minor updates.
    • “minoronly”: minor updates only.
    • “major”: patch, minor and major updates.
    • “majoronly”: major updates only.
    • a version constraint, such as “>= 1.0.0”.
  • relative patterns such as “minor” are resolved against the version each dependency currently declares, so “minor” gives the pattern “2.x” for “requests>=2.28”.
  • with kind “regex”, “pattern” accepts a regular expression.
  • more examples at https://www.updatecli.io/docs/core/versionfilter/

example:

versionfilter:
  kind: pep440
  pattern: ">=2.28"
    kindstring

“kind” defines the versioning scheme used to select a version.

default: latest

remark:

  • accepted values are “latest”, “semver”, “regex”, “regex/semver”, “time”, “regex/time”, “lex” and “pep440”.
  • “latest” returns the last version of the list.
  • “lex” sorts the versions lexicographically and returns the last one.
  • “pep440” follows https://peps.python.org/pep-0440/

example:

  • kind: semver
    patternstring

“pattern” defines the version pattern, according to “kind”.

default:

  • latest: “latest”
  • semver and pep440: “*”
  • regex: “.*”
  • time and regex/time: “2006-01-02”

remark:

  • for “latest”, “latest” returns the last version, any other value must match a version exactly.
  • for “semver” and “regex/semver”, it is a semantic versioning constraint.
  • for “pep440”, it is a pep440 version specifier.
  • for “regex”, it is a regular expression.
  • for “time” and “regex/time”, it is a Go date layout.
  • ignored by “lex”.

example:

  • pattern: ~1.2
  • pattern: “>=1.0.0 <2.0.0”
  • pattern: ^v\d+.\d+.\d+$
    regexstring

“regex” defines the regular expression extracting the version from each entry.

remark:

  • only used by the kinds “regex/semver” and “regex/time”.
  • the value of the first capture group is used as the version.

example:

  • regex: ^v(\d+.\d+.\d+)$
    replaceallobject

“replaceall” applies a regular expression replacement to each version before filtering.

remark:

  • only used by the kinds “regex”, “regex/semver” and “regex/time”.
  • the replacement runs before “pattern” or “regex” is evaluated.

example:

replaceall:
  pattern: "_"
  replacement: "."

turns “curl-8_15_0” into “curl-8.15.0”.

        patternstring

“pattern” defines the regular expression matching the text to replace.

example:

  • pattern: “_”
        replacementstring

“replacement” defines the text replacing each match of “pattern”.

remark:

  • capture groups can be referenced with $1, $2, and so on.

example:

  • replacement: “.”
    strictboolean

“strict” enforces strict semantic versioning rules when parsing versions.

default: false

remark:

  • only used by the kinds “semver” and “regex/semver”.
⚠ This table is generated from the Updatecli codebase and may contain inaccurate data. Feel free to report them on github.com/updatecli/updatecli
Note
Within a single only/ignore rule, path and packages must both match (AND); separate rules are combined with OR. A package version in a rule is a PEP 440 specifier evaluated against the version number extracted from the dependency’s constraint, for example 2.28 for requests>=2.28. An empty value matches any version.

Example

Basic Example

# updatecli.d/pyproject.yaml
autodiscovery:
  crawlers:
    pyproject:
      rootdir: "."
      versionfilter:
        kind: semver
        pattern: minor

Filter to Specific Packages

# updatecli.d/pyproject-only.yaml
autodiscovery:
  crawlers:
    pyproject:
      only:
        - packages:
            "requests": ""
            "flask": ""

Private PyPI Registry

# updatecli.d/pyproject-private.yaml
autodiscovery:
  crawlers:
    pyproject:
      rootdir: "."
      # Custom PyPI index URL propagated to all generated pypi source specs
      indexurl: "https://pypi.example.com/"
Note
The indexurl parameter is propagated as the url field of every generated pypi source, allowing consistent registry configuration across all discovered dependencies. It does not carry credentials: the crawler has no token parameter, so an authenticated registry requires adding the pypi resource token field to the generated manifests by hand. The uv lock target relies on `uv’s own index configuration.
Note
The alias python/uv can also be used instead of pyproject.