pyhanko.sign.diff_analysis.commons module

Module defining common helpers for use by rules and policies.

In principle, these aren’t relevant to the high-level validation API.

pyhanko.sign.diff_analysis.commons.qualify(level: pyhanko.sign.diff_analysis.policy_api.ModificationLevel, rule_result: Generator[pyhanko.sign.diff_analysis.commons.X, None, pyhanko.sign.diff_analysis.commons.R], transform: Callable[[pyhanko.sign.diff_analysis.commons.X], pyhanko.sign.diff_analysis.rules_api.ReferenceUpdate] = <function <lambda>>) Generator[Tuple[pyhanko.sign.diff_analysis.policy_api.ModificationLevel, pyhanko.sign.diff_analysis.rules_api.ReferenceUpdate], None, pyhanko.sign.diff_analysis.commons.R]

This is a helper function for rule implementors. It attaches a fixed modification level to an existing reference update generator, respecting the original generator’s return value (if relevant).

A prototypical use would be of the following form:

def some_generator_function():
    # do stuff
    for ref in some_list:
        # do stuff
        yield ref

    # do more stuff
    return summary_value

# ...

def some_qualified_generator_function():
    summary_value = yield from qualify(

Provided that some_generator_function yields ReferenceUpdate objects, the yield type of the resulting generator will be tuples of the form (level, ref).

  • level – The modification level to set.

  • rule_result – A generator that outputs references to be whitelisted.

  • transform – Function to apply to the reference object before appending the modification level and yielding it. Defaults to the identity.


A converted generator that outputs references qualified at the modification level specified.

pyhanko.sign.diff_analysis.commons.safe_whitelist(old: pyhanko.pdf_utils.reader.HistoricalResolver, old_ref, new_ref) Generator[pyhanko.pdf_utils.generic.Reference, None, None]

Checks whether an indirect reference in a PDF structure can be updated without clobbering an older object in a way that causes ramifications at the PDF syntax level.

The following are verified:

  • Does the old reference point to a non-stream object?

  • If the new reference is equal to the old one, does the new reference point to a non-stream object?

  • If the new reference is not equal to the old one, is the new reference a newly defined object?

This is a generator for syntactical convenience and integration with internal APIs, but it will always yield at most one element.

pyhanko.sign.diff_analysis.commons.compare_key_refs(key, old: pyhanko.pdf_utils.reader.HistoricalResolver, old_dict: pyhanko.pdf_utils.generic.DictionaryObject, new_dict: pyhanko.pdf_utils.generic.DictionaryObject) Generator[pyhanko.pdf_utils.generic.Reference, None, Tuple[Optional[pyhanko.pdf_utils.generic.PdfObject], Optional[pyhanko.pdf_utils.generic.PdfObject]]]

Ensure that updating a key in a dictionary has no undesirable side effects. The following scenarios are allowed:

  1. adding a key in new_dict

  2. replacing a direct value in old_dict with a reference in new_dict

  3. the reverse (allowed by default)

  4. replacing a reference with another reference (that doesn’t override anything else)

The restrictions of safe_whitelist apply to this function as well.

Note: this routine is only safe to use if the structure of the resulting values is also checked. Otherwise, it can lead to reference leaks if one is not careful.

pyhanko.sign.diff_analysis.commons.compare_dicts(old_dict: pyhanko.pdf_utils.generic.PdfObject, new_dict: pyhanko.pdf_utils.generic.PdfObject, ignored: Set[str] = frozenset({}), raise_exc=True) bool

Compare entries in two dictionaries, optionally ignoring certain keys.


Throw SuspiciousModification if the argument is a stream object.