o
    Pcy                     @   sl  d Z ddlZddlmZmZmZmZmZmZ ddl	m
Z
mZ ddlmZmZ ddlmZ ddlmZmZ dd	lmZ eeZg d
Zeee
j ee
j f Zdd Zdedeeddf fddZdede
jde
jdeedef fddZedZedZ dd fdedee def dee gef deeeef def fddZ!e" dfdededee# de$fd d!Z%dS )"z
Module defining common helpers for use by rules and policies.

In principle, these aren't relevant to the high-level validation API.
    N)Callable	GeneratorOptionalSetTupleTypeVar)genericmisc)	PdfObject	Reference)HistoricalResolver   )ModificationLevelSuspiciousModification)ReferenceUpdate)qualifysafe_whitelistcompare_key_refscompare_dictsassert_not_streamc                 C   s"   t | tjrtd| j ddS )zT
    Throw :class:`.SuspiciousModification` if the argument is a stream object.
    z!Unexpected stream encountered at !N)
isinstancer   StreamObjectr   container_ref)obj r   `/var/www/milkbook_backend/env/lib/python3.10/site-packages/pyhanko/sign/diff_analysis/commons.pyr      s
   r   oldreturnc                 c   sT    |r	t |  ||krt |  |V  dS | |r"|V  dS td| d)a~  
    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.
    zUpdate clobbers or reuses z in an unexpected way.N)r   
get_objectis_ref_availabler   )r   old_refnew_refr   r   r   r   &   s   



r   old_dictnew_dictc                 c   s    z| | }t|tjr|j}| }nd}W n ty%   d }}Y nw z| | }t|tjr:|j}| }nd}W n tyS   |durPtd|  dY dS w |durat|||E dH  ||fS )aj  
    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.
    NzKey z was deleted from dictionary)NN)	raw_getr   r   IndirectObject	referencer   KeyErrorr   r   )keyr   r#   r$   	old_valueold_value_ref	new_valuenew_value_refr   r   r   r   G   s8   




r   RXc                 C   s   | S Nr   )xr   r   r   <lambda>   s    r2   levelrule_result	transformc                    s   t | fddS )aL  
    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:

    .. code-block:: python

        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(
                ModificationLevel.FORM_FILLING,
                some_generator_function()
            )

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

    :param level:
        The modification level to set.
    :param rule_result:
        A generator that outputs references to be whitelisted.
    :param transform:
        Function to apply to the reference object before appending
        the modification level and yielding it.
        Defaults to the identity.
    :return:
        A converted generator that outputs references qualified at the
        modification level specified.
    c                    s    | fS r0   r   )refr3   r5   r   r   r2      s    zqualify.<locals>.<lambda>)r	   map_with_return)r3   r4   r5   r   r7   r   r   ~   s   .r   Tignoredc           	      C   s   t | tjstdt |tjstdt|  t| t| | }t|  | }||kr@|r>td| d| ddS |D ]"}|	|}| 	|}||krd|ratd| d| d	|  dS qBd
S )zP
    Compare entries in two dictionaries, optionally ignoring certain keys.
    z?Encountered unexpected non-dictionary object in prior revision.z.Dict is overridden by non-dict in new revisionzDict keys differ: z vs. .FzValues for dict key z differ:z changed to T)
r   r   DictionaryObjectr	   PdfReadErrorr   r   setkeysr%   )	r#   r$   r9   	raise_excnew_dict_keysold_dict_keysknew_valold_valr   r   r   r      sF   

	r   )&__doc__loggingtypingr   r   r   r   r   r   pyhanko.pdf_utilsr   r	   pyhanko.pdf_utils.genericr
   r   pyhanko.pdf_utils.readerr   
policy_apir   r   	rules_apir   	getLogger__name__logger__all__TwoVersionsr   r   r;   r   r.   r/   r   	frozensetstrboolr   r   r   r   r   <module>   sL     


!
3
4