Pygithub: рд╕рдорд░реНрдерди рдЦреЛрдЬ рджрд░ рд╕реАрдорд╛

рдХреЛ рдирд┐рд░реНрдорд┐рдд 10 рдЕрдкреНрд░реИрд▓ 2017  ┬╖  13рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: PyGithub/PyGithub

рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ get_rate_limit рдлрд╝рдВрдХреНрд╢рди рд╡рд╣ рд▓реМрдЯрд╛рдПрдЧрд╛ рдЬреЛ рдЧреАрдердм 'рдХреЛрд░' рджрд░ рд╕реАрдорд╛ рдорд╛рдирддрд╛ рд╣реИред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдХреЛрдб рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ-рдЕрд▓рдЧ рджрд░ рд╕реАрдорд╛рдПрдВ рд╣реИрдВред рдпрд╣рд╛рдВ рджреЗрдЦреЗрдВред

рдЕрднреА рдЬрд╣рд╛рдВ рддрдХ тАЛтАЛрдореИрдВ рдмрддрд╛ рд╕рдХрддрд╛ рд╣реВрдВ, рдЦреЛрдЬ рдХреЛрдб рджрд░ рд╕реАрдорд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рдХреЛрдИ рддрд░реАрдХрд╛ рдирд╣реАрдВ рд╣реИред

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рдЦреЛрдЬ рдЗрдВрдЬрди рд╕реЗ рдпрд╣рд╛рдВ рдЖрдиреЗ рд╡рд╛рд▓реЗ рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ @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

рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

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

рд╕рднреА 13 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдореИрдВ рд╡рд╣реА рдореБрджреНрджрд╛ рджреЗрдЦрддрд╛ рд╣реВрдВред рдпрд╣рд╛рдВ рдПрдХ рдЫреЛрдЯреА рд╕реА рд▓рд┐рдкрд┐ рд╣реИ рдЬреЛ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рдЙрджрд╛рд╣рд░рдг рджреЗрддреА рд╣реИред

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 рдпрд╣ рд╕реБрд╡рд┐рдзрд╛ рдЙрд╕ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдЬрд┐рд╕реЗ рдореИрдВ рдмрдирд╛рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ

рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдХреЗ рд▓рд┐рдП @brenthermana , рджрд░ рд╕реАрдорд╛ рд╢реАрд░реНрд╖рд▓реЗрдЦреЛрдВ рдХрд╛ рдирд┐рд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ (рдЕрдВрддрд┐рдо рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд╛; рдКрдкрд░ рдореЗрд░реЗ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ рджреЗрдЦреЗрдВ) рдпрд╛ /rate_limit рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ рдХреЛ рд╕реНрд╡рдпрдВ рдорддрджрд╛рди рдХрд░реЗрдВред рдЗрд╕рдореЗрдВ рд╕рднреА рдкреНрд░рдХрд╛рд░ рдХреА рджрд░ рд╕реАрдорд╛рдУрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд╣реЛрддреА рд╣реИ рдФрд░ рдХрд┐рд╕реА рднреА рджрд░ рд╕реАрдорд╛ рдХреА рдЧрдгрдирд╛ рдирд╣реАрдВ рдХреА рдЬрд╛рддреА рд╣реИред

рдЖрдЦрд┐рд░рдХрд╛рд░, рдпрд╣ рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ рдпрджрд┐ PyGithub рди рдХреЗрд╡рд▓ rate рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░реЗрдЧрд╛ рдмрд▓реНрдХрд┐ /rate_limit рд░рд┐рдЯрд░реНрди рд╕реЗ resources рдХреЛ рднреА рдкрд╛рд░реНрд╕ рдХрд░реЗрдЧрд╛ред рдЬрд╛рдирдХрд╛рд░реА рд╣реИ, рдпрд╣ рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рдЙрдкрднреЛрдХреНрддрд╛рдУрдВ рдХреЛ рдЙрдкрд▓рдмреНрдз рдирд╣реАрдВ рдХрд░рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рд╕рд╛рде рд╣реА, рдкреГрд╖реНрдард╛рдВрдХрд┐рдд рд╕реВрдЪреА рдХреЛ рдХреЛрдб рдЦреЛрдЬ рдХреЗ рд▓рд┐рдП рджрд░ рд╕реАрдорд╛ рд╡рд╛рдкрд╕ рдХрд░рдиреА рдЪрд╛рд╣рд┐рдП рдпрджрд┐ рдпрд╣ рдРрд╕реА рдЦреЛрдЬ рдХреЗ рдкрд░рд┐рдгрд╛рдо рд▓реМрдЯрд╛рддреА рд╣реИ, рдЕрд░реНрдерд╛рдд рдЬреЛ рдХреБрдЫ рднреА _headers['x-ratelimit-*'] рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рд╣реИред

