Files
camoufox/pythonlib/README.md
T
Jake WriterandClaude Opus 5.5 11969fa44a Prerelease on every tested merge, promote by tag, and pair each library release with its browser (#810)
* Pair each library release with the browser build it was tested with

Nothing tied a library release to a browser build: `camoufox fetch` took the
newest build in a channel, and a launch used whatever config.json marked
active, so an upgraded library could run a browser it was never tested with,
and an old library would pick up a newer, incompatible browser.

A released package now carries browser-pin.json, naming the browser release
built from the same sources. With it, and no explicit choice by the user:

- fetch installs exactly that build (no prerelease prompt: it is the build
  this release was tested with, prerelease or not);
- a launch uses exactly that build, whatever else is installed or active,
  and reports it as not installed rather than falling back to another;
- the fetcher's automatic install (TypeScript's first run) takes only it.

An explicit `camoufox set` still wins, with a one-time warning at launch;
`camoufox set --release` returns to the paired build, and `camoufox active`
says which is in use. The checked-in pin is `{}`, so development checkouts
follow their channel as before.

Also: prerelease library versions (0.5.8b1, 0.5.8-beta.1) parse as their
release; they were read as 0.5.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Release a prerelease on every tested merge; promote to stable by tag

Every merge to main whose tests pass now publishes a prerelease of all
three artifacts, and pushing vX.Y.Z on a tested main commit promotes it.

- Build and Release runs after Tests on main. It builds the browser only
  when its sources changed (ci.browser_inputs.source_digest: every browser
  input, not counting the release number). Each build gets the next unused
  beta.N on a release commit beside main -- main is protected -- and is
  published as a GitHub prerelease, not a draft, with its source digest in
  the notes.
- Publish to pypi follows it: <next>bN on PyPI, then Publish to npm puts
  <next>-beta.N under the `next` dist-tag. Both are stamped with the browser
  release built from the same sources.
- A vX.Y.Z tag is refused unless the commit is on main and `All tests
  passed` succeeded on it. The paired browser prerelease then becomes the
  stable, latest release (no rebuild, so users get the tested binaries), and
  X.Y.Z goes to PyPI and npm `latest`.

The tested commit travels between workflows as an artifact: a workflow_run
is told main's head, so two quick merges would otherwise publish the second,
untested one. ci/release.py holds the planning, stamping and promotion,
unit-tested in ci/tests/test_release.py.

Also fixes two checks that failed the manual release already: vermin
targeted Python 3.8 exactly, against a package that declares ^3.10 and a
code base that needs 3.9, and check-pack compared npm and PyPI prerelease
versions as strings, although each registry spells them differently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test driver-only pull requests against the release paired with their sources

The scope step matched the release tag named by upstream.sh. With release
numbers now allocated per build, that number is a floor, not a release, so
driver-only pull requests would nearly always rebuild, or fetch a build other
than the one their sources produce. It now asks `ci.release paired` for the
release built from exactly this tree's browser sources, and fetch-browser
installs it through the same pin a released package carries.

Documents the release flow in ci/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* pythonlib: replace asyncio.to_thread so the 3.8 vermin gate passes

publish-pypi.yml checks the package with
`vermin . --eval-annotations --target=3.8 --violations camoufox/`, and
asyncio.to_thread (Python 3.9+) in _resolve_proxy_geo failed it, stopping
the 0.5.7 release. loop.run_in_executor does the same off-loop lookup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pair with releases cut before the digest marker, and test the pairing against the step

The scope step now asks ci.release paired, which only knew releases whose notes
carry a source digest. None does yet: v156.0.1-beta.32, the release built from
main's sources, predates the marker. So every driver-only pull request would have
rebuilt the browser, the first merge would have cut a duplicate beta.33, and the
two scope tests in ci/tests/test_ci.py -- which ran the step in a scratch repo
where ci.release did not import -- failed.

find_paired falls back to the tag upstream.sh names when that release is
published (a prerelease counts; a draft does not) and no browser source changed
since, listing the files that did when they have. browser-plan and promote use
the same lookup. paired takes --root and --releases so the tests run the
workflow's own step against a scratch repo and a fixed release list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Release from one workflow, with trusted publishing

The release chain was four workflows linked by workflow_run, with the
tested commit carried between them as an artifact; a browser release
number committed beside main; pairing data in HTML comments in release
notes; a stored PyPI token; and packages rebuilt at each publish.

