
    jKj                       d dl Z d dlZd dlZd dlZd dl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
mZmZ d dlmZmZmZmZ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mZmZmZ d dl m!Z! d d	l"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z-m.Z/m0Z1m2Z2m3Z4m5Z6m7Z8 d d
l9m:Z: d dl;m<Z< d dl=m>Z>m?Z?m@Z@mAZAmBZBmCZCmDZDmEZEmFZFmGZG d dlHmIZImJZJmKZK d dlLmMZMmNZNmOZOmPZP d dlQmRZRmSZS d dlTmUZU d dlVmWZWmXZXmYZYmZZZm[Z[m\Z\m]Z]m^Z^m_Z_m`Z`maZambZbmcZcmdZd d dlemfZfmgZgmhZhmiZimjZjmkZkmlZlmmZmmnZnmoZompZpmqZq d dlrmsZsmtZtmuZumvZvmwZwmxZx d dlymzZz d dl{m|Z|m}Z}  e<        dZ~deeef   fdZdefdZd ZddZ G d de      Zd Zy)     N)datetime	timedeltatimezone)AnyDictListMappingOptionalUnion)UUIDuuid4)Unpack)_BackgroundEventLoopRunner)ID_TYPESExceptionArgOptionalCaptureArgsOptionalSetArgs)Consumer)_get_current_context,get_capture_exception_code_variables_context)get_code_variables_detect_secrets_context*get_code_variables_ignore_patterns_context(get_code_variables_mask_patterns_context/get_code_variables_mask_url_credentials_contextget_context_device_idget_context_distinct_idget_context_session_idget_tagsidentify_context_scopednew_contextset_context_device_idset_context_sessiontag)ExceptionCapture)_configure_posthog_logging)
%DEFAULT_CODE_VARIABLES_DETECT_SECRETS&DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS$DEFAULT_CODE_VARIABLES_MASK_PATTERNS+DEFAULT_CODE_VARIABLES_MASK_URL_CREDENTIALSexc_info_from_errorexception_is_already_capturedexceptions_from_error_tuplehandle_in_appmark_exception_as_captured#try_attach_code_variables_to_frames)FeatureFlagEvaluations_EvaluatedFlagRecord_FeatureFlagEvaluationsHost)InconclusiveMatchErrorRequiresServerEvaluationmatch_feature_flag_propertiesresolve_bucketing_value)FlagDefinitionCacheDataFlagDefinitionCacheProvider)Poller)AI_EVENTS_ENDPOINTEVENTS_ENDPOINTAPIErrorQuotaLimitErrorRequestsConnectionErrorRequestsTimeout
batch_postdetermine_server_hostflagsgetis_ai_eventnormalize_hostremote_configreset_sessions)FeatureFlagFeatureFlagErrorFeatureFlagResultFlagMetadataFlagsAndPayloadsFlagsResponse	FlagValueSendFeatureFlagsOptionsnormalize_flags_responseto_flags_and_payloadsto_payloads	to_values)	FlagCacheRedisFlagCacheSizeLimitedDictcleanguess_timezonesystem_context)VERSION)QueueFulliP  returnc                     t        |       }|rt        |      r|dfS t               }|r|dfS t        t	                     dfS )zMReturns the distinct id to use, and whether this is a personless event or notFT)stringify_idlenr   strr   )passedstringified
context_ids      _/Users/ahmed/devFolder/Ultron/claude-voice/.venv/lib/python3.12/site-packages/posthog/client.pyget_identity_staterg   u   sH    v&Ks;'U##(*JE""L$    c                     t        | t              rt        |       S t        |       }|st	        d| d      	 t        |       |S # t        $ r t	        d| d      d w xY w)NzInvalid event uuid z5. Expected a valid UUID string or uuid.UUID instance.)
isinstancer   rb   r`   
ValueError)valuerd   s     rf   _stringify_event_uuidrm      s    %5zu%K!%*_`
 	
[   !%*_`
	s   A A c                     | xs i } t               }|r?|j                         }t        |j                               | d<   |j	                  |        |} d| vrt               rt               | d<   | S )Nz$context_tags$session_id)r   collect_tagssetkeysupdater   )
propertiescurrent_contextcontext_tagss      rf   add_context_tagsrw      sq    !rJ*,O&335&),*;*;*=&>
?#J'!
J&+A+C$:$<
=!rh   c                       fd}|S )a'  
    Decorator to prevent raising exceptions from public API methods.
    Note that this doesn't prevent errors from propagating via `on_error`.
    Exceptions will still be raised if the debug flag is enabled.

    Args:
        default_return: Value to return on exception (default: None)
    c                 8     ddl m}  |        fd       }|S )Nr   )wrapsc                     	  | g|i |S # t         $ rE}| j                  r|| j                  j                  dj                   d|        cY d }~S d }~ww xY w)Nz	Error in z: )	Exceptiondebuglog	exception__name__)selfargskwargsedefault_returnfuncs       rf   wrapperz,no_throw.<locals>.decorator.<locals>.wrapper   sb    &D242622 &::G""Yt}}oRs#CD%%	&s    	A:AAA)	functoolsrz   )r   rz   r   r   s   `  rf   	decoratorzno_throw.<locals>.decorator   s#    #	t	& 
	& rh    )r   r   s   ` rf   no_throwr      s     rh   c            (          e Zd ZU dZ ej
                  d      Z ej                         Z	i Z
eeeef   ej                  f   ed<    e       Zeeeef      ed<   dddddd	d
dddddddddddddddddddddddddddddej(                  ej*                  ej,                  df(dedee   dee   dz  fdZd Zd Zd Zd{dee   fdZd{dee   fdZ dede!ddfdZ"de#ee!f   fdZ$deddfdZ%d eddfd!Z&d"eddfd#Z'e(d$        Z)e)jT                  d%        Z)	 	 	 	 	 	 d|de+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      d)ee   d*eee      d"ee   deee-eef   f   fd+Z/	 	 	 	 	 	 d|de+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      d)ee   d*eee      d"ee   deeef   fd,Z0	 	 	 	 	 	 d|de+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      d)ee   d*eee      d"ee   de1fd-Z2	 	 	 	 	 	 	 d}dee+   d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      d)ee   d*eee      d"ee   de3fd.Z4	 	 	 	 	 	 	 d}dee+   d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      d)ee   d*eee      d"ee   de3fd/Z5 e6       d0ed1e7e8   dee   fd2       Z9de:fd3Z; e6       d1e7e<   dee   fd4       Z e6       d1e7e<   dee   fd5       Z= e6       	 	 	 	 	 d~d6ed7ed8ee#ee!f      d9ee-e>ef      d:ee-ee?f      d)ee   dee+   dee   fd;       Z@ e6       	 	 	 dd<edee   d9ee-e>ef      d:ee   d)ee   dee   fd=       ZAd>eeB   d1e7e8   dee   fd?ZCeDd@        ZEdA ZFdB ZGddCeeH   ddfdDZIddEZJddFZKdG ZLdH ZM	 ddIeNdJee   ddfdKZOdL ZPdM ZQdN ZRddddddOdeSfdPZTddddddddQdRede+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSedTed)ee   d"ee   dee   fdUZUde+dRedeeV   fdVZWdddddddddWdRede+dXeeS   d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSedTed)ee   d"ee   deeV   fdYZXddddddddQdRede+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSedTed)ee   d"ee   deeV   fdZZYddddddddQdRede+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSedTed)ee   d"ee   deeS   fd[ZZ	 ddRede+d&e,ee-ee.f   f   d'eeef   d(eeeee!f   f   d"ee   deeS   fd\Z[ddddddddd]dRede+d^eeS   d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSedTed)ee   d"ee   dee\   fd_Z]	 ddRede+d&e,ee-ee.f   f   d'eeef   d(eeeee!f   f   d)ee   d"ee   deee^   ee   ee.   ef   fd`Z_	 dde+dRedaeeS   dbee   dced&e,ee-ee.f   f   d)ee   ddee   deee.   dfee^   dgee   fdhZ`dddide+dRedaeeS   d8eee!f   d&ee,ee-ee.f   f      d)ee   ddfdjZadRefdkZbdRed^eSdee   fdlZcddddddddmde+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSed)ee   d*eee      d"ee   deeee-eef   f      fdnZdddddddddmde+d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSed)ee   d*eee      d"ee   de1fdoZe	 dddddddddpdee+   d&ee,ee-ee.f   f      d'ee#ee!f      d(ee#ee#ee!f   f      dSed)ee   dqeefe      d"ee   degfdrZhdZieej   eds<   dejfdtZkddddddude+d&e,ee-ee.f   f   d*eee      d"ee   dee1ef   f
dvZldw Zmdx Zndy Zodz Zpy)Clientai  
    This is the SDK reference for the PostHog Python SDK.
    You can learn more about example usage in the [Python SDK documentation](/docs/libraries/python).
    You can also follow [Flask](/docs/libraries/flask) and [Django](/docs/libraries/django)
    guides to integrate PostHog into your project.

    For long-running applications, create one client during application startup
    and reuse it for the lifetime of the process. This keeps background queues
    predictable and makes shutdown flushing straightforward. Multiple clients are
    still supported for intentional multi-project or multi-host setups.

    Examples:
        ```python
        from posthog import Posthog
        posthog = Posthog('<ph_project_api_key>', host='<ph_client_api_host>')
        posthog.debug = True
        if settings.TEST:
            posthog.disabled = True
        ```
    posthog_client_registry_duplicate_client_warningsNF'  Td   g      @            project_api_keyflag_definition_cache_providerin_app_modulesc*                 h  , || _         t        |      | _        |xs dj                         | _        || _        || _        || _        || _        t        |      | _
        t        |      | _        d| _        |	| _        || _        d| _        d| _        d| _        d| _        || _        || _        t-        d|      | _        d| _        t3        t4        t6              | _        || _        | j=                  |      | _        d| _         d| _!        || _"        d| _#        tI        jJ                         | _&        |xs | j                   | _'        || _(        || _)        || _*        |)| _+        || _,        || _-        || _.        |%| _/        |&| _0        |'| _1        |(| _2        d| _3        || _4        || _5        || _6        | | ntn        | _8        |!|!ntr        | _:        |"|"ntv        | _<        |#|#ntz        | _>        |$| _?        |	 t        j                         }|| _C        t        |t              r|j                         n|xs d| _F        |r>t        j                          | j                  j                  t        j                         n)| j                  j                  t        j                         | j                  s| j                  j                  d       | j                  |       | jZ                  r=t        | | j^                  | j`                  | jb                  | jd                        | _3        |rd| _P        n|rt        j                  | j                         g | _P        t        |      D ]n  }*t        | j                  | j                  | j                  ||||	|
