PatcherClient#
- class PatcherClient(client_id: str | None = None, client_secret: str | None = None, server: str | None = None, *, config: ConfigManager | None = None, concurrency: int = 5, disable_cache: bool = False, debug: bool = False, enable_matching: bool = True, enable_homebrew: bool = False, integrations: Integrations | None = None, ui_config: dict | None = None, ignored_titles: list[str] | None = None, enable_installomator: bool | None = None)[source]#
Patcherβs top-level library entry point.
Composes the Jamf client, the catalog API client, and the data layer into one object library callers instantiate directly (or build from on-disk state via
from_state()). The CLI constructs the same object after itsSetupflow. Construction parameters are documented on__init__().Construct a
PatcherClientwith all collaborators wired up.Library callers pass credentials directly:
from patcher import PatcherClient async with PatcherClient( client_id="...", client_secret="...", server="https://myorg.jamfcloud.com", ) as patcher: summaries = await patcher.jamf.get_summaries( await patcher.jamf.get_policies() ) # connection pool released here
An in-memory
ConfigManageris built internally. No keyring backend required, no plist mutation, no disk I/O on construction.PatcherClientis usable as an async context manager (preferred for clean shutdown) or as a regular object (callaclose()manually when done).Exactly one of
(client_id + client_secret + server)orconfigmust be provided.Note
The
config=parameter exists for internal CLI use, where theSetupflow has already populated keyring with credentials. Library callers should use the credentials path above.- Parameters:
client_id (str | None) β Jamf Pro API client ID.
client_secret (str | None) β Jamf Pro API client secret.
server (str | None) β Jamf Pro instance URL (e.g.
https://myorg.jamfcloud.com).config (
ConfigManager| None) β ExistingConfigManagerinstance, mutually exclusive with the credentials arguments.concurrency (int) β Maximum concurrent API requests. Defaults to 5, the recommended ceiling per the Jamf Developer Guide.
disable_cache (bool) β If True,
DataManagerskips on-disk patch-data caching.debug (bool) β Enables debug-mode handling in collaborators (notably disables the spinner animation when set in the CLI path).
enable_matching (bool) β If False,
apiisNoneand all catalog matching (Installomator labels and Homebrew Cask) is skipped. Defaults to True.enable_homebrew (bool) β Deprecated force-on override for the Homebrew Cask integration; prefer
integrations. When True, enables thehomebrewsource regardless ofintegrations. Defaults to False (no override).integrations (
Integrations| None) β Per-source matching toggles (Integrations). Gates which sources are paged as match candidates and recorded on each titleβssourcesmap. Defaults to all sources enabled.ui_config (dict | None) β Optional dict of UI settings (header text, footer, font paths, header color, etc.) for PDF/HTML report styling. Defaults to
UIDefaultsvalues.ignored_titles (list[str] | None) β Extra Jamf-title skip patterns merged with the built-in
IGNORED_TITLESduring matching. Defaults to none.enable_installomator (bool | None) β Deprecated alias for
enable_matching, kept for backward compatibility. Emits aDeprecationWarningwhen passed; remove in favor ofenable_matching.
- Raises:
PatcherError β If neither credentials nor
configare provided.
- classmethod from_state(**overrides: Any) PatcherClient[source]#
Construct a
PatcherClientusing state already persisted on this Mac.Reads Jamf credentials from the macOS keychain, UI customization from the property list, and the
enable_matchingtoggle plus the per-sourceintegrations. Equivalent to what thepatcherctlCLI does on startup; useful for library callers running on a workstation that has already been through the setup wizard.Any keyword argument accepted by
__init__can be passed as an override (commonlyconcurrencyordebug).- Parameters:
overrides (Any) β Optional
PatcherClientconstructor kwargs that take precedence over whatβs read from on-disk state.- Returns:
A configured
PatcherClientready to call.- Return type:
- Raises:
PatcherError β If keychain credentials are missing (i.e.
patcherctlsetup hasnβt completed on this machine).
- async fetch_patches(*, match_installomator: bool = True, match_homebrew: bool | None = None, include_ios: bool = False, sort_by: str | None = None, omit_recent_hours: int | None = None) list[PatchTitle][source]#
Fetch patch summaries in one call. The library equivalent of what the CLIβs
exportflow gathers before writing a report.Composes the granular pipeline: policies β summaries β (optional Installomator match) β (optional iOS append) β (optional sort/filter). Equivalent to manually chaining
self.jamf.get_policies,self.jamf.get_summaries,match_titles(),append_ios_status(),omit_recent(), andsort_titles().- Parameters:
match_installomator (bool) β If True (default), match each title to its Installomator label via the Patcher API catalog (
match_titles()). No-op whenenable_matching=Falsewas passed at construction time.match_homebrew (bool | None) β Per-call override for the Homebrew Cask source.
Trueforces it on,Falseforces it off,None(default) uses theintegrationsconfigured at construction. Rides on the same match pass as Installomator, so it is a no-op whenmatch_installomatoris False orapiisNone.include_ios (bool) β If True, append per-iOS-version summaries to the returned list. Costs additional Jamf API calls.
sort_by (str | None) β Optional attribute name to sort titles by (e.g.
"released","completion_percent"). Normalized to lowercase + underscores.omit_recent_hours (int | None) β If provided, drop titles released within the past
Nhours. Mirrors the CLIβs--omitflag.
- Returns:
List of
PatchTitleobjects, optionally enriched and filtered.- Return type:
list[
PatchTitle]- Raises:
PatcherError β If the Jamf API calls fail or sort_by names an attribute that doesnβt exist on
PatchTitle.
- async analyze(titles: list[PatchTitle], criteria: str, *, threshold: float | None = 70.0, top_n: int | None = None, where: dict | None = None) list[PatchTitle][source]#
Filter and sort patch titles by a named criterion. The library equivalent of the CLIβs
analyzesubcommand.Accepts a CLI-style criterion string (e.g.
"most-installed","below-threshold"). Library callers who want type-checked, autocomplete-friendly access should constructTitleFilterdirectly and invoke the matching method.Changed in version 3.0: The
criteriaparameter no longer acceptsFilterCriteriaenum values; the enum was removed in favor ofTitleFiltermethods. Pass the kebab-case string form, or useTitleFilterdirectly.- Parameters:
titles (list[
PatchTitle]) β Patch titles to analyze. Typically the output offetch_patches().criteria (str) β CLI-style criterion (e.g.
"most-installed"). SeeTitleFilterfor the full list.threshold (float | None) β Completion-percent threshold for
below-thresholdcriterion. Ignored by other criteria. Defaults to 70.0.top_n (int | None) β If provided, return at most
top_nresults. Thebelow-thresholdandzero-completioncriteria ignore this (they return all matching titles).where (dict | None) β Optional pre-filter applied before the criterion runs. Keys are
min_compliance/min_hosts/released_after; unknown keys raisePatcherError.
- Returns:
Filtered + sorted list of
PatchTitleobjects.- Return type:
list[
PatchTitle]- Raises:
PatcherError β If
criteriais not a recognized value.
- async analyze_excel(excel_path: str | Path, criteria: str, *, threshold: float | None = 70.0, top_n: int | None = None, where: dict | None = None) list[PatchTitle][source]#
Filter and sort patch titles loaded from a saved Excel report.
Hydrates
PatchTitleobjects fromexcel_path(a previously-exported Patcher report) and analyzes those instead of the cached snapshot β the library equivalent ofpatcherctl analyze --excel-file. Useful for re-analyzing a shared or historical export without re-fetching from Jamf.- Parameters:
excel_path (str |
pathlib.Path) β Path to a previously-exported Patcher Excel report.criteria (str) β CLI-style filter criterion.
threshold (float | None) β Completion-percent cutoff for
below-threshold.top_n (int | None) β Optional result cap. Ignored by
below-thresholdandzero-completion.where (dict | None) β Optional pre-filter (
min_compliance/min_hosts/released_after), same asanalyze().
- Returns:
Filtered + sorted titles.
- Return type:
list[
PatchTitle]- Raises:
PatcherError β If the Excel file canβt be read or yields no titles.
- async analyze_trend(criteria: str, *, save_to: str | Path | None = None)[source]#
Compute trend analysis across every cached patch dataset.
Library equivalent of
patcherctl analyze --all-time --criteria. Reads every cached snapshot in the data cache and builds a trend DataFrame keyed on the requested criterion.Changed in version 3.0: The
criteriaparameter no longer acceptsTrendCriteriaenum values; the enum was removed in favor ofTrendAnalysismethods. Pass the kebab-case string form, or useTrendAnalysisdirectly.- Parameters:
- Returns:
Trend results as a ~pandas.DataFrame.
- Return type:
- Raises:
PatcherError β If fewer than two cached snapshots exist or
criteriais unrecognized.
- async diff(*, since: timedelta | None = None, all_time: bool = False, between: tuple[date, date] | None = None, no_fetch: bool = False) DiffResult[source]#
Pairwise comparison between two patch-state snapshots.
Default (no flags): live fetch via
fetch_patches()compared against the most-recent cached snapshot. Override behavior with one of the keyword arguments below.Added in version 3.1.
- Parameters:
since (timedelta | None) β When set, compare against the earliest cached snapshot in the trailing window (e.g.
timedelta(days=30)for βwhat changed in the last 30 daysβ).all_time (bool) β When True, compare against the earliest cached snapshot ever recorded. Mutually exclusive with
since.between (tuple[date, date] | None) β Two-date pair selecting cached snapshots closest to each date. Implies cache-only (no live fetch). Cannot be combined with
sinceorall_time.no_fetch (bool) β When True, skip the live fetch and compare two cached snapshots only. Defaults to the second-most-recent and most-recent unless
sinceorall_timeis also passed.
- Returns:
Structured delta covering added, removed, and changed titles.
- Return type:
- Raises:
PatcherError β On invalid flag combinations, or when no cached snapshots are available for the requested mode.
- async detect_drift(*, slug: str | None = None, vendor: str | None = None, source: str | None = None, limit: int = 100, offset: int = 0) DriftResponse | DriftEntry | None[source]#
Cross-source version drift detection via the Patcher catalog API.
Without
slug: returns aDriftResponselisting apps whose upstream sources disagree on the current version. Withslug: returns aDriftEntryfor that single app, orNoneif the app doesnβt exist or has no drift.Works without
enable_matching; the catalog API is constructed on demand when needed.Added in version 3.1.
- Parameters:
slug (str | None) β When set, narrow to a single app. Filters below are ignored.
Nonereturns the paginated list.vendor (str | None) β Case-insensitive exact vendor match for list mode.
source (str | None) β Drop list entries where this source did not participate in the disagreement.
limit (int) β Max entries per list page.
offset (int) β Entries to skip before the list page.
- Returns:
Drift result. Shape depends on whether
slugwas set.- Return type:
DriftResponse|DriftEntry| None- Raises:
PatcherError β If list-mode filters are passed with a slug.
- async export(titles: list[PatchTitle], *, output_dir: str | Path, formats: set[str] | None = None, report_title: str | None = None, date_format: str = '%B %d %Y', header_color: str | None = None, analysis: bool = False, device_reports: dict[str, list] | None = None, coverage: list[str] | None = None) dict[str, str][source]#
Export patch titles to one or more report formats. Builds and caches the canonical snapshot via
DataManager, then renders it throughExporter.- Parameters:
titles (list[
PatchTitle]) β Patch titles to include in the report.output_dir (str | Path) β Directory to write report file(s) into.
formats (set[str] | None) β Set of format strings to emit. Defaults to all four:
{"excel", "html", "pdf", "json"}.report_title (str | None) β Title used in PDF/HTML headers. Defaults to the
header_textvalue from this clientβsui_config.date_format (str) β Date format for PDF/HTML headers (strftime). Defaults to
"%B %d %Y".header_color (str | None) β Hex color for the HTML report header background. Falls back to
header_colorwhenNone.analysis (bool) β If True, treats this as an analysis report (affects HTML output path naming).
device_reports (dict[str, list] | None) β Optional per-title device detail data for Excelβs per-title sheets.
coverage (list[str] | None) β Source tokens to render as opt-in
Y/Ncoverage columns.None(default) renders no integration columns.
- Returns:
Mapping of format β output path for every report written.
- Return type:
- async reset(kind: Literal['full', 'UI', 'creds', 'cache'], *, credential: Literal['url', 'client_id', 'client_secret'] | None = None) None[source]#
Reset persisted state on this Mac. Library equivalent of
patcherctl reset <kind>.Unlike the CLI,
reset()does not re-launch the setup wizard after a full reset β library callers can re-construct aPatcherClientthemselves once theyβve supplied new credentials.Kinds:
"cache"β empty the on-disk patch-data cache. Works in any mode."creds"β delete Jamf credentials from the keychain. Passcredential=to scope to a single key. Requires keychain-backed mode (raises in in-memory mode)."UI"β clear UI customization from the property list. Requires keychain-backed mode."full"β every reset above, plus clears thesetup_completedflag so the nextpatcherctlinvocation re-runs the wizard.
- Parameters:
- Raises:
PatcherError β If
kindis not"cache"and this client was constructed with in-memory credentials (nothing on disk to reset).- Return type:
None