Skip to content

Adopt an existing project

Pyfaro works with the pyproject.toml and uv.lock you already have. This recipe adopts a small existing project: preview and apply init, sync, diagnose with doctor, then add the quality tools.

The example project is a src/ layout package with a requests dependency and no Pyfaro configuration:

[project]
name = "legacy"
version = "0.2.0"
requires-python = ">=3.11"
dependencies = ["requests>=2"]

[build-system]
requires = ["uv_build>=0.9.26,<0.10.0"]
build-backend = "uv_build"

1. Preview, then apply init

init previews by default and writes nothing:

$ pyfaro init
Initialization plan for .:
  Ensure the following configuration in pyproject.toml:

[tool.pyfaro]
schema-version = 1

  Run uv lock --no-build to create or update uv.lock.
Preview only: rerun with --apply to initialize this project.

When the plan looks right, apply it:

$ pyfaro init --apply
Initialization plan for .:
  Ensure the following configuration in pyproject.toml:

[tool.pyfaro]
schema-version = 1

  Run uv lock --no-build to create or update uv.lock.
Using CPython 3.13.11
Resolved 6 packages in 289ms
Initialized . with Pyfaro and uv.

Your existing metadata, dependencies and other tools' settings are left alone; Pyfaro adds only [tool.pyfaro] and creates uv.lock.

init --apply works the same on Linux, macOS and Windows: if uv fails or you press Ctrl+C, pyproject.toml and uv.lock are restored byte for byte, and running it again on an initialized project changes nothing. (Releases up to 0.5.0-beta.1 apply it on Linux only; there you can add [tool.pyfaro] with schema-version = 1 to pyproject.toml by hand and run pyfaro sync.)

2. Sync

$ pyfaro sync
Using CPython 3.13.11
Creating virtual environment at: .venv
Resolved 6 packages in 0.70ms
   Building legacy @ file://~/projects/legacy
      Built legacy @ file://~/projects/legacy
Prepared 1 package in 3ms
Installed 6 packages in 11ms
 + certifi==2026.10.19
 + charset-normalizer==3.5.2
 + idna==3.20
 + legacy==0.2.0 (from file://~/projects/legacy)
 + requests==2.34.2
 + urllib3==2.8.0

3. Diagnose with doctor

doctor reports what is configured and what is missing, without changing anything. Run before the sync, it flags the missing environment:

$ pyfaro doctor
OK: project: ~/projects/legacy
OK: pyproject.toml: valid Pyfaro configuration
OK: Python: /usr/bin/python3.14 (Python 3.14.8)
OK: uv: /home/you/.local/bin/uv (uv 0.9.26)
WARN: ruff: ruff: not found; install ruff in the project environment (.venv by default), activate an environment, or add it to PATH
WARN: ty: ty: not found; install ty in the project environment (.venv by default), activate an environment, or add it to PATH
WARN: pytest: pytest: not found; install pytest in the project environment (.venv by default), activate an environment, or add it to PATH
ERROR: dependencies: project interpreter ~/projects/legacy/.venv/bin/python is missing; run pyfaro sync
error: doctor found 1 blocking issue(s); follow the actions above and run pyfaro doctor again

After sync the blocking error is gone. The three warnings remain, because this project does not have the quality tools yet:

$ pyfaro doctor
OK: project: ~/projects/legacy
OK: pyproject.toml: valid Pyfaro configuration
OK: Python: ~/.local/share/uv/python/cpython-3.13.11-linux-x86_64-gnu/bin/python3.13 (Python 3.13.11)
OK: uv: /home/you/.local/bin/uv (uv 0.9.26)
WARN: ruff: ruff: not found; install ruff in the project environment (.venv by default), activate an environment, or add it to PATH
WARN: ty: ty: not found; install ty in the project environment (.venv by default), activate an environment, or add it to PATH
WARN: pytest: pytest: not found; install pytest in the project environment (.venv by default), activate an environment, or add it to PATH
OK: dependencies: 1 runtime distribution(s) installed in .venv
OK: doctor: no blocking issues

Warnings do not fail doctor; only ERROR lines do (exit code 1).

4. Add the quality tools

Put Ruff, ty and pytest in the dev group with add --dev, then sync. Pin them to the tested versions if you want results that match these examples; unpinned, uv resolves the latest release.

$ pyfaro add --dev "pytest==9.1.1" "ruff==0.16.10" "ty==0.0.85"
Resolved 14 packages in 11ms
$ pyfaro sync
Resolved 14 packages in 0.70ms
   Building legacy @ file://~/projects/legacy
      Built legacy @ file://~/projects/legacy
Prepared 1 package in 5ms
Uninstalled 1 package in 0.53ms
Installed 8 packages in 22ms
 + iniconfig==2.3.1
 ~ legacy==0.2.0 (from file://~/projects/legacy)
 + packaging==26.3
 + pluggy==1.7.0
 + pygments==2.21.0
 + pytest==9.1.1
 + ruff==0.16.10
 + ty==0.0.85
$ pyfaro tools
uv: /home/you/.local/bin/uv (uv 0.9.26)
ruff: ~/projects/legacy/.venv/bin/ruff (ruff 0.16.10)
ty: ~/projects/legacy/.venv/bin/ty (ty 0.0.85)
pytest: ~/projects/legacy/.venv/bin/pytest (pytest 9.1.1)

5. Run the first check

Adopted code rarely passes on the first try. Here the module has an unsorted import block and missing blank lines:

$ pyfaro check
unformatted: File would be reformatted
 --> src/legacy/__init__.py:3:1
  |
2 |
3 +
4 | def status(url):
  |

1 file would be reformatted
I001 [*] Import block is un-sorted or un-formatted
 --> src/legacy/__init__.py:1:1
  |
1 | import requests
  | ^^^^^^^^^^^^^^^
2 |
3 | def status(url):
  |
help: Organize imports
  |
2 |
3 +
4 | def status(url):
  |

Found 1 error.
[*] 1 fixable with the `--fix` option.
All checks passed!

== pyfaro check summary ==
format: failed (exit 1)
lint: failed (exit 1)
type: ok
check: failed (format, lint)

Both findings are safe to fix automatically:

$ pyfaro check --fix
fix: format and lint can be fixed automatically; type has no automatic fixes
Found 1 error (1 fixed, 0 remaining).
fix lint: ok
1 file left unchanged
fix format: ok
fix: rechecking without writing
1 file already formatted
All checks passed!
All checks passed!

== pyfaro check summary ==
format: ok
lint: ok
type: ok
check: ok

Review the diff with git diff, commit, and you are on the same footing as a project created by Pyfaro.

Next steps