||| jV                        }+| j                  j                  |+       |s_|+j                          p t        t        d      r.t        j                  |       ,t        j                  ,fd	       | j                          y# t        $ r d}Y 'w xY w)
a`  
        Initialize a new PostHog client instance.

        Args:
            project_api_key: PostHog project API key/token.
            host: PostHog host. Defaults to the US ingestion endpoint when not
                set. App hosts such as ``https://us.posthog.com`` are mapped to
                the corresponding ingestion host.
            debug: Enable verbose SDK logging and re-raise errors from public
                API methods.
            max_queue_size: Maximum number of events buffered before upload.
            send: If False, queueing succeeds but events are not sent.
            on_error: Optional callback invoked by background consumers when an
                upload fails.
            flush_at: Number of queued events that triggers a batch upload.
            flush_interval: Maximum seconds a background consumer waits before
                flushing a partial batch.
            gzip: Whether to gzip event upload payloads.
            max_retries: Number of upload retries for background consumers.
            sync_mode: If True, send each event synchronously instead of using
                background worker threads.
            timeout: HTTP request timeout in seconds for event uploads.
            thread: Number of background consumer threads.
            poll_interval: Seconds between local feature flag definition refreshes.
            personal_api_key: Personal API key used for local feature flag
                evaluation and remote config payloads.
            disabled: If True, disable captures and API requests. Useful in tests.
            disable_geoip: Whether to disable server-side GeoIP enrichment.
                Defaults to True.
            is_server: Whether events are emitted from a server-side runtime.
                Defaults to True; set to False when using the SDK as a client/CLI
                so the device OS is attributed to the person normally.
            historical_migration: Mark events as historical migration imports.
            feature_flags_request_timeout_seconds: Timeout in seconds for feature
                flag and remote config requests.
            feature_flags_request_max_retries: Number of retries for feature flag
                requests after network, transport, or timeout failures. Defaults
                to 1. Set to 0 to disable retries.
            super_properties: Properties merged into every captured event.
            enable_exception_autocapture: Automatically capture uncaught
                exceptions.
            log_captured_exceptions: Also log exceptions captured by error
                tracking.
            project_root: Root path used to determine in-app stack frames for
                captured exceptions. Defaults to the current working directory.
            privacy_mode: For AI observability, capture usage metadata without
                prompt inputs or outputs.
            before_send: Optional callback that can modify or drop events before
                upload. Return ``None`` to drop an event.
            flag_fallback_cache_url: Optional feature flag fallback cache URL,
                such as ``memory://local/?ttl=300&size=10000`` or a Redis URL.
            enable_local_evaluation: Whether to poll feature flag definitions for
                local evaluation when a personal API key is configured.
            flag_definition_cache_provider: Optional external cache provider for
                sharing feature flag definitions across workers.
            capture_exception_code_variables: Capture local variable values on
                exception stack frames.
            code_variables_mask_patterns: Variable-name patterns to mask when
                capturing code variables.
            code_variables_ignore_patterns: Variable-name patterns to omit when
                capturing code variables.
            code_variables_mask_url_credentials: Scrub credentials embedded in
                URLs/DSNs (e.g. ``user:pass@host``) from captured code variables,
                regardless of the surrounding variable name. Defaults to True.
            code_variables_detect_secrets: Last-resort entropy-based detection that
                redacts high-entropy secret-looking values (API keys, tokens, strong
                passwords) sitting in innocuously-named variables, after the name and
                URL checks. Skips structured ids (UUIDs, ObjectIds, hashes). Defaults
                to True.
            in_app_modules: Module/package prefixes treated as in-app frames in
                captured exceptions.
            enable_exception_autocapture_rate_limiting: Rate limit
                autocaptured exceptions client-side with a token bucket per
                exception type. Disabled by default.
            exception_autocapture_bucket_size: Maximum burst of autocaptured
                exceptions allowed per exception type (token bucket size,
                clamped to 0-100).
            exception_autocapture_refill_rate: Tokens restored per refill
                interval for each exception type's bucket.
            exception_autocapture_refill_interval_seconds: Seconds between
                token refills for autocaptured exception rate limiting.

        Examples:
            ```python
            from posthog import Posthog

            posthog = Posthog('<ph_project_api_key>', host='<ph_app_host>')
            ```

        Category:
            Initialization
         Nr   zFapi_key is empty after trimming whitespace; check your project API key)rate_limiting_enabledbucket_sizerefill_raterefill_interval_seconds)	hoston_errorflush_atflush_intervalgzipretriestimeouthistorical_migrationdedicated_ai_endpointregister_at_forkc                  .    t         j                         S N)r   _reinit_after_fork_weak)	weak_selfs   rf   <lambda>z!Client.__init__.<locals>.<lambda>  s    v'E'Ei'Prh   )after_in_child)]_max_queue_sizer\   queuestripapi_keyr   r}   send	sync_moderF   raw_hostrB   r   _duplicate_client_registry_keyr   r   _feature_flagsfeature_flags_by_keygroup_type_mappingcohortspoll_interval%feature_flags_request_timeout_secondsmax!feature_flags_request_max_retriespollerrW   MAX_DICT_SIZErq   #distinct_ids_feature_flags_reportedflag_fallback_cache_url_initialize_flag_cache
flag_cacheflag_definition_version_flags_etag_flag_definition_cache_provider,_flag_definition_cache_provider_async_runner	threadingLock1_flag_definition_cache_provider_async_runner_lockdisableddisable_geoip	is_serverr   _dedicated_ai_endpointsuper_propertiesenable_exception_autocapturelog_captured_exceptions*enable_exception_autocapture_rate_limiting!exception_autocapture_bucket_size!exception_autocapture_refill_rate-exception_autocapture_refill_interval_secondsexception_captureprivacy_modeenable_local_evaluation capture_exception_code_variablesr)   code_variables_mask_patternsr(   code_variables_ignore_patternsr*   #code_variables_mask_url_credentialsr'   code_variables_detect_secretsr   osgetcwdr|   project_rootrj   rb   personal_api_keyloggingbasicConfigr~   setLevelDEBUGWARNINGerror_set_before_sendr%   	consumersatexitregisterjoinranger   appendstarthasattrweakrefrefr   _warn_if_duplicate_async_client)-r   r   r   r}   max_queue_sizer   r   r   r   r   max_retriesr   r   threadr   r   r   r   r   r   r   r   r   r   r   r   r   before_sendr   r   r   r   r   r   r   r   r   r   r   r   r   r   _consumerr   s-                                               @rf   __init__zClient.__init__   s   P  .!.1
 (-2446 
	"&t,)$/	IM+	 	 ?C!<@15*1 	2 2502
. )-3B=RU3V0'>$556MN'($*./M,  	9 BKAQ> 4$4*"$8! '=# 0,H)'>$6 	7 2S.1R.9 	: "&('>$0P- ,7 )5 	) .9 +7 	+ 3> 0< 	0 -8 *6 	*
 -$!yy{ )
 *C0 ""$!! 	 	
  !HHgmm,HHgoo.||HHNNX 	k*,,%5&*&U&U BB BB(,(Z(Z&D" !DN 		*DN6]#JJLL%%#1'#)=*.*E*E %%h/ NN$% #( 2)*D)IP 	,,.U  $#$s   5P" "P10P1c                 :   | j                   s$| j                  r| j                  s| j                  sy | j                  | j                  f}d}t
        j                  5  t
        j                  j                  |t        j                               }t        |      dkD  }|j                  |        || _        |r3|t
        j                  vr!t
        j                  j                  |       d}d d d        |r| j                  j!                  d       y y # 1 sw Y   (xY w)NFr   TzMultiple active PostHog clients detected for the same project API key and host. Reuse one Posthog instance per app or process when possible to avoid competing background queues and missed shutdown flushes. Multiple clients are supported when intentional.)r   r   r   r   r   r   _client_registry_lockr   
setdefaultr   WeakSetra   addr   r   r~   warning)r   registry_keyshould_warnclientshas_existing_clients        rf   r   z&Client._warn_if_duplicate_async_client	  s    ==		T^^4<<dii0))--88goo/G #&g,"2KK2>D/ $ (I(II1155lC" * HH$  *)s   BDDc                 B   | j                   }|y t        j                  5  t        j                  j	                  |      }|C|j                  |        |s0t        j                  |= t        j                  j                  |       d | _         d d d        y # 1 sw Y   y xY wr   )r   r   r   r   rD   discardr   )r   r  r  s      rf   _unregister_duplicate_clientz#Client._unregister_duplicate_client(  s    ::))--11,?G"%//=55==lK26D/ *))s   A,BBc                     |6t        |      r|| _        y | j                  j                  d       d | _        y d | _        y )Nz/before_send is not callable, it will be ignored)callabler   r~   r  )r   r   s     rf   r   zClient._set_before_send7  s;    "$#.   !RS#' #Drh   capture_exceptionsc                     t        |||       S )au  
        Create a new context for managing shared state. Learn more about [contexts](/docs/libraries/python#contexts).

        Args:
            fresh: Whether to create a fresh context that doesn't inherit from parent.
            capture_exceptions: Whether to automatically capture exceptions in this context. If omitted, defaults to this client's exception autocapture setting.

        Examples:
            ```python
            with client.new_context():
                client.identify_context('<distinct_id>')
                client.capture('event_name')
            ```

        Category:
            Contexts
        freshr  client)r!   r   r  r  s      rf   r!   zClient.new_contextA  s    $ ,>t
 	
rh   c                     t        |||       S )a  
        Decorator that creates a new context for the wrapped function using this client.

        Args:
            fresh: Whether to create a fresh context that doesn't inherit from parent.
            capture_exceptions: Whether to automatically capture exceptions in this context. If omitted, defaults to this client's exception autocapture setting.

        Category:
            Contexts
        r  )_context_scopedr  s      rf   scopedzClient.scopedW  s     ,>t
 	
rh   namerl   r^   c                     t        ||       y)z
        Add a tag to the current context.

        Args:
            name: The tag key.
            value: The tag value.

        Category:
            Contexts
        N)_context_tag)r   r  rl   s      rf   r$   z
Client.tagg  s     	T5!rh   c                     t               S )z
        Get all tags from the current context.

        Returns:
            Dict of all tags in the current context.

        Category:
            Contexts
        )_context_get_tagsr   s    rf   r   zClient.get_tagst  s     !""rh   distinct_idc                     t        |       y)z
        Identify the current context with a distinct ID.

        Args:
            distinct_id: The distinct ID to associate with the current context and its children.

        Category:
            Contexts
        N)_context_identify_context)r   r  s     rf   r   zClient.identify_context  s     	"+.rh   
session_idc                     t        |       y)z
        Set the session ID for the current context.

        Args:
            session_id: The session ID to associate with the current context and its children.

        Category:
            Contexts
        N)_context_set_context_session)r   r  s     rf   r#   zClient.set_context_session  s     	%Z0rh   	device_idc                     t        |       y)z
        Set the device ID for the current context.

        Args:
            device_id: The device ID to associate with the current context and its children.

        Category:
            Contexts
        N)_context_set_context_device_id)r   r!  s     rf   r"   zClient.set_context_device_id  s     	'y1rh   c                     | j                   S )z9
        Get the local evaluation feature flags.
        )r   r  s    rf   feature_flagszClient.feature_flags  s    
 """rh   c                     |xs g | _         | j                   D ci c]  }|j                  d      |d   | c}| _        | j                  J d       yc c}w )z9
        Set the local evaluation feature flags.
        keyNDfeature_flags_by_key should be initialized when feature_flags is set)r   rD   r   )r   rC   flags      rf   r%  zClient.feature_flags  sr    
 $kr ++%
+xx* K+%
!
 ((4 	
R	
4%
s   Agroupsperson_propertiesgroup_propertiesr   flag_keys_to_evaluatec           	      P    | j                  |||||||      }t        |      xs i S )a  
        Get feature flag variants for a user.

        Args:
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            disable_geoip: Whether to disable GeoIP for this request.
            flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
                only these flags will be evaluated, improving performance.
            device_id: The device ID for this request.

        Category:
            Feature flags
        r!  )get_flags_decisionrT   	r   r  r*  r+  r,  r   r-  r!  	resp_datas	            rf   get_feature_variantszClient.get_feature_variants  s@    4 ++! , 
	 #)r)rh   c           	      P    | j                  |||||||      }t        |      xs i S )a  
        Get feature flag payloads for a user.

        Args:
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            disable_geoip: Whether to disable GeoIP for this request.
            flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
                only these flags will be evaluated, improving performance.
            device_id: The device ID for this request.

        Examples:
            ```python
            payloads = posthog.get_feature_payloads('<distinct_id>')
            ```

        Category:
            Feature flags
        r/  )r0  rS   r1  s	            rf   get_feature_payloadszClient.get_feature_payloads  s@    > ++! , 
	 9%++rh   c           	      H    | j                  |||||||      }t        |      S )a  
        Get feature flags and payloads for a user.

        Args:
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            disable_geoip: Whether to disable GeoIP for this request.
            flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
                only these flags will be evaluated, improving performance.
            device_id: The device ID for this request.

        Examples:
            ```python
            result = posthog.get_feature_flags_and_payloads('<distinct_id>')
            ```

        Category:
            Feature flags
        r/  )r0  rR   )	r   r  r*  r+  r,  r   r-  r!  resps	            rf   get_feature_flags_and_payloadsz%Client.get_feature_flags_and_payloads	  s;    > &&! ' 
 %T**rh   c           	          	 | j                  |||||||      S # t        $ r1}| j                  j                  d|       t	        i       cY d}~S d}~ww xY w)a  
        Get feature flags decision.

        Args:
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            disable_geoip: Whether to disable GeoIP for this request.
            flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
                only these flags will be evaluated, improving performance.
            device_id: The device ID for this request.

        Examples:
            ```python
            decision = posthog.get_flags_decision('user123')
            ```

        Category:
            Feature flags
        r/  zUnable to get feature flags: %sN)_get_flags_decisionr|   r~   r   rQ   )	r   r  r*  r+  r,  r   r-  r!  errs	            rf   r0  zClient.get_flags_decision3  sf    >	0++! %# ,    	0HH@#F+B//	0s    	A&A	AAc                 N   | j                   rt        i       S |xs i }|xs i }|xs i }|
t               }|
t               }|| j                  }|si }||||||d}|r||d<   t        | j                  | j                  f| j                  | j                  d|}	t        |	      S )N)r  r*  r+  r,  geoip_disabler!  r-  )r   r   )
r   rQ   r   r   r   rC   r   r   r   r   )
r   r  r*  r+  r,  r   r-  r!  request_datar2  s
             rf   r:  zClient._get_flags_decision`  s     ==+B//2-3+1r13K-/I  ..MF '!2 0*"(
 !4IL01LLII
 >>>>	

 
	 (	22rh   eventr   c           
      .   |j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd	      }	|j                  d
