
    -Ph_I                     <   d Z ddlZddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZ 	 ddlZn# e$ r  ed          w xY wddlmZmZmZmZ dd	lmZ g d
Z eg dez             Zd ZddZd Z G d de          Z G d de	          Z G d de
          Zd Z dS )am
  
A Cython plugin for coverage.py

Requires the coverage package at least in version 4.0 (which added the plugin API).

This plugin requires the generated C sources to be available, next to the extension module.
It parses the C file and reads the original source files from it, which are stored in C comments.
It then reports a source file to coverage.py when it hits one of its lines during line tracing.

Basically, Cython can (on request) emit explicit trace calls into the C code that it generates,
and as a general human debugging helper, it always copies the current source code line
(and its surrounding context) into the C files before it generates code for that line, e.g.

::

      /* "line_trace.pyx":147
       * def cy_add_with_nogil(a,b):
       *     cdef int z, x=a, y=b         # 1
       *     with nogil:                  # 2             # <<<<<<<<<<<<<<
       *         z = 0                    # 3
       *         z += cy_add_nogil(x, y)  # 4
       */
       __Pyx_TraceLine(147,6,1,__PYX_ERR(0, 147, __pyx_L4_error))
      [C code generated for file line_trace.pyx, line 147, follows here]

The crux is that multiple source files can contribute code to a single C (or C++) file
(and thus, to a single extension module) besides the main module source file (.py/.pyx),
usually shared declaration files (.pxd) but also literally included files (.pxi).

Therefore, the coverage plugin doesn't actually try to look at the file that happened
to contribute the current source line for the trace call, but simply looks up the single
.c file from which the extension was compiled (which usually lies right next to it after
the build, having the same name), and parses the code copy comments from that .c file
to recover the original source files and their code as a line-to-file mapping.

That mapping is then used to report the ``__Pyx_TraceLine()`` calls to the coverage tool.
The plugin also reports the line of source code that it found in the C file to the coverage
tool to support annotated source representations.  For this, again, it does not look at the
actual source files but only reports the source code that it found in the C code comments.

Apart from simplicity (read one file instead of finding and parsing many), part of the
reasoning here is that any line in the original sources for which there is no comment line
(and trace call) in the generated C code cannot count as executed, really, so the C code
comments are a very good source for coverage reporting.  They already filter out purely
declarative code lines that do not contribute executable code, and such (missing) lines
can then be marked as excluded from coverage analysis.
    N)defaultdict)CoveragePlugin
FileTracerFileReporter)canonical_filenamezuInstalled 'coverage' does not support plugins. See https://coverage.readthedocs.io/en/latest/install.html#c-extension   )find_root_package_diris_package_diris_cython_generated_fileopen_source_file__version__)z.cz.cppz.ccz.cxx).pyz.pyxz.pxdc                 d    t           j        j        }t          D ]}| |z   } ||          r|c S d S N)ospathexistsC_FILE_EXTENSIONS)	base_pathfile_existsext	file_names       O/var/www/html/test/jupyter/venv/lib/python3.11/site-packages/Cython/Coverage.py_find_c_sourcer   I   sK    '.K   O	;y!! 		4    Fc                    t           j                            |          }t           j                            |          s|                    d          s|rt           j                            t           j                            |           |          }t           j                            |          rt           j                            |          }t           j                            |          d         }t           j                            |          \  }}t           j                            |          }t           j                            |          }t          t          |                    t           j                            t          |                    t           j                                      }|D ]\  }	}
|	|
k    r nWt           j                            |           d         |z   }t           j                            |          rt          |          S t           j                            |          s|t          j        D ]o}t           j                            t           j                            ||                    }t           j                            |          rt          |          c S pt          |          S )N.pxir   )r   r   abspathr   endswithjoindirnamesplitextnormpathzipreversedsplitsepr   sysrealpath)	main_file	file_pathrelative_path_searchabs_pathrel_file_path
abs_no_extfile_no_ext	extensionmatching_pathsoneothermatching_abs_pathsys_path	test_paths                 r   _find_dep_file_pathr9   R   s0   wy))H7>>(## =););F)C)C =)== RW__Y%?%?KK7>>-(( 	6w}55HW%%h//2
!#!1!1)!<!<Y W%%j11
g&&{33Xj&6&6rv&>&>??+J[J[\^\bJcJcAdAdee( 	= 	=JCe||  !# 0 0 ; ;A > Jw~~/00 =)*;<<< 7>>(## 5 	5 	5H((h	)J)JKKIw~~i(( 5))444445h'''r   c                     | dz	  S )N	    )offsets    r   _offset_to_liner>   t   s    Q;r   c                   R    e Zd ZdZdZdZdZdZd Zd Z	d Z
d Zd Zd Zd	 Zd
 ZdS )PluginNr<   c                     dt           fgS )NzCython versionr   selfs    r   sys_infozPlugin.sys_info   s    !;/00r   c                 :    |                     d          | _        d S )Nzreport:exclude_lines)
get_option_excluded_line_patterns)rC   configs     r   	configurezPlugin.configure   s!     (.'8'89O'P'P$$$r   c                    |                     d          s|                     d          rdS dx}}t          t          j                            |                    }| j        r|| j        v r| j        |         d         }|9|                     |          \  }}|sdS |                     ||          \  }}|dS | j        i | _        t          |||| j        | j                  S )zR
        Try to find a C source file for a file path found by the tracer.
        <zmemory:Nr   )

