Ë
    Ï.f‹9  ã                   óª   — d Z ddlZddlZddlZddlmZmZ ddlmZ ddl	m
Z
 ddlmZ ddlmZ dZd	„ Z G d
„ d«      Z G d„ de«      Z G d„ de«      Zy)uà  :class:`.RateLimiter` and :class:`.AsyncRateLimiter` allow to perform bulk
operations while gracefully handling error responses and adding delays
when needed.

In the example below a delay of 1 second (``min_delay_seconds=1``)
will be added between each pair of ``geolocator.geocode`` calls; all
:class:`geopy.exc.GeocoderServiceError` exceptions will be retried
(up to ``max_retries`` times)::

    import pandas as pd
    df = pd.DataFrame({'name': ['paris', 'berlin', 'london']})

    from geopy.geocoders import Nominatim
    geolocator = Nominatim(user_agent="specify_your_app_name_here")

    from geopy.extra.rate_limiter import RateLimiter
    geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
    df['location'] = df['name'].apply(geocode)

    df['point'] = df['location'].apply(lambda loc: tuple(loc.point) if loc else None)

This would produce the following DataFrame::

    >>> df
         name                                           location  \
    0   paris  (Paris, ÃŽle-de-France, France mÃ©tropolitaine, ...
    1  berlin  (Berlin, 10117, Deutschland, (52.5170365, 13.3...
    2  london  (London, Greater London, England, SW1A 2DU, UK...

                               point
    0   (48.8566101, 2.3514992, 0.0)
    1  (52.5170365, 13.3888599, 0.0)
    2  (51.5073219, -0.1276474, 0.0)

To pass extra options to the `geocode` call::

    from functools import partial
    df['location'] = df['name'].apply(partial(geocode, language='de'))

To see a progress bar::

    from tqdm import tqdm
    tqdm.pandas()
    df['location'] = df['name'].progress_apply(geocode)

Before using rate limiting classes, please consult with the Geocoding
service ToS, which might explicitly consider bulk requests (even throttled)
a violation.
é    N)ÚchainÚcount)Úsleep©Údefault_timer)ÚGeocoderServiceError)Úlogger)ÚAsyncRateLimiterÚRateLimiterc                 ó<   — t        d„ t        | «      D «       dg«      S )z-list(_is_last_gen(2)) -> [False, False, True]c              3   ó    K  — | ]  }d –— Œ y­w)FN© )Ú.0Ú_s     úWC:\Users\user\Documents\project_loop\venv\Lib\site-packages\geopy/extra/rate_limiter.pyú	<genexpr>z_is_last_gen.<locals>.<genexpr>B   s   è ø€ Ò.˜A”%Ñ.ùs   ‚T)r   Úrange)r   s    r   Ú_is_last_genr   @   s   € äÑ.¤ u£Ô.°°Ó7Ð7ó    c                   ó4   — e Zd ZdZefZd„ Zd„ Zd„ Zd„ Z	d„ Z
y)ÚBaseRateLimiterz9Base Rate Limiter class for both sync and async versions.c                óŠ   — || _         || _        || _        || _        |dk\  sJ ‚t	        j
                  «       | _        d | _        y ©Nr   )Úmin_delay_secondsÚmax_retriesÚswallow_exceptionsÚreturn_value_on_exceptionÚ	threadingÚLockÚ_lockÚ
