Hello community,

here is the log from the commit of package python3-twine for openSUSE:Factory 
checked in at 2016-07-15 12:51:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python3-twine (Old)
 and      /work/SRC/openSUSE:Factory/.python3-twine.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python3-twine"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python3-twine/python3-twine.changes      
2016-06-02 09:36:09.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python3-twine.new/python3-twine.changes 
2016-07-15 12:51:11.000000000 +0200
@@ -1,0 +2,48 @@
+Sat Jul  9 15:29:42 UTC 2016 - [email protected]
+
+- update to version 1.7.4:
+  * Correct a packaging error.
+
+-------------------------------------------------------------------
+Fri Jul  8 23:37:34 UTC 2016 - [email protected]
+
+- update to version 1.7.3:
+  * :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.
+
+-------------------------------------------------------------------
+Thu Jul  7 06:33:53 UTC 2016 - [email protected]
+
+- update to version 1.7.2:
+  * :bug:`189`, :bug:`191` Fix issue where we were checking the
+    existence of packages even if the user didn't specify
+    --skip-existing.
+
+-------------------------------------------------------------------
+Wed Jul  6 07:22:05 UTC 2016 - [email protected]
+
+- update to version 1.7.1:
+  * Clint was not specified in the wheel metadata as a dependency.
+
+-------------------------------------------------------------------
+Tue Jul  5 01:16:19 UTC 2016 - [email protected]
+
+- update to version 1.7.0:
+  * :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.
+
+-------------------------------------------------------------------
@@ -12 +59,0 @@
-

Old:
----
  twine-1.6.5.tar.gz

New:
----
  twine-1.7.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python3-twine.spec ++++++
--- /var/tmp/diff_new_pack.75tzsH/_old  2016-07-15 12:51:12.000000000 +0200
+++ /var/tmp/diff_new_pack.75tzsH/_new  2016-07-15 12:51:12.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python3-twine
-Version:        1.6.5
+Version:        1.7.4
 Release:        0
 Summary:        Collection of utilities for interacting with PyPI
 License:        Apache-2.0

