U
    <g                     @   s  d Z ddlZddlmZ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d
dddddgZeeej eej f Zdd Zeeeddf dddZeejejeedef dddZe	dddZ e	dddZ!e	dedZ"e	deddZ#eee"de f eeee"f de f ddd
Z$eee!de f ee!ge#f eeee#f de f d d!dZ%e& dfee ee ee' e(d"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	FrozenSet	GeneratorOptionalSetTupleTypeVar)genericmisc)	PdfObject	Reference)HistoricalResolver   )ModificationLevelSuspiciousModification)ReferenceUpdatequalifyqualify_transforming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	   ZStreamObjectr   Zcontainer_ref)obj r   F/tmp/pip-unpacked-wheel-w101_d3s/pyhanko/sign/diff_analysis/commons.pyr      s    )oldreturnc                 c   sR   |rt |  ||kr,t |  |V  n"| |r>|V  ntd| ddS )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_objectZis_ref_availabler   )r   Zold_refZnew_refr   r   r   r   )   s    

)r   old_dictnew_dictr   c                 c   s   z.| | }t|tjr(|j}| }nd}W n tk
rJ   d }}Y nX z.| | }t|tjrt|j}| }nd}W n. tk
r   |dk	rtd|  dY dS X |dk	rt|||E dH  ||fS )a  
    Ensure that updating a key in a dictionary has no undesirable side effects.
    The following scenarios are allowed:

    0. replacing a direct value with another direct value
    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	   ZIndirectObject	referencer   KeyErrorr   r   )keyr   r    r!   	old_valueZold_value_ref	new_valueZnew_value_refr   r   r   r   K   s.    




RT)	covariant	QualifyIn)contravariantRefToUpd)bound	OutRefUpd)r-   r)   )levelrule_resultr   c                 C   s   t | |dd dS )a  
    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.
    :return:
        A converted generator that outputs references qualified at the
        modification level specified.
    c                 S   s   | S Nr   )xr   r   r   <lambda>       zqualify.<locals>.<lambda>)	transform)r   )r/   r0   r   r   r   r      s    *)r/   r0   r5   r   c                    s   t | fddS )a  
    This is a version of :func:`.qualify` that additionally allows
    a transformation to be applied to the output of the rule.

    :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.
    :return:
        A converted generator that outputs references qualified at the
        modification level specified.
    c                    s    | fS r1   r   )refr/   r5   r   r   r3      r4   z&qualify_transforming.<locals>.<lambda>)r
   Zmap_with_return)r/   r0   r5   r   r7   r   r      s     )r    r!   ignoredr   c           	      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ndS |D ]F}|	|}| 	|}||kr|rtd| d| d	| q dS qd
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
   ZPdfReadErrorr   r   setkeysr"   )	r    r!   r8   Z	raise_excZnew_dict_keysZold_dict_keysknew_valZold_valr   r   r   r      s8    


)*__doc__loggingtypingr   r   r   r   r   r   r   Zpyhanko.pdf_utilsr	   r
   Zpyhanko.pdf_utils.genericr   r   Zpyhanko.pdf_utils.readerr   Z
policy_apir   r   Z	rules_apir   	getLogger__name__logger__all__ZTwoVersionsr   r   r:   r   r(   r*   r,   r.   r   r   	frozensetstrboolr   r   r   r   r   <module>   s\   $
	$6.