рдмреАрдЯреАрдбрдмреНрд▓реНрдпреВ: рдореИрдВрдиреЗ рдЕрднреА рджреЗрдЦрд╛, рдЬреЗрдПрд╕рдУрдПрди рд╕реЗ rate /rate_limit рд▓реМрдЯрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ рдФрд░ resources рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдЕрдиреБрд╢рдВрд╕рд┐рдд рд╡рд┐рдХрд▓реНрдк рд╣реИ: https://developer.github.com/ v3/rate_limit/# рдкрджрд╛рд╡рдирддрд┐-рд╕реВрдЪрдирд╛

рдореИрдВ рдареАрдХ рдпрд╣реА рдХрд░ рд░рд╣рд╛ рд╣реВрдВред рдЕрдЧрд░ рдХреЛрдИ рдЗрд╕реЗ рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИ рдФрд░ рдПрдХ рдкреБрд▓ рдЕрдиреБрд░реЛрдз рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рдореЗрд░рд╛ рдЖрд╢реАрд░реНрд╡рд╛рдж рд╣реИ:

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)

рдореБрдЭреЗ рдПрдХ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рдирд╛ рдкрдбрд╝ рд░рд╣рд╛ рд╣реИ рдЬрд╣рд╛рдВ 1869 рдкрд░рд┐рдгрд╛рдо рд╣реЛрдиреЗ рдкрд░ 1020 рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рдмрд╛рдж search_issues рдХреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдкрд░ рдореЗрд░рд╛ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдмрдВрдж рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред рдореЗрд░реА рд▓рд┐рдкрд┐ рд╣рд░ рдмрд╛рд░ рдПрдХ рд╣реА рдмрд┐рдВрджреБ рдкрд░ рд░реБрдХрддреА рд╣реИред рдХреНрдпрд╛ рдпрд╣ рджрд░-рд╕реАрдорд┐рдд рдореБрджреНрджрд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ?

рдореБрдЭреЗ рдХреЛрдИ рддреНрд░реБрдЯрд┐ рдирд╣реАрдВ рдорд┐рд▓рддреА рд╣реИ, рдкрд░рд┐рдгрд╛рдо рдмрд╕ рд╕рдорд╛рдкреНрдд рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВред рдЕрдЧрд░ рдореИрдВ рдЕрдкрдиреА рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЛ рд╕реАрдзреЗ рдЧрд┐рдЯрд╣рдм рд╡реЗрдм рдЗрдВрдЯрд░рдлреЗрд╕ рдореЗрдВ рдбрд╛рд▓рддрд╛ рд╣реВрдВ рддреЛ рдореБрдЭреЗ рдЙрдореНрдореАрдж рдХреЗ рдореБрддрд╛рдмрд┐рдХ рд╕рднреА 1869 рдкрд░рд┐рдгрд╛рдо рджрд┐рдЦрд╛рдИ рджреЗрддреЗ рд╣реИрдВред 1020, 30 рдХрд╛ рдЧреБрдгрдЬ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рдореБрдЭреЗ рдЖрд╢реНрдЪрд░реНрдп рд╣реЛрддрд╛ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдкрд░ рдЕрдВрдХ рд▓рдЧрд╛рдиреЗ рдХреА рд╕рдорд╕реНрдпрд╛ рд╣реИ?

рдХреЛрдб рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ:

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)

рдХрд┐рд╕реА рднреА рд╕реБрдЭрд╛рд╡ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд-рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рдж рдЬреЛ рдЖрдк рд╕рд╛рдЭрд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣рд╛рдВ рдХреНрдпрд╛ рдЧрд▓рдд рд╣реЛ рд░рд╣рд╛ рд╣реИред

рдореИрдВрдиреЗ рдпрд╣ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП issues.reversed рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреБрдирд░рд╛рд╡реГрддрд┐ рдХрд░рдиреЗ рдХрд╛ рднреА рдкреНрд░рдпрд╛рд╕ рдХрд┐рдпрд╛ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдореЗрд░реЗ рдЕрдкреЗрдХреНрд╖рд┐рдд 1869 рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рдЕрдВрдд рдореЗрдВ рд╢реБрд░реВ рд╣реЛрдЧрд╛ред рд╣рд╛рд▓рд╛рдВрдХрд┐ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдореБрдЭреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рдкрд╣рд▓реЗ рдкреГрд╖реНрда рд╕реЗ рдХреЗрд╡рд▓ 30 рдЕрдВрдХ рдорд┐рд▓рддреЗ рд╣реИрдВред

