U
    祡cSW                     @   sN  d Z ddlmZmZmZmZmZmZmZ ddl	m
Z
 ddlmZ ddl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 dd
lmZ ddlmZ ddlZdgZ e Z!G dd de"Z#e$dddZ%G dd de"Z&eee$e'f  ddddZ(eee$e'f  ddddZ)G dd deZ*G dd de"Z+G dd de"Z,dS )a  
The `ClassTracker` is a facility delivering insight into the memory
distribution of a Python program. It can introspect memory consumption of
certain classes and objects. Facilities are provided to track and size
individual objects or all instances of certain classes. Tracked objects are
sized recursively to provide an overview of memory distribution between the
different tracked objects.
    )AnyCallableDictIOListOptionalTuple)defaultdict)partial)stackisclass)ThreadLock)sleeptimeref)ConsoleStats)	safe_reprNClassTrackerc                   @   s@   e Zd ZdZdZeeeeedddZ	eeeeddddZ
dS )	_ClassObserverzy
    Stores options for tracked classes.
    The observer also keeps the original constructor of the observed class.
    initnamedetailkeeptracec                 C   s"   || _ || _|| _|| _|| _d S Nr   )selfr   r   r   r   r    r   8/tmp/pip-unpacked-wheel-8ad_c8mj/pympler/classtracker.py__init__'   s
    z_ClassObserver.__init__N)r   r   r   r   returnc                 C   s   || _ || _|| _|| _d S r   )r   r   r   r   )r   r   r   r   r   r   r   r    modify/   s    z_ClassObserver.modify)__name__
__module____qualname____doc__	__slots__r   strintboolr!   r#   r   r   r   r    r       s   r   r"   c                   C   s
   t  t S )z=
    Get a timestamp relative to the program start time.
    )r   _local_startr   r   r   r    	_get_time6   s    r.   c                   @   s   e Zd ZdZdZdeeeee	e
 dddZed	d
dZeddddZdd	ddZeejddddZed	ddZeedddZeddddZeddddZdS ) TrackedObjectz
    Stores size and lifetime information of a tracked object. A weak reference
    is attached to monitor the object without preventing its deletion.
    )
r   idreprr   birthdeathr   	snapshots_resolution_level__dict__r   FN)instancer   resolution_levelr   	on_deletec                 C   s|   t || j| _t|| _d| _|| _t | _d| _|| _	d| _
|rJ|   t|pVd}t||}| j|fg| _|| _dS )a  
        Create a weak reference for 'instance' to observe an object but which
        won't prevent its deletion (which is monitored by the finalize
        callback). The size of the object is recorded in 'snapshots' as
        (timestamp, size) tuples.
         Nr   )weakref_reffinalizer   r0   r1   r   r.   r2   r3   r5   r   _save_traceasizeofZ	basicsizeZAsizedr4   r9   )r   r7   r   r8   r   r9   initial_sizesizer   r   r    r!   E   s    
zTrackedObject.__init__r,   c                 C   s:   i }t tddD ]$}t| |r|dkrt | |||< q|S )a7  
        Make the object serializable for dump_stats. Read the available slots
        and store the values in a dictionary. Derived values (stored in the
        dict) are not pickled as those can be reconstructed based on the other
        data. References cannot be serialized, ignore 'ref' as well.
        r(   r   )r   r6   )getattrr/   hasattr)r   stater   r   r   r    __getstate__^   s
    zTrackedObject.__getstate__)rC   r"   c                 C   s&   t | D ]\}}t| || qdS )zf
        Restore the state from pickled data. Needed because a slotted class is
        used.
        N)listitemssetattr)r   rC   keyvaluer   r   r    __setstate__k   s    zTrackedObject.__setstate__c                 C   sB   t  }z2g | _|dd D ]}| jd|dd  qW 5 ~X dS )z?
        Save current stack trace as formatted string.
           Nr      )r   r   insert)r   Zstack_tracefrmr   r   r    r=   s   s    zTrackedObject._save_trace)tssizerr"   c                 C   s>   |   }| j||j|| jdf |dk	r:t|dd| _dS )z
        Store timestamp and current size for later evaluation.
        The 'sizer' is a stateful sizing facility that excludes other tracked
        objects.
        )r   N   )Zclip)r   r4   appendZasizedr5   r   r1   )r   rO   rP   objr   r   r    
track_size   s    zTrackedObject.track_sizec                 C   s   t dd | jD S )z7
        Get the maximum of all sampled sizes.
        c                 S   s   g | ]\}}|j qS r   )r@   ).0_sr   r   r    
