Commit 61e132e1 authored by SVN-Git Migration's avatar SVN-Git Migration

Imported Upstream version 1.8.1

parent 5eeb4712
......@@ -9,3 +9,10 @@ Jannis Leidel <jannis@leidel.info>
Ralf Schmitt <ralf@systemexit.de>
Ian Cordasco <graffatcolmingov@gmail.com>
Marc Abramowitz <msabramo@gmail.com> (http://marc-abramowitz.com/)
Tom Myers <tom.stephen.myers@gmail.com>
Rodrigue Cloutier <rodcloutier@gmail.com>
Tyrel Souza <tyrelsouza@gmail.com> (https://tyrelsouza.com)
Adam Talsma <adam@talsma.ca>
Jens Diemer <github@jensdiemer.de> (http://jensdiemer.de/)
Andrew Watts <andrewwatts@gmail.com>
Anna Martelli Ravenscroft <annaraven@gmail.com>
Metadata-Version: 1.1
Name: twine
Version: 1.5.0
Version: 1.8.1
Summary: Collection of utilities for interacting with PyPI
Home-page: https://github.com/pypa/twine
Author: Donald Stufft and individual contributors
......@@ -11,17 +11,18 @@ Description: twine
Twine is a utility for interacting `with PyPI <https://pypi.python.org/pypi/twine>`_.
Currently it only supports uploading distributions.
Currently it only supports registering projects and uploading distributions.
Why Should I Use This?
----------------------
The biggest reason to use twine is that it securely authenticates you to PyPI
over HTTPS using a verified connection while ``python setup.py upload`` `uses
HTTP and exposes your credentials <http://bugs.python.org/issue12226>`_. This
means anytime you use it you expose your username and password to being
sniffed. Twine uses only verified TLS to upload to PyPI protecting your
over HTTPS using a verified connection while ``python setup.py upload`` `only
recently stopped using HTTP <http://bugs.python.org/issue12226>`_ in Python
2.7.9+ and Python 3.2+. This means anytime you use ``python setup.py upload``
with an older Python version, you expose your username and password to being
easily sniffed. Twine uses only verified TLS to upload to PyPI protecting your
credentials from theft.
Secondly it allows you to precreate your distribution files.
......@@ -61,17 +62,29 @@ Description: twine
1. Create some distributions in the normal way:
.. code-block:: bash
.. code-block:: bash
$ python setup.py sdist bdist_wheel
$ python setup.py sdist bdist_wheel
2. Upload with twine:
2. Register your project (if necessary):
.. code-block:: bash
.. code-block:: bash
$ # One needs to be explicit here, globbing dist/* would fail.
$ twine register dist/project_name-x.y.z.tar.gz
$ twine register dist/mypkg-0.1-py2.py3-none-any.whl
3. Upload with twine [#]_:
.. code-block:: bash
$ twine upload dist/*
.. [#] If you see the following error while uploading to PyPI, it probably means you need to register (see step 2)::
$ twine upload dist/*
$ HTTPError: 403 Client Error: You are not allowed to edit 'xyz' package information
3. Done!
4. Done!
Options
......@@ -80,8 +93,10 @@ Description: twine
.. code-block:: bash
$ twine upload -h
usage: twine upload [-h] [-r REPOSITORY] [-s] [-i IDENTITY] [-u USERNAME]
[-p PASSWORD] [-c COMMENT]
usage: twine upload [-h] [-r REPOSITORY] [-s] [--sign-with SIGN_WITH]
[-i IDENTITY] [-u USERNAME] [-p PASSWORD] [-c COMMENT]
[--config-file CONFIG_FILE] [--skip-existing]
dist [dist ...]
positional arguments:
......@@ -92,8 +107,10 @@ Description: twine
optional arguments:
-h, --help show this help message and exit
-r REPOSITORY, --repository REPOSITORY
The repository to upload the files to
The repository to upload the files to (default: pypi)
-s, --sign Sign files to upload using gpg
--sign-with SIGN_WITH
GPG program used to sign uploads (default: gpg)
-i IDENTITY, --identity IDENTITY
GPG identity used to sign files
-u USERNAME, --username USERNAME
......@@ -102,6 +119,9 @@ Description: twine
The password to authenticate to the repository with
-c COMMENT, --comment COMMENT
The comment to include with the distribution file
--config-file CONFIG_FILE
The .pypirc config file to use
--skip-existing Continue uploading files if one already exists
Resources
......@@ -110,7 +130,7 @@ Description: twine
* `IRC <http://webchat.freenode.net?channels=%23pypa>`_
(``#pypa`` - irc.freenode.net)
* `GitHub repository <https://github.com/pypa/twine>`_
* `Python Packaging User Guide <https://packaging.python.org/en/latest/distributing/>`_
Contributing
------------
......@@ -132,6 +152,15 @@ Description: twine
If you'd like to have a development environment for twine, you should create a
virtualenv and then do ``pip install -e .`` from within the directory.
Code of Conduct
---------------
Everyone interacting in the twine project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_.
.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
......@@ -143,11 +172,11 @@ Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
......@@ -3,17 +3,18 @@ twine
Twine is a utility for interacting `with PyPI <https://pypi.python.org/pypi/twine>`_.
Currently it only supports uploading distributions.
Currently it only supports registering projects and uploading distributions.
Why Should I Use This?
----------------------
The biggest reason to use twine is that it securely authenticates you to PyPI
over HTTPS using a verified connection while ``python setup.py upload`` `uses
HTTP and exposes your credentials <http://bugs.python.org/issue12226>`_. This
means anytime you use it you expose your username and password to being
sniffed. Twine uses only verified TLS to upload to PyPI protecting your
over HTTPS using a verified connection while ``python setup.py upload`` `only
recently stopped using HTTP <http://bugs.python.org/issue12226>`_ in Python
2.7.9+ and Python 3.2+. This means anytime you use ``python setup.py upload``
with an older Python version, you expose your username and password to being
easily sniffed. Twine uses only verified TLS to upload to PyPI protecting your
credentials from theft.
Secondly it allows you to precreate your distribution files.
......@@ -53,17 +54,29 @@ Usage
1. Create some distributions in the normal way:
.. code-block:: bash
.. code-block:: bash
$ python setup.py sdist bdist_wheel
$ python setup.py sdist bdist_wheel
2. Upload with twine:
2. Register your project (if necessary):
.. code-block:: bash
.. code-block:: bash
$ # One needs to be explicit here, globbing dist/* would fail.
$ twine register dist/project_name-x.y.z.tar.gz
$ twine register dist/mypkg-0.1-py2.py3-none-any.whl
3. Upload with twine [#]_:
.. code-block:: bash
$ twine upload dist/*
.. [#] If you see the following error while uploading to PyPI, it probably means you need to register (see step 2)::
$ twine upload dist/*
$ HTTPError: 403 Client Error: You are not allowed to edit 'xyz' package information
3. Done!
4. Done!
Options
......@@ -72,8 +85,10 @@ Options
.. code-block:: bash
$ twine upload -h
usage: twine upload [-h] [-r REPOSITORY] [-s] [-i IDENTITY] [-u USERNAME]
[-p PASSWORD] [-c COMMENT]
usage: twine upload [-h] [-r REPOSITORY] [-s] [--sign-with SIGN_WITH]
[-i IDENTITY] [-u USERNAME] [-p PASSWORD] [-c COMMENT]
[--config-file CONFIG_FILE] [--skip-existing]
dist [dist ...]
positional arguments:
......@@ -84,8 +99,10 @@ Options
optional arguments:
-h, --help show this help message and exit
-r REPOSITORY, --repository REPOSITORY
The repository to upload the files to
The repository to upload the files to (default: pypi)
-s, --sign Sign files to upload using gpg
--sign-with SIGN_WITH
GPG program used to sign uploads (default: gpg)
-i IDENTITY, --identity IDENTITY
GPG identity used to sign files
-u USERNAME, --username USERNAME
......@@ -94,6 +111,9 @@ Options
The password to authenticate to the repository with
-c COMMENT, --comment COMMENT
The comment to include with the distribution file
--config-file CONFIG_FILE
The .pypirc config file to use
--skip-existing Continue uploading files if one already exists
Resources
......@@ -102,7 +122,7 @@ Resources
* `IRC <http://webchat.freenode.net?channels=%23pypa>`_
(``#pypa`` - irc.freenode.net)
* `GitHub repository <https://github.com/pypa/twine>`_
* `Python Packaging User Guide <https://packaging.python.org/en/latest/distributing/>`_
Contributing
------------
......@@ -123,3 +143,12 @@ Contributing
If you'd like to have a development environment for twine, you should create a
virtualenv and then do ``pip install -e .`` from within the directory.
Code of Conduct
---------------
Everyone interacting in the twine project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_.
.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/
......@@ -4,6 +4,142 @@
Changelog
=========
* :release:`1.8.1 <2016-08-09>`
* Check if a package exists if the URL is one of:
- ``https://pypi.python.org/pypi/``
- ``https://upload.pypi.org/``
- ``https://upload.pypi.io/``
This helps people with ``https://upload.pypi.io`` still in their .pypirc
file.
* :release:`1.8.0 <2016-08-08>`
* :feature:`201` Switch from upload.pypi.io to upload.pypi.org.
* :feature:`144` Retrieve configuration from the environment as a default.
- Repository URL will default to ``TWINE_REPOSITORY``
- Username will default to ``TWINE_USERNAME``
- Password will default to ``TWINE_PASSWORD``
* :feature:`166` Allow the Repository URL to be provided on the command-line
(``--repository-url``) or via an environment variable
(``TWINE_REPOSITORY_URL``).
* Generate SHA256 digest for all packages by default.
* :feature:`171` Generate Blake2b 256 digests for packages *if* ``pyblake2``
is installed. Users can use ``python -m pip install twine[with-blake2]``
to have ``pyblake2`` installed with Twine.
* Stop testing on Python 2.6. 2.6 support will be "best effort" until 2.0.0
* Warn users if they receive a 500 error when uploading to \*pypi.python.org
* :release:`1.7.4 <2016-07-09>`
* Correct a packaging error.
* :release:`1.7.3 <2016-07-08>`
* :bug:`195` Fix uploads to instances of pypiserver using
``--skip-existing``. We were not properly checking the return status code
on the response after attempting an upload.
* Do not generate traffic to Legacy PyPI unless we're uploading to it or
uploading to Warehouse (e.g., pypi.io). This avoids the attempt to upload
a package to the index if we can find it on Legacy PyPI already.
* :release:`1.7.2 <2016-07-05>`
* :bug:`189`, :bug:`191` Fix issue where we were checking the existence of
packages even if the user didn't specify ``--skip-existing``.
* :release:`1.7.1 <2016-07-05>`
* :bug:`187` Clint was not specified in the wheel metadata as a dependency.
* :release:`1.7.0 <2016-07-04>`
* :feature:`142` Support ``--cert`` and ``--client-cert`` command-line flags
and config file options for feature parity with pip. This allows users to
verify connections to servers other than PyPI (e.g., local package
repositories) with different certificates.
* :feature:`152` Add progress bar to uploads.
* :feature:`162` Allow ``--skip-existing`` to work for 409 status codes.
* :feature:`167` Implement retries when the CDN in front of PyPI gives us a
5xx error.
* :feature:`177` Switch Twine to upload to pypi.io instead of
pypi.python.org.
* :bug:`186` Allow passwords to have ``%``\ s in them.
* :release:`1.6.5 <2015-12-16>`
* :bug:`155` Bump requests-toolbelt version to ensure we avoid
ConnectionErrors
* :release:`1.6.4 <2015-10-27>`
* :bug:`145` Paths with hyphens in them break the Wheel regular expression.
* :bug:`146` Exception while accessing the ``respository`` key when raising
a redirect exception.
* :release:`1.6.3 <2015-10-05>`
* :bug:`137`, :bug:`140` Uploading signatures was broken due to the pull
request that added large file support via ``requests-toolbelt``. This
caused a 500 error on PyPI and prevented package and signature upload in
twine 1.6.0
* :release:`1.6.2 <2015-09-28>`
* :bug:`132` Upload signatures with packages appropriately
As part of the refactor for the 1.6.0 release, we were using the wrong
name to find the signature file.
This also uncovered a bug where if you're using twine in a situation where
``*`` is not expanded by your shell, we might also miss uploading
signatures to PyPI. Both were fixed as part of this.
* :release:`1.6.1 <2015-09-18>`
* :bug:`130` Fix signing support for uploads
* :release:`1.6.0 <2015-09-14>`
* :feature:`106` Upload wheels first to PyPI
* :feature:`104` Large file support via the ``requests-toolbelt``
* :bug:`92` Raise an exception on redirects
* :feature:`97` Allow the user to specify the location of their ``.pypirc``
* :feature:`115` Add the ``--skip-existing`` flag to ``twine upload`` to
allow users to skip releases that already exist on PyPI.
* :bug:`114` Warnings triggered by pkginfo searching for ``PKG-INFO`` files
should no longer be user visible.
* :bug:`116` Work around problems with Windows when using
:func:`getpass.getpass`
* :bug:`111` Provide more helpful messages if ``.pypirc`` is out of date.
* :feature:`8` Support registering new packages with ``twine register``
* :release:`1.5.0 <2015-03-10>`
* :bug:`85` Display information about the version of setuptools installed
......
......@@ -12,14 +12,14 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# import sys
# import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath(os.pardir))
# sys.path.insert(0, os.path.abspath(os.pardir))
import twine
......@@ -107,7 +107,7 @@ pygments_style = "sphinx"
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = "default"
html_theme = "sphinx_rtd_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
......
......@@ -8,9 +8,13 @@ ignore =
[metadata]
requires-dist =
requests
pkginfo
clint
requests >= 2.5.0
requests-toolbelt >= 0.5.1
pkginfo >= 1.0
setuptools >= 0.7.0
argparse; python_version == '2.6'
pyblake2; extra == 'with-blake2'
[egg_info]
tag_build =
......
......@@ -19,8 +19,10 @@ import twine
install_requires = [
"pkginfo",
"requests >= 2.0",
"clint",
"pkginfo >= 1.0",
"requests >= 2.5.0",
"requests-toolbelt >= 0.5.1",
"setuptools >= 0.7.0",
]
......@@ -53,12 +55,12 @@ setup(
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],
......@@ -68,6 +70,7 @@ setup(
entry_points={
"twine.registered_commands": [
"upload = twine.commands.upload:main",
"register = twine.commands.register:main",
],
"console_scripts": [
"twine = twine.__main__:main",
......@@ -75,4 +78,9 @@ setup(
},
install_requires=install_requires,
extras_require={
'with-blake2': [
'pyblake2',
]
},
)
# Copyright 2016 Ian Cordasco
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Test functions useful across twine's tests."""
import contextlib
import os
@contextlib.contextmanager
def set_env(**environ):
"""Set the process environment variables temporarily.
>>> with set_env(PLUGINS_DIR=u'test/plugins'):
... "PLUGINS_DIR" in os.environ
True
>>> "PLUGINS_DIR" in os.environ
False
:param environ: Environment variables to set
:type environ: dict[str, unicode]
"""
old_environ = dict(os.environ)
os.environ.update(environ)
try:
yield
finally:
os.environ.clear()
os.environ.update(old_environ)
# Copyright 2015 Ian Cordasco
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import unicode_literals
from twine import package
import pretend
def test_sign_file(monkeypatch):
replaced_check_call = pretend.call_recorder(lambda args: None)
monkeypatch.setattr(package.subprocess, 'check_call', replaced_check_call)
filename = 'tests/fixtures/deprecated-pypirc'
pkg = package.PackageFile(
filename=filename,
comment=None,
metadata=pretend.stub(name="deprecated-pypirc"),
python_version=None,
filetype=None
)
try:
pkg.sign('gpg2', None)
except IOError:
pass
args = ('gpg2', '--detach-sign', '-a', filename)
assert replaced_check_call.calls == [pretend.call(args)]
def test_sign_file_with_identity(monkeypatch):
replaced_check_call = pretend.call_recorder(lambda args: None)
monkeypatch.setattr(package.subprocess, 'check_call', replaced_check_call)
filename = 'tests/fixtures/deprecated-pypirc'
pkg = package.PackageFile(
filename=filename,
comment=None,
metadata=pretend.stub(name="deprecated-pypirc"),
python_version=None,
filetype=None
)
try:
pkg.sign('gpg', 'identity')
except IOError:
pass
args = ('gpg', '--detach-sign', '--local-user', 'identity', '-a', filename)
assert replaced_check_call.calls == [pretend.call(args)]
def test_package_signed_name_is_correct():
filename = 'tests/fixtures/deprecated-pypirc'
pkg = package.PackageFile(
filename=filename,
comment=None,
metadata=pretend.stub(name="deprecated-pypirc"),
python_version=None,
filetype=None
)
assert pkg.signed_basefilename == "deprecated-pypirc.asc"
assert pkg.signed_filename == (filename + '.asc')
# Copyright 2015 Ian Cordasco
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from twine import repository
def test_gpg_signature_structure_is_preserved():
data = {
'gpg_signature': ('filename.asc', 'filecontent'),
}
tuples = repository.Repository._convert_data_to_list_of_tuples(data)
assert tuples == [('gpg_signature', ('filename.asc', 'filecontent'))]
def test_content_structure_is_preserved():
data = {
'content': ('filename', 'filecontent'),
}
tuples = repository.Repository._convert_data_to_list_of_tuples(data)
assert tuples == [('content', ('filename', 'filecontent'))]
def test_iterables_are_flattened():
data = {
'platform': ['UNKNOWN'],
}
tuples = repository.Repository._convert_data_to_list_of_tuples(data)
assert tuples == [('platform', 'UNKNOWN')]
data = {
'platform': ['UNKNOWN', 'ANOTHERPLATFORM'],
}
tuples = repository.Repository._convert_data_to_list_of_tuples(data)
assert tuples == [('platform', 'UNKNOWN'),
('platform', 'ANOTHERPLATFORM')]
def test_set_client_certificate():
repo = repository.Repository(
repository_url='https://pypi.python.org/pypi',
username='username',
password='password',
)
assert repo.session.cert is None
repo.set_client_certificate(('/path/to/cert', '/path/to/key'))
assert repo.session.cert == ('/path/to/cert', '/path/to/key')
def test_set_certificate_authority():
repo = repository.Repository(
repository_url='https://pypi.python.org/pypi',
username='username',
password='password',
)
assert repo.session.verify is True
repo.set_certificate_authority('/path/to/cert')
assert repo.session.verify == '/path/to/cert'
def test_make_user_agent_string():
repo = repository.Repository(
repository_url='https://pypi.python.org/pypi',
username='username',
password='password',
)
assert 'User-Agent' in repo.session.headers
user_agent = repo.session.headers['User-Agent']
assert 'twine/' in user_agent
assert 'requests/' in user_agent
assert 'requests-toolbelt/' in user_agent
assert 'pkginfo/' in user_agent
assert 'setuptools/' in user_agent
......@@ -11,15 +11,47 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import unicode_literals
import os
import textwrap
import pretend
import pytest
from twine.commands import upload
from twine import package, cli
import twine
import helpers
WHEEL_FIXTURE = 'tests/fixtures/twine-1.5.0-py2.py3-none-any.whl'
def test_ensure_wheel_files_uploaded_first():
files = upload.group_wheel_files_first(["twine/foo.py",
"twine/first.whl",
"twine/bar.py",
"twine/second.whl"])
expected = ["twine/first.whl",
"twine/second.whl",
"twine/foo.py",
"twine/bar.py"]
assert expected == files
def test_ensure_if_no_wheel_files():
files = upload.group_wheel_files_first(["twine/foo.py",
"twine/bar.py"])
expected = ["twine/foo.py",
"twine/bar.py"]
assert expected == files
def test_find_dists_expands_globs():
files = sorted(upload.find_dists(['twine/__*.py']))
expected = ['twine/__init__.py', 'twine/__main__.py']
expected = [os.path.join('twine', '__init__.py'),
os.path.join('twine', '__main__.py')]
assert expected == files
......@@ -35,20 +67,85 @@ def test_find_dists_handles_real_files():
assert expected == files
def test_sign_file(monkeypatch):
replaced_check_call = pretend.call_recorder(lambda args: None)
monkeypatch.setattr(upload.subprocess, 'check_call', replaced_check_call)
def test_get_config_old_format(tmpdir):
pypirc = os.path.join(str(tmpdir), ".pypirc")
dists = ["tests/fixtures/twine-1.5.0-py2.py3-none-any.whl"]
with open(pypirc, "w") as fp:
fp.write(textwrap.dedent("""
[server-login]
username:foo
password:bar
"""))
try:
upload.upload(dists=dists, repository="pypi", sign=None, identity=None,
username=None, password=None, comment=None,
cert=None, client_cert=None,
sign_with=None, config_file=pypirc, skip_existing=False,
repository_url=None,
)
except KeyError as err:
assert err.args[0]