рдЖрдЧреЗ рдХреА рдЬрд╛рдВрдЪ рдкрд░, рдРрд╕рд╛ рдкреНрд░рддреАрдд рд╣реЛрддрд╛ рд╣реИ рдХрд┐ рдореИрдВ рдкреНрд░рддрд┐ рдЦреЛрдЬ рд╕реАрдорд╛ 1000 рдкрд░рд┐рдгрд╛рдореЛрдВ рдореЗрдВ рднрд╛рдЧ рд░рд╣рд╛ рд╣реВрдВред

рд╣рдо рдЦреЛрдЬ рджрд░ рд╕реАрдорд╛ рдХреЗ рд▓рд┐рдП рдПрдХ рдФрд░ рддрд░реАрдХрд╛ get_search_rate_limit() рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВ, рдЬрдмрдХрд┐ рдореМрдЬреВрджрд╛ get_rate_limit() Github рджреНрд╡рд╛рд░рд╛ рд╕реБрдЭрд╛рдИ рдЧрдИ рдирд╡реАрдирддрдо "рдХреЛрд░" рджрд░ рд╕реАрдорд╛ рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░реЗрдЧрд╛: https://developer.github.com/ v3/рджрд░_рд╕реАрдорд╛/

рдЦреЛрдЬ API рджрд░ рд╕реАрдорд╛ рдФрд░ GraphQL рджрд░ рд╕реАрдорд╛ рдЕрдм рдЙрдкрд▓рдмреНрдз рд╣реИред рд╕рднреА рдХреЗ рд▓рд┐рдП рдПрдХ рддрд░реАрдХрд╛ред

рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдпрд╣ рдЖрдкрдХреЛ "рдХреЛрд░" рджрд░ рд╕реАрдорд╛ рджрд┐рдЦрд╛рдПрдЧрд╛ред рдЖрдк рд╕рдВрдмрдВрдзрд┐рдд рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рддрдХ рдкрд╣реБрдВрдЪ рдХрд░ рдЦреЛрдЬ/рдЧреНрд░рд╛рдлрдХрд▓ рджрд░ рд╕реАрдорд╛ рднреА рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

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)

рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд▓рдЧ рд░рд╣рд╛ рд╣реИ, рдзрдиреНрдпрд╡рд╛рдж @sfdye!

рдЦреЛрдЬ рджрд░ рд╕реАрдорд┐рдд рдХрд░рдиреЗ рдХреА рд╕рдорд╕реНрдпрд╛рдУрдВ рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП @brenthermana рдХреЗ рдкреНрд░рддреАрдХреНрд╖рд╛ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЕрдиреБрдХрд░рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЕрдм рдЖрдк рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

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

рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ get_rate_limit() рдкрд░ рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рдереЛрдбрд╝рд╛ рд╡рд┐рд▓рдВрдм рд╣реЛрдЧрд╛, рдЗрд╕рд▓рд┐рдП рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдЖрдк рдЗрд╕реЗ рдХрдо рд╕реЗ рдХрдо рдХрд┐рддрдиреА рдмрд╛рд░ рдХреЙрд▓ рдХрд░рдирд╛ рдЪрд╛рд╣реЗрдВред

рдЦреЛрдЬ рдЗрдВрдЬрди рд╕реЗ рдпрд╣рд╛рдВ рдЖрдиреЗ рд╡рд╛рд▓реЗ рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ @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

рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

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

рдХреЛрд░/рдЦреЛрдЬ/рдЧреНрд░рд╛рдлрдХрд▓ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрд░реЛрдХреНрдд рдкреЛрдХреА рдбреЗрдХреЛрд░реЗрдЯрд░ рдХрд╛ рд╕рдВрд╢реЛрдзрд┐рдд рд╕рдВрд╕реНрдХрд░рдгред
рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛ 30 рд╕реЗрдХрдВрдб рдХреА рджреЗрд░реА рднреА рдЬреЛрдбрд╝реА рдЧрдИ рдХреНрдпреЛрдВрдХрд┐ рдЬреАрдердм рджрд░ рд╕реАрдорд╛ рдХреЛ рдареАрдХ рдЙрд╕реА рд╕рдордп рд░реАрд╕реЗрдЯ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рдЬрдм рд╡рд╣ рдХрд╣рддрд╛ рд╣реИред

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

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