startswithr   r   r   r   _c_files_map_find_source_files_read_source_lines_file_path_mapCythonModuleTracer)rC   filenamec_filepy_file_codes         r   file_tracerzPlugin.file_tracer   s     s## 	x':':9'E'E 	4%bgooh&?&?@@ 	4T->!>!>&x03F>"55h??OFG t --fh??GAt|t&"$D!(GVT=NPTPcdddr   c                    t          t          j                            |                    }| j        r|| j        v r| j        |         \  }}}n9|                     |          \  }}|sd S |                     ||          \  }}|d S t          ||||| j        	                    |t                                          S r   )r   r   r   r   rM   rN   rO   CythonModuleReporter_excluded_lines_mapget	frozenset)rC   rR   rS   r/   rV   rU   s         r   file_reporterzPlugin.file_reporter   s     &bgooh&?&?@@ 	T->!>!>*.*;H*E'FM44//99IFA t"&"9"9&("K"KM4|t#$((	DD
 
 	
r   c                     t           j                            |          \  }}|                                }|t          v rn|dk    r?t          j        d|t
          j                  }|r|d |                                         }n|dk    r?t          j        d|t
          j                  }|r|d |                                         }nZ|dk    rR| 	                    t           j        
                    |          |           || j        v r| j        |         d         d fS ndS |t          v r|nt          |          }|t          j        |          }t           j                            ||                              t           j        j                  }t'          |          dk    r_t           j                            t           j        
                    |          d	                    |                    }t          |          }d }	|rt           j                            |          d         d
z   }	t           j                            |	          sd }	t-          |d          s%|	r!t           j                            |          rd }	d }||	fS )Nz.pydz[.]cp[0-9]+-win[_a-z0-9]*$z.soz&[.](?:cpython|pypy)-[0-9]+[-_a-z0-9]*$r   r   NNr   .r   F)if_not_found)r   r   r#   lowerMODULE_FILE_EXTENSIONSresearchIstart_find_c_source_filesr"   rM   r   r   r	   uncachedrelpathr'   r(   lenr!   r   r   )
rC   rR   basenamer   platform_suffixrS   package_rootpackage_pathtest_basepathpy_source_files
             r   rN   zPlugin._find_source_files   s]   ((22#iikk(((F]] i(ExQSQUVVO >#$<_%:%:%<%<$<=E\\ i(QS[]_]abbO >#$<_%:%:%<%<$<=F]] %%bgooh&?&?JJJ4,,,(215t;; - : $555>(;S;S>09(CCL7??8\BBHHUUL<  1$$ "RW__X-F-FQ]H^H^ _ _'66 		W--f55a85@N7>>.11 &!%+FGGG ! *bgnnV&<&< * &*N~%%r   c                    t           j                            |          sdS t           j        j        }t          j        |          D ]n} ||          d                                         }|t          v r@|                     t           j                            ||          |           || j	        v r dS ot          |          r5|                     t           j                            |          |           dS dS )z
        Desperately parse all C files in the directory or its package parents
        (not re-descending) to find the (included) source file in one of them.
        Nr   )r   r   isdirr#   listdirrb   r   rO   r!   rM   r
   rh   r"   )rC   dir_pathsource_filer#   rR   r   s         r   rh   zPlugin._find_c_source_files   s    
 w}}X&& 	F7#
