Pygithub: Límite de tasa de búsqueda de soporte

Creado en 10 abr. 2017  ·  13Comentarios  ·  Fuente: PyGithub/PyGithub

Parece que la función get_rate_limit devolverá lo que Github considera el límite de tasa 'núcleo'. Sin embargo, existen diferentes límites de velocidad para la búsqueda de código. Ver aquí

En este momento no hay una manera de obtener los límites de tasa de código de búsqueda por lo que puedo decir.

feature request

Comentario más útil

Para las personas que aterrizan aquí desde el motor de búsqueda, modifiqué un poco la función de @bbi-yggy:

from datetime import datetime, timezone

def rate_limited_retry(github):
    def decorator(func):
        def ret(*args, **kwargs):
            for _ in range(3):
                try:
                    return func(*args, **kwargs)
                except RateLimitExceededException:
                    limits = github.get_rate_limit()
                    reset = limits.search.reset.replace(tzinfo=timezone.utc)
                    now = datetime.now(timezone.utc)
                    seconds = (reset - now).total_seconds()
                    print(f"Rate limit exceeded")
                    print(f"Reset is in {seconds:.3g} seconds.")
                    if seconds > 0.0:
                        print(f"Waiting for {seconds:.3g} seconds...")
                        time.sleep(seconds)
                        print("Done waiting - resume!")
            raise Exception("Failed too many times")
        return ret
    return decorator

Esta función se puede utilizar de la siguiente manera:

@rate_limited_retry(github)
def run_query(import_string):
    query_string = f"language:Python \"{import_string}\""
    return list(github.search_code(query_string))

results = run_query(import_string)

Todos 13 comentarios

Veo el mismo problema. Aquí hay un pequeño script que ejemplifica el problema.

import os
from datetime import datetime
from github import Github

# Login
TOKEN = os.getenv("GITHUB_ACCESS_TOKEN")
github = Github(TOKEN)

# Get initial rate limit and reset time
rl1 = github.get_rate_limit().rate
print("RL1 | Limit: {}, Remaining: {}, Reset: {}.".format(
    rl1.limit, rl1.remaining, rl1.reset))
# RL1 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.

# Perform a search
results = github.search_code("Hello World")

# Rate limit of Github instance is unchanged after a search
rl2 = github.get_rate_limit().rate
print("RL2 | Limit: {}, Remaining: {}, Reset: {}.".format(
    rl2.limit, rl2.remaining, rl2.reset))
# RL2 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.

# The PaginatedList instance has a Requestor with the same info
rl3 = results._PaginatedList__requester.rate_limiting
rl3_reset = datetime.utcfromtimestamp(int(
        results._PaginatedList__requester.rate_limiting_resettime))
print("RL3 | Limit: {}, Remaining: {}, Reset: {}.".format(
    rl3[0], rl3[1], rl3_reset))
# RL3 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.

# However, the actual ContentFile results show a different limit
# The Requester of each individual result ...
result = results[0]
rl4 = result._requester.rate_limiting
rl4_reset = datetime.utcfromtimestamp(int(
        result._requester.rate_limiting_resettime))
print("RL4 | Limit: {}, Remaining: {}, Reset: {}.".format(
    rl4[1], rl4[0], rl4_reset))
# RL4 | Limit: 30, Remaining: 29, Reset: 2017-09-22 16:27:36.

# ... and headers stored in the content file directly show a different rate limit.
rl5_limit = result._headers['x-ratelimit-limit']
rl5_remaining = result._headers['x-ratelimit-remaining']
rl5_reset = datetime.utcfromtimestamp(int(
        result._headers['x-ratelimit-reset']))
print("RL5 | Limit: {}, Remaining: {}, Reset: {}.".format(
    rl5_limit, rl5_remaining, rl5_reset))
# RL5 | Limit: 30, Remaining: 29, Reset: 2017-09-22 16:27:36.

# In the end, the main Github instance still shows the original full rate limit
rl6 = github.get_rate_limit().rate
print("RL6 | Limit: {}, Remaining: {}, Reset: {}.".format(
    rl6.limit, rl6.remaining, rl6.reset))
# RL6 | Limit: 5000, Remaining: 5000, Reset: 2017-09-22 17:26:35.

+1 Esta característica es necesaria para una aplicación que estoy tratando de construir

@brentshermana para su aplicación, considere inspeccionar los encabezados de límite de tasa (de la última respuesta; vea en mi ejemplo anterior) o sondee el punto final /rate_limit usted mismo. Eso contiene información sobre todo tipo de límites de tarifas y no cuenta para ningún límite de tarifas.

Eventualmente, sería bueno si PyGithub no solo analizara rate sino que también analizara resources de lo que devuelve /rate_limit . La información está ahí, lamentablemente no está disponible para los consumidores de la biblioteca.

Además, la lista paginada debería devolver el límite de velocidad para la búsqueda de código si devuelve resultados de dicha búsqueda, es decir, lo que esté almacenado en _headers['x-ratelimit-*'] .

por cierto: me acabo de dar cuenta, el campo rate de JSON devuelto por /rate_limit está obsoleto y la información en resources es la alternativa recomendada: https://developer.github.com/ v3/rate_limit/#deprecation -aviso

Estoy haciendo exactamente eso. Si alguien quiere adaptar esto e intentar hacer una solicitud de extracción, tiene mi bendición:

def wait(seconds):
    print("Waiting for {} seconds ...".format(seconds))
    time.sleep(seconds)
    print("Done waiting - resume!")

