CodeVetter · Repo Unpack · Contributor handoff
httpie/cli
Read httpie/core.py, httpie/cli/argparser.py, httpie/client.py and httpie/cli/definition.py before touching anything -- those four hold the control flow, the mutation order, the transport glue, and the CLI contract. The project is conservative: contributions without tests are explicitly rejected, behaviour changes require documentation, and CHANGELOG.md is maintained by hand. Generated artefacts (extras/man/*.1, docs/installation/README.md) must never be hand-edited. The safest edit boundaries are adding a new plugin, adding a declarative flag to a group in definition.py, or adding a formatter; the riskiest are reordering `_process_*` calls, changing the `collect_messages` generator contract, and anything in httpie/internal/.
- Read first, in this order: httpie/core.py (both entry shells), httpie/cli/argparser.py (the ordered mutation chain), httpie/client.py (requests translation), httpie/cli/definition.py (the flag contract), and httpie/context.py (the I/O boundary every test overrides).
- CONTRIBUTING.md is explicit that PRs with code changes but no tests will generally not be merged, and that behaviour changes need documentation -- treat a failing or absent test as a blocker, not a nice-to-have.
- Local workflow is `make install` (creates ./venv and pip-installs the dev+test extras editable), then `make test`; for a single module use the venv's pytest directly, and `make codestyle` before pushing since a separate CI job enforces flake8.
- Never hand-edit extras/man/*.1 or docs/installation/README.md -- both are generated by `make content` (via extras/scripts/generate_man_pages.py and docs/installation/generate.py) and auto-PR'd by the content.yml workflow.
- Man-page generation sets `HTTPIE_BUILDING_MAN_PAGES=1` before importing httpie, which flips `IS_MAN_PAGE` in definition.py so machine-specific defaults (e.g. the computed `--ciphers` list) are excluded -- any new environment-dependent default must respect that flag.
- Add CLI flags only through `group.add_argument(...)` in httpie/cli/definition.py using `Qualifiers.*` instead of raw argparse constants; `to_argparse` translates them and `serialize()` exports them, so a raw argparse string will break `httpie cli export-args`.
- Every `--OPTION` automatically gets a `--no-OPTION` counterpart handled by `_apply_no_options`, which resets `args.<dest>` to the action's default -- so new flags must have a meaningful `default`, not rely on absence.
- Trap: the order of `_process_*` calls in `parse_args` is load-bearing. `_setup_standard_streams` must run before `_process_output_options` (which reads `env.stdout_isatty`), and `_parse_items` must run before `_process_url`/`_process_auth`. Reordering silently changes defaults.
- Trap: `collect_messages` is a generator whose laziness is the streaming contract -- the session save at the end only runs if the caller exhausts it, so wrapping it in anything that short-circuits will silently stop sessions from persisting.
- Trap: do not print with `print()` or touch `sys.stdout` directly. All output must go through `env.stdout`/`env.stderr` or the Rich consoles, because `--quiet`, `--output`, `--download` and the whole test harness swap those streams.
- Trap: `--doctest-modules` is on, so any docstring in httpie/ containing a `>>>` prompt is a test. Editing `humanize_bytes`, `ensure_path_as_is`, `detect_encoding` or `KeyValueArgType.tokenize` means updating their examples too.
- Trap: tests hit the network by design. `pie.dev` is used for chunked-upload and remote tests via the `httpbin_with_chunked_support` and `remote_httpbin` fixtures, which skip (not fail) when DNS does not resolve -- a green local run may simply have skipped them.
- Safe edit boundary: adding a formatter, converter, auth or transport plugin touches only httpie/plugins/registry.py plus the new module, and existing plugin tests show the register/unregister pattern.
- Risky edit boundary: httpie/internal/ (daemons.py, daemon_runner.py, update_warnings.py) forks processes and swallows every exception via `_get_suppress_context`; set `developer_mode: true` in config.json to surface errors while working there.
- Version and release conventions: `httpie/__init__.py` holds `__version__` (3.2.4) and `__date__`, setup.cfg reads it via `attr:`, the Makefile refuses to publish a version containing 'dev', and `make build` rewrites `internal/__build_channel__.py` to `BUILD_CHANNEL = "pip"` for the PyPI artifact.
- Security-relevant changes have a disclosure path rather than a public issue: SECURITY.md directs reports to security@httpie.io or huntr.dev with a reproducer, severity and CWE id.