d      }
i |xs i t               }t        |      }|J t        |      \  }}|r	d|vrd	|d<   |||||d}|r||d<   i }|/|	r| j                  j                  d       |j                         }n1i }| j                  |	      }|d   rt        j                  dt        d       	 |d   du r&| j                  ||xs i |d   |d   |
d|d         }nz|d   d	u r!| j                  |||d   |d   |
|d         }nR| j                  r&| j                  ||xs i |d   |d   |
d|d         }n | j                  |||d   |d   |
|d         }|xs i j!                         D ]  \  }}||d| <    |xs i j!                         D cg c]  \  }}|d	ur| }}}|r||d<   |ri ||}||d<   | j#                  ||
      S # t        $ r(}| j                  j                  d|        Y d}~d}~ww xY wc c}}w )a  
        Captures an event manually. [Learn about capture best practices](https://posthog.com/docs/product-analytics/capture-events)

        Args:
            event: The event name to capture.
            distinct_id: The distinct ID of the user.
            properties: A dictionary of properties to include with the event.
            timestamp: The timestamp of the event.
            uuid: A unique identifier for the event. If provided, it must be a
                valid UUID string or uuid.UUID instance; invalid values are
                ignored and replaced with a newly generated UUID.
            groups: A dictionary of group information.
            flags: A FeatureFlagEvaluations snapshot from evaluate_flags(). The
                exact values from the snapshot are attached with no extra /flags
                request.
            send_feature_flags: Deprecated. Prefer flags=... from
                evaluate_flags(). When truthy, evaluates flags during capture and
                attaches them to the event.
            disable_geoip: Whether to disable GeoIP for this event.

        Examples:
            ```python
            # Anonymous event
            posthog.capture('some-anon-event')
            ```
            ```python
            # Context usage
            from posthog import identify_context, new_context
            with new_context():
                identify_context('distinct_id_of_the_user')
                posthog.capture('user_signed_up')
                posthog.capture('user_logged_in')
                posthog.capture('some-custom-action', distinct_id='distinct_id_of_the_user')
            ```
            ```python
            # Set event properties
            posthog.capture(
                "user_signed_up",
                distinct_id="distinct_id_of_the_user",
                properties={
                    "login_type": "email",
                    "is_free_trial": "true"
                }
            )
            ```
            ```python
            # Page view event
            posthog.capture('$pageview', distinct_id="distinct_id_of_the_user", properties={'$current_url': 'https://example.com'})
            ```

        Category:
            Capture
        r  Nrt   	timestampuuidr*  rC   send_feature_flagsFr   z$process_person_profile)rt   rA  r  r?  rB  z$groupsz[FEATURE FLAGS] Both `flags` and `send_feature_flags` were passed to capture(); using `flags` and ignoring `send_feature_flags`.should_sendu  `send_feature_flags` is deprecated and will be removed in a future major version. Pass a `flags` snapshot from `posthog.evaluate_flags(...)` instead — it avoids a second `/flags` request per capture and guarantees the event carries the exact flag values your code branched on.   
stacklevelonly_evaluate_locallyTr+  r,  flag_keys_filter)r*  r+  r,  r   rH  r-  )r+  r,  r   r-  z0[FEATURE FLAGS] Unable to get feature variants: 	$feature/z$active_feature_flags)rD   rZ   rw   rg   r~   r  _get_event_properties_parse_send_feature_flagswarningswarnDeprecationWarningget_all_flagsr3  r%  r|   r   items_enqueue)r   r?  r   r  rt   rA  rB  r*  flags_snapshotrC  r   
personlessmsgextra_propertiesfeature_variantsflag_optionsr   featurevariantr'  rl   active_feature_flagss                         rf   capturezClient.capture  s   r jj5ZZd3
JJ{D1	zz&$'Hd+GT2#ZZ(<eD

?D9?)r?n.>?
%j1
%%%$6{$C!j3:E49J01 %"&
 $*Jy!+- %!  R  .CCEFH  99:LMLM*K ' .#$;<D+/+=+='$*Lb.:;N.O-9:L-M*7262>?Q2R ,> ,( &&=>%G+/+D+D'".:;N.O-9:L-M*72>?Q2R ,E ,( +++/+=+='$*Lb.:;N.O-9:L-M*7262>?Q2R ,> ,( ,0+D+D'".:;N.O-9:L-M*72>?Q2R ,E ,( &6%;$B$B$D :A 9WI!67 %E
 &6%;$B$B$D$$DLS%% $D ! $
 $<P !89;,;
;J *C}}S-00) ! HH&&J1#N $s   B'I $J	J&J		Jc                 
   t        |t              rDd|j                  d      |j                  d      |j                  d      |j                  d      dS t        |t              r|dddddS t	        dt        |       d	      )
a  
        Parse and normalize send_feature_flags parameter into a standard format.

        Args:
            send_feature_flags: Either bool or SendFeatureFlagsOptions dict

        Returns:
            SendFeatureFlagsOptions: Normalized options with keys: should_send, only_evaluate_locally,
                  person_properties, group_properties, flag_keys_filter

        Raises:
            TypeError: If send_feature_flags is not bool or dict
        TrH  r+  r,  rI  )rD  rH  r+  r,  rI  Nz%Invalid type for send_feature_flags: z. Expected bool or dict.)rj   dictrD   bool	TypeErrortype)r   rC  s     rf   rL  z Client._parse_send_feature_flagsG  s     ($/#);)?)?+* &8%;%;<O%P$6$:$:;M$N$6$:$:;M$N  *D11)-%)$($(  7=O8P7Q R) * rh   c                 2   |j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|xs i }t        |      }t        |      \  }}|s|sy|||d|d}| j                  ||      S )	a0  
        Set properties on a person profile.

        Args:
            distinct_id: The distinct ID of the user.
            properties: A dictionary of properties to set.
            timestamp: The timestamp of the event.
            uuid: A unique identifier for the event. If provided, it must be a
                valid UUID string or uuid.UUID instance; invalid values are
                ignored and replaced with a newly generated UUID.
            disable_geoip: Whether to disable GeoIP for this event.

        Examples:
            ```python
            # Set with distinct id
            posthog.set(distinct_id='user123', properties={'name': 'Max Hedgehog'})
            ```

        Category:
            Identification

        Note: This method will not raise exceptions. Errors are logged.
        r  Nrt   rA  rB  r   $set)rA  r  rc  r?  rB  rD   rw   rg   rR  	r   r   r  rt   rA  rB  r   rT  rU  s	            rf   rq   z
Client.setm  s    2 jj5ZZd3
JJ{D1	zz&$'

?D9%2
%j1
$6{$C!jZ #&
 }}S-00rh   c                 2   |j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|xs i }t        |      }t        |      \  }}|s|sy|||d|d}| j                  ||      S )	aI  
        Set properties on a person profile only if they haven't been set before.

        Args:
            distinct_id: The distinct ID of the user.
            properties: A dictionary of properties to set once.
            timestamp: The timestamp of the event.
            uuid: A unique identifier for the event. If provided, it must be a
                valid UUID string or uuid.UUID instance; invalid values are
                ignored and replaced with a newly generated UUID.
            disable_geoip: Whether to disable GeoIP for this event.

        Examples:
            ```python
            posthog.set_once(distinct_id='user123', properties={'initial_signup_date': '2024-01-01'})
            ```

        Category:
            Identification

        Note: This method will not raise exceptions. Errors are logged.
        r  Nrt   rA  rB  r   	$set_once)rA  r  rg  r?  rB  rd  re  s	            rf   set_oncezClient.set_once  s    0 jj5ZZd3
JJ{D1	zz&$'

?D9%2
%j1
$6{$C!jZ #&# 
 }}S-00rh   
group_type	group_keyrt   rA  rB  c                     |xs i }t        |      d   }d|||d|||d}t               rt        t                     |d   d<   | j                  ||      S )a  
        Identify a group and set its properties.

        Args:
            group_type: The type of group (e.g., 'company', 'team').
            group_key: The unique identifier for the group.
            properties: A dictionary of properties to set on the group.
            timestamp: The timestamp of the event.
            uuid: A unique identifier for the event. If provided, it must be a
                valid UUID string or uuid.UUID instance; invalid values are
                ignored and replaced with a newly generated UUID.
            disable_geoip: Whether to disable GeoIP for this event.
            distinct_id: The distinct ID of the user performing the action.

        Examples:
            ```python
            posthog.group_identify('company', 'company_id_in_your_db', {
                'name': 'Awesome Inc.',
                'employees': 11
            })
            ```

        Category:
            Identification

        Note: This method will not raise exceptions. Errors are logged.
        r   z$groupidentify)z$group_type
$group_keyz
$group_set)r?  rt   r  rA  rB  rt   ro   rg   r   rb   rR  )	r   ri  rj  rt   rA  rB  r   r  rU  s	            rf   group_identifyzClient.group_identify  sv    L  %2
 )5a8 &)'(
 '"

 "#/23I3K/LCm,}}S-00rh   previous_idc                     t        |      \  }}|ry||d|d||d}t               rt        t                     |d   d<   | j                  ||      S )a  
        Create an alias between two distinct IDs.

        Args:
            previous_id: The previous distinct ID.
            distinct_id: The new distinct ID to alias to.
            timestamp: The timestamp of the event.
            uuid: A unique identifier for the event. If provided, it must be a
                valid UUID string or uuid.UUID instance; invalid values are
                ignored and replaced with a newly generated UUID.
            disable_geoip: Whether to disable GeoIP for this event.

        Examples:
            ```python
            posthog.alias(previous_id='distinct_id', distinct_id='alias_id')
            ```

        Category:
            Identification

        Note: This method will not raise exceptions. Errors are logged.
        N)r  aliasz$create_alias)rt   rA  r?  r  rB  rt   ro   rm  )r   ro  r  rA  rB  r   rT  rU  s           rf   rq  zClient.alias  sl    > %7{$C!j  +$ #$&	
 "#/23I3K/LCm,}}S-00rh   r   c                    |j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }	 |xs i }|'t        |      r| j                  j                  d       y|t	        |      }nt        j                         }||d	k(  r| j                  j                  d
       yt        |      }	t        dd|	ii| j                  | j                        }
|
d   d   }d|i|}t               }t               }t               }t               }t!               }||n| j"                  }||n| j$                  }||n| j&                  }||n| j(                  }||n| j*                  }|rt-        ||||||       | j.                  r| j                  j1                  ||       |j                  dd      }|j                  dd      }|j                  dd      }| j3                  d||||||||	      }||t5        ||       |S # t6        $ r(}| j                  j1                  d|        Y d}~yd}~ww xY w)ay  
        Capture an exception for error tracking.

        Args:
            exception: The exception to capture.
            distinct_id: The distinct ID of the user.
            properties: A dictionary of additional properties.
            flags: A ``FeatureFlagEvaluations`` snapshot from ``evaluate_flags()``.
                Attaches those exact flag values to the captured `$exception` event.
            send_feature_flags: Deprecated. Pass ``flags`` from ``evaluate_flags()`` instead.
            disable_geoip: Whether to disable GeoIP for this event.

        Examples:
            ```python
            try:
                # Some code that might fail
                pass
            except Exception as e:
                posthog.capture_exception(e, 'user_distinct_id', properties=additional_properties)
            ```

        Category:
            Error Tracking
        r  Nrt   rC   rC  Fr   z$Exception already captured, skippingNNNz"No exception information availabler   values)in_app_includer   z$exception_list)mask_patternsignore_patternsmask_url_credentialsdetect_secrets)extrarA  rB  r*  z
$exception)r  rt   rA  rB  r*  rC   rC  r   zFailed to capture exception: )rD   r,   r~   r}   r+   sysexc_infor  r-   r.   r   r   r   r   r   r   r   r   r   r   r   r   r0   r   r   r\  r/   r|   )r   r   r   r  rt   rS  rC  r   r|  all_exceptions_with_tracer?  $all_exceptions_with_trace_and_in_appcontext_enabledcontext_maskcontext_ignorecontext_mask_url_credentialscontext_detect_secretsenabledrv  rw  rx  ry  rA  rB  r*  resr   s                              rf   capture_exceptionzClient.capture_exception@  s   : jj5ZZd3
GT2#ZZ(<eD

?D9j	#)rJ $)Fy)QEF$.y9<<>8/A#A  !EF )DH(M% " ";"
  $22!..E 493Eh3O0 "#GJ
 KLOCELGIN?A ) &O%P" #.  ::   + 66  "- 88  0; -== ! *5 '77  38"/$3)=#1 ++""9F";

;5I::fd+DZZ$/F,,'%#$#5+  
C $*9c:J 	HH!>qcBC	s&   .H/ AH/ EH/ /	I 8II c                 8     |        }|y|j                          y)zk
        Reinitialize the client after a fork.
        Garbage collected if the client is deleted.
        N)_reinit_after_fork)r   r   s     rf   r   zClient._reinit_after_fork_weak  s     {<!rh   c                 l   | j                   rt        | j                        | _        g }| j                   D ]  }t	        | j                  |j
                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                        }|j                  |       | j                   s|j#                           || _         | j$                  rKt'        t)        | j*                        | j,                        | _        | j.                  j#                          nd| _        d| _        t3        j4                         | _        t9        | j:                  t<              r | j?                  | j@                        | _        tC                y)a5  Reinitialize fork-unsafe client state in a forked child process.

        Registered via os.register_at_fork(after_in_child=...) so it runs
        exactly once in each child, before any user code, covering all code
        paths (capture, flush, join, etc.).

        Python threads do not survive fork() and queue.Queue internal locks
        may be in an inconsistent state, so the event queue, consumer threads
        and other state are replaced. Inherited queue items are not retained
        as they'll be handled by the parent process's consumers.
        )	r   r   r   r   r   r   r   r   r   secondsintervalexecuteN)"r   r\   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r:   r   r   _load_feature_flagsr   r   r   r   r   rj   r   rV   r   r   rH   )r   new_consumersoldr   s       rf   r  zClient._reinit_after_fork  sD    >>t334DJM~~#JJKK \\ \\#&#5#5KKKK),)A)A*-*C*C $$X.99NN$# && +DN'' "4+=+=>00DK KKDK =A9AJAQ> doo~6"99$:V:VWDOrh   c           	      4   | j                   ry|d   }|$t        j                  t        j                        }t        |      }|j                         |d<   d|v r"|j                  d      }|	 t        |      |d<   d|vrt        t                     |d<   |d   }|j                  d      si |d<   d|d   d<   t        |d   d	<   || j                   }|rd
|d   d<   | j"                  ri |d   | j"                  |d<   | j$                  rd
|d   d<   t        |j                  dd            |d<   t'        |      }| j(                  r2	 | j)                  |      }|| j                  j+                  d       y|}| j                  j+                  d|       | j0                  s|S | j2                  r| j                  j+                  d|d          | j4                  r t7        |j                  d            rt8        nt:        }t=        | j>                  | j@                  | jB                  | jD                  |g| jF                  |       |S 	 | jH                  jK                  |d       | j                  j+                  d|d          |S # t        $ r'}| j                  j                  d|       Y d}~6d}~ww xY w# t,        $ r)}| j                  j/                  d|        Y d}~md}~ww xY w# tL        $ r | j                  jO                  d       Y yw xY w)z8Push a new `msg` onto the queue, return `(success, msg)`NrA  tzrB  z$%s Falling back to a generated UUID.rt   zposthog-pythonz$libz$lib_versionTz$geoip_disablez
$is_serverr  z%Event dropped by before_send callbackzError in before_send callback: zqueueing: %szenqueued with blocking %s.r?  )r   r   batchr   pathF)blockzenqueued %s.zanalytics-python queue is full)(r   r   nowr   utcrY   	isoformatpoprm   rk   r~   r   r`   r   rD   r[   r   r   r   rX   r   r}   r|   r   r   r   r   rE   r;   r<   rA   r   r   r   r   r   r   putr]   r  )	r   rU  r   rA  rB  r   	sent_uuidmodified_msgr  s	            rf   rR  zClient._enqueue  s    ==$	 5I #9-	$..0KS=776?DN"7"=CK &uw/CKK	ww|$ "C$4L&!,3L.)  ..M26C./   N3|#4 N8M8M NC >>.2Cl+)#''-*FGMCjJ#//4'HHNN#JK"
 	~s+ yy>>HHNN7WF ..;swww?O3P #$ 
 		YYe%)%>%> 	JJNN3eN,HHNN>3w<8O " NHHNN#I1MMNN  J""%DQC#HIIJB  	HH=>	sH   /J 
.J; 9J; 
=K0 	J8J33J8;	K-K((K-0$LLtimeout_secondsc                 r   | j                   }|j                         }	 ||j                          nt        j                         |z   }|j
                  5  |j                  ru|t        j                         z
  }|dk  r1| j                  j                  d||j                         	 ddd       y|j
                  j                  |       |j                  ruddd       | j                  j                  d|       y# 1 sw Y   &xY w# t        $ r&}| j                  j                  d|       Y d}~yd}~ww xY w)a  
        Force a flush from the internal queue to the server. Do not use directly, call `shutdown()` instead.

        Args:
            timeout_seconds: Maximum seconds to wait for the queue to flush.
                Defaults to 10 seconds. Pass ``None`` to wait indefinitely.

        Examples:
            ```python
            posthog.capture('event_name')
            posthog.flush()  # Ensures the event is sent immediately
            ```
        Nr   z7flush timed out after %s seconds with %s items pending.zerror flushing queue: %sz$successfully flushed about %s items.)r   qsizer   time	monotonicall_tasks_doneunfinished_tasksr~   r  waitr|   r   r}   )r   r  r   sizedeadline	remainingr   s          rf   flushzClient.flusht  s    

{{}	&

>>+o=))00$,t~~/?$?	$> HH,, Y / % 6 6
 # *) ,,11)<  00 *  	=tD! *)  	HH91=	s<   6D AC;%D .'C;D ;D D 	D6D11D6c                 *   | j                   r2| j                   D ]#  }|j                          	 |j                          % | j                  r| j                  j                          | j                          | j                          y# t        $ r Y xw xY w)z
        End the consumer thread once the queue is empty. Do not use directly, call `shutdown()` instead.

        Examples:
            ```python
            posthog.join()
            ```
        N)r   pauser   RuntimeErrorr   stop(_shutdown_flag_definition_cache_providerr	  )r   r   s     rf   r   zClient.join  sx     >> NN MMO + ;;KK 	557))+ $ s   B	BBc                     | j                  d       | j                          | j                  j                          | j                  r| j                  j                          yy)z
        Flush all messages and cleanly shutdown the client. Call this before the process ends in serverless environments to avoid data loss.

        Examples:
            ```python
            posthog.shutdown()
            ```
        N)r  )r  r   r   clearr   closer  s    rf   shutdownzClient.shutdown  sM     	

4
(		00668!!""((* "rh   c                     t        j                  |      s|S | j                  5  | j                  t	               | _        | j                  j                  |      cd d d        S # 1 sw Y   y xY wr   )inspectisawaitabler   r   r   run)r   results     rf   ._resolve_flag_definition_cache_provider_resultz5Client._resolve_flag_definition_cache_provider_result  s[    ""6*MCC@@H.0 A DDHHP DCCs   6A$$A-c                    | j                   sy 	 | j                  | j                   j                                | j                  5  | j                  r!| j                  j                          d | _        d d d        y # t        $ r(}| j                  j                  d|        Y d }~od }~ww xY w# 1 sw Y   y xY w# | j                  5  | j                  r!| j                  j                          d | _        d d d        w # 1 sw Y   w xY wxY w)Nz/[FEATURE FLAGS] Cache provider shutdown error: )	r   r  r  r|   r~   r   r   r   r  )r   r   s     rf   r  z/Client._shutdown_flag_definition_cache_provider  s    33
	M??44==? GGDDEEKKMHLDE HG  	RHHNNLQCPQQ	R HGGGDDEEKKMHLDE HGGsM   )A; .B/;	B,B'"B; 'B,,B; /B8;D.C?6	D?DDdataold_flags_by_keyc                     |d   | _         |d   | _        |d   | _        | j                  rT|Q|| j                  xs i k7  r=| j
                  }| xj
                  dz  c_        | j                  j                  |       yyyy)zVUpdate internal flag state from cache data and invalidate evaluation cache if changed.rC   r   r   Nr   )r%  r   r   r   r   r   invalidate_version)r   r  r  old_versions       rf   _update_flag_statezClient._update_flag_state  s     "']"&';"<I OO , T%>%>%D"E66K((A-(OO..{;	 F - rh   c                    d}| j                   r*	 | j                  | j                   j                               }|s| j                   r	 | j                  | j                   j                               }|rf| j                  j                  d       | j                  || j                  xs i        t        j                  t        j                        | _        y | j                  s| j                  j                  d       d}|r| j!                          y y # t        $ r+}| j                  j                  d|        d}Y d }~d }~ww xY w# t        $ r*}| j                  j                  d|        d}Y d }~yd }~ww xY w)NTz3[FEATURE FLAGS] Cache provider should_fetch error: zA[FEATURE FLAGS] Using cached flag definitions from external cacher  r  zJ[FEATURE FLAGS] Cache empty and no flags loaded, falling back to API fetchz*[FEATURE FLAGS] Cache provider get error: )r   r  should_fetch_flag_definitionsr|   r~   r   get_flag_definitionsr}   r  r   r   r  r   r  _last_feature_flag_pollr%  _fetch_feature_flags_from_api)r   should_fetchr   cached_datas       rf   r  zClient._load_feature_flags  s`   //	$#RR88VVX   D D$"QQ88MMO HHNN[ ++#d6O6O6USU ,  4<<<8<<3PD0  --h (, ..0 G  $I!M  $$<  $!KA3OP#$s6   )D BE )E 	E
 EE
	F  E;;F c                    | j                   }|| j                  j                  d       y	 | j                  xs i }t	        |d| j
                   d| j                  d| j                        }|j                  | _        |j                  rE| j                  j                  d       t        j                  t        j                        | _        y|j                   | j                  j#                  d	       y| j%                  |j                   |
       | j&                  rY	 | j)                  | j&                  j+                  | j,                  xs g | j.                  xs i | j0                  xs i d             t        j                  t        j                        | _        y# t2        $ r(}| j                  j#                  d|        Y d}~Vd}~ww xY w# t4        $ r6}|j6                  dk(  rd|j8                   d}| j                  j#                  d|       g | _        i | _        i | _        | j:                  r| j:                  j=                          | j                  rt5        d|      |j6                  dk(  ro| j                  j                  d       g | _        i | _        i | _        | j:                  r| j:                  j=                          | j                  r+t5        dd      | j                  j#                  d|        Y d}~d}~wt2        $ rN}| j                  j                  d| j>                  z         | j                  j                  |       Y d}~d}~ww xY w)z)Fetch feature flags from the PostHog API.NL[FEATURE FLAGS] You have to specify a personal_api_key to use feature flags.z/flags/definitions?token=z&send_cohorts
   )r   etagz;[FEATURE FLAGS] Flags not modified (304), using cached datar  zB[FEATURE FLAGS] Unexpected empty response data in non-304 responser  )rC   r   r   z,[FEATURE FLAGS] Cache provider store error: i  zError loading feature flags: zw. Please verify both your project_api_key and personal_api_key. More information: https://posthog.com/docs/api/overviewz[FEATURE FLAGS] %s)statusmessagei  z[FEATURE FLAGS] PostHog feature flags quota limited, resetting feature flag data.  Learn more about billing limits at https://posthog.com/docs/billing/limits-alertsz#PostHog feature flags quota limitedz-[FEATURE FLAGS] Error loading feature flags: z`[FEATURE FLAGS] Fetching feature flags failed with following error. We will retry in %s seconds.) r   r~   r  r   rD   r   r   r   r  not_modifiedr}   r   r  r   r  r  r  r   r  r   r  on_flag_definitions_receivedr%  r   r   r|   r=   r  r  r   r  r   )r   r   r  responser   details         rf   r  z$Client._fetch_feature_flags_from_api  s   00#HH^ Y	 040I0I0OR +DLL>G		%%H  (}}D $$Q 08||x||/L,}}$X ##HMMDT#U 33WGG<<YY)-););)Ar6:6M6M6SQS+/<<+=2r (0||x||'D$a ! WHHNN%QRSQT#UVVW  $	Txx33AII; ?N N 
 3V<%'"*,'!??OO))+::"#v>>S   { &("*,'! ??OO))+::"" E 
 !NqcRS 	 HHr$$% HHQ	 sW   B#G 'G 8)G "AF$ $	G-GG GG 
M-"D+LM-AM((M-c                    | j                   rg | _        y| j                  s#| j                  j	                  d       g | _        y| j                          | j                  rr| j                  r| j                  j                         sKt        t        | j                        | j
                        | _        | j                  j                          yyy)z
        Load feature flags for local evaluation.

        Examples:
            ```python
            posthog.load_feature_flags()
            ```

        Category:
            Feature flags
        Nr  r  r  )r   r%  r   r~   r  r  r   r   is_aliver:   r   r   r   r  s    rf   load_feature_flagszClient.load_feature_flags  s     ==!#D$$HH^ "$D  " ''KKDKK002 "4+=+=>00DK KK 3 (rh   r*  r+  r,  warn_on_unknown_groupsr!  c                "   |xs i }|xs i }|xs i }i }|j                  dd      rt        d      |j                  d      sy|j                  d      xs i }	|	j                  d      }
| j                  xs i }|
|j                  t        |
            }|s/| j                  j                  d|
 d|d	           t        d
      ||vrH|r#| j                  j                  d|d	    d       y| j                  j                  d|d	    d       y||vrt        d| d      ||   }||   }t        |||| j                  | j                  ||||||      S t        |||      }t        |||| j                  | j                  ||||||      S )Nensure_experience_continuityFz&Flag has experience continuity enabledactivefiltersaggregation_group_type_indexz)[FEATURE FLAGS] Unknown group type index z for feature flag r'  z!Flag has unknown group type indexz2[FEATURE FLAGS] Can't compute group feature flag: z without group names passed inz(Flag has no group properties for group '')cohort_propertiesflags_by_keyevaluation_cacher!  bucketing_valuer   r*  r,  )rD   r4   r   rb   r~   r  r}   r6   r   r   r7   )r   feature_flagr  r*  r+  r,  r  r!  r  flag_filtersr  r   
group_namefocused_group_propertiesrj  r  s                   rf   _compute_flag_locallyzClient._compute_flag_locally  s!    2-3+1r <>:EB()QRR)#''	28b'3'7'78V'W$!44:'3+//4P0QRJ  ?@\?]]op|  ~C  qD  pE  F --PQQ' *HH$$L\Z_M`Laa  A  HHNNL\Z_M`Laa  A !11,>zl!L  (8
'C$z*I0("&,,!66!1# )#5!1  6k9O 1!"&,,!66!1# /#5!1 rh   r*  r+  r,  rH  send_feature_flag_eventsr   r!  r'  rH  r  c                    t        j                  dt        d       | j                  |||||||||		      }
|
r|
j	                         nd}|yt        |      S )a3  
        Check if a feature flag is enabled for a user.

        Args:
            key: The feature flag key.
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            only_evaluate_locally: Whether to only evaluate locally.
            send_feature_flag_events: Whether to send feature flag events.
            disable_geoip: Whether to disable GeoIP for this request.
            device_id: The device ID for this request.

        Examples:
            ```python
            is_my_flag_enabled = posthog.feature_enabled('flag-key', 'distinct_id_of_your_user')
            if is_my_flag_enabled:
                # Do something differently for this user
                # Optional: fetch the payload
                matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')
            ```

        Category:
            Feature flags
        u  `feature_enabled` is deprecated and will be removed in a future major version. Use `posthog.evaluate_flags(distinct_id, ...)` and call `flags.is_enabled(key)` instead — this consolidates flag evaluation into a single `/flags` request per incoming request.rE  rF  r  N)rM  rN  rO  _get_feature_flag_result	get_valuer_  )r   r'  r  r*  r+  r,  rH  r  r   r!  flag_resultr  s               rf   feature_enabledzClient.feature_enabled  sw    N 	  	
 33/-"7%=' 4 

 /:;((*tH~rh   c                     | j                   r>| j                   j                  ||      }|r | j                  j                  d|        |S y)z?Returns a stale cached flag value if available, otherwise None.z2[FEATURE FLAGS] Using stale cached value for flag N)r   get_stale_cached_flagr~   info)r   r  r'  stale_results       rf   _get_stale_flag_fallbackzClient._get_stale_flag_fallbackA  sI     ????@@cRLHN $#rh   override_match_valuer*  r+  r,  rH  r  r   r!  r  c                   | j                   ry | j                  |xs i |xs i |xs i       \  }}|xs i }|xs i }|xs i }d }d }d }d }d }|

t               }
| j                  ||      }| j	                  ||||||
      }|d u}|rm|xs |}|| j                  ||      nd }t        j                  |||      }| j                  r9|r6| j                  j                  |||| j                         n|r(| j                  | j                  j                  d       n	 | j                  ||||||	|
      \  }}}}g }|r|j                  t         j"                         ||j                  t         j$                         |rdj'                  |      }t        j(                  ||      }| j                  r*|r(| j                  j                  |||| j                         | j                  j+                  d| d|        |r;| jG                  |||r|jI                         nd |r|jJ                  nd |||	||||       |S # t,        $ rJ}| j                  j                  d|        t         j.                  }| j1                  ||      }Y d }~d }~wt2        $ rJ}| j                  j                  d|        t         j4                  }| j1                  ||      }Y d }~d }~wt6        $ rK}| j                  j                  d|        t         j8                  }| j1                  ||      }Y d }~0d }~wt:        $ rZ}| j                  j                  d	|        t!        j<                  |j>                        }| j1                  ||      }Y d }~d }~wt@        $ rK}| j                  jC                  d
|        t         jD                  }| j1                  ||      }Y d }~d }~ww xY w)Nz[FEATURE FLAGS] Local evaluation called but feature flag definitions are not loaded yet. Returning None. You can call load_feature_flags() to load flags explicitly.r/  ,z&Successfully computed flag remotely: #z -> #&[FEATURE FLAGS] Quota limit exceeded: z#[FEATURE FLAGS] Request timed out: z"[FEATURE FLAGS] Connection error: z[FEATURE FLAGS] API error: z-[FEATURE FLAGS] Unable to get flag remotely: )&r   &_add_local_person_and_group_propertiesr   '_person_properties_for_local_evaluation_locally_evaluate_flag_compute_payload_locallyrK   from_value_and_payloadr   set_cached_flagr   r%  r~   r  %_get_feature_flag_details_from_serverr   rJ   ERRORS_WHILE_COMPUTINGFLAG_MISSINGr   from_flag_detailsr}   r>   QUOTA_LIMITEDr  r@   TIMEOUTr?   CONNECTION_ERRORr=   	api_errorr  r|   r   UNKNOWN_ERROR_capture_feature_flag_calledr  payload)r   r'  r  r  r*  r+  r,  rH  r  r   r!  r  flag_details
request_idevaluated_atfeature_flag_errorlocal_person_properties
flag_valueflag_was_locally_evaluatedlookup_match_valuer  errors_while_computingerrorsr   s                           rf   r  zClient._get_feature_flag_resultN  s    == 77"!'R &B 	,+ 2-3+1r
,0 -/I"&"N"N*#
 00#

 &0t%;"%!5!C &1 --c3EF 
 ,BB'K
 ;//k43O3O #!!)  b
4N>>#)(%"+ ?  Oj,8N )MM"2"I"IJ'MM"2"?"?@),&)9&/AA "6
 ??{OO33#S+t7S7S <SE{mT0 $--+6%%'D'2##*" I # N  #I!!MN%5%C%C"";;KM" N  #Fqc!JK%5%=%="";;KM* N  #EaS!IJ%5%F%F"";;KM N  #>qc!BC%5%?%?%I"";;KM N""%RSTRU#VW%5%C%C"";;KMNsN   C!H> >	O'A JO'A KO')A L//O';ANO'A O""O'c                6    | j                  |||||||||		      S )a{  
        Get a FeatureFlagResult object which contains the flag result and payload for a key by evaluating locally or remotely
        depending on whether local evaluation is enabled and the flag can be locally evaluated.
        This also captures the `$feature_flag_called` event unless `send_feature_flag_events` is `False`.

        Examples:
            ```python
            flag_result = posthog.get_feature_flag_result('flag-key', 'distinct_id_of_your_user')
            if flag_result and flag_result.get_value() == 'variant-key':
                # Do something differently for this user
                # Optional: fetch the payload
                matched_flag_payload = flag_result.payload
            ```

        Args:
            key: The feature flag key.
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            only_evaluate_locally: Whether to only evaluate locally.
            send_feature_flag_events: Whether to send feature flag events.
            disable_geoip: Whether to disable GeoIP for this request.
            device_id: The device ID for this request.

        Returns:
            Optional[FeatureFlagResult]: The feature flag result or None if disabled/not found.
        r  )r  )
r   r'  r  r*  r+  r,  rH  r  r   r!  s
             rf   get_feature_flag_resultzClient.get_feature_flag_result  s8    R ,,/-"7%=' - 

 
	
rh   c                    t        j                  dt        d       | j                  |||||||||		      }
|
r|
j	                         S dS )au  
        Get multivariate feature flag value for a user.

        Args:
            key: The feature flag key.
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            only_evaluate_locally: Whether to only evaluate locally.
            send_feature_flag_events: Whether to send feature flag events.
            disable_geoip: Whether to disable GeoIP for this request.
            device_id: The device ID for this request.

        Examples:
            ```python
            enabled_variant = posthog.get_feature_flag('flag-key', 'distinct_id_of_your_user')
            if enabled_variant == 'variant-key': # replace 'variant-key' with the key of your variant
                # Do something differently for this user
                # Optional: fetch the payload
                matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')
            ```

        Category:
            Feature flags
        u   `get_feature_flag` is deprecated and will be removed in a future major version. Use `posthog.evaluate_flags(distinct_id, ...)` and call `flags.get_flag(key)` instead — this consolidates flag evaluation into a single `/flags` request per incoming request.rE  rF  r  N)rM  rN  rO  r  r  )r   r'  r  r*  r+  r,  rH  r  r   r!  feature_flag_results              rf   get_feature_flagzClient.get_feature_flag	  sk    N 	  	
 #;;/-"7%=' < 

 3F",,.O4Orh   c                 $   | j                   | j                  r| j                          d }| j                   rk| j                  J d       | j                  j	                  |      }|r;	 | j                  ||||||      }| j                  j                  d| d|        |S |S # t        t        f$ r,}	| j                  j                  d| d|	        Y d }	~	|S d }	~	wt        $ r)}	| j                  j                  d|	        Y d }	~	|S d }	~	ww xY w)Nr(  )r*  r+  r,  r!  z$Successfully computed flag locally: z -> zFailed to compute flag z
 locally: z7[FEATURE FLAGS] Error while computing variant locally: )r%  r   r  r   rD   r  r~   r}   r5   r4   r|   r   )
r   r'  r  r*  r+  r,  r!  r  r)  r   s
             rf   r  zClient._locally_evaluate_flagS	  s8    %$*?*?##%,,8 V8 ,,005D#99#%*;)9"+  :  H HHNN>se4zR x 12HI QHHNN%<SEA3#OPP
 	 ! HH&&QRSQTU  	s$   (8B$ $D3!CD&D

D)match_valuer*  r+  r,  rH  r  r   r!  r  c                    t        j                  dt        d       |rt        j                  dt        d       | j                  |||||||||	|

      }|r|j                  S dS )aj  
        Get the payload for a feature flag.

        Args:
            key: The feature flag key.
            distinct_id: The distinct ID of the user.
            match_value: The specific flag value to get payload for.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            only_evaluate_locally: Whether to only evaluate locally.
            send_feature_flag_events: Deprecated. Use get_feature_flag() instead if you need events.
            disable_geoip: Whether to disable GeoIP for this request.
            device_id: The device ID for this request.

        Returns:
            The payload associated with the matched feature flag value, or None.
            This method returns the payload only, not the FeatureFlagResult wrapper
            used internally to compute it.

        Examples:
            ```python
            is_my_flag_enabled = posthog.feature_enabled('flag-key', 'distinct_id_of_your_user')

            if is_my_flag_enabled:
                # Do something differently for this user
                # Optional: fetch the payload
                matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')
            ```

        Category:
            Feature flags
        u  `get_feature_flag_payload` is deprecated and will be removed in a future major version. Use `posthog.evaluate_flags(distinct_id, ...)` and call `flags.get_flag_payload(key)` instead — this consolidates flag evaluation into a single `/flags` request per incoming request.rE  rF  zsend_feature_flag_events is deprecated in get_feature_flag_payload() and will be removed in a future version. Use get_feature_flag() if you want to send $feature_flag_called events.r  N)rM  rN  rO  r  r  )r   r'  r  r  r*  r+  r,  rH  r  r   r!  r  s               rf   get_feature_flag_payloadzClient.get_feature_flag_payload{	  s    ^ 	> 	
 $MMo"	 #;;!,/-"7%=' < 
 /B"**KtKrh   c           	          | j                  ||||||g|      }|j                  d      }	|j                  d      }
|j                  dd      }|j                  d      }|r |j                  |      nd}||	|
|fS )z
        Calls /flags and returns the flag details, request id, evaluated at timestamp,
        and whether there were errors while computing flags.
        )r-  r!  	requestIdevaluatedAterrorsWhileComputingFlagsFrC   N)r:  rD   )r   r'  r  r*  r+  r,  r   r!  r2  r
  r  r  rC   r	  s                 rf   r  z,Client._get_feature_flag_details_from_server	  s     ,,#&% - 
	 ]];/
 }}]3!*/JE!Rg&).yuyy~DZ7MMMrh   r  r  r  r
  r  r	  r  c                 
   d|d|d|d| |i}|||d<   |r||d<   |	r|	|d<   t        |
t              r|
j                  r/|
j                  j                  r|
j                  j                  |d<   t        |
j                  t
              r^|
j                  j                  r|
j                  j                  |d	<   |
j                  j                  r|
j                  j                  |d
<   |r||d<   | j                  ||||||       y )Nz$feature_flagz$feature_flag_responselocally_evaluatedrJ  z$feature_flag_payloadz$feature_flag_request_idz$feature_flag_evaluated_atz$feature_flag_reasonz$feature_flag_versionz$feature_flag_idz$feature_flag_error)r  r'  r  rt   r*  r   )	rj   rI   reasondescriptionmetadatarL   versionid&_capture_feature_flag_called_if_needed)r   r  r'  r  r  r  r*  r   r
  r  r	  r  rt   s                rf   r  z#Client._capture_feature_flag_called	  s    S$h!;ux	&

 29J./5?J127CJ34lK0""|':':'F'F5A5H5H5T5T
12,//>((00:F:O:O:W:WJ67((++5A5J5J5M5MJ120BJ,-33#!' 	4 	
rh   )r*  r   c                4   |r)t        t        d |j                         D                    nd}|||f}| j                  j	                  |      }	|	t               }	|	| j                  |<   ||	v ry| j                  d|||xs i |       |	j                  |       y)a  Fire a ``$feature_flag_called`` event if the (distinct_id, flag, response,
        groups) tuple hasn't already been reported on this client. Group context is
        included so that group-scoped flags fire a separate event for each group a
        user is evaluated under. Shared by the single-flag evaluation path and
        ``FeatureFlagEvaluations.is_enabled() / get_flag()`` so both paths dedupe
        identically.
        c              3   N   K   | ]  \  }}t        |      t        |      f  y wr   )rb   ).0kvs      rf   	<genexpr>z@Client._capture_feature_flag_called_if_needed.<locals>.<genexpr>)
  s"     Enda#a&#a&)ns   #%r   Nz$feature_flag_called)r  rt   r*  r   )tuplesortedrQ  r   rD   rq   r\  r  )
