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
e5e706a4
Unverified
Commit
e5e706a4
authored
Aug 02, 2019
by
Sébastien Eustace
Committed by
GitHub
Aug 02, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for PyPI API tokens (#1275)
parent
11c2b9a5
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
81 additions
and
13 deletions
+81
-13
docs/docs/repositories.md
+13
-2
poetry/console/commands/config.py
+12
-1
poetry/console/commands/publish.py
+2
-0
poetry/masonry/publishing/publisher.py
+21
-7
tests/console/commands/test_config.py
+13
-0
tests/masonry/publishing/test_publisher.py
+20
-3
No files found.
docs/docs/repositories.md
View file @
e5e706a4
...
@@ -39,8 +39,18 @@ If you do not specify the password you will be prompted to write it.
...
@@ -39,8 +39,18 @@ If you do not specify the password you will be prompted to write it.
!!!note
!!!note
To publish to PyPI, you can set your credentials for the repository
To publish to PyPI, you can set your credentials for the repository named `pypi`.
named `pypi`:
Note that it is recommended to use [API tokens](https://pypi.org/help/#apitoken)
when uploading packages to PyPI.
Once you have created a new token, you can tell Poetry to use it:
```bash
poetry config pypi-token.pypi my-token
```
If you still want to use you username and password, you can do so with the following
call to `config`.
```bash
```bash
poetry config http-basic.pypi username password
poetry config http-basic.pypi username password
...
@@ -56,6 +66,7 @@ Keyring support is enabled using the [keyring library](https://pypi.org/project/
...
@@ -56,6 +66,7 @@ Keyring support is enabled using the [keyring library](https://pypi.org/project/
Alternatively, you can use environment variables to provide the credentials:
Alternatively, you can use environment variables to provide the credentials:
```
bash
```
bash
export
POETRY_PYPI_TOKEN_PYPI
=
my-token
export
POETRY_HTTP_BASIC_PYPI_USERNAME
=
username
export
POETRY_HTTP_BASIC_PYPI_USERNAME
=
username
export
POETRY_HTTP_BASIC_PYPI_PASSWORD
=
password
export
POETRY_HTTP_BASIC_PYPI_PASSWORD
=
password
```
```
...
...
poetry/console/commands/config.py
View file @
e5e706a4
...
@@ -176,7 +176,7 @@ To remove a repository (repo is a short alias for repositories):
...
@@ -176,7 +176,7 @@ To remove a repository (repo is a short alias for repositories):
)
)
# handle auth
# handle auth
m
=
re
.
match
(
r"^(http-basic)\.(.+)"
,
self
.
argument
(
"key"
))
m
=
re
.
match
(
r"^(http-basic
|pypi-token
)\.(.+)"
,
self
.
argument
(
"key"
))
if
m
:
if
m
:
if
self
.
option
(
"unset"
):
if
self
.
option
(
"unset"
):
keyring_repository_password_del
(
config
,
m
.
group
(
2
))
keyring_repository_password_del
(
config
,
m
.
group
(
2
))
...
@@ -209,6 +209,17 @@ To remove a repository (repo is a short alias for repositories):
...
@@ -209,6 +209,17 @@ To remove a repository (repo is a short alias for repositories):
auth_config_source
.
add_property
(
auth_config_source
.
add_property
(
"{}.{}"
.
format
(
m
.
group
(
1
),
m
.
group
(
2
)),
property_value
"{}.{}"
.
format
(
m
.
group
(
1
),
m
.
group
(
2
)),
property_value
)
)
elif
m
.
group
(
1
)
==
"pypi-token"
:
if
len
(
values
)
!=
1
:
raise
ValueError
(
"Expected only one argument (token), got {}"
.
format
(
len
(
values
))
)
token
=
values
[
0
]
auth_config_source
.
add_property
(
"{}.{}"
.
format
(
m
.
group
(
1
),
m
.
group
(
2
)),
token
)
return
0
return
0
...
...
poetry/console/commands/publish.py
View file @
e5e706a4
...
@@ -26,6 +26,8 @@ The --repository option should match the name of a configured repository using
...
@@ -26,6 +26,8 @@ The --repository option should match the name of a configured repository using
the config command.
the config command.
"""
"""
loggers
=
[
"poetry.masonry.publishing.publisher"
]
def
handle
(
self
):
def
handle
(
self
):
from
poetry.masonry.publishing.publisher
import
Publisher
from
poetry.masonry.publishing.publisher
import
Publisher
...
...
poetry/masonry/publishing/publisher.py
View file @
e5e706a4
from
poetry.locations
import
CONFIG_DIR
import
logging
from
poetry.utils._compat
import
Path
from
poetry.utils.helpers
import
get_http_basic_auth
from
poetry.utils.helpers
import
get_http_basic_auth
from
poetry.utils.toml_file
import
TomlFile
from
.uploader
import
Uploader
from
.uploader
import
Uploader
logger
=
logging
.
getLogger
(
__name__
)
class
Publisher
:
class
Publisher
:
"""
"""
Registers and publishes packages to remote repositories.
Registers and publishes packages to remote repositories.
...
@@ -55,10 +57,22 @@ class Publisher:
...
@@ -55,10 +57,22 @@ class Publisher:
url
=
repository
[
"url"
]
url
=
repository
[
"url"
]
if
not
(
username
and
password
):
if
not
(
username
and
password
):
auth
=
get_http_basic_auth
(
self
.
_poetry
.
config
,
repository_name
)
# Check if we have a token first
if
auth
:
token
=
self
.
_poetry
.
config
.
get
(
"pypi-token.{}"
.
format
(
repository_name
))
username
=
auth
[
0
]
if
token
:
password
=
auth
[
1
]
logger
.
debug
(
"Found an API token for {}."
.
format
(
repository_name
))
username
=
"@token"
password
=
token
else
:
auth
=
get_http_basic_auth
(
self
.
_poetry
.
config
,
repository_name
)
if
auth
:
logger
.
debug
(
"Found authentication information for {}."
.
format
(
repository_name
)
)
username
=
auth
[
0
]
password
=
auth
[
1
]
# Requesting missing credentials
# Requesting missing credentials
if
not
username
:
if
not
username
:
...
...
tests/console/commands/test_config.py
View file @
e5e706a4
...
@@ -92,3 +92,16 @@ virtualenvs.path = {path} # /foo{sep}virtualenvs
...
@@ -92,3 +92,16 @@ virtualenvs.path = {path} # /foo{sep}virtualenvs
assert
expected
==
tester
.
io
.
fetch_output
()
assert
expected
==
tester
.
io
.
fetch_output
()
assert
"poetry.toml"
==
init
.
call_args_list
[
2
][
0
][
1
]
.
path
.
name
assert
"poetry.toml"
==
init
.
call_args_list
[
2
][
0
][
1
]
.
path
.
name
assert
expected
==
tester
.
io
.
fetch_output
()
def
test_set_pypi_token
(
app
,
config_source
,
config_document
,
mocker
):
init
=
mocker
.
spy
(
ConfigSource
,
"__init__"
)
command
=
app
.
find
(
"config"
)
tester
=
CommandTester
(
command
)
tester
.
execute
(
"pypi-token.pypi mytoken"
)
tester
.
execute
(
"--list"
)
assert
"mytoken"
==
config_document
[
"pypi-token"
][
"pypi"
]
tests/masonry/publishing/test_publisher.py
View file @
e5e706a4
...
@@ -5,10 +5,11 @@ from poetry.masonry.publishing.publisher import Publisher
...
@@ -5,10 +5,11 @@ from poetry.masonry.publishing.publisher import Publisher
from
poetry.poetry
import
Poetry
from
poetry.poetry
import
Poetry
def
test_publish_publishes_to_pypi_by_default
(
fixture_dir
,
mocker
):
def
test_publish_publishes_to_pypi_by_default
(
fixture_dir
,
mocker
,
config
):
uploader_auth
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.auth"
)
uploader_auth
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.auth"
)
uploader_upload
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.upload"
)
uploader_upload
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.upload"
)
poetry
=
Poetry
.
create
(
fixture_dir
(
"sample_project"
))
poetry
=
Poetry
.
create
(
fixture_dir
(
"sample_project"
))
poetry
.
_config
=
config
poetry
.
config
.
merge
(
poetry
.
config
.
merge
(
{
"http-basic"
:
{
"pypi"
:
{
"username"
:
"foo"
,
"password"
:
"bar"
}}}
{
"http-basic"
:
{
"pypi"
:
{
"username"
:
"foo"
,
"password"
:
"bar"
}}}
)
)
...
@@ -20,10 +21,11 @@ def test_publish_publishes_to_pypi_by_default(fixture_dir, mocker):
...
@@ -20,10 +21,11 @@ def test_publish_publishes_to_pypi_by_default(fixture_dir, mocker):
assert
[(
"https://upload.pypi.org/legacy/"
,)]
==
uploader_upload
.
call_args
assert
[(
"https://upload.pypi.org/legacy/"
,)]
==
uploader_upload
.
call_args
def
test_publish_can_publish_to_given_repository
(
fixture_dir
,
mocker
):
def
test_publish_can_publish_to_given_repository
(
fixture_dir
,
mocker
,
config
):
uploader_auth
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.auth"
)
uploader_auth
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.auth"
)
uploader_upload
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.upload"
)
uploader_upload
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.upload"
)
poetry
=
Poetry
.
create
(
fixture_dir
(
"sample_project"
))
poetry
=
Poetry
.
create
(
fixture_dir
(
"sample_project"
))
poetry
.
_config
=
config
poetry
.
config
.
merge
(
poetry
.
config
.
merge
(
{
{
"repositories"
:
{
"my-repo"
:
{
"url"
:
"http://foo.bar"
}},
"repositories"
:
{
"my-repo"
:
{
"url"
:
"http://foo.bar"
}},
...
@@ -38,8 +40,9 @@ def test_publish_can_publish_to_given_repository(fixture_dir, mocker):
...
@@ -38,8 +40,9 @@ def test_publish_can_publish_to_given_repository(fixture_dir, mocker):
assert
[(
"http://foo.bar"
,)]
==
uploader_upload
.
call_args
assert
[(
"http://foo.bar"
,)]
==
uploader_upload
.
call_args
def
test_publish_raises_error_for_undefined_repository
(
fixture_dir
,
mocker
):
def
test_publish_raises_error_for_undefined_repository
(
fixture_dir
,
mocker
,
config
):
poetry
=
Poetry
.
create
(
fixture_dir
(
"sample_project"
))
poetry
=
Poetry
.
create
(
fixture_dir
(
"sample_project"
))
poetry
.
_config
=
config
poetry
.
config
.
merge
(
poetry
.
config
.
merge
(
{
"http-basic"
:
{
"my-repo"
:
{
"username"
:
"foo"
,
"password"
:
"bar"
}}}
{
"http-basic"
:
{
"my-repo"
:
{
"username"
:
"foo"
,
"password"
:
"bar"
}}}
)
)
...
@@ -47,3 +50,17 @@ def test_publish_raises_error_for_undefined_repository(fixture_dir, mocker):
...
@@ -47,3 +50,17 @@ def test_publish_raises_error_for_undefined_repository(fixture_dir, mocker):
with
pytest
.
raises
(
RuntimeError
):
with
pytest
.
raises
(
RuntimeError
):
publisher
.
publish
(
"my-repo"
,
None
,
None
)
publisher
.
publish
(
"my-repo"
,
None
,
None
)
def
test_publish_uses_token_if_it_exists
(
fixture_dir
,
mocker
,
config
):
uploader_auth
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.auth"
)
uploader_upload
=
mocker
.
patch
(
"poetry.masonry.publishing.uploader.Uploader.upload"
)
poetry
=
Poetry
.
create
(
fixture_dir
(
"sample_project"
))
poetry
.
_config
=
config
poetry
.
config
.
merge
({
"pypi-token"
:
{
"pypi"
:
"my-token"
}})
publisher
=
Publisher
(
poetry
,
NullIO
())
publisher
.
publish
(
None
,
None
,
None
)
assert
[(
"@token"
,
"my-token"
)]
==
uploader_auth
.
call_args
assert
[(
"https://upload.pypi.org/legacy/"
,)]
==
uploader_upload
.
call_args
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