_last_call)Úselfr   r   r   r   s        r   Ú__init__zBaseRateLimiter.__init__J   sF   € ð "3ˆÔØ&ˆÔØ"4ˆÔØ)BˆÔ&Ø˜aÒÑô —^‘^Ó%ˆŒ
Øˆ�r   c                 ó   — t        «       S ©Nr   )r"   s    r   Ú_clockzBaseRateLimiter._clock\   s
   € Ü‹Ðr   c              #   ó  K  — 	 | j                   5  | j                  «       }| j                  €|| _        	 d d d «       y || j                  z
  }| j                  |z
  }|dk  r|| _        	 d d d «       y 	 d d d «       –— Œ|# 1 sw Y   ŒxY w­wr   )r    r&   r!   r   )r"   ÚclockÚseconds_since_last_callÚwaits       r   Ú_acquire_request_slot_genz)BaseRateLimiter._acquire_request_slot_gen_   s�   è ø€ ð* Ø—‘ñ ØŸ™›�Ø—?‘?Ð*à&+�D”OØ÷ð ð +0°$·/±/Ñ*AÐ'Ø×-Ñ-Ð0GÑG�Ø˜1’9à&+�D”OØ÷ð ð ÷ð ŠJð ÷ð üs,   ‚B�%B µ	B¾+B Á)	BÁ3BÂ B	ÂBc           
   #   ó0  K  — t        t        «       t        | j                  «      «      D ]  \  }}	 |–—  y  y # | j                  $ rK |rd–— nAt        j                  t        | «      j                  dz   || j                  ||d¬«       d–— Y ŒfY Œhw xY w­w)NTzB caught an error, retrying (%s/%s tries). Called with (*%r, **%r).©Úexc_infoF)	Úzipr   r   r   Ú_retry_exceptionsr	   ÚwarningÚtypeÚ__name__)r"   ÚargsÚkwargsÚiÚis_last_trys        r   Ú_retries_genzBaseRateLimiter._retries_gen…   s£   è ø€ Ü!¤%£'¬<¸×8HÑ8HÓ+IÓJò 	‰NˆAˆ{ðØ’ñ$ ñ)	øð ×)Ñ)ò ÙØ“Jä—N‘NÜ˜T›
×+Ñ+ð /Bñ BàØ×(Ñ(ØØØ!%õð  ’KÙñ ðüs&   ‚.B±9µB¹ABÂBÂBÂBc                 ó¨   — | j                   rFt        j                  t        | «      j                  dz   | j
                  ||d¬«       | j                  S ‚ )Nz> swallowed an error after %r retries. Called with (*%r, **%r).Tr-   )r   r	   r1   r2   r3   r   r   )r"   r4   r5   s      r   Ú_handle_exczBaseRateLimiter._handle_excœ   sQ   € Ø×"Ò"Ü�N‰NÜ�T“
×#Ñ#ð '+ñ +à× Ñ ØØØõð ×1Ñ1Ð1àr   N)r3   Ú
__module__Ú__qualname__Ú__doc__r   r0   r#   r&   r+   r8   r:   r   r   r   r   r   E   s(   „ ÙCà-Ð/Ðòò$ò$òLó.r   r   c                   óB   ‡ — e Zd ZdZddddddœˆ fd„
Zd	„ Zd
„ Zd„ Zˆ xZS )r   a  This is a Rate Limiter implementation for synchronous functions
    (like geocoders with the default :class:`geopy.adapters.BaseSyncAdapter`).

    Examples::

        from geopy.extra.rate_limiter import RateLimiter
        from geopy.geocoders import Nominatim

        geolocator = Nominatim(user_agent="specify_your_app_name_here")

        search = ["moscow", "paris", "berlin", "tokyo", "beijing"]
        geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
        locations = [geocode(s) for s in search]

        search = [
            (55.47, 37.32), (48.85, 2.35), (52.51, 13.38),
            (34.69, 139.40), (39.90, 116.39)
        ]
        reverse = RateLimiter(geolocator.reverse, min_delay_seconds=1)
        locations = [reverse(s) for s in search]

    RateLimiter class is thread-safe. If geocoding service's responses
    are slower than `min_delay_seconds`, then you can benefit from
    parallelizing the work::

        import concurrent.futures

        geolocator = OpenMapQuest(api_key="...")
        geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1/20)

        with concurrent.futures.ThreadPoolExecutor() as e:
            locations = list(e.map(geocode, search))

    .. versionchanged:: 2.0
       Added thread-safety support.
    ç        é   ç      @TN©r   r   Úerror_wait_secondsr   r   c                ód   •— t         ‰| �  ||||¬«       || _        || _        ||k\  sJ ‚|dk\  sJ ‚y©a  
        :param callable func:
            A function which should be wrapped by the rate limiter.

        :param float min_delay_seconds:
            Minimum delay in seconds between the wrapped ``func`` calls.
            To convert :abbr:`RPS (Requests Per Second)` rate to
            ``min_delay_seconds`` you need to divide 1 by RPS. For example,
            if you need to keep the rate at 20 RPS, you can use
            ``min_delay_seconds=1/20``.

        :param int max_retries:
            Number of retries on exceptions. Only
            :class:`geopy.exc.GeocoderServiceError` exceptions are
            retried -- others are always re-raised. ``max_retries + 1``
            requests would be performed at max per query. Set
            ``max_retries=0`` to disable retries.

        :param float error_wait_seconds:
            Time to wait between retries after errors. Must be
            greater or equal to ``min_delay_seconds``.

        :param bool swallow_exceptions:
            Should an exception be swallowed after retries? If not,
            it will be re-raised. If yes, the ``return_value_on_exception``
            will be returned.

        :param return_value_on_exception:
            Value to return on failure when ``swallow_exceptions=True``.

        )r   r   r   r   r   N©Úsuperr#   ÚfuncrC   ©r"   rH   r   r   rC   r   r   Ú	__class__s          €r   r#   zRateLimiter.__init__Ñ   óO   ø€ ôR 	‰ÑØ/Ø#Ø1Ø&?ð	 	ô 	
