Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
python-poetry
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
python-poetry
Commits
6b3a6161
Commit
6b3a6161
authored
Nov 22, 2022
by
Nejc Habjan
Committed by
Randy Döring
Apr 04, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: respect retry-after header with 429 responses
parent
fba14ba5
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
43 additions
and
2 deletions
+43
-2
src/poetry/publishing/uploader.py
+1
-0
src/poetry/utils/authenticator.py
+11
-1
src/poetry/utils/constants.py
+3
-1
tests/utils/test_authenticator.py
+28
-0
No files found.
src/poetry/publishing/uploader.py
View file @
6b3a6161
...
...
@@ -69,6 +69,7 @@ class Uploader:
connect
=
5
,
total
=
10
,
allowed_methods
=
[
"GET"
],
respect_retry_after_header
=
True
,
status_forcelist
=
STATUS_FORCELIST
,
)
...
...
src/poetry/utils/authenticator.py
View file @
6b3a6161
...
...
@@ -24,6 +24,7 @@ from filelock import FileLock
from
poetry.config.config
import
Config
from
poetry.exceptions
import
PoetryException
from
poetry.utils.constants
import
REQUESTS_TIMEOUT
from
poetry.utils.constants
import
RETRY_AFTER_HEADER
from
poetry.utils.constants
import
STATUS_FORCELIST
from
poetry.utils.password_manager
import
HTTPAuthCredential
from
poetry.utils.password_manager
import
PasswordManager
...
...
@@ -251,6 +252,7 @@ class Authenticator:
send_kwargs
.
update
(
settings
)
attempt
=
0
resp
=
None
while
True
:
is_last_attempt
=
attempt
>=
5
...
...
@@ -267,7 +269,7 @@ class Authenticator:
if
not
is_last_attempt
:
attempt
+=
1
delay
=
0.5
*
attempt
delay
=
self
.
_get_backoff
(
resp
,
attempt
)
logger
.
debug
(
"Retrying HTTP request in
%
s seconds."
,
delay
)
time
.
sleep
(
delay
)
continue
...
...
@@ -275,6 +277,14 @@ class Authenticator:
# this should never really be hit under any sane circumstance
raise
PoetryException
(
"Failed HTTP {} request"
,
method
.
upper
())
def
_get_backoff
(
self
,
response
:
requests
.
Response
|
None
,
attempt
:
int
)
->
float
:
if
response
is
not
None
:
retry_after
=
response
.
headers
.
get
(
RETRY_AFTER_HEADER
,
""
)
if
retry_after
:
return
float
(
retry_after
)
return
0.5
*
attempt
def
get
(
self
,
url
:
str
,
**
kwargs
:
Any
)
->
requests
.
Response
:
return
self
.
request
(
"get"
,
url
,
**
kwargs
)
...
...
src/poetry/utils/constants.py
View file @
6b3a6161
...
...
@@ -4,5 +4,7 @@ from __future__ import annotations
# Timeout for HTTP requests using the requests library.
REQUESTS_TIMEOUT
=
15
RETRY_AFTER_HEADER
=
"retry-after"
# Server response codes to retry requests on.
STATUS_FORCELIST
=
[
500
,
501
,
502
,
503
,
504
]
STATUS_FORCELIST
=
[
429
,
500
,
501
,
502
,
503
,
504
]
tests/utils/test_authenticator.py
View file @
6b3a6161
...
...
@@ -242,6 +242,33 @@ def test_authenticator_request_raises_exception_when_attempts_exhausted(
assert
sleep
.
call_count
==
5
def
test_authenticator_request_respects_retry_header
(
mocker
:
MockerFixture
,
config
:
Config
,
http
:
type
[
httpretty
.
httpretty
],
):
sleep
=
mocker
.
patch
(
"time.sleep"
)
sdist_uri
=
f
"https://foo.bar/files/{uuid.uuid4()!s}/foo-0.1.0.tar.gz"
content
=
str
(
uuid
.
uuid4
())
seen
=
[]
def
callback
(
request
:
requests
.
Request
,
uri
:
str
,
response_headers
:
dict
)
->
list
[
int
|
dict
|
str
]:
if
not
seen
.
count
(
uri
):
seen
.
append
(
uri
)
return
[
429
,
{
"Retry-After"
:
"42"
},
"Retry later"
]
return
[
200
,
response_headers
,
content
]
http
.
register_uri
(
httpretty
.
GET
,
sdist_uri
,
body
=
callback
)
authenticator
=
Authenticator
(
config
,
NullIO
())
response
=
authenticator
.
request
(
"get"
,
sdist_uri
)
assert
sleep
.
call_args
[
0
]
==
(
42.0
,)
assert
response
.
text
==
content
@pytest.mark.parametrize
(
[
"status"
,
"attempts"
],
[
...
...
@@ -249,6 +276,7 @@ def test_authenticator_request_raises_exception_when_attempts_exhausted(
(
401
,
0
),
(
403
,
0
),
(
404
,
0
),
(
429
,
5
),
(
500
,
5
),
(
501
,
5
),
(
502
,
5
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment