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
1bc9cdcb
Unverified
Commit
1bc9cdcb
authored
Mar 08, 2018
by
Sébastien Eustace
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add the publish command
parent
63bb882e
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
520 additions
and
5 deletions
+520
-5
CHANGELOG.md
+1
-0
poetry/console/application.py
+2
-0
poetry/console/commands/__init__.py
+1
-0
poetry/console/commands/build.py
+0
-1
poetry/console/commands/command.py
+16
-0
poetry/console/commands/publish.py
+32
-0
poetry/masonry/__publisher.py
+143
-0
poetry/masonry/builder.py
+16
-0
poetry/masonry/builders/builder.py
+12
-0
poetry/masonry/builders/sdist.py
+3
-2
poetry/masonry/builders/wheel.py
+2
-2
poetry/masonry/publisher.py
+262
-0
poetry/packages/package.py
+28
-0
pyproject.toml
+2
-0
No files found.
CHANGELOG.md
View file @
1bc9cdcb
...
@@ -9,6 +9,7 @@
...
@@ -9,6 +9,7 @@
-
Added support for extras definition.
-
Added support for extras definition.
-
Added support for dependencies extras specification.
-
Added support for dependencies extras specification.
-
Added the
`config`
command.
-
Added the
`config`
command.
-
Added the
`publish`
command.
### Changed
### Changed
...
...
poetry/console/application.py
View file @
1bc9cdcb
...
@@ -12,6 +12,7 @@ from .commands import ConfigCommand
...
@@ -12,6 +12,7 @@ from .commands import ConfigCommand
from
.commands
import
InstallCommand
from
.commands
import
InstallCommand
from
.commands
import
LockCommand
from
.commands
import
LockCommand
from
.commands
import
NewCommand
from
.commands
import
NewCommand
from
.commands
import
PublishCommand
from
.commands
import
RemoveCommand
from
.commands
import
RemoveCommand
from
.commands
import
ShowCommand
from
.commands
import
ShowCommand
from
.commands
import
UpdateCommand
from
.commands
import
UpdateCommand
...
@@ -52,6 +53,7 @@ class Application(BaseApplication):
...
@@ -52,6 +53,7 @@ class Application(BaseApplication):
InstallCommand
(),
InstallCommand
(),
LockCommand
(),
LockCommand
(),
NewCommand
(),
NewCommand
(),
PublishCommand
(),
RemoveCommand
(),
RemoveCommand
(),
ShowCommand
(),
ShowCommand
(),
UpdateCommand
(),
UpdateCommand
(),
...
...
poetry/console/commands/__init__.py
View file @
1bc9cdcb
...
@@ -5,6 +5,7 @@ from .config import ConfigCommand
...
@@ -5,6 +5,7 @@ from .config import ConfigCommand
from
.install
import
InstallCommand
from
.install
import
InstallCommand
from
.lock
import
LockCommand
from
.lock
import
LockCommand
from
.new
import
NewCommand
from
.new
import
NewCommand
from
.publish
import
PublishCommand
from
.remove
import
RemoveCommand
from
.remove
import
RemoveCommand
from
.show
import
ShowCommand
from
.show
import
ShowCommand
from
.update
import
UpdateCommand
from
.update
import
UpdateCommand
poetry/console/commands/build.py
View file @
1bc9cdcb
...
@@ -19,7 +19,6 @@ class BuildCommand(Command):
...
@@ -19,7 +19,6 @@ class BuildCommand(Command):
package
=
self
.
poetry
.
package
package
=
self
.
poetry
.
package
self
.
line
(
f
'Building <info>{package.pretty_name}</> '
self
.
line
(
f
'Building <info>{package.pretty_name}</> '
f
'(<comment>{package.version}</>)'
)
f
'(<comment>{package.version}</>)'
)
self
.
line
(
''
)
builder
=
Builder
(
self
.
poetry
,
self
.
output
)
builder
=
Builder
(
self
.
poetry
,
self
.
output
)
builder
.
build
(
fmt
)
builder
.
build
(
fmt
)
poetry/console/commands/command.py
View file @
1bc9cdcb
from
cleo
import
Command
as
BaseCommand
from
cleo
import
Command
as
BaseCommand
from
cleo.inputs
import
ListInput
from
poetry.poetry
import
Poetry
from
poetry.poetry
import
Poetry
...
@@ -14,6 +15,21 @@ class Command(BaseCommand):
...
@@ -14,6 +15,21 @@ class Command(BaseCommand):
def
reset_poetry
(
self
)
->
None
:
def
reset_poetry
(
self
)
->
None
:
self
.
get_application
()
.
reset_poetry
()
self
.
get_application
()
.
reset_poetry
()
def
call
(
self
,
name
,
options
=
None
):
"""
Call another command.
Fixing style being passed rather than an output
"""
if
options
is
None
:
options
=
[]
command
=
self
.
get_application
()
.
find
(
name
)
options
=
[(
'command'
,
command
.
get_name
())]
+
options
return
command
.
run
(
ListInput
(
options
),
self
.
output
.
output
)
def
run
(
self
,
i
,
o
)
->
int
:
def
run
(
self
,
i
,
o
)
->
int
:
"""
"""
Initialize command.
Initialize command.
...
...
poetry/console/commands/publish.py
0 → 100644
View file @
1bc9cdcb
from
poetry.masonry.publisher
import
Publisher
from
.command
import
Command
class
PublishCommand
(
Command
):
"""
Publishes a package to a remote repository.
publish
{ --r|repository= : The repository to publish the package to. }
{ --no-build : Do not build the package before publishing. }
"""
help
=
"""The publish command builds and uploads the package to a remote repository.
By default, it will upload to PyPI but if you pass the --repository option it will
upload to it instead.
The --repository option should match the name of a configured repository using
the config command.
"""
def
handle
(
self
):
# Building package first, unless told otherwise
if
not
self
.
option
(
'no-build'
):
self
.
call
(
'build'
)
self
.
line
(
''
)
publisher
=
Publisher
(
self
.
poetry
,
self
.
output
)
publisher
.
publish
(
self
.
option
(
'repository'
))
poetry/masonry/__publisher.py
0 → 100644
View file @
1bc9cdcb
import
hashlib
import
toml
import
requests
from
pathlib
import
Path
from
poetry.locations
import
CONFIG_DIR
from
poetry.semver.constraints
import
Constraint
from
poetry.semver.constraints
import
MultiConstraint
from
.builders.builder
import
Builder
class
Publisher
:
def
__init__
(
self
,
poetry
,
io
):
self
.
_poetry
=
poetry
self
.
_package
=
poetry
.
package
self
.
_io
=
io
def
publish
(
self
,
repository_name
):
if
repository_name
:
self
.
_io
.
writeln
(
f
'Publishing <info>{self._package.pretty_name}</info> '
f
'(<comment>{self._package.pretty_version}</comment>) '
f
'to <fg=cyan>{repository_name}</>'
)
else
:
self
.
_io
.
writeln
(
f
'Publishing <info>{self._package.pretty_name}</info> '
f
'(<comment>{self._package.pretty_version}</comment>) '
f
'to <fg=cyan>PyPI</>'
)
if
not
repository_name
:
url
=
'https://upload.pypi.org/legacy/'
else
:
# Retrieving config information
config_file
=
Path
(
CONFIG_DIR
)
/
'config.toml'
if
not
config_file
.
exists
():
raise
RuntimeError
(
'Config file does not exist. '
'Unable to get repository information'
)
with
config_file
.
open
()
as
f
:
config
=
toml
.
loads
(
f
.
read
())
if
(
'repositories'
not
in
config
or
repository_name
not
in
config
[
'repositories'
]
):
raise
RuntimeError
(
f
'Repository {repository_name} is not defined'
)
url
=
config
[
'repositories'
][
repository_name
][
'url'
]
username
=
None
password
=
None
auth_file
=
Path
(
CONFIG_DIR
)
/
'auth.toml'
if
not
auth_file
.
exists
():
# No auth file, we will ask for info later
auth_config
=
{}
else
:
with
auth_file
.
open
()
as
f
:
auth_config
=
toml
.
loads
(
f
.
read
())
if
'http-basic'
in
auth_config
and
repository_name
in
auth_config
[
'http-basic'
]:
config
=
auth_config
[
'http-basic'
][
repository_name
]
username
=
config
.
get
(
'username'
)
password
=
config
.
get
(
'password'
)
return
self
.
upload
(
url
,
username
=
username
,
password
=
password
)
def
upload
(
self
,
url
,
username
=
None
,
password
=
None
):
data
=
self
.
build_post_data
(
'file_upload'
)
def
upload_file
(
self
,
file
,
url
,
username
,
password
):
data
=
self
.
build_post_data
(
'file_upload'
)
data
[
'protocol_version'
]
=
'1'
if
file
.
suffix
==
'.whl'
:
data
[
'filetype'
]
=
'bdist_wheel'
py2_support
=
self
.
_package
.
python_constraint
.
matches
(
MultiConstraint
([
Constraint
(
'>='
,
'2.0.0'
),
Constraint
(
'<'
,
'3.0.0'
)
])
)
data
[
'pyversion'
]
=
(
'py2.'
if
py2_support
else
''
)
+
'py3'
else
:
data
[
'filetype'
]
=
'sdist'
with
file
.
open
(
'rb'
)
as
f
:
content
=
f
.
read
()
files
=
{
'content'
:
(
file
.
name
,
content
)}
data
[
'md5_digest'
]
=
hashlib
.
md5
(
content
)
.
hexdigest
()
log
.
info
(
'Uploading
%
s...'
,
file
)
resp
=
requests
.
post
(
repo
[
'url'
],
data
=
data
,
files
=
files
,
auth
=
(
repo
[
'username'
],
repo
[
'password'
]),
)
resp
.
raise_for_status
()
def
build_post_data
(
self
,
action
):
builder
=
Builder
(
self
.
_poetry
,
self
.
_io
)
d
=
{
":action"
:
action
,
"name"
:
self
.
_package
.
name
,
"version"
:
self
.
_package
.
version
,
# additional meta-data
"metadata_version"
:
'1.2'
,
"summary"
:
self
.
_package
.
description
,
"home_page"
:
self
.
_package
.
homepage
or
self
.
_package
.
repository_url
,
"author"
:
self
.
_package
.
author_name
,
"author_email"
:
self
.
_package
.
author_email
,
"maintainer"
:
self
.
_package
.
author_name
,
"maintainer_email"
:
self
.
_package
.
author_email
,
"license"
:
self
.
_package
.
license
,
"description"
:
self
.
_package
.
readme
,
"keywords"
:
','
.
join
(
self
.
_package
.
keywords
),
"platform"
:
None
if
self
.
_package
.
platform
==
'*'
else
self
.
_package
.
platform
,
"classifiers"
:
builder
.
get_classifers
(),
"download_url"
:
None
,
"supported_platform"
:
None
if
self
.
_package
.
platform
==
'*'
else
self
.
_package
.
platform
,
"project_urls"
:
[],
"provides_dist"
:
[],
"obsoletes_dist"
:
[],
"requires_dist"
:
[
d
.
to_pep_508
()
for
d
in
self
.
_package
.
requires
],
"requires_external"
:
[],
"requires_python"
:
builder
.
convert_python_version
(),
}
return
{
k
:
v
for
k
,
v
in
d
.
items
()
if
v
}
poetry/masonry/builder.py
View file @
1bc9cdcb
from
poetry.semver.constraints
import
MultiConstraint
from
.builders
import
CompleteBuilder
from
.builders
import
CompleteBuilder
from
.builders
import
SdistBuilder
from
.builders
import
SdistBuilder
from
.builders
import
WheelBuilder
from
.builders
import
WheelBuilder
...
@@ -19,6 +21,20 @@ class Builder:
...
@@ -19,6 +21,20 @@ class Builder:
if
fmt
not
in
self
.
_FORMATS
:
if
fmt
not
in
self
.
_FORMATS
:
raise
ValueError
(
f
'Invalid format: {fmt}'
)
raise
ValueError
(
f
'Invalid format: {fmt}'
)
self
.
check
()
builder
=
self
.
_FORMATS
[
fmt
](
self
.
_poetry
,
self
.
_io
)
builder
=
self
.
_FORMATS
[
fmt
](
self
.
_poetry
,
self
.
_io
)
return
builder
.
build
()
return
builder
.
build
()
def
check
(
self
)
->
None
:
package
=
self
.
_poetry
.
package
# Checking for disjunctive python versions
if
isinstance
(
package
.
python_constraint
,
MultiConstraint
):
if
package
.
python_constraint
.
is_disjunctive
():
raise
RuntimeError
(
'Disjunctive python versions are not yet supported '
'when building packages. Rewrite your python requirements '
'in a conjunctive way.'
)
poetry/masonry/builders/builder.py
View file @
1bc9cdcb
...
@@ -5,6 +5,7 @@ from collections import defaultdict
...
@@ -5,6 +5,7 @@ from collections import defaultdict
from
pathlib
import
Path
from
pathlib
import
Path
from
poetry.semver.constraints
import
Constraint
from
poetry.semver.constraints
import
Constraint
from
poetry.semver.constraints
import
MultiConstraint
from
poetry.semver.version_parser
import
VersionParser
from
poetry.semver.version_parser
import
VersionParser
from
poetry.vcs
import
get_vcs
from
poetry.vcs
import
get_vcs
...
@@ -144,3 +145,14 @@ class Builder:
...
@@ -144,3 +145,14 @@ class Builder:
classifiers
.
append
(
f
'Programming Language :: Python :: {version}'
)
classifiers
.
append
(
f
'Programming Language :: Python :: {version}'
)
return
classifiers
return
classifiers
def
convert_python_version
(
self
):
constraint
=
self
.
_package
.
python_constraint
if
isinstance
(
constraint
,
MultiConstraint
):
python_requires
=
','
.
join
(
[
str
(
c
)
.
replace
(
' '
,
''
)
for
c
in
constraint
.
constraints
]
)
else
:
python_requires
=
str
(
constraint
)
.
replace
(
' '
,
''
)
return
python_requires
poetry/masonry/builders/sdist.py
View file @
1bc9cdcb
...
@@ -24,6 +24,7 @@ from setuptools import setup
...
@@ -24,6 +24,7 @@ from setuptools import setup
{before}
{before}
setup(
setup(
name={name!r},
name={name!r},
version={version!r}
description={description!r},
description={description!r},
author={author!r},
author={author!r},
author_email={author_email!r},
author_email={author_email!r},
...
@@ -50,7 +51,7 @@ class SdistBuilder(Builder):
...
@@ -50,7 +51,7 @@ class SdistBuilder(Builder):
super
()
.
__init__
(
poetry
,
io
)
super
()
.
__init__
(
poetry
,
io
)
def
build
(
self
,
target_dir
:
Path
=
None
)
->
Path
:
def
build
(
self
,
target_dir
:
Path
=
None
)
->
Path
:
self
.
_io
.
writeln
(
'Building <info>sdist</info>'
)
self
.
_io
.
writeln
(
'
-
Building <info>sdist</info>'
)
if
target_dir
is
None
:
if
target_dir
is
None
:
target_dir
=
self
.
_path
/
'dist'
target_dir
=
self
.
_path
/
'dist'
...
@@ -104,7 +105,7 @@ class SdistBuilder(Builder):
...
@@ -104,7 +105,7 @@ class SdistBuilder(Builder):
tar
.
close
()
tar
.
close
()
gz
.
close
()
gz
.
close
()
self
.
_io
.
writeln
(
f
'Built <comment>{target.name}</>'
)
self
.
_io
.
writeln
(
f
'
-
Built <comment>{target.name}</>'
)
return
target
return
target
...
...
poetry/masonry/builders/wheel.py
View file @
1bc9cdcb
...
@@ -69,7 +69,7 @@ class WheelBuilder(Builder):
...
@@ -69,7 +69,7 @@ class WheelBuilder(Builder):
return
cls
.
make_in
(
poetry
,
io
,
dist_dir
)
return
cls
.
make_in
(
poetry
,
io
,
dist_dir
)
def
build
(
self
)
->
None
:
def
build
(
self
)
->
None
:
self
.
_io
.
writeln
(
'Building <info>wheel</info>'
)
self
.
_io
.
writeln
(
'
-
Building <info>wheel</info>'
)
try
:
try
:
self
.
copy_module
()
self
.
copy_module
()
self
.
write_metadata
()
self
.
write_metadata
()
...
@@ -77,7 +77,7 @@ class WheelBuilder(Builder):
...
@@ -77,7 +77,7 @@ class WheelBuilder(Builder):
finally
:
finally
:
self
.
_wheel_zip
.
close
()
self
.
_wheel_zip
.
close
()
self
.
_io
.
writeln
(
f
'Built <comment>{self.wheel_filename}</>'
)
self
.
_io
.
writeln
(
f
'
-
Built <comment>{self.wheel_filename}</>'
)
def
copy_module
(
self
)
->
None
:
def
copy_module
(
self
)
->
None
:
if
self
.
_module
.
is_package
():
if
self
.
_module
.
is_package
():
...
...
poetry/masonry/publisher.py
0 → 100644
View file @
1bc9cdcb
# -*- coding: utf-8 -*-
import
toml
import
twine.utils
from
pathlib
import
Path
from
requests.exceptions
import
HTTPError
from
twine.commands.upload
import
find_dists
,
skip_upload
from
twine.repository
import
Repository
as
BaseRepository
from
twine.exceptions
import
PackageNotFound
,
RedirectDetected
from
twine.package
import
PackageFile
from
requests_toolbelt.multipart
import
(
MultipartEncoder
,
MultipartEncoderMonitor
)
from
poetry.locations
import
CONFIG_DIR
class
Repository
(
BaseRepository
):
def
__init__
(
self
,
io
,
url
,
username
,
password
):
self
.
_io
=
io
super
(
Repository
,
self
)
.
__init__
(
url
,
username
,
password
)
def
register
(
self
,
package
):
data
=
package
.
metadata_dictionary
()
data
.
update
({
":action"
:
"submit"
,
"protocol_version"
:
"1"
,
})
self
.
_io
.
writeln
(
" - Registering <info>{0}</>"
.
format
(
package
.
basefilename
)
)
data_to_send
=
self
.
_convert_data_to_list_of_tuples
(
data
)
encoder
=
MultipartEncoder
(
data_to_send
)
resp
=
self
.
session
.
post
(
self
.
url
,
data
=
encoder
,
allow_redirects
=
False
,
headers
=
{
'Content-Type'
:
encoder
.
content_type
}
)
# Bug 28. Try to silence a ResourceWarning by releasing the socket.
resp
.
close
()
return
resp
def
_upload
(
self
,
package
):
data
=
package
.
metadata_dictionary
()
data
.
update
({
# action
":action"
:
"file_upload"
,
"protocol_version"
:
"1"
,
})
data_to_send
=
self
.
_convert_data_to_list_of_tuples
(
data
)
with
open
(
package
.
filename
,
"rb"
)
as
fp
:
data_to_send
.
append
((
"content"
,
(
package
.
basefilename
,
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
(
package
.
basefilename
)
)
monitor
=
MultipartEncoderMonitor
(
encoder
,
lambda
monitor
:
bar
.
set_progress
(
monitor
.
bytes_read
)
)
bar
.
start
()
resp
=
self
.
session
.
post
(
self
.
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
class
Publisher
(
object
):
"""
Registers and publishes packages to remote repositories.
"""
def
__init__
(
self
,
poetry
,
io
):
self
.
_poetry
=
poetry
self
.
_package
=
poetry
.
package
self
.
_io
=
io
def
publish
(
self
,
repository_name
):
if
repository_name
:
self
.
_io
.
writeln
(
f
'Publishing <info>{self._package.pretty_name}</info> '
f
'(<comment>{self._package.pretty_version}</comment>) '
f
'to <fg=cyan>{repository_name}</>'
)
else
:
self
.
_io
.
writeln
(
f
'Publishing <info>{self._package.pretty_name}</info> '
f
'(<comment>{self._package.pretty_version}</comment>) '
f
'to <fg=cyan>PyPI</>'
)
if
not
repository_name
:
url
=
'https://upload.pypi.org/legacy/'
else
:
# Retrieving config information
config_file
=
Path
(
CONFIG_DIR
)
/
'config.toml'
if
not
config_file
.
exists
():
raise
RuntimeError
(
'Config file does not exist. '
'Unable to get repository information'
)
with
config_file
.
open
()
as
f
:
config
=
toml
.
loads
(
f
.
read
())
if
(
'repositories'
not
in
config
or
repository_name
not
in
config
[
'repositories'
]
):
raise
RuntimeError
(
f
'Repository {repository_name} is not defined'
)
url
=
config
[
'repositories'
][
repository_name
][
'url'
]
username
=
None
password
=
None
auth_file
=
Path
(
CONFIG_DIR
)
/
'auth.toml'
if
auth_file
.
exists
():
with
auth_file
.
open
()
as
f
:
auth_config
=
toml
.
loads
(
f
.
read
())
if
'http-basic'
in
auth_config
and
repository_name
in
auth_config
[
'http-basic'
]:
config
=
auth_config
[
'http-basic'
][
repository_name
]
username
=
config
.
get
(
'username'
)
password
=
config
.
get
(
'password'
)
# Requesting missing credentials
if
not
username
:
username
=
self
.
_io
.
ask
(
'Username:'
)
if
not
password
:
password
=
self
.
_io
.
ask_hidden
(
'Password:'
)
repository
=
Repository
(
self
.
_io
,
url
,
username
,
password
)
# TODO: handle certificates
self
.
upload
(
repository
)
def
register
(
self
,
repository
):
"""
Register a package to a repository.
"""
dist
=
self
.
_poetry
.
file
.
parent
/
'dist'
package
=
dist
/
f
'{self._package.name}-{self._package.version}.tar.gz'
if
package
.
exists
():
raise
PackageNotFound
(
'"{0}" does not exist on the file system.'
.
format
(
package
)
)
resp
=
repository
.
register
(
PackageFile
.
from_filename
(
str
(
package
),
None
)
)
repository
.
close
()
if
resp
.
is_redirect
:
raise
RedirectDetected
(
(
'"{0}" attempted to redirect to "{1}" during registration.'
' Aborting...'
)
.
format
(
repository
.
url
,
resp
.
headers
[
"location"
]))
resp
.
raise_for_status
()
def
upload
(
self
,
repository
):
"""
Upload packages for the current project.
"""
try
:
self
.
_upload
(
repository
)
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
(
repository
)
except
HTTPError
:
raise
def
_upload
(
self
,
repository
):
skip_existing
=
False
dist
=
self
.
_poetry
.
file
.
parent
/
'dist'
packages
=
list
(
dist
.
glob
(
f
'{self._package.name}-{self._package.version}*'
))
dists
=
find_dists
([
str
(
p
)
for
p
in
packages
])
uploads
=
[
i
for
i
in
dists
if
not
i
.
endswith
(
".asc"
)]
for
filename
in
uploads
:
package
=
PackageFile
.
from_filename
(
filename
,
None
)
skip_message
=
(
" - Skipping <comment>{0}</> because it appears to already exist"
.
format
(
package
.
basefilename
)
)
# Note: The skip_existing check *needs* to be first, because otherwise
# we're going to generate extra HTTP requests against a hardcoded
# URL for no reason.
if
skip_existing
and
repository
.
package_is_uploaded
(
package
):
self
.
_io
.
writeln
(
skip_message
)
continue
resp
=
repository
.
upload
(
package
)
# Bug 92. If we get a redirect we should abort because something seems
# funky. The behaviour is not well defined and redirects being issued
# by PyPI should never happen in reality. This should catch malicious
# redirects as well.
if
resp
.
is_redirect
:
raise
RedirectDetected
(
(
'"{0}" attempted to redirect to "{1}" during upload.'
' Aborting...'
)
.
format
(
repository
.
url
,
resp
.
headers
[
"location"
]))
if
skip_upload
(
resp
,
skip_existing
,
package
):
self
.
_io
.
writeln
(
skip_message
)
continue
twine
.
utils
.
check_status_code
(
resp
)
# Bug 28. Try to silence a ResourceWarning by clearing the connection
# pool.
repository
.
close
()
poetry/packages/package.py
View file @
1bc9cdcb
import
re
from
typing
import
Union
from
typing
import
Union
from
poetry.semver.helpers
import
parse_stability
from
poetry.semver.helpers
import
parse_stability
...
@@ -7,6 +8,8 @@ from poetry.version import parse as parse_version
...
@@ -7,6 +8,8 @@ from poetry.version import parse as parse_version
from
.dependency
import
Dependency
from
.dependency
import
Dependency
from
.vcs_dependency
import
VCSDependency
from
.vcs_dependency
import
VCSDependency
AUTHOR_REGEX
=
re
.
compile
(
'(?u)^(?P<name>[- .,
\
w
\
d
\'
’"()]+) <(?P<email>.+?)>$'
)
class
Package
:
class
Package
:
...
@@ -121,6 +124,31 @@ class Package:
...
@@ -121,6 +124,31 @@ class Package:
return
self
.
_authors
return
self
.
_authors
@property
@property
def
author_name
(
self
)
->
str
:
return
self
.
_get_author
()[
'name'
]
@property
def
author_email
(
self
)
->
str
:
return
self
.
_get_author
()[
'email'
]
def
_get_author
(
self
)
->
dict
:
if
not
self
.
_authors
:
return
{
'name'
:
None
,
'email'
:
None
}
m
=
AUTHOR_REGEX
.
match
(
self
.
_authors
[
0
])
name
=
m
.
group
(
'name'
)
email
=
m
.
group
(
'email'
)
return
{
'name'
:
name
,
'email'
:
email
}
@property
def
python_versions
(
self
):
def
python_versions
(
self
):
return
self
.
_python_versions
return
self
.
_python_versions
...
...
pyproject.toml
View file @
1bc9cdcb
...
@@ -23,6 +23,8 @@ requests = "^2.18"
...
@@ -23,6 +23,8 @@ requests = "^2.18"
toml
=
"^0.9"
toml
=
"^0.9"
cachy
=
"^0.1.0"
cachy
=
"^0.1.0"
pip-tools
=
"^1.11"
pip-tools
=
"^1.11"
twine
=
"^1.10"
requests-toolbelt
=
"^0.8.0"
[tool.poetry.dev-dependencies]
[tool.poetry.dev-dependencies]
pytest
=
"~3.4"
pytest
=
"~3.4"
...
...
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