Pygithub: Prise en charge de la limite de taux de recherche

Créé le 10 avr. 2017  ·  13Commentaires  ·  Source: PyGithub/PyGithub

Il semble que la fonction get_rate_limit renverra ce que Github considère comme la limite de débit "de base". Cependant, il existe différentes limites de débit pour la recherche de code. Voir ici .

À l'heure actuelle, il n'y a aucun moyen d'obtenir les limites de taux de code de recherche pour autant que je sache.

feature request

Commentaire le plus utile

Pour les personnes qui atterrissent ici depuis un moteur de recherche, j'ai un peu modifié la fonction 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

Cette fonction peut être utilisée comme suit :

@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)

Tous les 13 commentaires

Je vois le même problème. Voici un petit script qui illustre le problème.

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 Cette fonctionnalité est nécessaire pour une application que j'essaie de créer

@brentshermana pour votre application, envisagez d'inspecter les en-têtes de limite de débit (de la dernière réponse ; voir dans mon exemple ci-dessus) ou d'interroger vous-même le point de terminaison /rate_limit . Cela contient des informations sur tous les types de limites de taux et ne compte dans aucune limite de taux.

Finalement, ce serait bien si PyGithub n'analysait pas seulement rate mais aussi resources partir de ce que /rate_limit retourne. L'information est là, elle n'est malheureusement pas mise à la disposition des consommateurs de la bibliothèque.

En outre, la liste paginée doit renvoyer la limite de taux de recherche de code si elle renvoie les résultats d'une telle recherche, c'est-à-dire tout ce qui est stocké dans _headers['x-ratelimit-*'] .

btw : Je viens de remarquer que le champ rate de JSON renvoyé par /rate_limit est obsolète et les informations dans resources sont l'alternative recommandée : https://developer.github.com/ v3/rate_limit/#deprecation -notice

Je fais exactement ça. Si quelqu'un veut adapter cela et essayer de faire une pull request, vous avez ma bénédiction :

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)

Je rencontre un problème où mon itération sur les résultats de search_issues s'arrête après 1020 résultats alors qu'il devrait y avoir 1869 résultats. Mon script s'arrête au même point à chaque fois. Cela pourrait-il être un problème de limitation de débit ?

Je ne reçois pas d'erreur, les résultats s'épuisent. Si je mets ma chaîne de requête directement dans l'interface Web GitHub, je vois tous les 1869 résultats, comme prévu. 1020 est un multiple de 30, ce qui me fait me demander si c'est un problème de pagination ?

Le code est le suivant :

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)

Merci beaucoup pour tous les conseils que vous pouvez partager sur ce qui pourrait mal se passer ici.

J'ai également essayé de parcourir issues.reversed pour voir si cela commencerait à la fin de mes résultats attendus de 1869. Cependant, dans ce cas, je n'obtiens que 30 numéros, à partir de la première page de résultats.

Après une enquête plus approfondie, il semble que je me heurte à la limite de 1 000 résultats par recherche .

Et si nous fournissions une autre méthode get_search_rate_limit() pour la limite de taux de recherche tandis que le get_rate_limit() existant analysera la dernière limite de taux "de base" suggérée par Github : https://developer.github.com/ v3/rate_limit/

La limite de taux de l'API de recherche et la limite de taux GraphQL sont maintenant disponibles. Une méthode pour tous.

Par défaut, il vous montrera la limite de taux "de base". Vous pouvez également obtenir la limite de taux de recherche/graphql en accédant aux attributs respectifs.

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)

Ça a l'air super, merci @sfdye !

Pour émuler la fonction d'attente de @brentshermana afin d'éviter les problèmes de limitation du taux de recherche, vous pouvez maintenant faire quelque chose comme ceci :

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!"

Notez que l'appel get_rate_limit() introduira un petit délai, vous voudrez peut-être minimiser la fréquence à laquelle vous appelez ceci.

Pour les personnes qui atterrissent ici depuis un moteur de recherche, j'ai un peu modifié la fonction 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

Cette fonction peut être utilisée comme suit :

@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)

Version modifiée du décorateur de pokey ci-dessus pour prendre en compte core/search/graphql.
Ajout également d'un délai de 30 secondes car Github ne réinitialise pas la limite de débit exactement au moment indiqué.

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

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

Borkason picture Borkason  ·  4Commentaires

mlainez picture mlainez  ·  7Commentaires

nchammas picture nchammas  ·  3Commentaires

diegotejadav picture diegotejadav  ·  5Commentaires

psychemedia picture psychemedia  ·  5Commentaires