o
    i>G                     @  s  d dl m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 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 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 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& e"e'Z(erd dl)m*Z* G dd deZ+G dd de+Z,G dd  d Z-dS )!    )annotationsN)TYPE_CHECKING)Any)Optional)uuid4)	SpanTypes)BufferedEncoder)packb)COVERAGE_TAG_NAME)
EVENT_TYPE)ITR_CORRELATION_ID_TAG_NAME)	MODULE_ID)MODULE_TYPE)
SESSION_ID)SESSION_TYPE)SUITE_ID)
SUITE_TYPE)ENDPOINT)$record_endpoint_payload_events_count)1record_endpoint_payload_events_serialization_time)JSONEncoderV2)
get_logger)	StopWatch)NoEncodableSpansError)Spanc                      s   e Zd ZdZdZdZdZejZ	dZ
d= fd	d
Zdd Zd>ddZdd Zdd Zdd Zd?ddZd@dd ZdAd!d"ZdBd&d'ZdCd)d*ZdDd.d/Zed0d1 Z	3dEdFd9d:ZedGd;d<Z  ZS )HCIVisibilityEncoderV01zapplication/msgpack      i  P argsr   returnNonec                   s:   t t|   i | _t | _tdd u| _	| 
  d S )NPYTEST_XDIST_WORKER)superr   __init__	_metadata	threadingRLock_lockosgetenv_is_xdist_worker_init_buffer)selfr   	__class__ Z/home/ubuntu/.local/lib/python3.10/site-packages/ddtrace/internal/ci_visibility/encoder.pyr#   .   s
   
zCIVisibilityEncoderV01.__init__c                 C  s4   | j  t| jW  d    S 1 sw   Y  d S N)r'   lenbufferr,   r/   r/   r0   __len__7   s   $zCIVisibilityEncoderV01.__len__
event_typestrmetadatadict[str, str]c                 C  s   | j |i | d S r1   )r$   
setdefaultupdate)r,   r6   r8   r/   r/   r0   set_metadata;   s   z#CIVisibilityEncoderV01.set_metadatac                 C  s2   | j  g | _W d    d S 1 sw   Y  d S r1   )r'   r3   r4   r/   r/   r0   r+   >   s   "z#CIVisibilityEncoderV01._init_bufferc                 C  s8   | j  | j| W d    d S 1 sw   Y  d S r1   )r'   r3   appendr,   itemr/   r/   r0   putB   s   "zCIVisibilityEncoderV01.putc                 C  s   t  )zQ
        Only used for LogWriter, not called for CI Visibility currently
        )NotImplementedError)r,   tracesr/   r/   r0   encode_tracesF   s   z$CIVisibilityEncoderV01.encode_traces!list[tuple[Optional[bytes], int]]c              	   C  s   | j > | jsg W  d    S g }t }| | j}W d    n1 s&w   Y  t| j| d |   |W  d    S 1 sDw   Y  d S )N)endpointseconds)r'   r3   r   _build_payloadr   ENDPOINT_TYPEelapsedr+   )r,   payloadsswr/   r/   r0   encodeL   s   $zCIVisibilityEncoderV01.encoderB   list[list['Span']]intc                 C  s>   |D ]}|D ]}| ttkr|jd ur|j    S qqdS Nr   )get_tagr   r   	parent_id)r,   rB   tracespanr/   r/   r0   _get_parent_sessionW   s   z*CIVisibilityEncoderV01._get_parent_sessionc                 C  s&   |sg S |  |}| |dt||S )aS  
        Build multiple payloads from traces, splitting when necessary to stay under size limits.
        Uses index-based recursive approach to avoid copying slices.

        Returns a list of (payload_bytes, trace_count) tuples, where each payload contains
        as many traces as possible without exceeding _MAX_PAYLOAD_SIZE.
        r   )rT   _build_payloads_recursiver2   )r,   rB   new_parent_session_span_idr/   r/   r0   rG   ^   s   
