How to patch a non-ops dependency¶
--patch is not limited to ops. Any package the charm declares can be patched, using the same PEP 508 grammar. Use this to:
Pin a transitive dependency to a candidate release.
Point a library at a fork or a development branch.
Test the fleet against a local checkout of a library you maintain.
Pin to a PyPI version¶
hyrum check unit --patch 'requests==2.31.0'
Specifiers other than == are accepted too:
hyrum check unit --patch 'requests>=1.2,<2'
If no patch is given hyrum defaults to patching ops to canonical:main. As soon as one --patch is given, the default goes away — only the packages you list are patched. To run with both ops and another dependency patched, pass --patch for each:
hyrum check unit \
--patch 'ops @ canonical:main' \
--patch 'requests==2.31.0'
--patch and --no-patch are mutually exclusive. Pass --patch once per package; specifying it twice for the same package is an error.
Patch from a git source¶
hyrum check unit --patch 'requests @ git+https://github.com/psf/requests@main'
A bare https://… URL is accepted too:
hyrum check unit --patch 'requests @ https://github.com/psf/requests@main'
#subdirectory=<path> is honoured for git sources where the package lives in a monorepo subdirectory:
hyrum check unit --patch 'mylib @ git+https://github.com/me/monorepo@main#subdirectory=packages/mylib'
The owner:branch shorthand (canonical:fix/X) is accepted only for ops and for charmlibs-* packages. For any other package, pass an explicit git+<url> or bare https://… URL.
Patch from a local checkout¶
hyrum check unit --patch 'mylib @ ~/code/mylib'
hyrum check unit --patch 'mylib @ /abs/path/mylib'
hyrum check unit --patch 'mylib @ file:///abs/path/mylib'
Patch a charm library from canonical/charmlibs¶
Packages named charmlibs-* get their own shorthand, pointing the dependency at a branch of the charmlibs monorepo:
hyrum check unit --patch 'charmlibs-nginx_k8s @ canonical:main'
The subdirectory inside the monorepo is taken from the package name verbatim, so type the separators the way the directory exists on disk. Interface libraries live under interfaces/, and the shorthand follows:
# nginx_k8s/ in the monorepo:
hyrum check unit --patch 'charmlibs-nginx_k8s @ canonical:main'
# interfaces/k8s-service/ in the monorepo:
hyrum check unit --patch 'charmlibs-interfaces-k8s-service @ canonical:main'
The name used to match the charm’s own dependency declaration is reduced to a single form separately, so the match works whichever separators you type.
A charmlib must be patched from a git source. A version pin (charmlibs-apt==1.0.0) or a local path is rejected; use the generic form above if you need those.
Most charms do not depend on any given charm library. Those charms are reported as skipped with the reason dep_not_declared, not as failures.
Replace a vendored charm library¶
Charms have historically vendored charm libraries as lib/charms/<author>/v<n>/<lib>.py, imported as charms.<author>.v<n>.<lib>. To find out whether a charm still works when that vendored copy is replaced by the equivalent package, use the -> form:
hyrum check unit --patch 'charms.operator_libs_linux.v0.apt -> charmlibs-apt==1.0.0'
The left side is the dotted import path of the vendored file. The right side is the replacement package, in any of the forms above — including a git source from the charmlibs monorepo:
hyrum check unit \
--patch 'charms.operator_libs_linux.v0.apt -> charmlibs-apt @ git+https://github.com/canonical/charmlibs@main#subdirectory=apt'
For each charm, hyrum:
Deletes the vendored
lib/charms/<author>/v<n>/<lib>.pyfile.Adds the replacement package to the charm’s dependency declarations.
Rewrites imports of the old dotted module to the new one (
charmlibs.<lib>) acrosssrc/andtests/.Removes the matching
charm-libsentry fromcharmcraft.yaml, so charmcraft does not re-fetch the library.Restores every touched file, including the deleted one, when the run finishes.
Charms that do not vendor that library are reported as skipped with the reason vendored_lib_absent.
Combine with an ops patch¶
--patch may be repeated, with one occurrence per package:
hyrum check unit \
--patch 'ops @ canonical:fix/my-change' \
--patch 'requests==2.31.0' \
--patch 'mylib @ ~/code/mylib'
Hyrum applies each patcher in turn; lockfiles are regenerated once after all rewrites complete.
What gets rewritten¶
The generic dependency patcher behaves like the ops-source patcher except that the ops[testing] / ops[tracing] companion handling is specific to ops. For any other package, hyrum rewrites declarations in:
requirements.txt(pip)pyproject.tomlunder[project.dependencies],[project.optional-dependencies],[dependency-groups](PEP 735),[tool.poetry.dependencies], and[tool.uv.sources]The corresponding lockfile (
poetry.lockoruv.lock) is regenerated when present
Charms whose declarations cannot be parsed are reported as patcher_error rather than failed, so an infrastructure problem is not mis-attributed to a charm regression. Charms that do not declare the package at all are reported as skipped, since there was nothing to patch. See How to triage a run.
For what each status in the summary means, see the output reference.