<listcomp>   s     z.TrackedObject.get_max_size.<locals>.<listcomp>)maxr4   r   r   r   r    get_max_size   s    zTrackedObject.get_max_size)	timestampr"   c                 C   s&   d}| j D ]\}}||kr
|j}q
|S )z
        Get the size of the object at a specific time (snapshot).
        If the object was not alive/sized at that instant, return 0.
        r   )r4   r@   )r   r\   r@   trW   r   r   r    get_size_at_time   s
    zTrackedObject.get_size_at_time)r8   r"   c                 C   s
   || _ dS )z
        Set resolution level to a new value. The next size estimation will
        respect the new value. This is useful to set different levels for
        different instances of tracked classes.
        N)r5   )r   r8   r   r   r    set_resolution_level   s    z"TrackedObject.set_resolution_level)r   r"   c                 C   s4   zt  | _| jr|   W n tk
r.   Y nX dS )a7  
        Mark the reference as dead and remember the timestamp.  It would be
        great if we could measure the pre-destruction size.  Unfortunately, the
        object is gone by the time the weakref callback is called.  However,
        weakref callbacks are useful to be informed when tracked objects died
        without the need of destructors.

        If the object is destroyed at the end of the program execution, it's
        not possible to import modules anymore. Hence, the finalize callback
        just does nothing (self.death stays None).
        N)r.   r3   r9   	Exception)r   r   r   r   r    r<      s    zTrackedObject.finalize)r   FN)r$   r%   r&   r'   r(   r   r)   r*   r+   r   r   r!   r   rD   rJ   r=   floatr>   AsizerrT   r[   r^   r_   r;   r<   r   r   r   r    r/   =   s"        r/   )time_seriesr"   c                 C   s,   | r| d d nd}|  t |d f d S )NrL   r   rR   r.   rc   Znum_instancesr   r   r    track_object_creation   s    rg   c                 C   s$   | d d }|  t |d f d S )Nrd   rL   re   rf   r   r   r    track_object_deletion   s    rh   c                       s:   e Zd ZdZdeeed fddZdddd	Z  ZS )
PeriodicThreadz7
    Thread object to take snapshots periodically.
    r   )trackerintervalargskwargsc                    s(   || _ || _d| _tt| j|| dS )zb
        Create thread with given interval and associated with the given
        tracker.
        FN)rk   rj   stopsuperri   r!   )r   rj   rk   rl   rm   	__class__r   r    r!      s    zPeriodicThread.__init__Nr,   c                 C   s&   d| _ | j s"| j  t| j qdS )z2
        Loop until a stop signal is set.
        FN)rn   rj   create_snapshotr   rk   rZ   r   r   r    run   s    
zPeriodicThread.run)	r$   r%   r&   r'   ra   r   r!   rs   __classcell__r   r   rp   r    ri      s
   ri   c                   @   sH   e Zd ZdZdeeddddZeeddd	Z	eedd
dZ
dS )Snapshotz6Sample sizes of objects and the process at an instant.r:   N)r\   descriptionr"   c                 C   s4   d| _ d| _d| _|| _tj | _|| _d| _	dS )z)Initialize process-wide size information.r   N)
tracked_totalasizeof_totaloverheadr\   pymplerprocessZProcessMemoryInfosystem_totaldescclasses)r   r\   rv   r   r   r    r!      s    zSnapshot.__init__r,   c                 C   s&   | j jr| j jS | jr| jS | jS dS )z
        Return the total (virtual) size of the process in bytes. If process
        information is not available, get the best number available, even if it
        is a poor approximation of reality.
        N)r|   	availableZvszrx   rw   rZ   r   r   r    total   s
    zSnapshot.totalc                 C   s    | j sd| j S d| j | jf S )z?Return timestamped label for this snapshot, or a raw timestamp.z%.3fsz
%s (%.3fs))r}   r\   rZ   r   r   r    label   s    
zSnapshot.label)r:   )r$   r%   r&   r'   ra   r)   r!   propertyr*   r   r   r   r   r   r    ru      s   
ru   c                   @   sl  e Zd Zd5ee dddZeedddZe	e
e
e
ddd	d
ZeeeeeeddddZeedddZeeeeeddddZeddddZd6e
eddddZd7e
ee eeeddddZd8eee eeedddd Zeddd!d"Zddd#d$Zddd%d&Zddd'd(Zddd)d*Zd9edd,d-d.Zddd/d0Ze  Z!d:eedd2d3d4Z"dS );r   N)streamc                 C   s<   t t| _i | _g | _t t| _g | _i | _d| _|| _	dS )z
        Creates a new `ClassTracker` object.

        :param stream: Output stream to use when printing statistics via
            ``stats``.
        N)
