a
    }c9                     @   s   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	d
 ZG dd dZG dd deZG dd deZdS )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                 C   s   t dd t| D dgS )z-list(_is_last_gen(2)) -> [False, False, True]c                 s   s   | ]
}d V  qdS )FN ).0_r   r   T/var/www/html/django/DPS/env/lib/python3.9/site-packages/geopy/extra/rate_limiter.py	<genexpr>B       z_is_last_gen.<locals>.<genexpr>T)r   range)r   r   r   r   _is_last_gen@   s    r   c                   @   s>   e Zd ZdZefZdd Zdd Zdd Zdd	 Z	d
d Z
dS )BaseRateLimiterz9Base Rate Limiter class for both sync and async versions.c                C   s8   || _ || _|| _|| _|dks$J t | _d | _d S Nr   )min_delay_secondsmax_retriesswallow_exceptionsreturn_value_on_exception	threadingLock_lock
_last_call)selfr   r   r   r   r   r   r   __init__J   s    
zBaseRateLimiter.__init__c                 C   s   t  S Nr   )r   r   r   r   _clock\   s    zBaseRateLimiter._clockc                 c   s   | j j |  }| jd u r0|| _W d    d S || j }| j| }|dkrb|| _W d    d S W d    n1 sv0    Y  |V  q d S r   )r   r    r   r   )r   clockZseconds_since_last_callwaitr   r   r   _acquire_request_slot_gen_   s    


.z)BaseRateLimiter._acquire_request_slot_genc                 c   s~   t t t| jD ]f\}}z
|V  W nL | jyp   |r@dV  n,tjt| jd || j||dd dV  Y qY q0  d S qd S )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iZis_last_tryr   r   r   _retries_gen   s"    
	
zBaseRateLimiter._retries_genc                 C   s2   | j r,tjt| jd | j||dd | jS  d S )Nz> swallowed an error after %r retries. Called with (*%r, **%r).Tr$   )r   r   r(   r)   r*   r   r   )r   r+   r,   r   r   r   _handle_exc   s    zBaseRateLimiter._handle_excN)r*   
__module____qualname____doc__r   r'   r   r    r#   r.   r/   r   r   r   r   r   E   s   &r   c                       sF   e Zd ZdZdddddd fdd	
Zd
d Zdd Zdd Z  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                   s<   t  j||||d || _|| _||ks,J |dks8J dS 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   funcr7   r   r;   r   r   r7   r   r   	__class__r   r   r      s    )zRateLimiter.__init__c                 C   s"   t t| jd | t| d S Nz
 sleep(%r))r   debugr)   r*   r   r   secondsr   r   r   _sleep  s    zRateLimiter._sleepc                 C   s   |   D ]}| | qd S r   r#   rC   r   r"   r   r   r   _acquire_request_slot	  s    z!RateLimiter._acquire_request_slotc                 O   s   |  ||}|D ]}|   z,| j|i |}t|r@td|W   S  | jy } z>||r| ||W  Y d }~  S | 	| j
 W Y d }~qd }~0 0 qtdd S )NzoAn async awaitable has been passed to `RateLimiter`. Use `AsyncRateLimiter` instead, which supports awaitables.Should not have been reached)r.   rF   r;   inspectisawaitable
ValueErrorr'   throwr/   rC   r7   RuntimeError)r   r+   r,   genr   reser   r   r   __call__  s    


$zRateLimiter.__call__	r*   r0   r1   r2   r   rC   rF   rP   __classcell__r   r   r=   r   r
      s   )4r
   c                       sF   e Zd ZdZdddddd fdd	
Zd
d Zdd Zdd Z  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
    r3   r4   r5   TNr6   c                   s<   t  j||||d || _|| _||ks,J |dks8J dS r8   r9   r<   r=   r   r   r   L  s    )zAsyncRateLimiter.__init__c                    s*   t t| jd | t|I d H  d S r?   )r   r@   r)   r*   asyncior   rA   r   r   r   rC     s    zAsyncRateLimiter._sleepc                    s"   |   D ]}| |I d H  qd S r   rD   rE   r   r   r   rF     s    z&AsyncRateLimiter._acquire_request_slotc                    s   |  ||}|D ]}|  I d H  z| j|i |I d H W   S  | jy } zD||rv| ||W  Y d }~  S | | jI d H  W Y d }~qd }~0 0 qtdd S )NrG   )	r.   rF   r;   r'   rK   r/   rC   r7   rL   )r   r+   r,   rM   r   rO   r   r   r   rP     s    
*zAsyncRateLimiter.__call__rQ   r   r   r=   r   r	   "  s   -4r	   )r2   rS   rH   r   	itertoolsr   r   timer   Ztimeitr   Z	geopy.excr   Z
geopy.utilr   __all__r   r   r
   r	   r   r   r   r   <module>   s   2fw