release.yml now does all of it with `needs`:

- On a push to main it calls tests.yml on the pushed commit (tests.yml
  loses its own push trigger), then builds the browser only when its
  sources changed, and publishes a library prerelease only when something
  a package ships changed. Docs- and CI-only merges publish nothing.
- A browser release's number lives only in its tag, which points at the
  tested main commit; `ci.release set-build` writes it into the build's
  working tree. Nothing is committed.
- Each browser release carries a manifest.json asset (source digest,
  commit), which is what a library pairs by. Builds are attested with
  actions/attest-build-provenance.
- Both packages are built once, in build-library, and the publish jobs
  upload exactly those files. PyPI and npm use trusted publishing; no
  credential is stored.
- A vX.Y.Z tag builds and checks both packages before promoting the
  paired browser and publishing.
- Every published library version is tagged (vX.Y.ZbN for a prerelease),
  which is how the next merge tells whether the library changed.
- A failed publish is retried with "Re-run failed jobs"; the retry-only
  workflow_dispatch path is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:46:41 +00:00

316 lines
9.9 KiB
Markdown

<div align="center">
# Camoufox Python Interface
#### Lightweight wrapper around the Playwright API to help launch Camoufox.
</div>
> [!NOTE]
> All the latest documentation is available [here](https://camoufox.com/python).
---
## What is this?
This Python library wraps around Playwright's API to help automatically generate & inject unique device characteristics (OS, CPU info, navigator, fonts, headers, screen dimensions, viewport size, WebGL, addons, etc.) into Camoufox.
It uses [fpgen](https://github.com/scrapfly/fingerprint-generator) under the hood to generate fingerprints that mimic the statistical distribution of device characteristics in real-world traffic.
In addition, it will also calculate your target geolocation, timezone, and locale to avoid proxy protection ([see demo](https://i.imgur.com/UhSHfaV.png)).
---
## Installation
First, install the `camoufox` package:
```bash
pip install -U camoufox[geoip]
```
The `geoip` parameter is optional, but heavily recommended if you are using proxies. It will download an extra dataset to determine the user's longitude, latitude, timezone, country, & locale.
Next, download the Camoufox browser:
```bash
camoufox fetch
```
`fetch` also installs fpgen's model, pinned by sha256, into fpgen's package directory. Otherwise the first generated fingerprint installs it. Run `fetch` as that directory's owner if the browser will run as another user, e.g. while building a Docker image.
To uninstall, run `camoufox remove`.
---
# Installing multiple Camoufox versions & from other repos
## UI Manager
Manage installed browsers, active version, IP geolocation databases, and package info. Basically a Qt front end for the Python CLI tool.
More updates on it will be coming soon.
<img width="802" height="552" alt="ui-screenshot" src="https://github.com/user-attachments/assets/6668f8f0-5b08-4c36-bbea-fea4baeccc9c" />
<hr width=50>
To use the gui, install Camoufox with the `[gui]` extra:
```bash
pip install 'camoufox[gui]'
```
To launch:
```bash
camoufox gui
```
---
## CLI Manager
#### Demonstration
https://github.com/user-attachments/assets/992b1830-6b21-4024-9165-728854df1473
<details>
<summary>See help message</summary>
```
$ python -m camoufox --help
Usage: python -m camoufox [OPTIONS] COMMAND [ARGS]...
╭─ Options ─────────────────────────────────────────────────────────────────────────────╮
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────────────╮
│ active Print the current active version │
│ fetch Install the active version, or a specific version │
│ gui Launch the Camoufox Manager GUI (requires PySide6) │
│ list List Camoufox versions │
│ path Print the install directory path │
│ remove Remove downloaded data. By default, this removes everything. │
│ Pass --select to pick a browser version to remove. │
│ server Launch a Playwright server │
│ set Set the active Camoufox version to use & fetch. │
│ By default, this opens an interactive selector for versions and settings. │
│ You can also pass a specifier to activate directly: │
│ Pin version: │
│ camoufox set official/stable/134.0.2-beta.20 │
│ Automatically find latest in a channel source: │
│ camoufox set official/stable │
│ sync Sync available versions from remote repositories │
│ test Open the Playwright inspector │
│ version Display version, package, browser, and storage info │
╰───────────────────────────────────────────────────────────────────────────────────────╯
```
</details>
### `sync`
Pull a list of release assets from GitHub.
```bash
> camoufox sync
Syncing repositories...
Official... 24 versions
CoryKing... 2 versions
Synced 26 versions from 2 repos.
```
<hr width=50>
### Which browser build is used
Each camoufox release is paired with the one browser build it was built and tested with. By default, `camoufox fetch` installs exactly that build and every launch uses it. That holds even when other builds are installed, and even when the paired build is a prerelease (a prerelease of this package pairs with a prerelease browser). Upgrading the package therefore never runs a browser it was not tested with. Run `camoufox fetch` after upgrading to install the new pairing.
Choosing a channel or a build with `camoufox set` overrides the pairing. The choice is kept, and a launch warns that the build differs from the paired one. `camoufox set --release` goes back to the paired build.
A development checkout (installed from the repository, not from PyPI) is paired with nothing and follows its channel, `official/stable` by default.
<hr width=50>
### `set`
Choose a version channel or pin a specific version, overriding the paired build. Can also be called with a specifier to activate directly.
Interactive selector:
```bash
> camoufox set
```
You can also pass a specifier to pin a specific version or choose a channel to follow directly. Following `official/stable` pulls the latest stable version from the official repo on `camoufox fetch`:
```bash
> camoufox set official/stable
```
Follow latest prerelease version from the official repo, if applicable:
```bash
> camoufox set official/prerelease
```
Pin a specific version:
```bash
> camoufox set official/stable/134.0.2-beta.20
```
Go back to the build this release is paired with:
```bash
> camoufox set --release
```
<hr width=50>
### `active`
Prints the current active version string:
```bash
> camoufox active # A released package uses its paired build by default
official/prerelease/156.0.1-beta.33 (1a2b3c4d) (paired with this release)
```
```bash
> camoufox set coryking/stable/142.0.1-fork.26
Pinned: coryking/stable/142.0.1-fork.26
Run 'camoufox fetch' to install.
> camoufox active # A specific version is pinned
coryking/stable/142.0.1-fork.26 (not installed)
```
<hr width=50>
### `fetch`
Install the browser build this release is paired with, or, after `camoufox set`, the latest version from the chosen channel. This will also automatically sync repository assets.
```bash
> camoufox fetch # Install the paired build (or the latest in the chosen channel)
```
To download the latest from a different channel, or pin a version:
```bash
> camoufox set coryking/stable
> camoufox fetch # Will download the latest release from CoryKing's repo for now on
```
Or pass in the identifier to download directly without activating it:
```bash
> camoufox fetch official/stable/135.0-beta.25 # Install a specific version
```
<hr width=50>
### `list`
List installed or all available Camoufox versions as a tree.
```bash
> camoufox list # show installed versions
> camoufox list all # show all available versions from synced repos
> camoufox list --path # show full install paths
```
<hr width=50>
### `remove`
By default, removes the entire camoufox data directory.
```bash
> camoufox remove
> camoufox remove -y # skip confirmation prompt
```
Remove a specific version:
```bash
> camoufox remove official/stable/134.0.2-beta.20
```
Interactively select a version to remove:
```bash
> camoufox remove --select
```
<hr width=50>
### `version`
Display the Python package version, active browser version, channel, and update status.
```bash
> camoufox version
Python Packages
Camoufox v0.5.7
fpgen v1.3.0
Playwright v1.62.0
Browser
Active official/stable/152.0.4-beta.31
Current browser v152.0.4-beta.31
Installed Yes
Latest in official/stable? Yes
Last Sync 2026-03-07 00:23
GeoIP
Database MaxMind GeoLite2
Updated 2026-03-07 00:24
Storage
Install path /home/name/.cache/camoufox
Browser(s) directory size 1.2 GB
GeoIP database size 40.7 MB
Config file /home/name/.cache/camoufox/config.json
Repo cache /home/name/.cache/camoufox/repo_cache.json
```
<hr width=50>
### `path`
Print the install directory path.
```bash
> camoufox path
/home/name/.cache/camoufox
```
<hr width=50>
### `test`
Open Camoufox with the Playwright inspector for debugging.
```bash
> camoufox test
> camoufox test https://example.com
```
<hr width=50>
### `server`
Launch a remote Playwright server.
```bash
> camoufox server
```
---
## Usage
All of the latest stable documentation is available at [camoufox.com/python](https://camoufox.com/python).