U
    祡c.                     @   s   d 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 ddlmZ dgZi Zed	krrd
ed< G dd deZG dd deZG dd deZdS )z
This module exposes utilities to illustrate objects and their references as
(directed) graphs. The current implementation requires 'graphviz' to be
installed.
    )Asizer
named_refs)	safe_reprtrunc)get_referents)PopenPIPE)copy)platformReferenceGraph)win32T	close_fdsc                   @   s   e Zd ZdZdZdd ZdS )_MetaObjectz
    The _MetaObject stores meta-information, like a string representation,
    corresponding to each object passed to a ReferenceGraph.
    )sizeidtypestrgroupcyclec                 C   s
   d| _ d S )NF)r   self r   4/tmp/pip-unpacked-wheel-8ad_c8mj/pympler/refgraph.py__init__!   s    z_MetaObject.__init__N)__name__
__module____qualname____doc__	__slots__r   r   r   r   r   r      s   r   c                   @   s4   e Zd ZdZdZdd Zdd Zdd Zd	d
 ZdS )_EdgezN
    Describes a reference from one object `src` to another object `dst`.
    srcdstlabelr   c                 C   s   || _ || _|| _d | _d S Nr    )r   r!   r"   r#   r   r   r   r   +   s    z_Edge.__init__c                 C   s   d| j | j| j| jf S )Nz<%08x => %08x, '%s', %s>r    r   r   r   r   __repr__1   s    z_Edge.__repr__c                 C   s   | j | j| jf S r$   )r!   r"   r#   __hash__r   r   r   r   r&   5   s    z_Edge.__hash__c                 C   s   |   |  kS r$   )r&   )r   otherr   r   r   __eq__8   s    z_Edge.__eq__N)	r   r   r   r   r   r   r%   r&   r(   r   r   r   r   r   %   s   r   c                   @   s|   e Zd ZdZd ddZdd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zd!ddZdd ZdS )"r   a`  
    The ReferenceGraph illustrates the references between a collection of
    objects by rendering a directed graph. That requires that 'graphviz' is
    installed.

    >>> from pympler.refgraph import ReferenceGraph
    >>> a = 42
    >>> b = 'spam'
    >>> c = {a: b}
    >>> gb = ReferenceGraph([a,b,c])
    >>> gb.render('spam.eps')
    True
    Fc                 C   sR   t || _t| j| _d| _d| _|r8|  | _| | _nd| _|   | 	  dS )zO
        Initialize the ReferenceGraph with a collection of `objects`.
        zN/AN)
listobjectslencountnum_in_cyclesedges_reduce_to_cycles_reduced
_get_edges_annotate_objects)r   r*   reducer   r   r   r   J   s    

zReferenceGraph.__init__c                 C   sN   g }t dd |D }|D ].}t dd t|D }||r|| q|S )z
        Eliminate leaf objects - that are objects not referencing any other
        objects in the list `graph`. Returns the list of objects without the
        objects identified as leafs.
        c                 S   s   g | ]}t |qS r   r   .0xr   r   r   
<listcomp>c   s     z3ReferenceGraph._eliminate_leafs.<locals>.<listcomp>c                 S   s   g | ]}t |qS r   r4   r5   r   r   r   r8   e   s     )setr   intersectionappend)r   graphresultidsetnrefsetr   r   r   _eliminate_leafs\   s    
zReferenceGraph._eliminate_leafsc                 C   sB   | j dd }d}|t|kr2t|}| |}q|| _ t| j S )a  
        Iteratively eliminate leafs to reduce the set of objects to only those
        that build cycles. Return the number of objects involved in reference
        cycles. If there are no cycles, `self.objects` will be an empty list
        and this method returns 0.
        Nr   )r*   r+   rA   )r   ZcyclesZcntr   r   r   r/   j   s    z ReferenceGraph._reduce_to_cyclesc                 C   sv   | j spt| }| jdd |_g |_g |_| | _| j|_| jrf|  |  |jD ]
}d|_	qXnd}|| _ | j S )z
        Iteratively eliminate leafs to reduce the set of objects to only those
        that build cycles. Return the reduced graph. If there are no cycles,
        None is returned.
        NT)
r0   r	   r*   metadatar.   r/   r-   r1   r2   r   )r   Zreducedmetar   r   r   reduce_to_cyclesy   s    


zReferenceGraph.reduce_to_cyclesc           	      C   s   t dd | jD }t g | _| jD ]}t dd t|D }||D ]d}d}d}t|trf| }|srt|}|D ]\}}t	||krv|} qqv| j
tt	||| qHq$dS )z
        Compute the edges for the reference graph.
        The function returns a set of tuples (id(a), id(b), ref) if a
        references b with the referent 'ref'.
        c                 S   s   g | ]}t |qS r   r4   r5   r   r   r   r8      s     z-ReferenceGraph._get_edges.<locals>.<listcomp>c                 S   s   g | ]}t |qS r   r4   r5   r   r   r   r8      s      N)r9   r*   r.   r   r:   
isinstancedictitemsr   r   addr   )	r   r>   r?   r@   refr#   memberskvr   r   r   r1      s     


zReferenceGraph._get_edgesc                 C   s   i }| j D ]}|||j< q
d}| j D ]}t|ds@||_|d7 }t }| jD ]4}|j|jkrh||j |j|jkrL||j qL|D ]"}t	|jt
|| d||| _qq$| jD ]}||j j|_q|| _dS )zt
        Annotate the objects belonging to separate (non-connected) graphs with
        individual indices.
        r   r      N)rB   r   hasattrr   r9   r.   r!   rI   r"   mingetattr
_max_group)r   gr7   idxZ	neighborsenbr   r   r   _annotate_groups   s&    



"
zReferenceGraph._annotate_groupsc                    sx    fdd| j D | _ tdd | j D fdd| jD | _t| j | _| j g krZdS  fdd| jD | _| `dS )z
        Eliminate all objects but those which belong to `group`.
        ``self.objects``, ``self.metadata`` and ``self.edges`` are modified.
        Returns `True` if the group is non-empty. Otherwise returns `False`.
        c                    s   g | ]}|j  kr|qS r   r   r5   rX   r   r   r8      s     
 z0ReferenceGraph._filter_group.<locals>.<listcomp>c                 S   s   g | ]
}|j qS r   r4   r5   r   r   r   r8      s     c                    s   g | ]}t | kr|qS r   r4   )r6   obj)	group_setr   r   r8      s      Fc                    s   g | ]}|j  kr|qS r   rX   )r6   rU   rX   r   r   r8      s     
 T)rB   r9   r*   r+   r,   r.   rR   )r   r   r   )r   rZ   r   _filter_group   s    
zReferenceGraph._filter_groupc                 c   sx   |    d}t| jD ]\}t| }| jdd |_| j |_||rtdd |jD |_||_	|d7 }|V  qdS )a7  
        Split the graph into sub-graphs. Only connected objects belong to the
        same graph. `split` yields copies of the Graph object. Shallow copies
        are used that only replicate the meta-information, but share the same
        object list ``self.objects``.

        >>> from pympler.refgraph import ReferenceGraph
        >>> a = 42
        >>> b = 'spam'
        >>> c = {a: b}
        >>> t = (1,2,3)
        >>> rg = ReferenceGraph([a,b,c,t])
        >>> for subgraph in rg.split():
        ...   print (subgraph.index)
        0
        1
        r   Nc                 S   s   g | ]
}|j qS r   )r   r5   r   r   r   r8      s     z(ReferenceGraph.split.<locals>.<listcomp>rN   )
rW   rangerR   r	   rB   r.   r[   sum
total_sizeindex)r   r_   r   Zsubgraphr   r   r   split   s    
zReferenceGraph.splitc                 C   s8   t |  }|jdd d t|D ]\}}||_q$|S )z
        Split the graphs into sub graphs and return a list of all graphs sorted
        by the number of nodes. The graph with most nodes is returned first.
        c                 S   s   t | j S r$   )r+   rB   )r7   r   r   r   <lambda>       z/ReferenceGraph.split_and_sort.<locals>.<lambda>)key)r)   r`   sort	enumerater_   )r   Zgraphsr_   r<   r   r   r   split_and_sort   s
    zReferenceGraph.split_and_sortc              
   C   s   g | _ t }|j| j }|j| _t| j|D ]l\}}t }||_t	||_	z|j