r   r  r'  r  rt   r*  r   
groups_keyfeature_flag_reported_keyreported_flagss
             rf   r'  z-Client._capture_feature_flag_called_if_needed
  s    $ KQE&EfllnEEFVX 	 &)(J$?!AAEEkR! UNDRD44[A$6"#!<R' 	 	
 	45rh   c                 F   | j                   ry| j                  | j                  j                  d       y	 t	        | j                  | j
                  | j                  || j                        S # t        $ r(}| j                  j                  d|        Y d}~yd}~ww xY w)a  
        Get the payload for a remote config feature flag.

        Args:
            key: The remote config feature flag key.

        Returns:
            The payload associated with the feature flag, or ``None`` if the
            client is disabled, no personal API key is configured, or the request
            fails. Encrypted payloads are decrypted by PostHog before being
            returned.

        Note:
            Requires ``personal_api_key`` for authentication.

        Category:
            Feature flags
        Nz`[FEATURE FLAGS] You have to specify a personal_api_key to fetch decrypted feature flag payloads.)r   z>[FEATURE FLAGS] Unable to get decrypted feature flag payload: )
r   r   r~   r  rG   r   r   r   r|   r   )r   r'  r   s      rf   get_remote_config_payloadz Client.get_remote_config_payload>
  s    & ==  (HHr 	 %%		BB   	HHPQRPST 	s   7A/ /	B 8BB c                    d }| j                   |S | j                   j                  |      }|r[|j                  d      xs i }|j                  d      xs i }t        |t              r|rdn
t	        |      }|j                  |d       }|S )Nr  payloadstrue)r   rD   rj   r_  rb   )r   r'  r  r  flag_definitionr  flag_payloadslookup_values           rf   r  zClient._compute_payload_locallyg
  s     $$,N3377<*..y9?RL(,,Z8>BM
 k40[ % 
 $''d;Grh   r*  r+  r,  rH  r   r-  r!  c          
      >    | j                  ||||||||      }	|	d   S )a<  
        Get all feature flags for a user.

        Args:
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            only_evaluate_locally: Whether to only evaluate locally.
            disable_geoip: Whether to disable GeoIP for this request.
            flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
                only these flags will be evaluated, improving performance.
            device_id: The device ID for this request.

        Examples:
            ```python
            posthog.get_all_flags('distinct_id_of_your_user')
            ```

        Category:
            Feature flags
        r;  featureFlags)get_all_flags_and_payloads)
r   r  r*  r+  r,  rH  r   r-  r!  r  s
             rf   rP  zClient.get_all_flags}
  s?    D 22/-"7'"7 3 	
 ''rh   c          	         | j                   rdddS | j                  |||      \  }}|
t               }|xs i }| j                  ||      }	| j	                  |||	|||      \  }
}|r&|s$	 | j                  |||||||      }t        |      S |
S # t        $ r)}| j                  j                  d|        Y d}~|
S d}~ww xY w)a\  
        Get all feature flags and their payloads for a user.

        Args:
            distinct_id: The distinct ID of the user.
            groups: A dictionary of group information.
            person_properties: A dictionary of person properties.
            group_properties: A dictionary of group properties.
            only_evaluate_locally: Whether to only evaluate locally.
            disable_geoip: Whether to disable GeoIP for this request.
            flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
                only these flags will be evaluated, improving performance.
            device_id: The device ID for this request.

        Examples:
            ```python
            posthog.get_all_flags_and_payloads('distinct_id_of_your_user')
            ```

        Category:
            Feature flags
        Nr=  featureFlagPayloads)r*  r+  r,  r-  r!  r*  r+  r,  r   r-  r!  z:[FEATURE FLAGS] Unable to get feature flags and payloads: )
r   r  r   r  #_get_all_flags_and_payloads_locallyr:  rR   r|   r~   r   )r   r  r*  r+  r,  rH  r   r-  r!  r  r  fallback_to_flagsdecide_responser   s                 rf   r>  z!Client.get_all_flags_and_payloads
  s"   D ==$(FF 77)+; 	,+ -/I2"&"N"N*#
 '+&N&N5-"7 'O '
## %:"&":":!&7%5"/*?' #; # -_==   ""PQRPST  s   +"B 	CB==C)r*  r+  r,  rH  r   	flag_keysr!  rF  c                   | j                         }	|
t               }|
t               }|r| j                  rt	        |	di       S | j                  |xs i |xs i |xs i       \  }}|xs i }i }
d}d}d}d}t               }| j                  ||      }| j                  |t        |      |||      \  }}| j                  xs i }|j                  d      xs i }|j                  d      xs i }|j                         D ]s  \  }}|j                  |      xs i }t        ||dut        |t              r|nd|j                  |      |j                  d      dd	d
      |
|<   |j!                  |       u |r|s	 | j#                  |||||||      }|j                  d      }|j                  d      }t        |t$              r|nd}t'        |j                  dd            }|j                  di       j                         D ]C  \  }}||v rd}t        |j(                  t*              r|j(                  j,                  nt/        |j(                  dd      }t        |t              r|r	 t1        j2                  |      }n||}t        ||j8                  |j:                  |t        |j(                  t*              r|j(                  j<                  ndt        |j(                  t*              r|j(                  j>                  nd|j@                  r,|j@                  jB                  r|j@                  jB                  ndd      |
|<   F 	 t	        |	t        |      |
||||||	      S # t0        j4                  t6        f$ r |}Y w xY w# tD        $ r*}| jF                  jI                  d|        d
}Y d}~md}~wtJ        $ r(}| jF                  jM                  d|        Y d}~d}~ww xY w)u  Evaluate all feature flags for a user in a single call and return a
        :class:`FeatureFlagEvaluations` snapshot. Branch on ``.is_enabled()`` /
        ``.get_flag()`` and pass the same snapshot to :meth:`capture` via the
        ``flags`` option so events carry the exact flag values the code branched on.

        Prefer this over repeated ``get_feature_flag()`` calls and over
        ``capture(send_feature_flags=True)`` — it consolidates flag evaluation into
        a single ``/flags`` request per incoming request.

        Local evaluation is transparent: when the poller resolves a flag, the
        snapshot's ``$feature_flag_called`` events are tagged ``locally_evaluated=True``
        and reason ``"Evaluated locally"``.

        Args:
            distinct_id: The user's distinct ID. If ``None``, falls back to the
                context distinct_id. If still unresolvable, returns an empty snapshot.
            groups: Mapping of group type to group key.
            person_properties: Person properties to use for evaluation.
            group_properties: Group properties keyed by group type.
            only_evaluate_locally: If True, never fall back to remote evaluation —
                flags that can't be evaluated locally are simply omitted from the snapshot.
            disable_geoip: Whether to disable GeoIP lookup.
            flag_keys: Optional list of flag keys to scope the underlying ``/flags``
                request to a subset.
            device_id: Optional device ID override. If not provided, falls back to the
                context device_id (which may be set via tracing headers). Used by
                experience-continuity flags to match users across distinct_id changes.

        Returns:
            A :class:`FeatureFlagEvaluations` snapshot.

        Examples:
            ```python
            flags = posthog.evaluate_flags(
                "user_123",
                person_properties={"plan": "enterprise"},
            )
            if flags.is_enabled("new-dashboard"):
                render_new_dashboard()
            posthog.capture("page_viewed", distinct_id="user_123", flags=flags)
            ```

        Category:
            Feature flags
        Nr   )r   r  rC   F)r*  r+  r,  r-  r=  rA  r&  zEvaluated locallyT)r'  r  rZ  r  r&  r%  r"  r!  rB  r  r  r  rC   r  r  z3[FEATURE FLAGS] Unable to evaluate flags remotely: )	r   r  rC   r*  r   r
  r  r  quota_limited)'"_get_feature_flag_evaluations_hostr   r   r   r1   r  rq   r  rC  r^  r   rD   rQ  r2   rj   rb   r  r:  intr_  r$  rL   r  getattrjsonloadsJSONDecodeErrorr`  r  rZ  r&  r%  r"  r#  r>   r~   r  r|   r   )r   r  r*  r+  r,  rH  r   rF  r!  r   recordsr
  r  r  rH  locally_evaluated_keysr  local_resultfallback_to_serverr   local_flagslocal_payloadsr'  rl   flag_defr  raw_evaluated_atr  r  raw_payloadr   s                                  rf   evaluate_flagszClient.evaluate_flags
  s   r 66813K -/Idmm *t2NN 77"!'R &B 	,+ 235$(
&*!&+.5 #'"N"N*#
 ,0+S+S<5-"+ ,T ,
(( 04/H/H/NB"&&~6<"%))*?@FB%++-JC+//4:H/U*!+E3!7T&**3/<<% *"&GCL #&&s+ .& &;>33!&7%5"/*3' 4  &\\+6
#+<<#> (23CS(I$t  *.LL!<eD*& $,<<#<#B#B#DKC44 -1G &foo|D //$V__iF  
 "+s32&*jj&=G %0"-#7 & & '  *&//<H #OO..!%  *&//<H #OO33!%  &}}1J1J #MM55!%*/)$GCL! $E\ &K('!%#9'

 
	
E !% 4 4i@ 2&1G24 # %  #I!!MN $ ""I!M sJ   <C0N -M*C
N *N	N N		N 	O. N::O.O))O.$_feature_flag_evaluations_host_cachec                 r      j                   t         j                   fd       _          j                   S )Nc                 :    j                   j                  |       S r   )r~   r  )r  r   s    rf   r   z;Client._get_feature_flag_evaluations_host.<locals>.<lambda>  s    DHH,<,<W,Erh   )#capture_flag_called_event_if_neededlog_warning)rY  r3   r'  r  s   `rf   rI  z)Client._get_feature_flag_evaluations_host  s6    44<8S484_4_E9D5 888rh   )r+  r,  r  r-  r!  c          
      :   |xs i }|xs i }| j                   | j                  r| j                          i }i }	d}