z%CIVisibilityEncoderV01._build_payload	start_idxend_idxrV   c                 C  s   ||krg S || }|  ||||}dd |D }|s"td g S | |}t|| jks2|dkr@t| jt|d ||fgS ||d d  }	| |||	|}
| ||	||}|
| S )av  
        Recursively build payloads using start/end indexes to avoid slice copying.

        Args:
            traces: Full list of traces
            start_idx: Start index (inclusive)
            end_idx: End index (exclusive)
            new_parent_session_span_id: Parent session span ID

        Returns:
            list of (payload_bytes, trace_count) tuples
        c                 S  s   g | ]\}}|D ]}|qqS r/   r/   ).0_trace_spansrS   r/   r/   r0   
<listcomp>   s    zDCIVisibilityEncoderV01._build_payloads_recursive.<locals>.<listcomp>z2No spans to encode after filtering, skipping chunkr   rE   countr   )	 _convert_traces_to_spans_indexedlogdebug_create_payload_from_spansr2   _MAX_PAYLOAD_SIZEr   rH   rU   )r,   rB   rW   rX   rV   trace_countall_spans_with_trace_info	all_spanspayloadmid_idxleft_payloadsright_payloadsr/   r/   r0   rU   l   s$   


z0CIVisibilityEncoderV01._build_payloads_recursive&list[tuple[int, list[dict[str, Any]]]]c                   sD   g }t ||D ]}||  fddD }|||f q|S )zUConvert traces to spans with xdist filtering applied, using indexes to avoid slicing.c                   s6   g | ]}j r|ttkr|d  jj qS r   )r*   rP   r   r   _convert_spancontext	dd_originrY   rS   rV   r,   rR   r/   r0   r\      s    zKCIVisibilityEncoderV01._convert_traces_to_spans_indexed.<locals>.<listcomp>)ranger=   )r,   rB   rW   rX   rV   re   	trace_idxr[   r/   rq   r0   r_      s   z7CIVisibilityEncoderV01._convert_traces_to_spans_indexedspanslist[dict[str, Any]]bytesc                 C  s   t | j| j|dS )z&Create a payload from the given spans.)versionr8   events)r   _pack_payloadPAYLOAD_FORMAT_VERSIONr$   )r,   rt   r/   r/   r0   rb      s   z1CIVisibilityEncoderV01._create_payload_from_spansc                 C  s   t | S r1   )msgpack_packb)rg   r/   r/   r0   ry      s   z$CIVisibilityEncoderV01._pack_payloadNr   rS   'Span'ro   Optional[str]dict[str, Any]c                 C  s   t |}t |}|tp|j|d< |j|d< tt|j	
 |d< tt|j
 |d< |d ur<|d d|i t||}tj}|tdkrOtj}|jdkrZ|t}nd}|||dS )	Ntypedurationmetametricsz
_dd.origintestrS   )rw   r   content)r   _span_to_dict_normalize_spanrP   r   	span_typeduration_nsdictsorted_metaitems_metricsr;   r   _filter_idsTEST_SUITE_EVENT_VERSIONTEST_EVENT_VERSION)r,   rS   ro   rV   sprw   r6   r/   r/   r0   rm      s    



z$CIVisibilityEncoderV01._convert_spanc                 C  sx  | d  ttttfv r| d= | d= | d= n!t|  dpd| d< t|  dp(d| d< t|  dp3d| d< | d  tttttjfv r[|pM| d  t}|r[t|| t< | d t= | d  ttttjfv r|| d  t	}|r|t|| t	< | d t	= | d  tttjfv r| d  t
}|rt|| t
< | d t
= t| d v r| d t= t| d v r| d t | t< | d t= | S )z
        Remove trace/span/parent IDs if non-test event, move session/module/suite IDs from meta to outer content layer.
        r   trace_idspan_idrQ   1)getr   r   r   r   rN   r   TESTr   r   r   r
   r   )r   rV   test_session_idtest_module_idtest_suite_idr/   r/   r0   r      s8   




z"CIVisibilityEncoderV01._filter_ids)r   r   r   r    )r6   r7   r8   r9   )r   rD   )rB   rM   r   rN   rB   rM   r   rD   )
rB   rM   rW   rN   rX   rN   rV   rN   r   rD   )
rB   rM   rW   rN   rX   rN   rV   rN   r   rk   )rt   ru   r   rv   rO   rS   r|   ro   r}   rV   rN   r   r~   rl   )__name__
__module____qualname__content_typerz   r   r   r   
TEST_CYCLErH   rc   r#   r5   r<   r+   r@   rC   rL   rT   rG   rU   r_   rb   staticmethodry   rm   r   __classcell__r/   r/   r-   r0   r   &   s2    	