++++++ twine-1.6.5.tar.gz -> twine-1.7.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/AUTHORS new/twine-1.7.4/AUTHORS
--- old/twine-1.6.5/AUTHORS     2015-09-09 03:16:47.000000000 +0200
+++ new/twine-1.7.4/AUTHORS     2016-06-15 03:16:21.000000000 +0200
@@ -14,3 +14,5 @@
 Tyrel Souza <[email protected]> (https://tyrelsouza.com)
 Adam Talsma <[email protected]>
 Jens Diemer <[email protected]> (http://jensdiemer.de/)
+Andrew Watts <[email protected]>
+Anna Martelli Ravenscroft <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/PKG-INFO new/twine-1.7.4/PKG-INFO
--- old/twine-1.6.5/PKG-INFO    2015-12-17 00:36:32.000000000 +0100
+++ new/twine-1.7.4/PKG-INFO    2016-07-09 13:22:23.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: twine
-Version: 1.6.5
+Version: 1.7.4
 Summary: Collection of utilities for interacting with PyPI
 Home-page: https://github.com/pypa/twine
 Author: Donald Stufft and individual contributors
@@ -11,7 +11,7 @@
         
         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?
@@ -62,17 +62,29 @@
         
         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/*
         
-            $ twine upload dist/*
+           .. [#] If you see the following error while uploading to PyPI, it 
probably means you need to register (see step 2)::
         
-        3. Done!
+                     $ HTTPError: 403 Client Error: You are not allowed to 
edit 'xyz' package information
+        
+        4. Done!
         
         
         Options
@@ -81,8 +93,10 @@
         .. 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:
@@ -93,8 +107,10 @@
             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
@@ -103,8 +119,9 @@
                                     The password to authenticate to the 
repository with
               -c COMMENT, --comment COMMENT
                                     The comment to include with the 
distribution file
-              --config-file FILE    
+              --config-file CONFIG_FILE
                                     The .pypirc config file to use
+              --skip-existing       Continue uploading files if one already 
exists
         
         
         Resources
@@ -113,7 +130,7 @@
         * `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
         ------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/README.rst new/twine-1.7.4/README.rst
--- old/twine-1.6.5/README.rst  2015-09-13 22:06:52.000000000 +0200
+++ new/twine-1.7.4/README.rst  2016-06-15 03:16:21.000000000 +0200
@@ -3,7 +3,7 @@
 
 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?
@@ -54,17 +54,29 @@
 
 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/*
 
-    $ twine upload dist/*
+   .. [#] If you see the following error while uploading to PyPI, it probably 
means you need to register (see step 2)::
 
-3. Done!
+             $ HTTPError: 403 Client Error: You are not allowed to edit 'xyz' 
package information
+
+4. Done!
 
 
 Options
@@ -73,8 +85,10 @@
 .. 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:
@@ -85,8 +99,10 @@
     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
@@ -95,8 +111,9 @@
                             The password to authenticate to the repository with
       -c COMMENT, --comment COMMENT
                             The comment to include with the distribution file
-      --config-file FILE    
+      --config-file CONFIG_FILE
                             The .pypirc config file to use
+      --skip-existing       Continue uploading files if one already exists
 
 
 Resources
@@ -105,7 +122,7 @@
 * `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
 ------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/docs/changelog.rst 
new/twine-1.7.4/docs/changelog.rst
--- old/twine-1.6.5/docs/changelog.rst  2015-12-17 00:34:22.000000000 +0100
+++ new/twine-1.7.4/docs/changelog.rst  2016-07-09 13:21:25.000000000 +0200
@@ -4,6 +4,48 @@
 Changelog
 =========
 
+* :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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/docs/conf.py new/twine-1.7.4/docs/conf.py
--- old/twine-1.6.5/docs/conf.py        2015-04-16 06:50:41.000000000 +0200
+++ new/twine-1.7.4/docs/conf.py        2016-06-15 03:16:21.000000000 +0200
@@ -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
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/setup.cfg new/twine-1.7.4/setup.cfg
--- old/twine-1.6.5/setup.cfg   2015-12-17 00:36:32.000000000 +0100
+++ new/twine-1.7.4/setup.cfg   2016-07-09 13:22:23.000000000 +0200
@@ -8,8 +8,9 @@
 
 [metadata]
 requires-dist = 
-       requests >= 2.3.0
-       requests-toolbelt >= 0.4.0
+       clint
+       requests >= 2.5.0
+       requests-toolbelt >= 0.5.1
        pkginfo >= 1.0
        setuptools >= 0.7.0
        argparse; python_version == '2.6'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/setup.py new/twine-1.7.4/setup.py
--- old/twine-1.6.5/setup.py    2015-12-17 00:32:09.000000000 +0100
+++ new/twine-1.7.4/setup.py    2016-06-15 03:16:21.000000000 +0200
@@ -19,8 +19,9 @@
 
 
 install_requires = [
+    "clint",
     "pkginfo >= 1.0",
-    "requests >= 2.3.0",
+    "requests >= 2.5.0",
     "requests-toolbelt >= 0.5.1",
     "setuptools >= 0.7.0",
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/tests/test_repository.py 
new/twine-1.7.4/tests/test_repository.py
--- old/twine-1.6.5/tests/test_repository.py    2015-12-17 00:32:47.000000000 
+0100
+++ new/twine-1.7.4/tests/test_repository.py    2016-07-04 21:45:58.000000000 
+0200
@@ -47,3 +47,46 @@
     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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/tests/test_upload.py 
new/twine-1.7.4/tests/test_upload.py
--- old/twine-1.6.5/tests/test_upload.py        2015-12-17 00:32:47.000000000 
+0100
+++ new/twine-1.7.4/tests/test_upload.py        2016-06-15 03:16:21.000000000 
+0200
@@ -78,6 +78,7 @@
     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)
     except KeyError as err:
         assert err.args[0] == (
@@ -94,6 +95,19 @@
         reason='A file named "twine-1.5.0-py2.py3-none-any.whl" already '
                'exists for twine-1.5.0.')
 
+    pkg = package.PackageFile.from_filename(WHEEL_FIXTURE, None)
+    assert upload.skip_upload(response=response,
+                              skip_existing=True,
+                              package=pkg) is True
+
+
+def test_skip_existing_skips_files_already_on_pypiserver(monkeypatch):
+    # pypiserver (https://pypi.python.org/pypi/pypiserver) responds with 409
+    response = pretend.stub(
+        status_code=409,
+        reason='A file named "twine-1.5.0-py2.py3-none-any.whl" already '
+               'exists for twine-1.5.0.')
+
     pkg = package.PackageFile.from_filename(WHEEL_FIXTURE, None)
     assert upload.skip_upload(response=response,
                               skip_existing=True,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine/__init__.py 
new/twine-1.7.4/twine/__init__.py
--- old/twine-1.6.5/twine/__init__.py   2015-12-17 00:34:33.000000000 +0100
+++ new/twine-1.7.4/twine/__init__.py   2016-07-09 13:21:34.000000000 +0200
@@ -23,7 +23,7 @@
 __summary__ = "Collection of utilities for interacting with PyPI"
 __uri__ = "https://github.com/pypa/twine";
 
-__version__ = "1.6.5"
+__version__ = "1.7.4"
 
 __author__ = "Donald Stufft and individual contributors"
 __email__ = "[email protected]"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine/cli.py new/twine-1.7.4/twine/cli.py
--- old/twine-1.6.5/twine/cli.py        2015-09-14 15:56:03.000000000 +0200
+++ new/twine-1.7.4/twine/cli.py        2016-07-04 21:45:58.000000000 +0200
@@ -18,7 +18,9 @@
 import pkg_resources
 import setuptools
 
+import clint
 import requests
+import requests_toolbelt
 import pkginfo
 
 import twine
@@ -30,13 +32,20 @@
     return dict((c.name, c) for c in registered_commands)
 
 
+def list_dependencies_and_versions():
+    return [
+        ('pkginfo', Installed(pkginfo).version),
+        ('requests', requests.__version__),
+        ('setuptools', setuptools.__version__),
+        ('requests-toolbelt', requests_toolbelt.__version__),
+        ('clint', clint.__version__),
+    ]
+
+
 def dep_versions():
-    return 'pkginfo: {0}, requests: {1}, setuptools: {2}'.format(
-        Installed(pkginfo).version,
-        # __version__ is always defined but requests does not always have
-        # PKG-INFO to read from
-        requests.__version__,
-        setuptools.__version__,
+    return ', '.join(
+        '{0}: {1}'.format(*dependency)
+        for dependency in list_dependencies_and_versions()
     )
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine/commands/register.py 
new/twine-1.7.4/twine/commands/register.py
--- old/twine-1.6.5/twine/commands/register.py  2015-12-17 00:32:47.000000000 
+0100
+++ new/twine-1.7.4/twine/commands/register.py  2015-12-17 00:37:02.000000000 
+0100
@@ -23,7 +23,8 @@
 from twine import utils
 
 
-def register(package, repository, username, password, comment, config_file):
+def register(package, repository, username, password, comment, config_file,
+             cert, client_cert):
     config = utils.get_repository_from_config(config_file, repository)
     config["repository"] = utils.normalize_repository_url(
         config["repository"]
@@ -33,8 +34,12 @@
 
     username = utils.get_username(username, config)
     password = utils.get_password(password, config)
+    ca_cert = utils.get_cacert(cert, config)
+    client_cert = utils.get_clientcert(client_cert, config)
 
     repository = Repository(config["repository"], username, password)
+    repository.set_certificate_authority(ca_cert)
+    repository.set_client_certificate(client_cert)
 
     if not os.path.exists(package):
         raise exc.PackageNotFound(
@@ -79,6 +84,17 @@
         help="The .pypirc config file to use",
     )
     parser.add_argument(
+        "--cert",
+        metavar="path",
+        help="Path to alternate CA bundle",
+    )
+    parser.add_argument(
+        "--client-cert",
+        metavar="path",
+        help="Path to SSL client certificate, a single file containing the "
+             "private key and the certificate in PEM forma",
+    )
+    parser.add_argument(
         "package",
         metavar="package",
         help="File from which we read the package metadata",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine/commands/upload.py 
new/twine-1.7.4/twine/commands/upload.py
--- old/twine-1.6.5/twine/commands/upload.py    2015-12-17 00:32:47.000000000 
+0100
+++ new/twine-1.7.4/twine/commands/upload.py    2016-07-09 13:20:09.000000000 
+0200
@@ -55,14 +55,24 @@
 
 def skip_upload(response, skip_existing, package):
     filename = package.basefilename
-    msg = 'A file named "{0}" already exists for'.format(filename)
-    return (response.status_code == 400 and
-            response.reason.startswith(msg) and
-            skip_existing)
+    # NOTE(sigmavirus24): Old PyPI returns the first message while Warehouse
+    # returns the latter. This papers over the differences.
+    msg = ('A file named "{0}" already exists for'.format(filename),
+           'File already exists')
+    # NOTE(sigmavirus24): PyPI presently returns a 400 status code with the
+    # error message in the reason attribute. Other implementations return a
+    # 409 status code. We only want to skip an upload if:
+    # 1. The user has told us to skip existing packages (skip_existing is
+    #    True) AND
+    # 2. a) The response status code is 409 OR
+    # 2. b) The response status code is 400 AND it has a reason that matches
+    #       what we expect PyPI to return to us.
+    return (skip_existing and (response.status_code == 409 or
+            (response.status_code == 400 and response.reason.startswith(msg))))
 
 
 def upload(dists, repository, sign, identity, username, password, comment,
-           sign_with, config_file, skip_existing):
+           sign_with, config_file, skip_existing, cert, client_cert):
     # Check that a nonsensical option wasn't given
     if not sign and identity:
         raise ValueError("sign must be given along with identity")
@@ -85,11 +95,26 @@
 
     username = utils.get_username(username, config)
     password = utils.get_password(password, config)
+    ca_cert = utils.get_cacert(cert, config)
+    client_cert = utils.get_clientcert(client_cert, config)
 
     repository = Repository(config["repository"], username, password)
+    repository.set_certificate_authority(ca_cert)
+    repository.set_client_certificate(client_cert)
 
     for filename in uploads:
         package = PackageFile.from_filename(filename, comment)
+        skip_message = (
+            "  Skipping {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):
+            print(skip_message)
+            continue
 
         signed_name = package.signed_basefilename
         if signed_name in signatures:
@@ -109,10 +134,8 @@
                  ' Aborting...').format(config["repository"],
                                         resp.headers["location"]))
 
-        # Otherwise, raise an HTTPError based on the status code.
         if skip_upload(resp, skip_existing, package):
-            print("  Skipping {0} because it appears to already exist".format(
-                package.basefilename))
+            print(skip_message)
             continue
 
         resp.raise_for_status()
@@ -165,7 +188,20 @@
         "--skip-existing",
         default=False,
         action="store_true",
-        help="Continue uploading files if one already exists",
+        help="Continue uploading files if one already exists. (Only valid "
+             "when uploading to PyPI. Other implementations may not support "
+             "this.)",
+    )
+    parser.add_argument(
+        "--cert",
+        metavar="path",
+        help="Path to alternate CA bundle",
+    )
+    parser.add_argument(
+        "--client-cert",
+        metavar="path",
+        help="Path to SSL client certificate, a single file containing the "
+             "private key and the certificate in PEM forma",
     )
     parser.add_argument(
         "dists",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine/repository.py 
new/twine-1.7.4/twine/repository.py
--- old/twine-1.6.5/twine/repository.py 2015-12-17 00:32:47.000000000 +0100
+++ new/twine-1.7.4/twine/repository.py 2016-07-09 13:20:09.000000000 +0200
@@ -13,18 +13,50 @@
 # limitations under the License.
 from __future__ import absolute_import, unicode_literals, print_function
 
+from clint.textui.progress import Bar as ProgressBar
+
 import requests
-from requests_toolbelt.multipart import MultipartEncoder
+from requests import adapters
+from requests import codes
+from requests.packages.urllib3 import util
+from requests_toolbelt.multipart import (
+    MultipartEncoder, MultipartEncoderMonitor
+)
+from requests_toolbelt.utils.user_agent import user_agent
 
+import twine
 
 KEYWORDS_TO_NOT_FLATTEN = set(["gpg_signature", "content"])
 
+LEGACY_PYPI = 'https://pypi.python.org/'
+WAREHOUSE = 'https://upload.pypi.io/'
+
 
 class Repository(object):
     def __init__(self, repository_url, username, password):
         self.url = repository_url
         self.session = requests.session()
         self.session.auth = (username, password)
+        self.session.headers['User-Agent'] = self._make_user_agent_string()
+        for scheme in ('http://', 'https://'):
+            self.session.mount(scheme, self._make_adapter_with_retries())
+        self._releases_json_data = {}
+
+    @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)
+
+    @staticmethod
+    def _make_user_agent_string():
+        from twine import cli
+        dependencies = cli.list_dependencies_and_versions()
+        return user_agent('twine', twine.__version__, extras=dependencies)
 
     def close(self):
         self.session.close()
@@ -41,6 +73,14 @@
                     data_to_send.append((key, item))
         return data_to_send
 
+    def set_certificate_authority(self, cacert):
+        if cacert:
+            self.session.verify = cacert
+
+    def set_client_certificate(self, clientcert):
+        if clientcert:
+            self.session.cert = clientcert
+
     def register(self, package):
         data = package.metadata_dictionary()
         data.update({
@@ -62,7 +102,7 @@
         resp.close()
         return resp
 
-    def upload(self, package):
+    def _upload(self, package):
         data = package.metadata_dictionary()
         data.update({
             # action
@@ -80,12 +120,70 @@
                 (package.basefilename, fp, "application/octet-stream"),
             ))
             encoder = MultipartEncoder(data_to_send)
+            bar = ProgressBar(expected_size=encoder.len, filled_char='=')
+            monitor = MultipartEncoderMonitor(
+                encoder, lambda monitor: bar.show(monitor.bytes_read)
+            )
 
             resp = self.session.post(
                 self.url,
-                data=encoder,
+                data=monitor,
                 allow_redirects=False,
-                headers={'Content-Type': encoder.content_type},
+                headers={'Content-Type': monitor.content_type},
             )
+            bar.done()
 
         return resp
+
+    def upload(self, package, max_redirects=5):
+        number_of_redirects = 0
+        while number_of_redirects < max_redirects:
+            resp = self._upload(package)
+
+            if resp.status_code == codes.OK:
+                return resp
+            if 500 <= resp.status_code < 600:
+                number_of_redirects += 1
+                print('Received "{status_code}: {reason}" Package upload '
+                      'appears to have failed.  Retry {retry} of 5'.format(
+                          status_code=resp.status_code,
+                          reason=resp.reason,
+                          retry=number_of_redirects,
+                      ))
+            else:
+                return resp
+
+        return resp
+
+    def package_is_uploaded(self, package, bypass_cache=False):
+        # NOTE(sigmavirus24): Not all indices are PyPI and pypi.io doesn't
+        # have a similar interface for finding the package versions.
+        if not self.url.startswith((LEGACY_PYPI, WAREHOUSE)):
+            return False
+
+        safe_name = package.safe_name
+        releases = None
+
+        if not bypass_cache:
+            releases = self._releases_json_data.get(safe_name)
+
+        if releases is None:
+            url = '{url}pypi/{package}/json'.format(package=safe_name,
+                                                    url=LEGACY_PYPI)
+            headers = {'Accept': 'application/json'}
+            response = self.session.get(url, headers=headers)
+            releases = response.json()['releases']
+            self._releases_json_data[safe_name] = releases
+
+        packages = releases.get(package.metadata.version, [])
+
+        for uploaded_package in packages:
+            if uploaded_package['filename'] == package.basefilename:
+                return True
+
+        return False
+
+    def verify_package_integrity(self, package):
+        # TODO(sigmavirus24): Add a way for users to download the package and
+        # check it's hash against what it has locally.
+        pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine/utils.py 
new/twine-1.7.4/twine/utils.py
--- old/twine-1.6.5/twine/utils.py      2015-12-17 00:32:47.000000000 +0100
+++ new/twine-1.7.4/twine/utils.py      2016-07-04 21:45:58.000000000 +0200
@@ -37,7 +37,7 @@
     input_func = raw_input
 
 
-DEFAULT_REPOSITORY = "https://pypi.python.org/pypi";
+DEFAULT_REPOSITORY = "https://upload.pypi.io/legacy/";
 
 
 def get_config(path="~/.pypirc"):
@@ -52,12 +52,12 @@
                 }
 
     # Parse the rc file
-    parser = configparser.ConfigParser()
+    parser = configparser.RawConfigParser()
     parser.read(path)
 
     # Get a list of repositories from the config file
-    if (parser.has_section("distutils")
-            and parser.has_option("distutils", "index-servers")):
+    if (parser.has_section("distutils") and
+            parser.has_option("distutils", "index-servers")):
         repositories = parser.get("distutils", "index-servers").split()
     else:
         repositories = ["pypi"]
@@ -116,14 +116,15 @@
     return urlunparse(parsed)
 
 
-def get_userpass_value(cli_value, config, key, prompt_strategy):
+def get_userpass_value(cli_value, config, key, prompt_strategy=None):
     """Gets the username / password from config.
 
     Uses the following rules:
 
     1. If it is specified on the cli (`cli_value`), use that.
     2. If `config[key]` is specified, use that.
-    3. Otherwise prompt using `prompt_strategy`.
+    3. If `prompt_strategy`, prompt using `prompt_strategy`.
+    4. Otherwise return None
 
     :param cli_value: The value supplied from the command line or `None`.
     :type cli_value: unicode or `None`
@@ -140,8 +141,10 @@
         return cli_value
     elif config.get(key):
         return config[key]
-    else:
+    elif prompt_strategy:
         return prompt_strategy()
+    else:
+        return None
 
 
 def password_prompt(prompt_text):  # Always expects unicode for our own sanity
@@ -161,3 +164,11 @@
     key='password',
     prompt_strategy=password_prompt('Enter your password: '),
 )
+get_cacert = functools.partial(
+    get_userpass_value,
+    key='ca_cert',
+)
+get_clientcert = functools.partial(
+    get_userpass_value,
+    key='client_cert',
+)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine.egg-info/PKG-INFO 
new/twine-1.7.4/twine.egg-info/PKG-INFO
--- old/twine-1.6.5/twine.egg-info/PKG-INFO     2015-12-17 00:36:32.000000000 
+0100
+++ new/twine-1.7.4/twine.egg-info/PKG-INFO     2016-07-09 13:22:22.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: twine
-Version: 1.6.5
+Version: 1.7.4
 Summary: Collection of utilities for interacting with PyPI
 Home-page: https://github.com/pypa/twine
 Author: Donald Stufft and individual contributors
@@ -11,7 +11,7 @@
         
         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?
@@ -62,17 +62,29 @@
         
         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/*
         
-            $ twine upload dist/*
+           .. [#] If you see the following error while uploading to PyPI, it 
probably means you need to register (see step 2)::
         
-        3. Done!
+                     $ HTTPError: 403 Client Error: You are not allowed to 
edit 'xyz' package information
+        
+        4. Done!
         
         
         Options
@@ -81,8 +93,10 @@
         .. 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:
@@ -93,8 +107,10 @@
             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
@@ -103,8 +119,9 @@
                                     The password to authenticate to the 
repository with
               -c COMMENT, --comment COMMENT
                                     The comment to include with the 
distribution file
-              --config-file FILE    
+              --config-file CONFIG_FILE
                                     The .pypirc config file to use
+              --skip-existing       Continue uploading files if one already 
exists
         
         
         Resources
@@ -113,7 +130,7 @@
         * `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
         ------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/twine-1.6.5/twine.egg-info/requires.txt 
new/twine-1.7.4/twine.egg-info/requires.txt
--- old/twine-1.6.5/twine.egg-info/requires.txt 2015-12-17 00:36:32.000000000 
+0100
+++ new/twine-1.7.4/twine.egg-info/requires.txt 2016-07-09 13:22:22.000000000 
+0200
@@ -1,4 +1,5 @@
+clint
 pkginfo >= 1.0
-requests >= 2.3.0
+requests >= 2.5.0
 requests-toolbelt >= 0.5.1
 setuptools >= 0.7.0


Reply via email to