| j                   r| j                   }|r,t        |      }| j                   D cg c]  }|d   |v s| }}|D ]F  }	 | j	                  |||||||      ||d   <   | j                  |d   ||d            }|||	|d   <   H nd}
||	d|
fS c c}w # t        $ r d}
Y et        $ r*}| j                  j                  d|        d}
Y d }~d }~ww xY w)NFr'  r  Tz;[FEATURE FLAGS] Error while computing variant and payload: r@  )
r%  r   r  rq   r  r  r4   r|   r~   r   )r   r  r*  r+  r,  r  r-  r!  rC   r6  rD  flags_to_processflag_keys_setr)  matched_payloadr   s                   rf   rC  z*Client._get_all_flags_and_payloads_locally  s    .3+1r%$*?*?##%&(#%!#11$ #$9 :%)%7%7$%7T4;-;WD%7 ! $ )-)-)C)C#%*;)9/E"+ *D *E$u+& '+&C&CUU4;%7'O '20?e- )2 !% "#+
  	?$( . -(,%  -HH&&UVWUXY )-%	-s+   .C;CACD(D0 DDc                    |syddl m}m} 	  ||      }|j                  j	                         } ||j
                        }t        |j                  ddg      d         }|dk(  r+t        |j                  ddg      d         }t        ||      S |d	k(  r	 ddl	}	|j                   d
}
|j                  s|j                  r'|
|j                  xs d d|j                  xs d dz  }
|
|j                  xs d d|j                  xs d z  }
|j                  r|
|j                  z  }
|	j                  |
      }|j!                          t#        ||      S t-        d| d      # t$        $ r | j&                  j)                  d       Y yt*        $ r)}| j&                  j)                  d| d       Y d}~yd}~ww xY w# t*        $ r+}| j&                  j)                  d| d|        Y d}~yd}~ww xY w)a9  Initialize feature flag cache for graceful degradation during service outages.

        When enabled, the cache stores flag evaluation results and serves them as fallback
        when the PostHog API is unavailable. This ensures your application continues to
        receive flag values even during outages.

        Args:
            cache_url: Cache configuration URL. Examples:
                - None: Disable caching
                - "memory://local/?ttl=300&size=10000": Memory cache with TTL and size
                - "redis://localhost:6379/0/?ttl=300": Redis cache with TTL
                - "redis://username:password@host:port/?ttl=300": Redis with auth

        Example usage:
            # Memory cache
            client = Client(
                "your-api-key",
                flag_fallback_cache_url="memory://local/?ttl=300&size=10000"
            )

            # Redis cache
            client = Client(
                "your-api-key",
                flag_fallback_cache_url="redis://localhost:6379/0/?ttl=300"
            )

            # Normal evaluation - cache is populated
            flag_value = client.get_feature_flag("my-flag", "user123")

            # During API outage - returns cached value instead of None
            flag_value = client.get_feature_flag("my-flag", "user123")  # Uses cache
        Nr   )parse_qsurlparsettli,  memoryr  r   redisz://r   :@	localhosti  )default_ttlz:[FEATURE FLAGS] Redis not available, flag caching disabledz)[FEATURE FLAGS] Redis connection failed: z, flag caching disabledzUnknown cache URL scheme: z". Supported schemes: memory, redisz+[FEATURE FLAGS] Failed to parse cache URL 'z': )urllib.parserc  rd  schemelowerqueryrJ  rD   rU   rg  usernamepasswordhostnameportr  from_urlpingrV   ImportErrorr~   r  r|   rk   )r   	cache_urlrc  rd  parsedrm  query_paramsre  r  rg  	redis_urlr  r   s                rf   r   zClient._initialize_flag_cache  s   B 32	i(F]]((*F#FLL1Ll&&use4Q78C!<++FUG<Q?@ s++7"  #)==/ 5I&//!(=2'>a@USU?VVW%XX	!??9k:!FKK<O4;PQI {{!V[[0	"^^I6F KKM)&cBB !08Z[  #  HH$$T     HH$$CA3F]^  	   	HH=i[A3O 		sU   BG  G  C E& G  &$F=
G  F=F83G  8F==G   	G4	!G//G4c                     | j                   S )a  
        Return feature flag definitions loaded for local evaluation.

        Returns:
            The currently loaded feature flag definitions, or ``None`` before
            local evaluation has loaded definitions.

        Category:
            Feature flags
        )r%  r  s    rf   feature_flag_definitionszClient.feature_flag_definitionsg  s     !!!rh   c                 H    t        |xs i       }|j                  d|       |S )Nr  )r^  r   )r   r  r+  r  s       rf   r  z.Client._person_properties_for_local_evaluationt  s)    "&'8'>B"?**=+F&&rh   c                 r    |xs i }i }|r*|D ]%  }d||   i|xs i j                  |      xs i ||<   ' ||fS )Nrl  )rD   )r   r*  r+  r,  all_group_propertiesr  s         rf   r  z-Client._add_local_person_and_group_propertiesy  sd     .3!$
 &"44(.B33J?E24$Z0 % !"666rh   )FN)NNNNNN)NNNNNNN)NNNNNrs  )r  )r^   Nr   )qr   
__module____qualname____doc__r   	getLoggerr~   r   r   r   r   r^  r.  rb   r   r   __annotations__rq   r   r%   DEFAULT_BUCKET_SIZEDEFAULT_REFILL_RATEDEFAULT_REFILL_INTERVAL_SECONDSr
   r9   listr   r   r	  r   r_  r!   r  r   r$   r   r   r   r#   r"   propertyr%  setterr   r	   r   rJ  r3  r5  rM   r8  rN   r0  r:  r   r   r   r\  rP   rL  r   rh  r   r   rn  rq  r   r  staticmethodr   r  rR  floatr  r   r  r  r  r8   r  r  r  r  rO   r  r  rK   r  r  r  r  r  objectr  rI   r  r  r'  r4  r  rP  r>  r   r1   rX  rY  r3   rI  rC  r   r|  r  r  r   rh   rf   r   r      sc   * '

I
&C*INN,?Ad5c?GOO;<A7:uE#s(O 4<
 "./*+%* % $ $PT).%)'+,0&*+/38*:*N*N*:*N*N6F6f6f$Uj/j/> )11L(M?j/J S	D(Kj/X	>7$
8D> 
,
htn 
 " "C "D "
#$sCx. 
#
/C 
/D 
/
1c 
1d 
1
2s 
2t 
2 # # 
 
" ;?6:@D(,59#'#*#* eCHo!567#* $DcN3	#*
 #4T#s(^(;#<=#*  ~#*  (S	2#* C=#* 
c5s##	$#*P ;?6:@D(,59#'(,(, eCHo!567(, $DcN3	(,
 #4T#s(^(;#<=(,  ~(,  (S	2(, C=(, 
c3h(,Z ;?6:@D(,59#'(+(+ eCHo!567(+ $DcN3	(+
 #4T#s(^(;#<=(+  ~(+  (S	2(+ C=(+ 
(+X +/:>6:@D(,59#'+0h'+0 eCHo!567+0 $DcN3	+0
 #4T#s(^(;#<=+0  ~+0  (S	2+0 C=+0 
+0^ +/:>6:@D(,59#'13h'13 eCHo!56713 $DcN3	13
 #4T#s(^(;#<=13  ~13  (S	213 C=13 
13f Zq1q1$*+>$?q1	#q1 q1f$?V $L Z/1F?3 /1 /1 /1b Z-1!8 -1Xc] -1 -1^ Z
 0448+/(,*.:1:1 :1 T#s(^,	:1
 E(C-01:1 uS$Y'(:1  ~:1 h':1 
#:1 :1x Z
 59"(,1111 c]11 E(C-01	11
 sm11  ~11 
#11 11fNL)N ,-N 
#	N` " "7r^@%EXe_ %Ed %EN,2+ 	QM" QU<+<?G~<	<$+1ZdEL! P #V 
Vz ;?6:@D&+)-(,#'@@ @
 eCHo!567@ $DcN3@ #4T#s(^(;#<=@  $@ #'@  ~@ C=@ 
$@D#*-	#	$$ 59:>6:@D&+)-(,#'PP P
 'y1P eCHo!567P $DcN3P #4T#s(^(;#<=P  $P #'P  ~P C=P 
#	$Pn ;?6:@D&+)-(,#'3
3
 3

 eCHo!5673
 $DcN33
 #4T#s(^(;#<=3
  $3
 #'3
  ~3
 C=3
 
#	$3
t ;?6:@D&+)-(,#'<P<P <P
 eCHo!567<P $DcN3<P #4T#s(^(;#<=<P  $<P #'<P  ~<P C=<P 
)	<PJ $(&& & U38_,-	&
  S>& sDcN23& C=& 
)	&Z ,0:>6:@D&+).(,#'KLKL KL
 i(KL eCHo!567KL $DcN3KL #4T#s(^(;#<=KL  $KL #'KL  ~KL C=KL 
&	KLj $(NN N U38_,-	N
  S>N sDcN23N  ~N C=N 
x$hsmXc]DH	INT -1/
/
 /
 9%	/

 #/
 %)/
 U38_,-/
  ~/
 SM/
 sm/
 {+/
 %SM/
p ;?(,%6 %6 	%6
 9%%6 cN%6 eCHo!567%6  ~%6 
%6N'S 'R%.	#4 ;?6:@D&+(,59#'-(-( eCHo!567	-(
 $DcN3-( #4T#s(^(;#<=-(  $-(  ~-(  (S	2-( C=-( 
$sE$),,-	.-(f ;?6:@D&+(,59#'NN eCHo!567	N
 $DcN3N #4T#s(^(;#<=N  $N  ~N  (S	2N C=N 
Nd +/G
 ;?6:@D&+(,)-#'G
h'G
 eCHo!567	G

 $DcN3G
 #4T#s(^(;#<=G
  $G
  ~G
 DI&G
 C=G
 
 G
R SW((3N*OV94O 9 $59#'<< U38_,-	<  (S	2< C=< 
%	&<|Xt"'
7rh   r   c                 B    | y t        | t              r| S t        |       S r   )rj   rb   )vals    rf   r`   r`     s"    
{#s
s8Orh   r   )r   r  rL  r   r   r{  r   r  rM  r   r   r   r   typingr   r   r   r	   r
   r   rB  r   r   typing_extensionsr   posthog._async_utilsr   posthog.argsr   r   r   r   posthog.consumerr   posthog.contextsr   r   r   r   r   r   r   r   r   r   r  r   r  r    r  r!   r"   r#  r#   r   r$   r  posthog.exception_capturer%   posthog._loggingr&   posthog.exception_utilsr'   r(   r)   r*   r+   r,   r-   r.   r/   r0    posthog.feature_flag_evaluationsr1   r2   r3   posthog.feature_flagsr4   r5   r6   r7   posthog.flag_definition_cacher8   r9   posthog.pollerr:   posthog.requestr;   r<   r=   r>   r?   r@   rA   rB   rC   rD   rE   rF   rG   rH   posthog.typesrI   rJ   rK   rL   rM   rN   rO   rP   rQ   rR   rS   rT   posthog.utilsrU   rV   rW   rX   rY   rZ   posthog.versionr[   r   r\   r]   r   r.  rb   r_  rg   rm   rw   r   r  r   r`   r   rh   rf   <module>r     s       	 
     2 2 < <  $ ; U U %    $ 7 7   
  "         $   
 %T	"2 
 C ( 8D/7V D/7N^rh   