Skip to content
BETAGuardrails are in beta. The library, the configuration format and the CLI command can still change.

Guardrails / Collectors

Python project

The Python project in the project directory: its manifest and build backend, the Python version it asks for, the requirements it declares and which of them are pinned, and the lock files committed beside them.

Facts keypython
Versionv1
Scriptpython.py
Timeout30 seconds

Inputs

InputDescriptionDefaultEnvironment
projectDirDirectory holding the project, relative to the directory the CLI runs in..GUARDRAIL_INPUT_PROJECTDIR
requirementsComma separated globs naming the requirements files to read, relative to projectDir.requirements.txt,requirements-dev.txt,requirements/*.txtGUARDRAIL_INPUT_REQUIREMENTS

A guardrail that declares an input of the same name passes it through, so the guardrail's configuration in buildnote.json is what decides these values.

A guardrail asks for these facts by name, and reads them back the same way:

json
{
  "collect": ["python"]
}
python
python = guardrail.facts("python")

Facts

The document python collects. A path carrying [] is an entry of the list before it, and one carrying [path] is a key of the object before it.

FactMeaning
directoryThe projectDir input, as the guardrail configured it.
existsWhether that directory is there. Nothing else is collected when it is not.
manifestPath of the primary manifest, relative to projectDir: pyproject.toml when there is one, otherwise setup.cfg, setup.py or the first requirements file found, in that order.
sourcesEvery manifest that was read and understood, in the order they were read. A manifest the reader could not handle is in unparsed instead.
declaredThe Python version the project asks for, or null when it asks for none. .python-version wins over requires-python, because it is what a developer's shell reads.
declared.versionConstraint exactly as written, such as >=3.11 or 3.12.2.
declared.sourceFile declaring it, relative to projectDir.
declared.pinnedWhether the constraint names one exact version rather than a range. A >=, a ~=, a ^, a * or a comma separated range is not pinned.
backendThe build backend [build-system] names, such as hatchling.build or poetry.core.masonry.api, or null when the project declares none and setuptools is implied.
packagingHow the project declares its dependencies: pep621 for [project], poetry for [tool.poetry], setuptools for setup.cfg or setup.py, requirements when only requirements files were found.
projectsEvery package in the build: the root, followed by every member a [tool.uv.workspace] or [tool.poetry.group] path names.
projects[].pathDirectory of the package, relative to projectDir, . for the project directory itself.
projects[].manifestThat package's pyproject.toml, relative to projectDir.
projects[].nameName the manifest declares, or null when it declares none.
dependenciesWhat the manifests declare, split by whether the project asks for it itself.
dependencies.directEvery requirement any manifest declares, in manifest order.
dependencies.direct[].nameDistribution name, lower cased with runs of _, . and - normalised to -, which is how the index compares two names.
dependencies.direct[].versionConstraint exactly as written, such as >=0.27, or null when the requirement names none.
dependencies.direct[].scopesWhich group declares it: default, an extra's own name, a Poetry group's name, or the requirements file's stem.
dependencies.direct[].sourceManifest declaring it, relative to projectDir.
dependencies.direct[].pinnedWhether the constraint names one exact version with ==, which is the only operator that does.
dependencies.direct[].markerEnvironment marker the requirement carries, such as python_version < "3.12", or null when it carries none. A marked requirement is not installed everywhere.
dependencies.transitiveAlways empty. What a lock file resolves is not read: lockfiles says whether one is committed.
requirementsEvery requirements file that was read, relative to projectDir.
lockfilesEvery lock file committed, relative to projectDir. More than one means two installers each believe they own the environment.
unparsedEvery manifest the reader could not handle, so a guardrail can tell a project that declares nothing from one nobody could read.
unparsed[].pathPath of that manifest, relative to projectDir.
unparsed[].reasonWhat stopped the reader.

A collector that cannot finish prints what it had along with an incomplete key saying why, so the facts below the point it stopped are absent. A check that cares reads incomplete before the rest.

Example facts

The example facts collected by python collector:

json
{
  "directory": ".",
  "exists": true,
  "manifest": "pyproject.toml",
  "sources": [
    "pyproject.toml"
  ],
  "declared": {
    "version": "3.12.2",
    "source": ".python-version",
    "pinned": true
  },
  "backend": "hatchling.build",
  "packaging": "pep621",
  "projects": [
    {
      "path": ".",
      "manifest": "pyproject.toml",
      "name": "widget"
    }
  ],
  "dependencies": {
    "direct": [
      {
        "name": "httpx",
        "version": ">=0.27",
        "scopes": [
          "default"
        ],
        "source": "pyproject.toml",
        "pinned": false,
        "marker": null
      },
      {
        "name": "pydantic",
        "version": "~=2.6",
        "scopes": [
          "default"
        ],
        "source": "pyproject.toml",
        "pinned": false,
        "marker": null
      },
      {
        "name": "tomli",
        "version": "==2.0.1",
        "scopes": [
          "default"
        ],
        "source": "pyproject.toml",
        "pinned": true,
        "marker": "python_version < \"3.11\""
      },
      {
        "name": "pytest",
        "version": ">=8.0",
        "scopes": [
          "dev"
        ],
        "source": "pyproject.toml",
        "pinned": false,
        "marker": null
      },
      {
        "name": "ruff",
        "version": "==0.4.4",
        "scopes": [
          "dev"
        ],
        "source": "pyproject.toml",
        "pinned": true,
        "marker": null
      }
    ],
    "transitive": []
  },
  "requirements": [],
  "lockfiles": [
    "uv.lock"
  ],
  "unparsed": []
}

Collected for

GuardrailCategoryInputs
python/build-backend-declaredpythonprojectDir
python/lockfile-committedpythonprojectDir
python/single-lockfilepythonprojectDir
python/version-declaredpythonprojectDir

All collectors

Buildnote Limited
Registered in England and Wales, Reg: 16140412