def api_wait():
    url = 'https://api.github.com/rate_limit'
    response = urlopen(url).read()
    data = json.loads(response.decode())
    if data['resources']['core']['remaining'] <= 10:  # extra margin of safety
        reset_time = data['resources']['core']['reset']
        wait(reset_time - time.time() + 10)
    elif data['resources']['search']['remaining'] <= 2:
        reset_time = data['resources']['search']['reset']
        wait(reset_time - time.time() + 10)

Tengo un problema en el que mi iteración sobre los resultados de search_issues se detiene después de 1020 resultados cuando debería haber 1869 resultados. Mi guión se detiene en el mismo punto cada vez. ¿Podría ser esto un problema de limitación de velocidad?

No obtengo un error, los resultados simplemente se agotan. Si coloco mi cadena de consulta directamente en la interfaz web de GitHub, veo todos los resultados de 1869, como se esperaba. 1020 es un múltiplo de 30, lo que me hace preguntarme si es un problema de paginación.

El código es el siguiente:

querystring = "type:pr is:closed repo:xxxx closed:2017-07-01..2018-06-30"
issues = git.search_issues(query=querystring, sort="updated", order="asc")
for issue in issues:
    pull = issue.as_pull_request()
    print "%s: %s" % (pull.number, pull.title)

Muchas gracias por cualquier consejo que pueda compartir sobre lo que podría estar fallando aquí.

También intenté iterar a través issues.reversed para ver si comenzaría al final de mis resultados esperados de 1869. Sin embargo, en este caso solo obtengo 30 números, desde la primera página de resultados.

En una investigación más profunda, parece que me encuentro con el límite de 1000 resultados por búsqueda .

¿Qué tal si proporcionamos un método más get_search_rate_limit() para el límite de tasa de búsqueda mientras que el get_rate_limit() existente analizará el último límite de tasa "básico" sugerido por Github: https://developer.github.com/ v3/límite_tasa/

El límite de frecuencia de la API de búsqueda y el límite de frecuencia de GraphQL ya están disponibles. Un método para todos.

De forma predeterminada, le mostrará el límite de tarifa "básico". También puede obtener el límite de velocidad de búsqueda/graphql accediendo a los atributos respectivos.

r = g.get_rate_limit()
>>> r
RateLimit(core=Rate(remaining=4923, limit=5000))
>>> r.search
Rate(remaining=30, limit=30)
>>> r.graphql
Rate(remaining=5000, limit=5000)

Se ve genial, gracias @sfdye!

Para emular la función de espera de @brentshermana para evitar problemas con la limitación de la tasa de búsqueda, ahora puede hacer algo como esto:

from datetime import datetime

def api_wait_search(git):
  limits = git.get_rate_limit()
  if limits.search.remaining <= 2:
    seconds = (limits.search.reset - datetime.now()).total_seconds()
    print "Waiting for %d seconds ..." % (seconds)
    time.sleep(seconds)
    print "Done waiting - resume!"

Tenga en cuenta que llamar a get_rate_limit() introducirá un pequeño retraso, por lo que es posible que desee minimizar la frecuencia con la que llama.

Para las personas que aterrizan aquí desde el motor de búsqueda, modifiqué un poco la función de @bbi-yggy:

from datetime import datetime, timezone

def rate_limited_retry(github):
    def decorator(func):
        def ret(*args, **kwargs):
            for _ in range(3):
                try:
                    return func(*args, **kwargs)
                except RateLimitExceededException:
                    limits = github.get_rate_limit()
                    reset = limits.search.reset.replace(tzinfo=timezone.utc)
                    now = datetime.now(timezone.utc)
                    seconds = (reset - now).total_seconds()
                    print(f"Rate limit exceeded")
                    print(f"Reset is in {seconds:.3g} seconds.")
                    if seconds > 0.0:
                        print(f"Waiting for {seconds:.3g} seconds...")
                        time.sleep(seconds)
                        print("Done waiting - resume!")
            raise Exception("Failed too many times")
        return ret
    return decorator

Esta función se puede utilizar de la siguiente manera:

@rate_limited_retry(github)
def run_query(import_string):
    query_string = f"language:Python \"{import_string}\""
    return list(github.search_code(query_string))

results = run_query(import_string)

Versión modificada del decorador de pokey anterior para tener en cuenta core/search/graphql.
También se agregó un retraso de 30 segundos porque Github no restablece el límite de velocidad exactamente en el momento que dice.

def rate_limited_retry():
    def decorator(func):
        def ret(*args, **kwargs):
            for _ in range(3):
                try:
                    return func(*args, **kwargs)
                except RateLimitExceededException:
                    limits = gh.get_rate_limit()
                    print(f"Rate limit exceeded")
                    print("Search:", limits.search, "Core:", limits.core, "GraphQl:", limits.graphql)

                    if limits.search.remaining == 0:
                        limited = limits.search
                    elif limits.graphql.remaining == 0:
                        limited = limits.graphql
                    else:
                        limited = limits.core
                    reset = limited.reset.replace(tzinfo=timezone.utc)
                    now = datetime.now(timezone.utc)
                    seconds = (reset - now).total_seconds() + 30
                    print(f"Reset is in {seconds} seconds.")
                    if seconds > 0.0:
                        print(f"Waiting for {seconds} seconds...")
                        time.sleep(seconds)
                        print("Done waiting - resume!")
            raise Exception("Failed too many times")
        return ret
    return decorator

¿Fue útil esta página
0 / 5 - 0 calificaciones