r	   rE   indexobjectsr4   history
_keepalive
_observers_periodic_thread_stream)r   r   r   r   r    r!     s    

zClassTracker.__init__r,   c                 C   s   t | | jdS )zu
        Return a ``ConsoleStats`` instance initialized with the current state
        of the class tracker.
        )rj   r   )r   r   rZ   r   r   r    stats'  s    zClassTracker.stats)
_observer__self_rl   kwdsr"   c                 O   s2   | j ||j|j|j|jd |j|f|| dS )a  
        Injected constructor for tracked classes.
        Call the actual constructor of the object and track the object.  Attach
        to the object before calling the constructor to track the object with
        the parameters of the most specialized class.
        )r   r8   r   r   N)track_objectr   r   r   r   r   )r   r   r   rl   r   r   r   r    _tracker/  s    zClassTracker._tracker)clsfuncr   r8   r   r   r"   c           	         sp   z
|j }W n( tk
r2   tttdddd}Y nX t|||||| j|< ttdd fdd}||_ dS )z
        Modifying Methods in Place - after the recipe 15.7 in the Python
        Cookbook by Ken Seehof. The original constructors may be restored
        later.
        N)r   _args_kwargsr"   c                 _   s   d S r   r   )r   r   r   r   r   r    constructorI  s    z5ClassTracker._inject_constructor.<locals>.constructor)rl   rm   r"   c                     s    f| |S r   r   )rl   rm   r   Zobserverr   r    new_constructorW  s    z9ClassTracker._inject_constructor.<locals>.new_constructor)r!   AttributeErrorr   r   r   )	r   r   r   r   r8   r   r   r   r   r   r   r    _inject_constructor>  s    

z ClassTracker._inject_constructor)r   r"   c                 C   s
   || j kS )z4
        Determine if the class is tracked.
        )r   r   r   r   r   r    _is_tracked\  s    zClassTracker._is_tracked)r   r   r   r   r   r"   c                 C   s   | j | |||| dS )z4
        Modify settings of a tracked class
        N)r   r#   )r   r   r   r   r   r   r   r   r    _track_modifyb  s    zClassTracker._track_modifyc                 C   s   | j | j|_| j |= dS )zH
        Restore the original constructor, lose track of class.
        N)r   r   r!   r   r   r   r    _restore_constructori  s    z!ClassTracker._restore_constructorr   )r7   r8   r"   c                 C   s   | j t| }|| dS )z
        Change tracking options for the already tracked object 'instance'.
        If instance is not tracked, a KeyError will be raised.
        N)r   r0   r_   )r   r7   r8   tobjr   r   r    track_changep  s    zClassTracker.track_changeF)r7   r   r8   r   r   r"   c                 C   s   t || jkr(| jt |  dk	r(dS |r0|n|jj}t| j|  tt| j| }t	|||||d}| j