2


r   c                      s   e Zd ZdZejZe jZ	de	 Z
dZdd Z fddZd%ddZd&ddZd%ddZd'ddZd(ddZ	d)d*d#d$Z  ZS )+CIVisibilityCoverageEncoderV02r   z multipart/form-data; boundary=%sFc                 C  s
   || _ d S r1   )itr_suite_skipping_mode)r,   	new_valuer/   r/   r0   _set_itr_suite_skipping_mode   s   
z;CIVisibilityCoverageEncoderV02._set_itr_suite_skipping_modec                   sR   dd |D }t dd |D d }|r||vr|| |s!t tt| |S )Nc                 S  s*   g | ]}t | v s|t d ur|qS r1   )r
   get_tags_get_struct_tagrp   r/   r/   r0   r\     s
    z6CIVisibilityCoverageEncoderV02.put.<locals>.<listcomp>c                 s  s"    | ]}| ttkr|V  qd S r1   )rP   r   r   rp   r/   r/   r0   	<genexpr>	  s     z5CIVisibilityCoverageEncoderV02.put.<locals>.<genexpr>)nextr=   r   r"   r   r@   )r,   r?   spans_with_coveragesession_spanr-   r/   r0   r@     s   
z"CIVisibilityCoverageEncoderV02.putdatarv   r   list[bytes]c                 C  s   d| j d ddd|gS )N   --%sutf-8sN   Content-Disposition: form-data; name="coverage1"; filename="coverage1.msgpack"s!   Content-Type: application/msgpack    boundaryrL   r,   r   r/   r/   r0   _build_coverage_attachment     z9CIVisibilityCoverageEncoderV02._build_coverage_attachmentc                 C  s   d| j d ddddgS )Nr   r   sC   Content-Disposition: form-data; name="event"; filename="event.json"s   Content-Type: application/jsonr   s   {"dummy":true}r   r4   r/   r/   r0   _build_event_json_attachment  r   z;CIVisibilityCoverageEncoderV02._build_event_json_attachmentc                 C  s$   |  ||   d| jd g S )Ns   --%s--r   )r   r   r   rL   r   r/   r/   r0   _build_body#  s   z*CIVisibilityCoverageEncoderV02._build_bodyrB   rM   Optional[bytes]c                   sH    |  fdd|D }|sd S ttjt|d tj|dS )Nc                   s>   g | ]}|D ]}t | v s|t d urj| dqqS )N)rV   )r
   r   r   rm   )rY   rR   rS   rV   r,   r/   r0   r\   ,  s    z>CIVisibilityCoverageEncoderV02._build_data.<locals>.<listcomp>r]   )rw   	coverages)rT   r   r   CODE_COVERAGEr2   r{   rz   )r,   rB   normalized_covsr/   r   r0   _build_data*  s   