ð ˆŒ	Ø"4ˆÔØ!Ð%6Ò6Ñ6Ø˜aÒÑÐr   c                 ór   — t        j                  t        | «      j                  dz   |«       t	        |«       y ©Nz
 sleep(%r))r	   Údebugr2   r3   r   ©r"   Úsecondss     r   Ú_sleepzRateLimiter._sleep  s&   € Ü�‰”T˜$“Z×(Ñ(¨<Ñ7¸ÔAÜˆg�r   c                 óP   — | j                  «       D ]  }| j                  |«       Œ y r%   ©r+   rQ   ©r"   r*   s     r   Ú_acquire_request_slotz!RateLimiter._acquire_request_slot	  s&   € Ø×2Ñ2Ó4ò 	ˆDØ�K‰K˜Õñ	r   c                 ó˜  — | j                  ||«      }|D ]H  }| j                  «        	  | j                  |i |¤Ž}t        j                  |«      rt        d«      ‚|c S  t        d«      ‚# | j                  $ rO}|j                  |«      r| j                  ||«      cY d }~c S | j                  | j                  «       Y d }~Œ¯d }~ww xY w)NzoAn async awaitable has been passed to `RateLimiter`. Use `AsyncRateLimiter` instead, which supports awaitables.úShould not have been reached)r8   rU   rH   ÚinspectÚisawaitableÚ
