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
c63da256
Unverified
Commit
c63da256
authored
Mar 29, 2018
by
Sébastien Eustace
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor the publishing module
parent
f4e02009
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
309 additions
and
270 deletions
+309
-270
poetry/masonry/publishing/publisher.py
+4
-270
poetry/masonry/publishing/uploader.py
+305
-0
No files found.
poetry/masonry/publishing/publisher.py
View file @
c63da256
import
hashlib
import
io
import
re
import
requests
import
toml
from
pathlib
import
Path
from
requests
import
adapters
from
requests.exceptions
import
HTTPError
from
requests.packages.urllib3
import
util
from
requests_toolbelt
import
user_agent
from
requests_toolbelt.multipart
import
(
MultipartEncoder
,
MultipartEncoderMonitor
)
from
poetry
import
__version__
from
poetry.locations
import
CONFIG_DIR
from
..metadata
import
Metadata
wheel_file_re
=
re
.
compile
(
r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>\d.+?))?)
((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?)
\.whl|\.dist-info)$"""
,
re
.
VERBOSE
)
KEYWORDS_TO_NOT_FLATTEN
=
{
'gpg_signature'
,
'content'
}
from
.uploader
import
Uploader
class
Publisher
:
...
...
@@ -40,6 +16,7 @@ class Publisher:
self
.
_poetry
=
poetry
self
.
_package
=
poetry
.
package
self
.
_io
=
io
self
.
_uploader
=
Uploader
(
poetry
,
io
)
def
publish
(
self
,
repository_name
):
if
repository_name
:
...
...
@@ -106,251 +83,8 @@ class Publisher:
if
not
password
:
password
=
self
.
_io
.
ask_hidden
(
'Password:'
)
session
=
requests
.
session
()
session
.
auth
=
(
username
,
password
)
session
.
headers
[
'User-Agent'
]
=
self
.
_make_user_agent_string
()
for
scheme
in
(
'http://'
,
'https://'
):
session
.
mount
(
scheme
,
self
.
_make_adapter_with_retries
())
# TODO: handle certificates
try
:
self
.
upload
(
session
,
url
)
finally
:
session
.
close
()
def
register
(
self
,
session
,
url
):
"""
Register a package to a repository.
"""
dist
=
self
.
_poetry
.
file
.
parent
/
'dist'
file
=
dist
/
'{}-{}.tar.gz'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
if
not
file
.
exists
():
raise
RuntimeError
(
'"{0}" does not exist on the file system.'
.
format
(
file
.
name
)
)
data
=
self
.
post_data
(
file
)
data
.
update
({
":action"
:
"submit"
,
"protocol_version"
:
"1"
,
})
data_to_send
=
self
.
_convert_data_to_list_of_tuples
(
data
)
encoder
=
MultipartEncoder
(
data_to_send
)
resp
=
session
.
post
(
url
,
data
=
encoder
,
allow_redirects
=
False
,
headers
=
{
'Content-Type'
:
encoder
.
content_type
},
)
return
resp
def
upload
(
self
,
session
,
url
):
"""
Upload packages for the current project.
"""
try
:
self
.
_upload
(
session
,
url
)
except
HTTPError
as
e
:
if
(
e
.
response
.
status_code
not
in
(
403
,
400
)
or
e
.
response
.
status_code
==
400
and
'was ever registered'
not
in
e
.
response
.
text
):
raise
# It may be the first time we publish the package
# We'll try to register it and go from there
try
:
self
.
register
(
session
,
url
)
except
HTTPError
:
raise
def
post_data
(
self
,
file
):
meta
=
Metadata
.
from_package
(
self
.
_package
)
file_type
=
self
.
_get_type
(
file
)
blake2_256_hash
=
hashlib
.
blake2b
(
digest_size
=
256
//
8
)
md5_hash
=
hashlib
.
md5
()
sha2_hash
=
hashlib
.
sha256
()
with
file
.
open
(
'rb'
)
as
fp
:
for
content
in
iter
(
lambda
:
fp
.
read
(
io
.
DEFAULT_BUFFER_SIZE
),
b
''
):
md5_hash
.
update
(
content
)
sha2_hash
.
update
(
content
)
blake2_256_hash
.
update
(
content
)
md5_digest
=
md5_hash
.
hexdigest
()
sha2_digest
=
sha2_hash
.
hexdigest
()
blake2_256_digest
=
blake2_256_hash
.
hexdigest
()
if
file_type
==
'bdist_wheel'
:
wheel_info
=
wheel_file_re
.
match
(
file
.
name
)
py_version
=
wheel_info
.
group
(
"pyver"
)
else
:
py_version
=
None
data
=
{
# identify release
"name"
:
meta
.
name
,
"version"
:
meta
.
version
,
# file content
"filetype"
:
file_type
,
"pyversion"
:
py_version
,
# additional meta-data
"metadata_version"
:
meta
.
metadata_version
,
"summary"
:
meta
.
summary
,
"home_page"
:
meta
.
home_page
,
"author"
:
meta
.
author
,
"author_email"
:
meta
.
author_email
,
"maintainer"
:
meta
.
maintainer
,
"maintainer_email"
:
meta
.
maintainer_email
,
"license"
:
meta
.
license
,
"description"
:
meta
.
description
,
"keywords"
:
meta
.
keywords
,
"platform"
:
meta
.
platforms
,
"classifiers"
:
meta
.
classifiers
,
"download_url"
:
meta
.
download_url
,
"supported_platform"
:
meta
.
supported_platforms
,
"comment"
:
None
,
"md5_digest"
:
md5_digest
,
"sha256_digest"
:
sha2_digest
,
"blake2_256_digest"
:
blake2_256_digest
,
# PEP 314
"provides"
:
meta
.
provides
,
"requires"
:
meta
.
requires
,
"obsoletes"
:
meta
.
obsoletes
,
# Metadata 1.2
"project_urls"
:
meta
.
project_urls
,
"provides_dist"
:
meta
.
provides_dist
,
"obsoletes_dist"
:
meta
.
obsoletes_dist
,
"requires_dist"
:
meta
.
requires_dist
,
"requires_external"
:
meta
.
requires_external
,
"requires_python"
:
meta
.
requires_python
,
}
# Metadata 2.1
if
meta
.
description_content_type
:
data
[
'description_content_type'
]
=
meta
.
description_content_type
# TODO: Provides extra
return
data
def
_upload
(
self
,
session
,
url
):
dist
=
self
.
_poetry
.
file
.
parent
/
'dist'
packages
=
dist
.
glob
(
'{}-{}*'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
)
files
=
(
i
for
i
in
packages
if
(
i
.
match
(
'{}-{}-*.whl'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
)
or
i
.
match
(
'{}-{}.tar.gz'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
)
)
)
for
file
in
files
:
# TODO: Check existence
resp
=
self
.
_upload_file
(
session
,
url
,
file
)
resp
.
raise_for_status
()
def
_upload_file
(
self
,
session
,
url
,
file
):
data
=
self
.
post_data
(
file
)
data
.
update
({
# action
":action"
:
"file_upload"
,
"protocol_version"
:
"1"
,
})
data_to_send
=
self
.
_convert_data_to_list_of_tuples
(
data
)
with
file
.
open
(
'rb'
)
as
fp
:
data_to_send
.
append
((
"content"
,
(
file
.
name
,
fp
,
"application/octet-stream"
),
))
encoder
=
MultipartEncoder
(
data_to_send
)
bar
=
self
.
_io
.
create_progress_bar
(
encoder
.
len
)
bar
.
set_format
(
" - Uploading <info>{0}</> <comment>
%
percent
%%
</>"
.
format
(
file
.
name
)
)
monitor
=
MultipartEncoderMonitor
(
encoder
,
lambda
monitor
:
bar
.
set_progress
(
monitor
.
bytes_read
)
)
bar
.
start
()
resp
=
session
.
post
(
url
,
data
=
monitor
,
allow_redirects
=
False
,
headers
=
{
'Content-Type'
:
monitor
.
content_type
}
)
if
resp
.
ok
:
bar
.
finish
()
self
.
_io
.
writeln
(
''
)
else
:
self
.
_io
.
overwrite
(
''
)
return
resp
def
_convert_data_to_list_of_tuples
(
self
,
data
):
data_to_send
=
[]
for
key
,
value
in
data
.
items
():
if
(
key
in
KEYWORDS_TO_NOT_FLATTEN
or
not
isinstance
(
value
,
(
list
,
tuple
))):
data_to_send
.
append
((
key
,
value
))
else
:
for
item
in
value
:
data_to_send
.
append
((
key
,
item
))
return
data_to_send
def
_get_type
(
self
,
file
):
exts
=
file
.
suffixes
if
exts
[
-
1
]
==
'.whl'
:
return
'bdist_wheel'
elif
len
(
exts
)
>=
2
and
''
.
join
(
exts
[
-
2
:])
==
'.tar.gz'
:
return
'sdist'
raise
ValueError
(
'Unknown distribution format {}'
.
format
(
''
.
join
(
exts
))
)
@staticmethod
def
_make_adapter_with_retries
():
retry
=
util
.
Retry
(
connect
=
5
,
total
=
10
,
method_whitelist
=
[
'GET'
],
status_forcelist
=
[
500
,
501
,
502
,
503
],
)
return
adapters
.
HTTPAdapter
(
max_retries
=
retry
)
self
.
_uploader
.
auth
(
username
,
password
)
@staticmethod
def
_make_user_agent_string
():
return
user_agent
(
'poetry'
,
__version__
,
)
return
self
.
_uploader
.
upload
(
url
)
poetry/masonry/publishing/uploader.py
0 → 100644
View file @
c63da256
import
hashlib
import
io
import
re
import
requests
from
requests
import
adapters
from
requests.exceptions
import
HTTPError
from
requests.packages.urllib3
import
util
from
requests_toolbelt
import
user_agent
from
requests_toolbelt.multipart
import
(
MultipartEncoder
,
MultipartEncoderMonitor
)
from
poetry.__version__
import
__version__
from
..metadata
import
Metadata
wheel_file_re
=
re
.
compile
(
r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>\d.+?))?)
((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?)
\.whl|\.dist-info)$"""
,
re
.
VERBOSE
)
KEYWORDS_TO_NOT_FLATTEN
=
{
'gpg_signature'
,
'content'
}
_has_blake2
=
hasattr
(
hashlib
,
'blake2b'
)
class
Uploader
:
def
__init__
(
self
,
poetry
,
io
):
self
.
_poetry
=
poetry
self
.
_package
=
poetry
.
package
self
.
_io
=
io
self
.
_username
=
None
self
.
_password
=
None
@property
def
user_agent
(
self
):
return
user_agent
(
'poetry'
,
__version__
)
@property
def
adapter
(
self
):
retry
=
util
.
Retry
(
connect
=
5
,
total
=
10
,
method_whitelist
=
[
'GET'
],
status_forcelist
=
[
500
,
501
,
502
,
503
],
)
return
adapters
.
HTTPAdapter
(
max_retries
=
retry
)
def
auth
(
self
,
username
,
password
):
self
.
_username
=
username
self
.
_password
=
password
def
make_session
(
self
):
session
=
requests
.
session
()
if
self
.
is_authenticated
():
session
.
auth
=
(
self
.
_username
,
self
.
_password
)
session
.
headers
[
'User-Agent'
]
=
self
.
user_agent
for
scheme
in
(
'http://'
,
'https://'
):
session
.
mount
(
scheme
,
self
.
adapter
)
return
session
def
is_authenticated
(
self
):
return
self
.
_username
is
not
None
and
self
.
_password
is
not
None
def
upload
(
self
,
url
):
session
=
self
.
make_session
()
try
:
self
.
_upload
(
session
,
url
)
finally
:
session
.
close
()
def
post_data
(
self
,
file
):
meta
=
Metadata
.
from_package
(
self
.
_package
)
file_type
=
self
.
_get_type
(
file
)
if
_has_blake2
:
blake2_256_hash
=
hashlib
.
blake2b
(
digest_size
=
256
//
8
)
md5_hash
=
hashlib
.
md5
()
sha256_hash
=
hashlib
.
sha256
()
with
file
.
open
(
'rb'
)
as
fp
:
for
content
in
iter
(
lambda
:
fp
.
read
(
io
.
DEFAULT_BUFFER_SIZE
),
b
''
):
md5_hash
.
update
(
content
)
sha256_hash
.
update
(
content
)
if
_has_blake2
:
blake2_256_hash
.
update
(
content
)
md5_digest
=
md5_hash
.
hexdigest
()
sha2_digest
=
sha256_hash
.
hexdigest
()
if
_has_blake2
:
blake2_256_digest
=
blake2_256_hash
.
hexdigest
()
else
:
blake2_256_digest
=
None
if
file_type
==
'bdist_wheel'
:
wheel_info
=
wheel_file_re
.
match
(
file
.
name
)
py_version
=
wheel_info
.
group
(
"pyver"
)
else
:
py_version
=
None
data
=
{
# identify release
"name"
:
meta
.
name
,
"version"
:
meta
.
version
,
# file content
"filetype"
:
file_type
,
"pyversion"
:
py_version
,
# additional meta-data
"metadata_version"
:
meta
.
metadata_version
,
"summary"
:
meta
.
summary
,
"home_page"
:
meta
.
home_page
,
"author"
:
meta
.
author
,
"author_email"
:
meta
.
author_email
,
"maintainer"
:
meta
.
maintainer
,
"maintainer_email"
:
meta
.
maintainer_email
,
"license"
:
meta
.
license
,
"description"
:
meta
.
description
,
"keywords"
:
meta
.
keywords
,
"platform"
:
meta
.
platforms
,
"classifiers"
:
meta
.
classifiers
,
"download_url"
:
meta
.
download_url
,
"supported_platform"
:
meta
.
supported_platforms
,
"comment"
:
None
,
"md5_digest"
:
md5_digest
,
"sha256_digest"
:
sha2_digest
,
"blake2_256_digest"
:
blake2_256_digest
,
# PEP 314
"provides"
:
meta
.
provides
,
"requires"
:
meta
.
requires
,
"obsoletes"
:
meta
.
obsoletes
,
# Metadata 1.2
"project_urls"
:
meta
.
project_urls
,
"provides_dist"
:
meta
.
provides_dist
,
"obsoletes_dist"
:
meta
.
obsoletes_dist
,
"requires_dist"
:
meta
.
requires_dist
,
"requires_external"
:
meta
.
requires_external
,
"requires_python"
:
meta
.
requires_python
,
}
# Metadata 2.1
if
meta
.
description_content_type
:
data
[
'description_content_type'
]
=
meta
.
description_content_type
# TODO: Provides extra
return
data
def
_upload
(
self
,
session
,
url
):
try
:
self
.
_do_upload
(
session
,
url
)
except
HTTPError
as
e
:
if
(
e
.
response
.
status_code
not
in
(
403
,
400
)
or
e
.
response
.
status_code
==
400
and
'was ever registered'
not
in
e
.
response
.
text
):
raise
# It may be the first time we publish the package
# We'll try to register it and go from there
try
:
self
.
_register
(
session
,
url
)
except
HTTPError
:
raise
def
_do_upload
(
self
,
session
,
url
):
dist
=
self
.
_poetry
.
file
.
parent
/
'dist'
packages
=
dist
.
glob
(
'{}-{}*'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
)
files
=
(
i
for
i
in
packages
if
(
i
.
match
(
'{}-{}-*.whl'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
)
or
i
.
match
(
'{}-{}.tar.gz'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
)
)
)
for
file
in
files
:
# TODO: Check existence
resp
=
self
.
_upload_file
(
session
,
url
,
file
)
resp
.
raise_for_status
()
def
_upload_file
(
self
,
session
,
url
,
file
):
data
=
self
.
post_data
(
file
)
data
.
update
({
# action
":action"
:
"file_upload"
,
"protocol_version"
:
"1"
,
})
data_to_send
=
self
.
_prepare_data
(
data
)
with
file
.
open
(
'rb'
)
as
fp
:
data_to_send
.
append
((
"content"
,
(
file
.
name
,
fp
,
"application/octet-stream"
),
))
encoder
=
MultipartEncoder
(
data_to_send
)
bar
=
self
.
_io
.
create_progress_bar
(
encoder
.
len
)
bar
.
set_format
(
" - Uploading <info>{0}</> <comment>
%
percent
%%
</>"
.
format
(
file
.
name
)
)
monitor
=
MultipartEncoderMonitor
(
encoder
,
lambda
monitor
:
bar
.
set_progress
(
monitor
.
bytes_read
)
)
bar
.
start
()
resp
=
session
.
post
(
url
,
data
=
monitor
,
allow_redirects
=
False
,
headers
=
{
'Content-Type'
:
monitor
.
content_type
}
)
if
resp
.
ok
:
bar
.
finish
()
self
.
_io
.
writeln
(
''
)
else
:
self
.
_io
.
overwrite
(
''
)
return
resp
def
_register
(
self
,
session
,
url
):
"""
Register a package to a repository.
"""
dist
=
self
.
_poetry
.
file
.
parent
/
'dist'
file
=
dist
/
'{}-{}.tar.gz'
.
format
(
self
.
_package
.
name
,
self
.
_package
.
version
)
if
not
file
.
exists
():
raise
RuntimeError
(
'"{0}" does not exist.'
.
format
(
file
.
name
)
)
data
=
self
.
post_data
(
file
)
data
.
update
({
":action"
:
"submit"
,
"protocol_version"
:
"1"
,
})
data_to_send
=
self
.
_prepare_data
(
data
)
encoder
=
MultipartEncoder
(
data_to_send
)
resp
=
session
.
post
(
url
,
data
=
encoder
,
allow_redirects
=
False
,
headers
=
{
'Content-Type'
:
encoder
.
content_type
},
)
return
resp
def
_prepare_data
(
self
,
data
):
data_to_send
=
[]
for
key
,
value
in
data
.
items
():
if
(
key
in
KEYWORDS_TO_NOT_FLATTEN
or
not
isinstance
(
value
,
(
list
,
tuple
))):
data_to_send
.
append
((
key
,
value
))
else
:
for
item
in
value
:
data_to_send
.
append
((
key
,
item
))
return
data_to_send
def
_get_type
(
self
,
file
):
exts
=
file
.
suffixes
if
exts
[
-
1
]
==
'.whl'
:
return
'bdist_wheel'
elif
len
(
exts
)
>=
2
and
''
.
join
(
exts
[
-
2
:])
==
'.tar.gz'
:
return
'sdist'
raise
ValueError
(
'Unknown distribution format {}'
.
format
(
''
.
join
(
exts
))
)
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