| | || jt |< |r| j| dS )a  
        Track object 'instance' and sample size and lifetime information.  Not
        all objects can be tracked; trackable objects are class instances and
        other objects that can be weakly referenced. When an object cannot be
        tracked, a `TypeError` is raised.

        :param resolution_level: The recursion depth up to which referents are
            sized individually. Resolution level 0 (default) treats the object
            as an opaque entity, 1 sizes all direct referents individually, 2
            also sizes the referents of the referents and so forth.
        :param keep: Prevent the object's deletion by keeping a (strong)
            reference to the object.
        N)r8   r   r9   )r0   r   r   rq   r$   rg   r   r
   rh   r/   r   rR   r   )r   r7   r   r8   r   r   r9   r   r   r   r    r   x  s"    zClassTracker.track_object)r   r   r8   r   r   r"   c                 C   s`   t |std|dkr(|jd |j }| |rF| ||||| n| || j|||| dS )au  
        Track all objects of the class `cls`. Objects of that type that already
        exist are *not* tracked. If `track_class` is called for a class already
        tracked, the tracking parameters are modified. Instantiation traces can
        be generated by setting `trace` to True.
        A constructor is injected to begin instance tracking on creation
        of the object. The constructor calls `track_object` internally.

        :param cls: class to be tracked, may be an old-style or a new-style
            class
        :param name: reference the class by a name, default is the
            concatenation of module and class name
        :param resolution_level: The recursion depth up to which referents are
            sized individually. Resolution level 0 (default) treats the object
            as an opaque entity, 1 sizes all direct referents individually, 2
            also sizes the referents of the referents and so forth.
        :param keep: Prevent the object's deletion by keeping a (strong)
            reference to the object.
        :param trace: Save instantiation stack trace for each instance
        z!only class objects can be trackedN.)r   	TypeErrorr%   r$   r   r   r   r   )r   r   r   r8   r   r   r   r   r    track_class  s    
  zClassTracker.track_classc                 C   s   |  | dS )z
        Stop tracking class 'cls'. Any new objects of that type are not
        tracked anymore. Existing objects are still tracked.
        N)r   r   r   r   r    detach_class  s    zClassTracker.detach_classc                 C   s&   t | j }|D ]}| | qdS )z2
        Detach from all tracked classes.
        N)rE   r   keysr   )r   r~   r   r   r   r    detach_all_classes  s    zClassTracker.detach_all_classesc                 C   s.   |    | j  | j  g | jdd< dS )z
        Detach from all tracked classes and objects.
        Restore the original constructors and cleanse the tracking lists.
        N)r   r   clearr   r   rZ   r   r   r    
detach_all  s    

zClassTracker.detach_allc                 C   s   |    g | jdd< dS )zV
        Clear all gathered data and detach from all tracked objects/classes.
        N)r   r4   rZ   r   r   r    r     s    zClassTracker.clearc                 C   s   |    dS )am  
        Detach from tracked classes by removing injected constructors. Makes it
        possible to use ClassTracker in `contextlib.closing` to safely remove
        profiling hooks when the tracker goes out of scope::

            import contextlib
            with contextlib.closing(ClassTracker()) as tracker:
                tracker.track_class(Foo)

        N)r   rZ   r   r   r    close  s    zClassTracker.close      ?)rk   r"   c                 C   s:   | j s.t| |dd| _ | j d | j   n|| j _dS )a?  
        Start a thread which takes snapshots periodically. The `interval`
        specifies the time in seconds the thread waits between taking
        snapshots. The thread is started as a daemon allowing the program to
        exit. If periodic snapshots are already active, the interval is
        updated.
        ZBackgroundMonitor)r   TN)r   ri   	setDaemonstartrk   )r   rk   r   r   r    start_periodic_snapshots  s    z%ClassTracker.start_periodic_snapshotsc                 C   s,   | j r(| j  r(d| j _| j   d| _ dS )z
        Post a stop signal to the thread that takes the periodic snapshots. The
        function waits for the thread to terminate which can take some time
        depending on the configured interval.
        TN)r   is_alivern   joinrZ   r   r   r    stop_periodic_snapshots  s    
z$ClassTracker.stop_periodic_snapshotsr:   )rv   compute_totalr"   c           	      C   s   z| j   t }t }dd t| j D }|j	|  t| j }|j
dd d |D ]}||| q^t|t|}|j|_|rtjddd|_d|_|jr|| |_|jr| j|j8  _| j| W 5 | j   X d	S )
am  
        Collect current per instance statistics and saves total amount of
        memory associated with the Python process.

        If `compute_total` is `True`, the total consumption of all objects
        known to *asizeof* is computed. The latter might be very slow if many
        objects are mapped into memory at the time the snapshot is taken.
        Therefore, `compute_total` is set to `False` by default.

        The overhead of the `ClassTracker` structure is also computed.

        Snapshots can be taken asynchronously. The function is protected with a
        lock to prevent race conditions.
        c                 S   s   g | ]}|  qS r   r   )rU   r   r   r   r    rX   2  s     z0ClassTracker.create_snapshot.<locals>.<listcomp>c                 S   s   | j S r   )r2   )xr   r   r    <lambda>:      z.ClassTracker.create_snapshot.<locals>.<lambda>)rH   T)allcoder   N)snapshot_lockreleaseacquirer.   r>   rb   rE   r   valuesZexclude_refssortrT   ru   r)   r   rw   rx   ry   r4   rR   )	r   rv   r   r\   rP   objsZtracked_objectsr   Zsnapshotr   r   r    rr     s*    

zClassTracker.create_snapshot)N)r   )Nr   FF)Nr   FF)r   )r:   F)#r$   r%   r&   r   r   r!   r   r   r   r   r   r   typer   r)   r*   r+   r   r   r   r   r   r   r   r   r   r   r   r   ra   r   r   r   r   rr   r   r   r   r    r      sl   %         
  ,     
  !
   )-r'   typingr   r   r   r   r   r   r   collectionsr	   	functoolsr
   inspectr   r   	threadingr   r   r   r   weakrefr   r;   Zpympler.classtracker_statsr   Zpympler.util.stringutilsr   Zpympler.asizeofr>   Zpympler.processrz   __all__r-   objectr   ra   r.   r/   r*   rg   rh   ri   ru   r   r   r   r   r    <module>   s*   	$|#