ValueErrorr0   Úthrowr:   rQ   rC   ÚRuntimeError)r"   r4   r5   Úgenr   ÚresÚes          r   Ú__call__zRateLimiter.__call__  sÊ   € Ø×Ñ  fÓ-ˆØò 	5ˆAØ×&Ñ&Ô(ð5Ø�d—i‘i Ð0¨Ñ0�Ü×&Ñ& sÔ+Ü$ðUóð ð ’
ð	5ô  Ð9Ó:Ð:øð ×)Ñ)ò 5Ø—9‘9˜Q”<à×+Ñ+¨D°&Ó9×9Ø—‘˜D×3Ñ3×4Ñ4ûð	5ús#   ©3A+Á+C	Á:"CÂC	Â$CÃC	©	r3   r;   r<   r=   r#   rQ   rU   r`   Ú__classcell__©rJ   s   @r   r   r   «   s/   ø„ ñ#ðR ØØØØ"&ö2 òhòö;r   r   c                   óB   ‡ — e Zd ZdZddddddœˆ fd„
Zd	„ Zd
„ Zd„ Zˆ xZS )r
   aÛ  This is a Rate Limiter implementation for asynchronous functions
    (like geocoders with :class:`geopy.adapters.BaseAsyncAdapter`).

    Examples::

        from geopy.adapters import AioHTTPAdapter
        from geopy.extra.rate_limiter import AsyncRateLimiter
        from geopy.geocoders import Nominatim

        async with Nominatim(
            user_agent="specify_your_app_name_here",
            adapter_factory=AioHTTPAdapter,
        ) as geolocator:

            search = ["moscow", "paris", "berlin", "tokyo", "beijing"]
            geocode = AsyncRateLimiter(geolocator.geocode, min_delay_seconds=1)
            locations = [await geocode(s) for s in search]

            search = [
                (55.47, 37.32), (48.85, 2.35), (52.51, 13.38),
                (34.69, 139.40), (39.90, 116.39)
            ]
            reverse = AsyncRateLimiter(geolocator.reverse, min_delay_seconds=1)
            locations = [await reverse(s) for s in search]

    AsyncRateLimiter class is safe to use across multiple concurrent tasks.
    If geocoding service's responses are slower than `min_delay_seconds`,
    then you can benefit from parallelizing the work::

        import asyncio

        async with OpenMapQuest(
            api_key="...", adapter_factory=AioHTTPAdapter
        ) as geolocator:

            geocode = AsyncRateLimiter(geolocator.geocode, min_delay_seconds=1/20)
            locations = await asyncio.gather(*(geocode(s) for s in search))

    .. versionadded:: 2.0
    r?   r@   rA   TNrB   c                ód   •— t         ‰| �  ||||¬«       || _        || _        ||k\  sJ ‚|dk\  sJ ‚yrE   rF   rI   s          €r   r#   zAsyncRateLimiter.__init__L  rK   r   c              ƒ   ó¢   K  — t        j                  t        | «      j                  dz   |«       t	        j
                  |«      ƒ d {  –—†  y 7 Œ­wrM   )r	   rN   r2   r3   Úasyncior   rO   s     r   rQ   zAsyncRateLimiter._sleep€  s5   è ø€ Ü�‰”T˜$“Z×(Ñ(¨<Ñ7¸ÔAÜ�m‰m˜GÓ$×$Ò$ús   ‚AAÁAÁAc              ƒ   ól   K  — | j                  «       D ]  }| j                  |«      ƒ d {  –—†  Œ y 7 Œ­wr%   rS   rT   s     r   rU   z&AsyncRateLimiter._acquire_request_slot„  s3   è ø€ Ø×2Ñ2Ó4ò 	$ˆDØ—+‘+˜dÓ#×#Ñ#ñ	$Ø#ús   ‚(4ª2«4c              �   ó–  K  — | j                  ||«      }|D ]6  }| j                  «       ƒ d {  –—†  	  | j                  |i |¤Žƒ d {  –—† c S  t        d«      ‚7 Œ-7 Œ# | j                  $ rX}|j	                  |«      r| j                  ||«      cY d }~c S | j                  | j                  «      ƒ d {  –—†7   Y d }~Œªd }~ww xY w­w)NrW   )	r8   rU   rH   r0   r[   r:   rQ   rC   r\   )r"   r4   r5   r]   r   r_   s         r   r`   zAsyncRateLimiter.__call__ˆ  s¹   è ø€ Ø×Ñ  fÓ-ˆØò 	;ˆAØ×,Ñ,Ó.×.Ð.ð;Ø&˜TŸY™Y¨Ð7°Ñ7×7Ò7ð	;ô Ð9Ó:Ð:ð /øà7ùØ×)Ñ)ò ;Ø—9‘9˜Q”<à×+Ñ+¨D°&Ó9×9Ø—k‘k $×"9Ñ"9Ó:×:Ö:ûð	;üsh   ‚+C	­A®C	³AÁAÁ	AÁC	ÁAÁCÁ."CÂCÂC	ÂCÂ6B9Â7CÂ<C	ÃCÃC	ra   rc   s   @r   r
   r
   "  s/   ø„ ñ'ðZ ØØØØ"&ö2 òh%ò$ö;r   r
   )r=   rg   rX   r   Ú	itertoolsr   r   Útimer   Útimeitr   Ú	geopy.excr   Ú
geopy.utilr	   Ú__all__r   r   r   r
   r   r   r   ú<module>rp      sY   ðñ0ód Û Û ß "Ý Ý  å *Ý à
-€ò8÷
cñ côLt;�/ô t;ônr;�õ r;r   