z*CIVisibilityCoverageEncoderV02._build_datarD   c                 C  s,   |  |}|s	g S d| |t|fgS )N   
)r   joinr   r2   )r,   rB   r   r/   r/   r0   rG   8  s   
z-CIVisibilityCoverageEncoderV02._build_payloadNr   rS   r|   ro   r}   rV   rN   r~   c                 C  s   i }| t}|d urd|v r|d }nt| v r&tt|td }|p0t|tp/dt|t	p7d|d}| j
sD|j|d< td| |S )Nfilesr   )r   r   r   r   z$Span converted to coverage event: %s)r   r
   r   jsonloadsr7   rP   rN   r   r   r   r   r`   ra   )r,   rS   ro   rV   r   files_struct_tag_valueconverted_spanr/   r/   r0   rm   >  s   


z,CIVisibilityCoverageEncoderV02._convert_span)r   rv   r   r   )r   r   )rB   rM   r   r   r   rO   r   )r   r   r   rz   r   r   rH   r   hexr   r   r   r   r@   r   r   r   r   rG   rm   r   r/   r/   r-   r0   r      s    

	
	

r   c                   @  sJ   e Zd ZdZdZejZdd ZdddZ	dd
dZ
dddZdddZdS )!CIVisibilityCoverageReportEncodera  Simple encoder specifically for coverage report uploads.

    This encoder handles a single coverage report upload per session,
    creating multipart form data with the compressed report and metadata.

    Note: This encoder is called directly via encode_coverage_report() by the recorder,
    not through the put()/encode() BufferedEncoder pattern. However, it still needs
    to implement the encoder interface methods for compatibility with the writer's
    generic code paths.
    zmultipart/form-datac                 C  s   t  j| _d| j | _d S )Nzmultipart/form-data; boundary=)r   r   r   r   r4   r/   r/   r0   r#   g  s   
z*CIVisibilityCoverageReportEncoder.__init__r   rN   c                 C     dS )aS  Return 0 as this encoder doesn't use a buffer.

        This encoder handles coverage reports differently from standard encoders:
        - Standard encoders: buffer spans via put(), then encode() on periodic flush
        - Coverage encoder: direct upload via encode_coverage_report() for immediate synchronous delivery

        The no-op implementation satisfies the writer's generic interface (which iterates
        over all clients and checks len(encoder)), while the actual upload happens through
        the custom encode_coverage_report() method called directly by the recorder.
        r   r/   r4   r/   r/   r0   r5   k  s   z)CIVisibilityCoverageReportEncoder.__len__r    c                 C  r   )a  No-op: Coverage reports are uploaded directly, not buffered.

        This method exists for interface compatibility but is not used. Coverage reports
        require immediate synchronous upload at session finish (before process exit),
        so they bypass the standard put()/encode() buffering pattern.

        Args:
            item: Unused - coverage reports are uploaded via encode_coverage_report()
        Nr/   r>   r/   r/   r0   r@   x     
z%CIVisibilityCoverageReportEncoder.putlistc                 C  s   g S )a_  Return empty list as this encoder doesn't buffer items.

        Coverage reports are uploaded immediately via encode_coverage_report() rather than
        being buffered and flushed periodically. This ensures synchronous delivery before
        the test session ends.

        Returns:
            Empty list - no buffered payloads to encode
        r/   r4   r/   r/   r0   rL     r   z(CIVisibilityCoverageReportEncoder.encodereport_bytesrv   coverage_formatr7   
event_datar   c           
      C  s   ddl }||}g }|d| j d| dddg d|d }d| j d	d
dt|d| j dg}d|}	| | d |	  S )al  Encode coverage report as multipart form data.

        Args:
            report_bytes: The raw coverage report content
            coverage_format: The format of the report (e.g., 'lcov')
            event_data: Metadata about the coverage report (git info, service, timestamp, etc.)

        Returns:
            The encoded multipart form data as bytes
        r   Nz--zDContent-Disposition: form-data; name="coverage"; filename="coverage.z.gz"zContent-Type: application/gzip z
zCContent-Disposition: form-data; name="event"; filename="event.json"zContent-Type: application/jsonr   )gzipcompressextendr   r   r   dumpsrL   )
r,   r   r   r   r   compressed_reportparts
parts_textevent_parts
event_textr/   r/   r0   encode_coverage_report  s(   





z8CIVisibilityCoverageReportEncoder.encode_coverage_reportN)r   rN   )r   r    )r   r   )r   rv   r   r7   r   r   r   rv   )r   r   r   __doc__r   r   COVERAGE_REPORTrH   r#   r5   r@   rL   r   r/   r/   r/   r0   r   X  s    


r   ).
__future__r   r   r(   r%   typingr   r   r   uuidr   ddtrace.extr   ddtrace.internal._encodingr   r	   r{   (ddtrace.internal.ci_visibility.constantsr
   r   r   r   r   r   r   r   r   0ddtrace.internal.ci_visibility.telemetry.payloadr   r   r   ddtrace.internal.encodingr   ddtrace.internal.loggerr   ddtrace.internal.utils.timer   ddtrace.internal.writer.writerr   r   r`   ddtrace._trace.spanr   r   r   r   r/   r/   r/   r0   <module>   sD     S`