8,, 	 	H(8$$Q'--//C'''''Xx(H(H+VVV$"333FF(## 	N%%bgooh&?&?MMMMM	N 	Nr   c                 X   | j         i | _         || j         v r| j         |         }n|                     |          }|| j         |<   | j        i | _        |                                D ]$\  }}t	          ||d          }|||f| j        |<   %|| j        vrdS | j        |         dd         S )z
        Parse a Cython generated C/C++ source file and find the executable lines.
        Each executable line starts with a comment header that states source file
        and line number, as well as the surrounding range of source code lines.
        NT)r-   r_   r   )_parsed_c_files_parse_cfile_linesrM   itemsr9   )rC   rS   
sourcefile
code_linesrR   rV   r.   s          r   rO   zPlugin._read_source_lines  s     '#%D T)))-f5JJ0088J+5D ($ "D(..00 	C 	CNHd*68@DF F FH+18T*BDh''T...; ,QRR00r   c                 v   t          j        d          j        }t          j        d          j        }t          j        d          j        }t          j        d          j        }t          j        d          j        }| j        r<t          j        d                    d | j        D                                 j        }nd }t          t                    }t          t                    }	d	}
| j	        t          t                    | _	        t          |d
          5 }t          |          }|D ]} ||          }|sQd|v rL|
J ||          }|r=t          |                    d                    }|	|
                             |           a|                                \  }}|}
t          |          }|D ]} ||          }|rn|                    d                                          } ||          r nH ||          r"| j	        |                             |            n|||         |<    n ||          r n	 d	d	d	           n# 1 swxY w Y   |                                D ]C\  }}t          |                              |	                    |d                    }|D ]}||= D|S )zb
        Parse a C file and extract all source file lines that generated executable code.
        z */[*] +"(.*)":([0-9]+)$z *[*] (.*) # <<<<<<+$z *[*]/$z *__Pyx_TraceLine\(([0-9]+),zX\s*c(?:type)?def\s+(?:(?:public|external)\s+)?(?:struct|union|enum|class)(\s+[^:]+|)\s*:|c                     g | ]}d |z  S )z(?:%s)r<   ).0regexs     r   
<listcomp>z-Plugin._parse_cfile_lines.<locals>.<listcomp>/  s    3o3o3oHu4D3o3o3or   c                     dS )NFr<   )lines    r   <lambda>z+Plugin._parse_cfile_lines.<locals>.<lambda>1  s    E r   Nutf8)encodingz__Pyx_TraceLine(r   r<   )rd   compilematchrG   r!   re   r   dictsetrZ   openiterintgroupaddgroupsrstriprz   
differencer[   )rC   rS   match_source_path_linematch_current_code_linematch_comment_endmatch_trace_linenot_executableline_is_excludedr|   executable_linescurrent_filenamelinesr   r   
trace_linelinenorR   comment_line	code_line
dead_liness                       r   ry   zPlugin._parse_cfile_lines   sD    "$,G!H!H!N"$*-E"F"F"LJz228:&EFFL
 

  	 ' 	2!z#((3o3oRVRn3o3o3o*p*pqqx11 &&
&s++#+'23'7'7D$&6*** 	eKKE  ..t44 )T116F6R%5%5d%;%;
% K%()9)9!)<)<%=%=F,-=>BB6JJJ#(<<>> &#+ V$)  L33LAAE $)KKNN$9$9$;$;	)>)44 "!E++I66 " 4X>BB6JJJ!E7@
8,V4**<88 /	 	 	 	 	 	 	 	 	 	 	 	 	 	 	<  *//11 	" 	"OHeU../?/C/CHb/Q/QRRJ$ " "&MM"s   D*III)__name__
__module____qualname__rP   rM   rx   rZ   rG   rD   rI   rW   r]   rN   rh   rO   ry   r<   r   r   r@   r@   x   s        NLO 1 1 1Q Q Q
e e e<
 
 
2/& /& /&bN N N$1 1 14; ; ; ; ;r   r@   c                   .     e Zd ZdZ fdZd Zd Z xZS )rQ   zA
    Find the Python/Cython source file for a Cython module.
    c                     t                                                       || _        || _        || _        || _        || _        d S r   )super__init__module_filerT   rS   rM   rP   )rC   r   rT   rS   c_files_mapfile_path_map	__class__s         r   r   zCythonModuleTracer.__init__b  sE    &'+r   c                     dS )NTr<   rB   s    r   has_dynamic_source_filenamez.CythonModuleTracer.has_dynamic_source_filenamej  s    tr   c                 P   |j         j        }	 | j        |         S # t          $ r Y nw xY wt	          ||          }| j        r6|dd                                         dk    r| j        | j        |<   | j        S | j        J || j        vr| j        |df| j        |<   || j        |<   |S )zR
        Determine source file path.  Called by the function call tracer.
        Nr   )	f_codeco_filenamerP   KeyErrorr9   rT   rb   rM   rS   )rC   rR   framerv   r.   s        r   dynamic_source_filenamez*CythonModuleTracer.dynamic_source_filenamem  s     l.	&{33 	 	 	D	&x==< 	 K,2244==/3|D,< ,,,4,,,+/;T*JDh'+3K(s    
(()r   r   r   __doc__r   r   r   __classcell__r   s   @r   rQ   rQ   ^  s`         , , , , ,        r   rQ   c                   @     e Zd ZdZ fdZd Zd Zd Zd Zd Z	 xZ
S )rY   zP
    Provide detailed trace information for one source file to coverage.py.
    c                     t                                          |           || _        || _        || _        || _        d S r   )r   r   namerS   _code_excluded_lines)rC   rS   rv   r/   rV   excluded_linesr   s         r   r   zCythonModuleReporter.__init__  s>    %%%!	
-r   c                 *    t          | j                  S )zJ
        Return set of line numbers that are possibly executable.
        )r   r   rB   s    r   r   zCythonModuleReporter.lines  s     4:r   c                     | j         S )zM
        Return set of line numbers that are excluded from coverage.
        )r   rB   s    r   r   z#CythonModuleReporter.excluded_lines  s     ##r   c              #      K   d}t          | j                                                  D ]&\  }}||k    rg V  |dz  }||k    d|fgV  |dz  }'d S )Nr   txt)sortedr   rz   )rC   current_lineline_nor   s       r   _iter_source_tokensz(CythonModuleReporter._iter_source_tokens  s      "()9)9););"<"< 	 	GYL((! L(( 9%&&&&ALL	 	r   c                 0   t           j                            | j                  rBt	          | j                  5 }|                                cddd           S # 1 swxY w Y   dS d                    d |                                 D                       S )zA
        Return the source code of the file as a string.
        N
c              3   :   K   | ]}|r|d          d         ndV  dS )r   r    Nr<   )r   tokenss     r   	<genexpr>z.CythonModuleReporter.source.<locals>.<genexpr>  sG       : : "(/1R: : : : : :r   )r   r   r   rR   r   readr!   r   )rC   fs     r   sourcezCythonModuleReporter.source  s     7>>$-(( 	:!$-00  Avvxx                                    99 : :"6688: : : : : :s   AA!Ac              #   ,  K   t           j                            | j                  rOt	          | j                  5 }|D ]}d|                    d          fgV  	 ddd           dS # 1 swxY w Y   dS |                                 D ]	}d|fgV  
dS )z6
        Iterate over the source code tokens.
        r   r   N)r   r   r   rR   r   r   r   )rC   r   r   s      r   source_token_linesz'CythonModuleReporter.source_token_lines  s      7>>$-(( 	&!$-00 7A 7 7D!4;;t#4#456666677 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 0022 & &t}o%%%%& &s    A))A-0A-)r   r   r   r   r   r   r   r   r   r   r   r   s   @r   rY   rY     s         . . . . .  $ $ $  
: 
: 
:
& 
& 
& 
& 
& 
& 
&r   rY   c                 v    t                      }|                     |           |                     |           d S r   )r@   add_configureradd_file_tracer)regoptionsplugins      r   coverage_initr     s9    XXFvr   )F)!r   rd   os.pathr   r)   collectionsr   coverage.pluginr   r   r   coverage.filesr   coverage.tracercoverageImportErrorUtilsr	   r
   r   r   r   r   r   r   rc   r   r9   r>   r@   rQ   rY   r   r<   r   r   <module>r      s  . .b 
			  



 # # # # # # D D D D D D D D D D - - - - - -` ` ` `
+ _ ` ` `` e d d d d d d d d d d d       211 4447HHII   ( ( ( (D  c c c c c^ c c cL# # # # # # # #L6& 6& 6& 6& 6&< 6& 6& 6&r         s   + <