j|_W n$ ttfk
r|   t|j|_Y nX t|dd|_| j | q,dS )zB
        Extract meta-data describing the stored objects.
           )ZclipN)rB   r   Zasizesofr*   totalr^   zipr   r   r   	__class__r   r   AttributeErrorReferenceErrorr   r   r;   )r   ZsizerZsizesrY   szmdr   r   r   r2     s    
z ReferenceGraph._annotate_objectsc                 C   s   g }d}| | | d | d | jD ]`}t|jddd}d}|jdkrXd	}n|jd
krfd}| dt|jdd ||j|f  q,| jD ]J}d}|j	dkrd}| dt|j
dd t|jdd |j	|f  q| d d|S )z
        Emit a graph representing the connections between the objects described
        within the metadata list. The text representation can be transformed to
        a graph with graphviz. Returns a string.
        z#// Process this file with graphviz
zdigraph G {
z    node [shape=box];
0   "'rE   Zinstancemethodz, color=redframez, color=orangez#    "X%s" [ label = "%s\n%s" %s ];
rN   N__dict__z,weight=100z    X%s -> X%s [label="%s"%s];
z}
)r;   rB   r   r   replacer   hexr   r.   r#   r!   r"   join)r   sheaderrn   r#   extrarU   r   r   r   _get_graphviz_data  s0    







(
z!ReferenceGraph._get_graphviz_datadotpsc                 C   s   | j g krdS |  }d}|d| d|f| }|r|td
ttdt}t|fd|jit}	||  |	  |	jdkS t|fdtit}
|
|  |
jdkS d	S )aW  
        Render the graph to `filename` using graphviz. The graphviz invocation
        command may be overridden by specifying `cmd`. The `format` may be any
        specifier recognized by the graph renderer ('-Txxx' command).  The
        graph can be preprocessed by the *unflatten* tool if the `unflatten`
        parameter is True.  If there are no objects to illustrate, the method
        does not invoke graphviz and returns False. If the renderer returns
        successfully (return code 0), True is returned.

        An `OSError` is raised if the graphviz tool cannot be found.
        F)z-Nfontsize=10z-Efontsize=10z-Nstyle=filledz-Nfillcolor=#E5EDB8z-Ncolor=#CCCCCCz-T%sz-o	unflattenz-l7)stdinstdoutr   r   N)r}   )	r*   rz   r   r   popen_flagsr   communicateencode
returncode)r   filenamecmdformatr~   dataoptionsZcmdlinep1p2pr   r   r   render4  s     

zReferenceGraph.renderc                 C   s$   t |d}||   |  dS )zR
        Write raw graph data which can be post-processed using graphviz.
        wN)openwriterz   close)r   r   fr   r   r   write_graphX  s    
zReferenceGraph.write_graphN)F)r{   r|   F)r   r   r   r   r   rA   r/   rD   r1   rW   r[   r`   rf   r2   rz   r   r   r   r   r   r   r   <   s   
 
$N)r   Zpympler.asizeofr   r   Zpympler.util.stringutilsr   r   gcr   
subprocessr   r   r	   sysr
   __all__r   objectr   r   r   r   r   r   r   <module>   s   