U
    x^A!                     @   s   d Z ddlZddlZddlZddlZddlZddl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mZ zddlZW n ek
r   dZY nX dZdZG dd deZdddZejdddZdS )zProfiling support for unit and performance tests.

These are special purpose profiling methods which operate
in a more fine-grained way than nose's profiling plugin.

    N   )config)
gc_collect   )jython)pypyupdate_wrapper)win32c                   @   sT   e Zd ZdZdd Zedd Zdd Zdd	 Zd
d Z	dd Z
dd Zdd ZdS )ProfileStatsFilez"Store per-platform/fn profiling results in a file.

    We're still targeting Py2.5, 2.4 on 0.7 with no dependencies,
    so no json lib :(  need to roll something silly

    c                 C   sz   t jd k	ot jj| _| jp*t jd k	o*t jj| _tj|| _	tj
| j	d | _tdd | _|   | jrv|   d S )Nc                   S   s
   t tS N)collectionsdefaultdictdict r   r   D/tmp/pip-install-dq5v43_d/SQLAlchemy/sqlalchemy/testing/profiling.py<lambda>:       z+ProfileStatsFile.__init__.<locals>.<lambda>)r   optionsZforce_write_profilesforce_writeZwrite_profileswriteospathabspathfnamesplitshort_fnamer   r   data_read_write)selffilenamer   r   r   __init__0   s    zProfileStatsFile.__init__c                 C   s   t jjd t jj }ddd tjdd D }|g}|| trP|d t	r^|d t
rl|d	 |t jjjr~d
nd t j }||rdpd d|S )N_.c                 S   s   g | ]}t |qS r   str).0vr   r   r   
<listcomp>H   s     z1ProfileStatsFile.platform_key.<locals>.<listcomp>r   r   r   r   winZnativeunicodeZdbapiunicodeZcextensionsZnocextensions)r   dbnameZdriverjoinsysversion_infoappendr   r   r
   dialectZconvert_unicoderequirementsZ_has_cextensions)r!   Z	dbapi_key
py_versionZplatform_tokensZ	_has_cextr   r   r   platform_keyB   s$    




zProfileStatsFile.platform_keyc                 C   s   t }|| jko| j| j| kS r   )_current_testr   r5   )r!   test_keyr   r   r   	has_stats[   s    zProfileStatsFile.has_statsc           	      C   s   t }| j| }|| j }d|kr.g  |d< }n|d }d|krLd |d< }n|d }t||k}|s|| | jr||   d }n|d || f}|d  d7  < |S )Ncountscurrent_countr   linenor   )r6   r   r5   lenr1   r   r    )	r!   	callcountr7   per_fnper_platformr9   r:   Z	has_countresultr   r   r   r@   a   s$    


zProfileStatsFile.resultc                 C   s\   t }| j| }|| j }|d }|d }|t|k rB|||d < n||d< | jrX|   d S )Nr9   r:   r   r   )r6   r   r5   r<   r   r    )r!   r=   r7   r>   r?   r9   r:   r   r   r   replace|   s    

zProfileStatsFile.replacec                 C   s
   d| j  S )Na  # %s
# This file is written out on a per-environment basis.
# For each test in aaa_profiling, the corresponding function and 
# environment is located within this file.  If it doesn't exist,
# the test is skipped.
# If a callcount does exist, it is compared to what we received. 
# assertions are raised if the counts do not match.
# 
# To add a new callcount test, apply the function_call_count 
# decorator and re-run the tests using the --write-profiles 
# option - this file will be rewritten including the new count.
# 
)r   )r!   r   r   r   _header   s    zProfileStatsFile._headerc           
      C   s   zt | j}W n tk
r$   Y d S X t|D ]p\}}| }|r.|drNq.| \}}}| j| }|| }dd |dD }	|	|d< |d |d< d|d	< q.|  d S )
N#c                 S   s   g | ]}t |qS r   )intr(   countr   r   r   r*      s     z*ProfileStatsFile._read.<locals>.<listcomp>,r9   r   r;   r   r:   )	openr   IOError	enumeratestrip
startswithr   r   close)
r!   	profile_fr;   liner7   r5   r9   r>   r?   cr   r   r   r      s     

zProfileStatsFile._readc                 C   s   t d| j  t| jd}||   t| jD ]^}| j| }|d|  t|D ]8}|| }ddd |d D }|d|||f  qVq2|  d S )	NzWriting profile file %swz
# TEST: %s

rG   c                 s   s   | ]}t |V  qd S r   r&   rE   r   r   r   	<genexpr>   s     z*ProfileStatsFile._write.<locals>.<genexpr>r9   z	%s %s %s
)	printr   rH   r   rB   sortedr   r.   rM   )r!   rN   r7   r>   r5   r?   rP   r   r   r   r       s    
zProfileStatsFile._writeN)__name__
__module____qualname____doc__r#   propertyr5   r8   r@   rA   rB   r   r    r   r   r   r   r   (   s   
r   皙?c                    s    fdd}|S )a  Assert a target for a test case's function call count.

    The main purpose of this assertion is to detect changes in
    callcounts for various functions - the actual number is not as important.
    Callcounts are stored in a file keyed to Python version and OS platform
    information.  This file is generated automatically for new tests,
    and versioned so that unexpected changes in callcounts will be detected.

    c                    s    fdd}t | S )Nc               
      s,   t d  | |W  5 Q R  S Q R X d S )Nvariance)count_functions)argskw)fnr\   r   r   wrap   s    z3function_call_count.<locals>.decorate.<locals>.wrapr   )r`   ra   r[   )r`   r   decorate   s    z%function_call_count.<locals>.decorater   )r\   rb   r   r[   r   function_call_count   s    rc   c           	      c   s
  t d krtdt s0tjs0tdtj  t  t 	 }|
  d V  |  tj|tjd}|j}t|}|d krd }n|\}}td||f  |d |  |rt||  }t|| |k}|stjrtjrt| ntd|| d |tjf d S )NzcProfile is not installedzNo profiling stats available on this platform for this function.  Run tests with --write-profiles to add statistics to %s for this platform.)streamzPstats calls: %d Expected %sZ
cumulativezAdjusted function call count %s not within %s%% of expected %s, platform %s. Rerun with --write-profiles to regenerate this callcount.d   )cProfiler   Z_skip_test_exception_profile_statsr8   r   Z	skip_testr   r   ZProfileenabledisablepstatsZStatsr/   stdoutZtotal_callsr@   rS   Z
sort_statsZprint_statsrD   absr   rA   AssertionErrorr5   )	r\   prstatsr=   expectedZexpected_countZline_noZdeviancefailedr   r   r   r]      sJ    


r]   )rZ   )rZ   )rX   r   
contextlibr   rj   r/    r   utilr   r   r   r	   r
   rf   ImportErrorr6   rg   objectr   rc   contextmanagerr]   r   r   r   r   <module>   s,   
 
