diff -Nru twine-1.10.0/AUTHORS twine-1.11.0/AUTHORS --- twine-1.10.0/AUTHORS 2018-01-29 15:17:16.000000000 +0000 +++ twine-1.11.0/AUTHORS 2018-03-18 18:04:08.000000000 +0000 @@ -17,3 +17,4 @@ Andrew Watts Anna Martelli Ravenscroft Sumana Harihareswara +Dustin Ingram (https://di.codes) diff -Nru twine-1.10.0/debian/changelog twine-1.11.0/debian/changelog --- twine-1.10.0/debian/changelog 2018-03-17 00:13:20.000000000 +0000 +++ twine-1.11.0/debian/changelog 2018-03-20 05:57:54.000000000 +0000 @@ -1,3 +1,13 @@ +twine (1.11.0-1) unstable; urgency=medium + + * New upstream release. + * Update copyright. + * Install README.rst too, due to the filesystem structure, it wasn't + included in our rst directory. (Closes: #827568, again) + * Bump pkginfo dependency (following upstream). + + -- Stefano Rivera Mon, 19 Mar 2018 22:57:54 -0700 + twine (1.10.0-1) unstable; urgency=medium [ Stefano Rivera ] diff -Nru twine-1.10.0/debian/control twine-1.11.0/debian/control --- twine-1.10.0/debian/control 2018-03-17 00:13:20.000000000 +0000 +++ twine-1.11.0/debian/control 2018-03-20 05:57:54.000000000 +0000 @@ -9,7 +9,7 @@ dh-python, python3-all, python3-pkg-resources, - python3-pkginfo (>= 1.0), + python3-pkginfo (>= 1.4.2), python3-releases (>= 0.6.1), python3-requests (>= 2.3.0), python3-setuptools (>= 0.7), diff -Nru twine-1.10.0/debian/copyright twine-1.11.0/debian/copyright --- twine-1.10.0/debian/copyright 2018-03-17 00:13:20.000000000 +0000 +++ twine-1.11.0/debian/copyright 2018-03-20 05:57:54.000000000 +0000 @@ -5,10 +5,11 @@ Files: * Copyright: Donald Stufft - Jannis Leidel - Ralf Schmitt + Dustin Ingram (https://di.codes) Ian Cordasco + Jannis Leidel Marc Abramowitz + Ralf Schmitt Sumana Harihareswara License: APACHE-2.0 Licensed under the Apache License, Version 2.0 (the "License"); diff -Nru twine-1.10.0/debian/docs twine-1.11.0/debian/docs --- twine-1.10.0/debian/docs 2018-03-17 00:13:20.000000000 +0000 +++ twine-1.11.0/debian/docs 2018-03-20 05:57:54.000000000 +0000 @@ -1 +1,2 @@ +README.rst build/sphinx/html diff -Nru twine-1.10.0/docs/changelog.rst twine-1.11.0/docs/changelog.rst --- twine-1.10.0/docs/changelog.rst 2018-03-07 20:49:39.000000000 +0000 +++ twine-1.11.0/docs/changelog.rst 2018-03-19 23:24:08.000000000 +0000 @@ -4,6 +4,19 @@ Changelog ========= +* :release:`1.11.0 <2018-03-19>` +* :bug:`269 major` Avoid uploading to PyPI when given alternate + repository URL, and require ``http://`` or ``https://`` in + ``repository_url``. +* :support:`277` Add instructions on how to use keyring. +* :support:`314` Add new maintainer, release checklists. +* :bug:`322 major` Raise exception if attempting upload to deprecated legacy + PyPI URLs. +* :feature:`320` Remove PyPI as default ``register`` package index. +* :feature:`319` Support Metadata 2.1 (:pep:`566`), including Markdown + for ``description`` fields. +* :support:`318` `Update PyPI URLs + `_. * :release:`1.10.0 <2018-03-07>` * :bug:`315 major` Degrade gracefully when keyring is unavailable * :feature:`304` Reorganize & improve user & developer documentation. diff -Nru twine-1.10.0/docs/conf.py twine-1.11.0/docs/conf.py --- twine-1.10.0/docs/conf.py 2018-03-07 20:31:56.000000000 +0000 +++ twine-1.11.0/docs/conf.py 2018-03-19 16:58:11.000000000 +0000 @@ -277,4 +277,4 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"http://docs.python.org/": None} +intersphinx_mapping = {"https://docs.python.org/": None} diff -Nru twine-1.10.0/docs/contributing.rst twine-1.11.0/docs/contributing.rst --- twine-1.10.0/docs/contributing.rst 2018-03-07 20:37:01.000000000 +0000 +++ twine-1.11.0/docs/contributing.rst 2018-03-18 18:04:08.000000000 +0000 @@ -12,14 +12,14 @@ --------------- We recommend you use a development environment. Using a ``virtualenv`` -keeps your development environment isolated, so that ``twine`` and its -dependencies do not interfere with packages already installed on your +keeps your development environment isolated, so ``twine`` and its +dependencies do not interfere with other packages installed on your machine. You can use `virtualenv`_ or `pipenv`_ to isolate your development environment. -Clone the twine repository from GitHub, and then make and activate -your virtual environment, using Python 3.6 as the Python version in -the virtual environment. Example: +Clone the twine repository from GitHub, and then make and activate a +virtual environment that uses Python 3.6 as the default +Python. Example: .. code-block:: console @@ -32,7 +32,7 @@ pip install -e /path/to/your/local/twine Now, in your virtual environment, ``twine`` is pointing at your local copy, so -when you have made changes, you can easily see their effect. +when you make changes, you can easily see their effect. Building the documentation ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -51,8 +51,9 @@ may need the `tox-pipenv`_ plugin so that tox can use pipenv environments instead of virtualenvs. -To build the docs locally using ``tox``, activate your virtual -environment, then run: +After making docs changes, lint and build the docs locally, using +``tox``, before making a pull request. Activate your virtual +environment, then, in the root directory, run: .. code-block:: console @@ -60,13 +61,6 @@ The HTML of the docs will be visible in :file:`twine/docs/_build/`. -When you have made your changes to the docs, please lint them before making a -pull request. To run the linter from the root directory: - -.. code-block:: console - - doc8 docs - Testing ^^^^^^^ @@ -86,29 +80,152 @@ 1. Fork `the GitHub repository`_. 2. Make a branch off of ``master`` and commit your changes to it. -3. Run the tests with ``tox`` and lint any docs changes with ``doc8``. +3. Run the tests with ``tox`` and lint any docs changes with ``tox -e docs``. 4. Ensure that your name is added to the end of the :file:`AUTHORS` file using the format ``Name (url)``, where the ``(url)`` portion is optional. -5. Submit a Pull Request to the ``master`` branch on GitHub. +5. Submit a pull request to the ``master`` branch on GitHub. Architectural overview ---------------------- Twine is a command-line tool for interacting with PyPI securely over -HTTPS. Its command line arguments are parsed in -:file:`twine/cli.py`. Currently, twine has two principal functions: -uploading new packages and registering new `projects`_. The code for -registering new projects is in :file:`twine/commands/register.py`, and -the code for uploading is in :file:`twine/commands/upload.py`. The -file :file:`twine/package.py` contains a single class, -``PackageFile``, which hashes the project files and extracts their -metadata. The file :file:`twine/repository.py` contains the -``Repository`` class, whose methods control the URL the package is -uploaded to (which the user can specify either as a default, in the -:file:`.pypirc` file, or pass on the command line), and the methods -that upload the package securely to a URL. +HTTPS. Its three purposes are to be: + +1. A user-facing tool for publishing on pypi.org +2. A user-facing tool for publishing on other Python package indexes + (e.g., ``devpi`` instances) +3. A useful API for other programs (e.g., ``zest.releaser``) to call + for publishing on any Python package index + + +Currently, twine has two principal functions: uploading new packages +and registering new `projects`_ (``register`` is no longer supported +on PyPI, and is in Twine for use with other package indexes). + +Its command line arguments are parsed in :file:`twine/cli.py`. The +code for registering new projects is in +:file:`twine/commands/register.py`, and the code for uploading is in +:file:`twine/commands/upload.py`. The file :file:`twine/package.py` +contains a single class, ``PackageFile``, which hashes the project +files and extracts their metadata. The file +:file:`twine/repository.py` contains the ``Repository`` class, whose +methods control the URL the package is uploaded to (which the user can +specify either as a default, in the :file:`.pypirc` file, or pass on +the command line), and the methods that upload the package securely to +a URL. + +Where Twine gets configuration and credentials +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A user can set the repository URL, username, and/or password via +command line, ``.pypirc`` files, environment variables, and +``keyring``. + + +Adding a maintainer +------------------- + +A checklist for adding a new maintainer to the project. + +#. Add her as a Member in the GitHub repo settings. (This will also + give her privileges on the `Travis CI project + `_.) +#. Get her Test PyPI and canon PyPI usernames and add her as a + Maintainer on `our Test PyPI project + `_ and + `canon PyPI + `_. + + +Making a new release +-------------------- + +A checklist for creating, testing, and distributing a new version. + +#. Choose a version number, e.g., "1.15." + +#. Merge the last planned PR before the new release: + + #. Add new changes to :file:`docs/changelog.rst`. + #. Update the ``__version__`` string in :file:`twine/__init__.py`, + which is where :file:`setup.py` pulls it from, with ``{number}rc1`` + for "release candidate 1". + #. Update copyright dates. + +#. Run Twine tests: + + #. ``tox -e py{27,34,35,36,py}`` + #. ``tox -e pep8`` for the linter + #. ``tox -e docs`` (this checks the Sphinx docs and uses + ``readme_renderer`` to check that the ``long_description`` and other + metadata will render fine on the PyPI description) + +#. Run integration tests with downstreams: + + #. Test ``pypiserver`` support: + + .. code-block:: console + + git clone git@github.com:pypiserver/pypiserver + cd pypiserver + tox -e pre_twine + + #. Create a test package to upload to Test PyPI, version-control it + with git, and test ``zest.releaser`` per directions in `this + comment + `_. + #. Test ``devpi`` support: + + .. code-block:: console + + pip install devpi-client + devpi use https://m.devpi.net + devpi user -c {username} password={password} + devpi login {username} --password={password} + devpi index -c testpypi type=mirror mirror_url=https://test.pypi.org/simple/ + devpi use {username}/testpypi + python setup.py sdist + twine upload --repository-url https://m.devpi.net/{username}/testpypi/ dist/{testpackage}.tar.gz + +#. Create a git tag with ``git tag -sam 'Release v{number}' {number}``. + + * ``{number}``, such as ``1.15.1rc1`` + * ``-s`` signs it with your PGP key + * ``-a`` creates an annotated tag for GitHub + * ``-m`` adds the message; optional if you want to compose a longer + message + +#. View your tag: ``git tag -v {number}`` +#. Push your tag: ``git push upstream {number}``. +#. Delete old distributions: ``rm dist/*``. +#. Create distributions with ``python setup.py sdist bdist_wheel``. +#. Set your TestPyPI and canon PyPI credentials in your session with + ``keyring`` (docs forthcoming). +#. Upload to Test PyPI: :command:`twine upload --repository-url + https://test.pypi.org/legacy/ --skip-existing dist/*` +#. Verify that everything looks good, downloads ok, etc. Make needed fixes. +#. Merge the last PR before the new release: + + #. Add new changes and new release to :file:`docs/changelog.rst`, + with the new version ``{number}``, this time without the + ``rc1`` suffix. + #. Update the ``__version__`` string in :file:`twine/__init__.py` + with ``{number}``. + +#. Run tests again. Check the changelog to verify that it looks right. +#. Create a new git tag with ``git tag -sam 'Release v{number}' {number}``. +#. View your tag: ``git tag -v {number}`` +#. Push your tag: ``git push upstream {number}``. +#. Delete old distributions: ``rm dist/*``. +#. Create distributions with ``python setup.py sdist bdist_wheel``. +#. On a Monday or Tuesday, upload to canon PyPI: :command:`twine + upload --skip-existing dist/*` + + .. note:: Will be replaced by ``tox -e release`` at some point. +#. Send announcement email to `pypa-dev mailing list`_ and celebrate. + Future development ------------------ @@ -126,7 +243,7 @@ .. _`virtualenv`: https://virtualenv.pypa.io/en/stable/installation/ .. _`pipenv`: https://pipenv.readthedocs.io/en/latest/ .. _`tox`: https://tox.readthedocs.io/en/latest/ -.. _`tox-pipenv`: https://pypi.python.org/pypi/tox-pipenv +.. _`tox-pipenv`: https://pypi.org/project/tox-pipenv .. _`plugin`: https://github.com/bitprophet/releases .. _`projects`: https://packaging.python.org/glossary/#term-project .. _`open issues`: https://github.com/pypa/twine/issues diff -Nru twine-1.10.0/PKG-INFO twine-1.11.0/PKG-INFO --- twine-1.10.0/PKG-INFO 2018-03-07 20:54:12.000000000 +0000 +++ twine-1.11.0/PKG-INFO 2018-03-19 23:30:58.000000000 +0000 @@ -1,15 +1,14 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: twine -Version: 1.10.0 +Version: 1.11.0 Summary: Collection of utilities for publishing packages on PyPI -Home-page: http://twine.readthedocs.io/ +Home-page: https://twine.readthedocs.io/ Author: Donald Stufft and individual contributors Author-email: donald@stufft.io License: Apache License, Version 2.0 Project-URL: Packaging tutorial, https://packaging.python.org/tutorials/distributing-packages/ -Project-URL: Twine source, https://github.com/pypa/twine/ Project-URL: Twine documentation, https://twine.readthedocs.io/en/latest/ -Description-Content-Type: UNKNOWN +Project-URL: Twine source, https://github.com/pypa/twine/ Description: twine ===== @@ -26,13 +25,14 @@ The goal of ``twine`` is to improve PyPI interaction by improving security and testability. - 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`` `only - recently stopped using HTTP `_ 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. + 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`` `only recently stopped using HTTP + `_ 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. ``python setup.py upload`` only allows you to upload something that you've @@ -40,12 +40,12 @@ exact file you're going to upload to PyPI to ensure that it works before uploading it. - Finally, it allows you to pre-sign your files and pass the ``.asc`` - files into the command line invocation (``twine upload - twine-1.0.1.tar.gz twine-1.0.1.tar.gz.asc``). This enables you to be - assured that you're typing your ``gpg`` passphrase into ``gpg`` itself - and not anything else, since *you* will be the one directly executing - ``gpg --detach-sign -a ``. + Finally, ``twine`` allows you to pre-sign your files and pass the + ``.asc`` files into the command line invocation (``twine upload + myproject-1.0.1.tar.gz myproject-1.0.1.tar.gz.asc``). This enables you + to be assured that you're typing your ``gpg`` passphrase into ``gpg`` + itself and not anything else, since *you* will be the one directly + executing ``gpg --detach-sign -a ``. Features @@ -55,7 +55,7 @@ - Uploading doesn't require executing ``setup.py`` - Uploading files that have already been created, allowing testing of distributions before release - - Supports uploading any packaging format (including wheels) + - Supports uploading any packaging format (including `wheels`_) Installation @@ -66,8 +66,8 @@ $ pip install twine - Usage - ----- + Using Twine + ----------- 1. Create some distributions in the normal way: @@ -75,20 +75,78 @@ $ python setup.py sdist bdist_wheel - 2. Upload with ``twine``: + 2. Upload with ``twine`` to `Test PyPI`_ and verify things look right. Twine will automatically prompt for your username and password: + + .. code-block:: console + + $ twine upload --repository-url https://test.pypi.org/legacy/ dist/* + username: ... + password: + ... + + 3. Upload to `PyPI`_: .. code-block:: console $ twine upload dist/* - 3. Done! + 4. Done! More documentation on using ``twine`` to upload packages to PyPI is in the `Python Packaging User Guide`_. + Keyring Support + --------------- + + Instead of typing in your password every time you upload a distribution, Twine + allows you to store your username and password securely using `keyring`_. + + To use the keyring, you must first install the keyring packages: + + - On Windows and MacOS you just need to install ``keyring``, for example, + ``pip install --user keyring``. + - On Linux, in addition to the ``keyring`` package you also need to ensure the + ``python3-dbus`` system package is installed. For example, ``apt install + python3-dbus``. See `Keyring's installation instructions`_ for more details. + + Once keyring is installed you can use the ``keyring`` program to set your + username and password to use for each package index (repository) you want to + upload to using Twine. + + To set your username and password for test PyPI run the following command. + ``keyring`` will prompt you for your password: + + .. code-block:: console + + $ keyring set https://test.pypi.org/legacy/ your-username + # or + $ python3 -m keyring set https://test.pypi.org/legacy/ your-username + + To set your username and password for PyPI run this command, again, ``keyring`` + will prompt for the password: + + .. code-block:: console + + $ keyring set https://upload.pypi.org/legacy/ your-username + # or + $ python3 -m keyring set https://upload.pypi.org/legacy/ your-username + + + The next time you run ``twine`` it will prompt you for a username and will grab the appropriate password from the keyring. + + .. Note:: If you are using Linux in a headless environment (such as on a + server) you'll need to do some additional steps to ensure that Keyring can + store secrets securely. See `Using Keyring on headless systems`_. + + .. _`keyring`: https://pypi.org/project/keyring/ + .. _`Keyring's installation instructions`: + https://keyring.readthedocs.io/en/latest#installation-instructions + .. _`Using Keyring on headless systems`: + https://keyring.readthedocs.io/en/latest/#using-keyring-on-headless-linux-systems + Options - ^^^^^^^ + ------- .. code-block:: console @@ -101,48 +159,47 @@ dist [dist ...] positional arguments: - dist The distribution files to upload to the repository, - may additionally contain a .asc file to include an - existing signature with the file upload + dist The distribution files to upload to the repository + (package index). Usually dist/* . May additionally + contain a .asc file to include an existing signature + with the file upload. optional arguments: -h, --help show this help message and exit -r REPOSITORY, --repository REPOSITORY - The repository to upload the package to. Can be a - section in the config file or a full URL to the - repository (default: pypi). (Can also be set via - TWINE_REPOSITORY environment variable) + The repository (package index) to upload the package + to. Should be a section in the config file (default: + pypi). (Can also be set via TWINE_REPOSITORY + environment variable.) --repository-url REPOSITORY_URL - The repository URL to upload the package to. This can - be specified with --repository because it will be used - if there is no configuration for the value passed to - --repository. (Can also be set via - TWINE_REPOSITORY_URL environment variable.) - -s, --sign Sign files to upload using gpg + The repository (package index) URL to upload the + package to. This overrides --repository. (Can also be + set via TWINE_REPOSITORY_URL environment variable.) + -s, --sign Sign files to upload using GPG. --sign-with SIGN_WITH - GPG program used to sign uploads (default: gpg) + GPG program used to sign uploads (default: gpg). -i IDENTITY, --identity IDENTITY - GPG identity used to sign files + GPG identity used to sign files. -u USERNAME, --username USERNAME - The username to authenticate to the repository as (can - also be set via TWINE_USERNAME environment variable) + The username to authenticate to the repository + (package index) as. (Can also be set via + TWINE_USERNAME environment variable.) -p PASSWORD, --password PASSWORD - The password to authenticate to the repository with - (can also be set via TWINE_PASSWORD environment - variable) + The password to authenticate to the repository + (package index) with. (Can also be set via + TWINE_PASSWORD environment variable.) -c COMMENT, --comment COMMENT - The comment to include with the distribution file + The comment to include with the distribution file. --config-file CONFIG_FILE - The .pypirc config file to use + The .pypirc config file to use. --skip-existing Continue uploading files if one already exists. (Only valid when uploading to PyPI. Other implementations may not support this.) --cert path Path to alternate CA bundle (can also be set via - TWINE_CERT environment variable) + TWINE_CERT environment variable). --client-cert path Path to SSL client certificate, a single file containing the private key and the certificate in PEM - format - + format. Twine also includes a ``register`` command. @@ -160,45 +217,47 @@ .. code-block:: console $ twine register -h - usage: twine register [-h] [-r REPOSITORY] [--repository-url REPOSITORY_URL] + + usage: twine register [-h] -r REPOSITORY [--repository-url REPOSITORY_URL] [-u USERNAME] [-p PASSWORD] [-c COMMENT] [--config-file CONFIG_FILE] [--cert path] [--client-cert path] package positional arguments: - package File from which we read the package metadata + package File from which we read the package metadata. optional arguments: -h, --help show this help message and exit -r REPOSITORY, --repository REPOSITORY - The repository to register the package to. Can be a - section in the config file or a full URL to the - repository (default: pypi). (Can also be set via - TWINE_REPOSITORY environment variable) + The repository (package index) to register the package + to. Should be a section in the config file. (Can also + be set via TWINE_REPOSITORY environment variable.) + Initial package registration no longer necessary on + pypi.org: + https://packaging.python.org/guides/migrating-to-pypi- + org/ --repository-url REPOSITORY_URL - The repository URL to upload the package to. This can - be specified with --repository because it will be used - if there is no configuration for the value passed to - --repository. (Can also be set via - TWINE_REPOSITORY_URL environment variable.) + The repository (package index) URL to register the + package to. This overrides --repository. (Can also be + set via TWINE_REPOSITORY_URL environment variable.) -u USERNAME, --username USERNAME - The username to authenticate to the repository as (can - also be set via TWINE_USERNAME environment variable) + The username to authenticate to the repository + (package index) as. (Can also be set via + TWINE_USERNAME environment variable.) -p PASSWORD, --password PASSWORD - The password to authenticate to the repository with - (can also be set via TWINE_PASSWORD environment - variable) + The password to authenticate to the repository + (package index) with. (Can also be set via + TWINE_PASSWORD environment variable.) -c COMMENT, --comment COMMENT - The comment to include with the distribution file + The comment to include with the distribution file. --config-file CONFIG_FILE - The .pypirc config file to use + The .pypirc config file to use. --cert path Path to alternate CA bundle (can also be set via - TWINE_CERT environment variable) + TWINE_CERT environment variable). --client-cert path Path to SSL client certificate, a single file containing the private key and the certificate in PEM - format - + format. Environment Variables ^^^^^^^^^^^^^^^^^^^^^ @@ -209,18 +268,18 @@ it is not convenient to create a `.pypirc` file, such as a CI/build server, for example. - * ``TWINE_USERNAME`` - the username to use for authentication to the repository - * ``TWINE_PASSWORD`` - the password to use for authentication to the repository + * ``TWINE_USERNAME`` - the username to use for authentication to the repository. + * ``TWINE_PASSWORD`` - the password to use for authentication to the repository. * ``TWINE_REPOSITORY`` - the repository configuration, either defined as a - section in `.pypirc` or provided as a full URL - * ``TWINE_REPOSITORY_URL`` - the repository URL to use + section in `.pypirc` or provided as a full URL. + * ``TWINE_REPOSITORY_URL`` - the repository URL to use. * ``TWINE_CERT`` - custom CA certificate to use for repositories with - self-signed or untrusted certificates + self-signed or untrusted certificates. Resources --------- - * `IRC `_ + * `IRC `_ (``#pypa`` - irc.freenode.net) * `GitHub repository `_ * User and developer `documentation`_ @@ -242,13 +301,15 @@ .. _`a utility`: https://pypi.org/project/twine/ .. _`publishing`: https://packaging.python.org/tutorials/distributing-packages/ .. _`PyPI`: https://pypi.org + .. _`Test PyPI`: https://packaging.python.org/guides/using-testpypi/ .. _`Python Packaging User Guide`: https://packaging.python.org/tutorials/distributing-packages/ - .. _`documentation`: http://twine.readthedocs.io/ + .. _`documentation`: https://twine.readthedocs.io/ .. _`developer documentation`: https://twine.readthedocs.io/en/latest/contributing.html .. _`projects`: https://packaging.python.org/glossary/#term-project .. _`distributions`: https://packaging.python.org/glossary/#term-distribution-package .. _`PyPA Code of Conduct`: https://www.pypa.io/en/latest/code-of-conduct/ .. _`Warehouse`: https://github.com/pypa/warehouse + .. _`wheels`: https://packaging.python.org/glossary/#term-wheel Platform: UNKNOWN Classifier: Intended Audience :: Developers @@ -269,3 +330,5 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +Provides-Extra: keyring +Provides-Extra: with-blake2 diff -Nru twine-1.10.0/README.rst twine-1.11.0/README.rst --- twine-1.10.0/README.rst 2018-03-02 12:07:16.000000000 +0000 +++ twine-1.11.0/README.rst 2018-03-19 16:58:11.000000000 +0000 @@ -14,13 +14,14 @@ The goal of ``twine`` is to improve PyPI interaction by improving security and testability. -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`` `only -recently stopped using HTTP `_ 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. +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`` `only recently stopped using HTTP +`_ 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. ``python setup.py upload`` only allows you to upload something that you've @@ -28,12 +29,12 @@ exact file you're going to upload to PyPI to ensure that it works before uploading it. -Finally, it allows you to pre-sign your files and pass the ``.asc`` -files into the command line invocation (``twine upload -twine-1.0.1.tar.gz twine-1.0.1.tar.gz.asc``). This enables you to be -assured that you're typing your ``gpg`` passphrase into ``gpg`` itself -and not anything else, since *you* will be the one directly executing -``gpg --detach-sign -a ``. +Finally, ``twine`` allows you to pre-sign your files and pass the +``.asc`` files into the command line invocation (``twine upload +myproject-1.0.1.tar.gz myproject-1.0.1.tar.gz.asc``). This enables you +to be assured that you're typing your ``gpg`` passphrase into ``gpg`` +itself and not anything else, since *you* will be the one directly +executing ``gpg --detach-sign -a ``. Features @@ -43,7 +44,7 @@ - Uploading doesn't require executing ``setup.py`` - Uploading files that have already been created, allowing testing of distributions before release -- Supports uploading any packaging format (including wheels) +- Supports uploading any packaging format (including `wheels`_) Installation @@ -54,8 +55,8 @@ $ pip install twine -Usage ------ +Using Twine +----------- 1. Create some distributions in the normal way: @@ -63,20 +64,78 @@ $ python setup.py sdist bdist_wheel -2. Upload with ``twine``: +2. Upload with ``twine`` to `Test PyPI`_ and verify things look right. Twine will automatically prompt for your username and password: + + .. code-block:: console + + $ twine upload --repository-url https://test.pypi.org/legacy/ dist/* + username: ... + password: + ... + +3. Upload to `PyPI`_: .. code-block:: console $ twine upload dist/* -3. Done! +4. Done! More documentation on using ``twine`` to upload packages to PyPI is in the `Python Packaging User Guide`_. +Keyring Support +--------------- + +Instead of typing in your password every time you upload a distribution, Twine +allows you to store your username and password securely using `keyring`_. + +To use the keyring, you must first install the keyring packages: + +- On Windows and MacOS you just need to install ``keyring``, for example, + ``pip install --user keyring``. +- On Linux, in addition to the ``keyring`` package you also need to ensure the + ``python3-dbus`` system package is installed. For example, ``apt install + python3-dbus``. See `Keyring's installation instructions`_ for more details. + +Once keyring is installed you can use the ``keyring`` program to set your +username and password to use for each package index (repository) you want to +upload to using Twine. + +To set your username and password for test PyPI run the following command. +``keyring`` will prompt you for your password: + +.. code-block:: console + + $ keyring set https://test.pypi.org/legacy/ your-username + # or + $ python3 -m keyring set https://test.pypi.org/legacy/ your-username + +To set your username and password for PyPI run this command, again, ``keyring`` +will prompt for the password: + +.. code-block:: console + + $ keyring set https://upload.pypi.org/legacy/ your-username + # or + $ python3 -m keyring set https://upload.pypi.org/legacy/ your-username + + +The next time you run ``twine`` it will prompt you for a username and will grab the appropriate password from the keyring. + +.. Note:: If you are using Linux in a headless environment (such as on a + server) you'll need to do some additional steps to ensure that Keyring can + store secrets securely. See `Using Keyring on headless systems`_. + +.. _`keyring`: https://pypi.org/project/keyring/ +.. _`Keyring's installation instructions`: + https://keyring.readthedocs.io/en/latest#installation-instructions +.. _`Using Keyring on headless systems`: + https://keyring.readthedocs.io/en/latest/#using-keyring-on-headless-linux-systems + Options -^^^^^^^ +------- .. code-block:: console @@ -89,48 +148,47 @@ dist [dist ...] positional arguments: - dist The distribution files to upload to the repository, - may additionally contain a .asc file to include an - existing signature with the file upload + dist The distribution files to upload to the repository + (package index). Usually dist/* . May additionally + contain a .asc file to include an existing signature + with the file upload. optional arguments: -h, --help show this help message and exit -r REPOSITORY, --repository REPOSITORY - The repository to upload the package to. Can be a - section in the config file or a full URL to the - repository (default: pypi). (Can also be set via - TWINE_REPOSITORY environment variable) + The repository (package index) to upload the package + to. Should be a section in the config file (default: + pypi). (Can also be set via TWINE_REPOSITORY + environment variable.) --repository-url REPOSITORY_URL - The repository URL to upload the package to. This can - be specified with --repository because it will be used - if there is no configuration for the value passed to - --repository. (Can also be set via - TWINE_REPOSITORY_URL environment variable.) - -s, --sign Sign files to upload using gpg + The repository (package index) URL to upload the + package to. This overrides --repository. (Can also be + set via TWINE_REPOSITORY_URL environment variable.) + -s, --sign Sign files to upload using GPG. --sign-with SIGN_WITH - GPG program used to sign uploads (default: gpg) + GPG program used to sign uploads (default: gpg). -i IDENTITY, --identity IDENTITY - GPG identity used to sign files + GPG identity used to sign files. -u USERNAME, --username USERNAME - The username to authenticate to the repository as (can - also be set via TWINE_USERNAME environment variable) + The username to authenticate to the repository + (package index) as. (Can also be set via + TWINE_USERNAME environment variable.) -p PASSWORD, --password PASSWORD - The password to authenticate to the repository with - (can also be set via TWINE_PASSWORD environment - variable) + The password to authenticate to the repository + (package index) with. (Can also be set via + TWINE_PASSWORD environment variable.) -c COMMENT, --comment COMMENT - The comment to include with the distribution file + The comment to include with the distribution file. --config-file CONFIG_FILE - The .pypirc config file to use + The .pypirc config file to use. --skip-existing Continue uploading files if one already exists. (Only valid when uploading to PyPI. Other implementations may not support this.) --cert path Path to alternate CA bundle (can also be set via - TWINE_CERT environment variable) + TWINE_CERT environment variable). --client-cert path Path to SSL client certificate, a single file containing the private key and the certificate in PEM - format - + format. Twine also includes a ``register`` command. @@ -148,45 +206,47 @@ .. code-block:: console $ twine register -h - usage: twine register [-h] [-r REPOSITORY] [--repository-url REPOSITORY_URL] + + usage: twine register [-h] -r REPOSITORY [--repository-url REPOSITORY_URL] [-u USERNAME] [-p PASSWORD] [-c COMMENT] [--config-file CONFIG_FILE] [--cert path] [--client-cert path] package positional arguments: - package File from which we read the package metadata + package File from which we read the package metadata. optional arguments: -h, --help show this help message and exit -r REPOSITORY, --repository REPOSITORY - The repository to register the package to. Can be a - section in the config file or a full URL to the - repository (default: pypi). (Can also be set via - TWINE_REPOSITORY environment variable) + The repository (package index) to register the package + to. Should be a section in the config file. (Can also + be set via TWINE_REPOSITORY environment variable.) + Initial package registration no longer necessary on + pypi.org: + https://packaging.python.org/guides/migrating-to-pypi- + org/ --repository-url REPOSITORY_URL - The repository URL to upload the package to. This can - be specified with --repository because it will be used - if there is no configuration for the value passed to - --repository. (Can also be set via - TWINE_REPOSITORY_URL environment variable.) + The repository (package index) URL to register the + package to. This overrides --repository. (Can also be + set via TWINE_REPOSITORY_URL environment variable.) -u USERNAME, --username USERNAME - The username to authenticate to the repository as (can - also be set via TWINE_USERNAME environment variable) + The username to authenticate to the repository + (package index) as. (Can also be set via + TWINE_USERNAME environment variable.) -p PASSWORD, --password PASSWORD - The password to authenticate to the repository with - (can also be set via TWINE_PASSWORD environment - variable) + The password to authenticate to the repository + (package index) with. (Can also be set via + TWINE_PASSWORD environment variable.) -c COMMENT, --comment COMMENT - The comment to include with the distribution file + The comment to include with the distribution file. --config-file CONFIG_FILE - The .pypirc config file to use + The .pypirc config file to use. --cert path Path to alternate CA bundle (can also be set via - TWINE_CERT environment variable) + TWINE_CERT environment variable). --client-cert path Path to SSL client certificate, a single file containing the private key and the certificate in PEM - format - + format. Environment Variables ^^^^^^^^^^^^^^^^^^^^^ @@ -197,18 +257,18 @@ it is not convenient to create a `.pypirc` file, such as a CI/build server, for example. -* ``TWINE_USERNAME`` - the username to use for authentication to the repository -* ``TWINE_PASSWORD`` - the password to use for authentication to the repository +* ``TWINE_USERNAME`` - the username to use for authentication to the repository. +* ``TWINE_PASSWORD`` - the password to use for authentication to the repository. * ``TWINE_REPOSITORY`` - the repository configuration, either defined as a - section in `.pypirc` or provided as a full URL -* ``TWINE_REPOSITORY_URL`` - the repository URL to use + section in `.pypirc` or provided as a full URL. +* ``TWINE_REPOSITORY_URL`` - the repository URL to use. * ``TWINE_CERT`` - custom CA certificate to use for repositories with - self-signed or untrusted certificates + self-signed or untrusted certificates. Resources --------- -* `IRC `_ +* `IRC `_ (``#pypa`` - irc.freenode.net) * `GitHub repository `_ * User and developer `documentation`_ @@ -230,10 +290,12 @@ .. _`a utility`: https://pypi.org/project/twine/ .. _`publishing`: https://packaging.python.org/tutorials/distributing-packages/ .. _`PyPI`: https://pypi.org +.. _`Test PyPI`: https://packaging.python.org/guides/using-testpypi/ .. _`Python Packaging User Guide`: https://packaging.python.org/tutorials/distributing-packages/ -.. _`documentation`: http://twine.readthedocs.io/ +.. _`documentation`: https://twine.readthedocs.io/ .. _`developer documentation`: https://twine.readthedocs.io/en/latest/contributing.html .. _`projects`: https://packaging.python.org/glossary/#term-project .. _`distributions`: https://packaging.python.org/glossary/#term-distribution-package .. _`PyPA Code of Conduct`: https://www.pypa.io/en/latest/code-of-conduct/ .. _`Warehouse`: https://github.com/pypa/warehouse +.. _`wheels`: https://packaging.python.org/glossary/#term-wheel diff -Nru twine-1.10.0/setup.cfg twine-1.11.0/setup.cfg --- twine-1.10.0/setup.cfg 2018-03-07 20:54:12.000000000 +0000 +++ twine-1.11.0/setup.cfg 2018-03-19 23:30:58.000000000 +0000 @@ -11,7 +11,7 @@ tqdm >= 4.14 requests >= 2.5.0, != 2.15, != 2.16 requests-toolbelt >= 0.8.0 - pkginfo >= 1.0 + pkginfo >= 1.4.2 setuptools >= 0.7.0 argparse; python_version == '2.6' pyblake2; extra == 'with-blake2' and python_version < '3.6' diff -Nru twine-1.10.0/setup.py twine-1.11.0/setup.py --- twine-1.10.0/setup.py 2018-03-07 20:49:39.000000000 +0000 +++ twine-1.11.0/setup.py 2018-03-18 18:04:08.000000000 +0000 @@ -20,7 +20,7 @@ install_requires = [ "tqdm >= 4.14", - "pkginfo >= 1.0", + "pkginfo >= 1.4.2", "requests >= 2.5.0, != 2.15, != 2.16", "requests-toolbelt >= 0.8.0", "setuptools >= 0.7.0", diff -Nru twine-1.10.0/tests/test_package.py twine-1.11.0/tests/test_package.py --- twine-1.10.0/tests/test_package.py 2018-01-24 20:26:45.000000000 +0000 +++ twine-1.11.0/tests/test_package.py 2018-03-18 18:04:08.000000000 +0000 @@ -15,6 +15,7 @@ from twine import package import pretend +import pytest def test_sign_file(monkeypatch): @@ -70,3 +71,95 @@ assert pkg.signed_basefilename == "deprecated-pypirc.asc" assert pkg.signed_filename == (filename + '.asc') + + +@pytest.mark.parametrize('gpg_signature', [ + (None), + (pretend.stub()), +]) +def test_metadata_dictionary(gpg_signature): + meta = pretend.stub( + name='whatever', + version=pretend.stub(), + metadata_version=pretend.stub(), + summary=pretend.stub(), + home_page=pretend.stub(), + author=pretend.stub(), + author_email=pretend.stub(), + maintainer=pretend.stub(), + maintainer_email=pretend.stub(), + license=pretend.stub(), + description=pretend.stub(), + keywords=pretend.stub(), + platforms=pretend.stub(), + classifiers=pretend.stub(), + download_url=pretend.stub(), + supported_platforms=pretend.stub(), + provides=pretend.stub(), + requires=pretend.stub(), + obsoletes=pretend.stub(), + project_urls=pretend.stub(), + provides_dist=pretend.stub(), + obsoletes_dist=pretend.stub(), + requires_dist=pretend.stub(), + requires_external=pretend.stub(), + requires_python=pretend.stub(), + provides_extras=pretend.stub(), + description_content_type=pretend.stub(), + ) + + pkg = package.PackageFile( + filename='tests/fixtures/twine-1.5.0-py2.py3-none-any.whl', + comment=pretend.stub(), + metadata=meta, + python_version=pretend.stub(), + filetype=pretend.stub(), + ) + pkg.gpg_signature = gpg_signature + + result = pkg.metadata_dictionary() + + # identify release + assert result['name'] == pkg.safe_name + assert result['version'] == meta.version + + # file content + assert result['filetype'] == pkg.filetype + assert result['pyversion'] == pkg.python_version + + # additional meta-data + assert result['metadata_version'] == meta.metadata_version + assert result['summary'] == meta.summary + assert result['home_page'] == meta.home_page + assert result['author'] == meta.author + assert result['author_email'] == meta.author_email + assert result['maintainer'] == meta.maintainer + assert result['maintainer_email'] == meta.maintainer_email + assert result['license'] == meta.license + assert result['description'] == meta.description + assert result['keywords'] == meta.keywords + assert result['platform'] == meta.platforms + assert result['classifiers'] == meta.classifiers + assert result['download_url'] == meta.download_url + assert result['supported_platform'] == meta.supported_platforms + assert result['comment'] == pkg.comment + + # PEP 314 + assert result['provides'] == meta.provides + assert result['requires'] == meta.requires + assert result['obsoletes'] == meta.obsoletes + + # Metadata 1.2 + assert result['project_urls'] == meta.project_urls + assert result['provides_dist'] == meta.provides_dist + assert result['obsoletes_dist'] == meta.obsoletes_dist + assert result['requires_dist'] == meta.requires_dist + assert result['requires_external'] == meta.requires_external + assert result['requires_python'] == meta.requires_python + + # Metadata 2.1 + assert result['provides_extras'] == meta.provides_extras + assert result['description_content_type'] == meta.description_content_type + + # GPG signature + assert result.get('gpg_signature') == gpg_signature diff -Nru twine-1.10.0/tests/test_repository.py twine-1.11.0/tests/test_repository.py --- twine-1.10.0/tests/test_repository.py 2018-03-02 22:19:36.000000000 +0000 +++ twine-1.11.0/tests/test_repository.py 2018-03-18 18:04:08.000000000 +0000 @@ -14,6 +14,7 @@ import requests from twine import repository +from twine.utils import DEFAULT_REPOSITORY import pretend @@ -55,7 +56,7 @@ def test_set_client_certificate(): repo = repository.Repository( - repository_url='https://pypi.python.org/pypi', + repository_url=DEFAULT_REPOSITORY, username='username', password='password', ) @@ -68,7 +69,7 @@ def test_set_certificate_authority(): repo = repository.Repository( - repository_url='https://pypi.python.org/pypi', + repository_url=DEFAULT_REPOSITORY, username='username', password='password', ) @@ -81,7 +82,7 @@ def test_make_user_agent_string(): repo = repository.Repository( - repository_url='https://pypi.python.org/pypi', + repository_url=DEFAULT_REPOSITORY, username='username', password='password', ) @@ -107,7 +108,7 @@ def test_package_is_uploaded_404s(): repo = repository.Repository( - repository_url='https://pypi.python.org/pypi', + repository_url=DEFAULT_REPOSITORY, username='username', password='password', ) @@ -124,7 +125,7 @@ def test_package_is_uploaded_200s_with_no_releases(): repo = repository.Repository( - repository_url='https://pypi.python.org/pypi', + repository_url=DEFAULT_REPOSITORY, username='username', password='password', ) diff -Nru twine-1.10.0/tests/test_upload.py twine-1.11.0/tests/test_upload.py --- twine-1.10.0/tests/test_upload.py 2018-03-02 22:19:36.000000000 +0000 +++ twine-1.11.0/tests/test_upload.py 2018-03-19 16:58:11.000000000 +0000 @@ -20,7 +20,7 @@ import pytest from twine.commands import upload -from twine import package, cli +from twine import package, cli, exceptions import twine import helpers @@ -95,6 +95,40 @@ ).format(pypirc) +def test_deprecated_repo(tmpdir): + with pytest.raises(exceptions.UploadToDeprecatedPyPIDetected) as err: + 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(""" + [pypi] + repository: https://pypi.python.org/pypi/ + username:foo + password:bar + """)) + + 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, + ) + + assert err.args[0] == ( + "You're trying to upload to the legacy PyPI site " + "'https://pypi.python.org/pypi/'. " + "Uploading to those sites is deprecated. \n " + "The new sites are pypi.org and test.pypi.org. Try using " + "https://upload.pypi.org/legacy/ " + "(or https://test.pypi.org/legacy/) " + "to upload your packages instead. " + "These are the default URLs for Twine now. \n " + "More at " + "https://packaging.python.org/guides/migrating-to-pypi-org/ ." + ) + + def test_skip_existing_skips_files_already_on_PyPI(monkeypatch): response = pretend.stub( status_code=400, @@ -108,7 +142,8 @@ def test_skip_existing_skips_files_already_on_pypiserver(monkeypatch): - # pypiserver (https://pypi.python.org/pypi/pypiserver) responds with 409 + # pypiserver (https://pypi.org/project/pypiserver) responds with a + # 409 when the file already exists. response = pretend.stub( status_code=409, reason='A file named "twine-1.5.0-py2.py3-none-any.whl" already ' diff -Nru twine-1.10.0/tests/test_utils.py twine-1.11.0/tests/test_utils.py --- twine-1.10.0/tests/test_utils.py 2018-03-07 20:33:28.000000000 +0000 +++ twine-1.11.0/tests/test_utils.py 2018-03-19 15:56:54.000000000 +0000 @@ -53,6 +53,10 @@ def test_get_config_no_distutils(tmpdir): + """ + Even if the user hasn't set PyPI has an index server + in 'index-servers', default to uploading to PyPI. + """ pypirc = os.path.join(str(tmpdir), ".pypirc") with open(pypirc, "w") as fp: diff -Nru twine-1.10.0/twine/commands/register.py twine-1.11.0/twine/commands/register.py --- twine-1.10.0/twine/commands/register.py 2018-01-24 20:27:30.000000000 +0000 +++ twine-1.11.0/twine/commands/register.py 2018-03-18 18:04:08.000000000 +0000 @@ -69,11 +69,12 @@ "-r", "--repository", action=utils.EnvironmentDefault, env="TWINE_REPOSITORY", - default="pypi", - help="The repository to register the package to. " - "Should be a section in the config file (default: " - "%(default)s). (Can also be set via %(env)s environment " - "variable)", + default=None, + help="The repository (package index) to register the package to. " + "Should be a section in the config file. (Can also be set " + "via %(env)s environment variable.) " + "Initial package registration no longer necessary on pypi.org: " + "https://packaging.python.org/guides/migrating-to-pypi-org/", ) parser.add_argument( "--repository-url", @@ -81,8 +82,8 @@ env="TWINE_REPOSITORY_URL", default=None, required=False, - help="The repository URL to register the package to. " - "This overrides --repository." + help="The repository (package index) URL to register the package to. " + "This overrides --repository. " "(Can also be set via %(env)s environment variable.)" ) parser.add_argument( @@ -90,25 +91,25 @@ action=utils.EnvironmentDefault, env="TWINE_USERNAME", required=False, help="The username to authenticate to the repository " - "as (can also be set via %(env)s environment " - "variable)", + "(package index) as. (Can also be set via " + "%(env)s environment variable.)", ) parser.add_argument( "-p", "--password", action=utils.EnvironmentDefault, env="TWINE_PASSWORD", required=False, help="The password to authenticate to the repository " - "with (can also be set via %(env)s environment " - "variable)", + "(package index) with. (Can also be set via " + "%(env)s environment variable.)", ) parser.add_argument( "-c", "--comment", - help="The comment to include with the distribution file", + help="The comment to include with the distribution file.", ) parser.add_argument( "--config-file", default="~/.pypirc", - help="The .pypirc config file to use", + help="The .pypirc config file to use.", ) parser.add_argument( "--cert", @@ -118,18 +119,18 @@ required=False, metavar="path", help="Path to alternate CA bundle (can also be set via %(env)s " - "environment variable)", + "environment variable).", ) 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 format", + "private key and the certificate in PEM format.", ) parser.add_argument( "package", metavar="package", - help="File from which we read the package metadata", + help="File from which we read the package metadata.", ) args = parser.parse_args(args) diff -Nru twine-1.10.0/twine/commands/upload.py twine-1.11.0/twine/commands/upload.py --- twine-1.10.0/twine/commands/upload.py 2018-01-24 20:27:30.000000000 +0000 +++ twine-1.11.0/twine/commands/upload.py 2018-03-18 18:04:08.000000000 +0000 @@ -21,7 +21,7 @@ import twine.exceptions as exc from twine.package import PackageFile -from twine.repository import Repository, LEGACY_PYPI +from twine.repository import Repository, LEGACY_PYPI, LEGACY_TEST_PYPI from twine import utils @@ -98,11 +98,20 @@ print("Uploading distributions to {0}".format(config["repository"])) - if config["repository"].startswith(LEGACY_PYPI): - print( - "Note: you are uploading to the old upload URL. It's recommended " - "to use the new URL \"{0}\" or to leave the URL unspecified and " - "allow twine to choose.".format(utils.DEFAULT_REPOSITORY)) + if config["repository"].startswith((LEGACY_PYPI, LEGACY_TEST_PYPI)): + raise exc.UploadToDeprecatedPyPIDetected( + "You're trying to upload to the legacy PyPI site '{0}'. " + "Uploading to those sites is deprecated. \n " + "The new sites are pypi.org and test.pypi.org. Try using " + "{1} (or {2}) to upload your packages instead. " + "These are the default URLs for Twine now. \n More at " + "https://packaging.python.org/guides/migrating-to-pypi-org/ " + ".".format( + config["repository"], + utils.DEFAULT_REPOSITORY, + utils.TEST_REPOSITORY + ) + ) username = utils.get_username(username, config) password = utils.get_password( @@ -165,10 +174,10 @@ action=utils.EnvironmentDefault, env="TWINE_REPOSITORY", default="pypi", - help="The repository to upload the package to. " + help="The repository (package index) to upload the package to. " "Should be a section in the config file (default: " "%(default)s). (Can also be set via %(env)s environment " - "variable)", + "variable.)", ) parser.add_argument( "--repository-url", @@ -176,49 +185,49 @@ env="TWINE_REPOSITORY_URL", default=None, required=False, - help="The repository URL to upload the package to. " - "This overrides --repository." + help="The repository (package index) URL to upload the package to. " + "This overrides --repository. " "(Can also be set via %(env)s environment variable.)" ) parser.add_argument( "-s", "--sign", action="store_true", default=False, - help="Sign files to upload using gpg", + help="Sign files to upload using GPG.", ) parser.add_argument( "--sign-with", default="gpg", - help="GPG program used to sign uploads (default: %(default)s)", + help="GPG program used to sign uploads (default: %(default)s).", ) parser.add_argument( "-i", "--identity", - help="GPG identity used to sign files", + help="GPG identity used to sign files.", ) parser.add_argument( "-u", "--username", action=utils.EnvironmentDefault, env="TWINE_USERNAME", required=False, help="The username to authenticate to the repository " - "as (can also be set via %(env)s environment " - "variable)", + "(package index) as. (Can also be set via " + "%(env)s environment variable.)", ) parser.add_argument( "-p", "--password", action=utils.EnvironmentDefault, env="TWINE_PASSWORD", required=False, help="The password to authenticate to the repository " - "with (can also be set via %(env)s environment " - "variable)", + "(package index) with. (Can also be set via " + "%(env)s environment variable.)", ) parser.add_argument( "-c", "--comment", - help="The comment to include with the distribution file", + help="The comment to include with the distribution file.", ) parser.add_argument( "--config-file", default="~/.pypirc", - help="The .pypirc config file to use", + help="The .pypirc config file to use.", ) parser.add_argument( "--skip-existing", @@ -236,21 +245,22 @@ required=False, metavar="path", help="Path to alternate CA bundle (can also be set via %(env)s " - "environment variable)", + "environment variable).", ) 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 format", + "private key and the certificate in PEM format.", ) parser.add_argument( "dists", nargs="+", metavar="dist", - help="The distribution files to upload to the repository, may " - "additionally contain a .asc file to include an existing " - "signature with the file upload", + help="The distribution files to upload to the repository " + "(package index). Usually dist/* . May additionally contain " + "a .asc file to include an existing signature with the " + "file upload.", ) args = parser.parse_args(args) diff -Nru twine-1.10.0/twine/exceptions.py twine-1.11.0/twine/exceptions.py --- twine-1.10.0/twine/exceptions.py 2018-01-24 20:20:44.000000000 +0000 +++ twine-1.11.0/twine/exceptions.py 2018-03-19 15:56:54.000000000 +0000 @@ -30,3 +30,16 @@ This is only used when attempting to register a package. """ pass + + +class UploadToDeprecatedPyPIDetected(Exception): + """An upload attempt was detected to deprecated legacy PyPI + sites pypi.python.org or testpypi.python.org.""" + pass + + +class UnreachableRepositoryURLDetected(Exception): + """An upload attempt was detected to a URL without a protocol prefix. + + """ + pass diff -Nru twine-1.10.0/twine/__init__.py twine-1.11.0/twine/__init__.py --- twine-1.10.0/twine/__init__.py 2018-03-07 20:49:39.000000000 +0000 +++ twine-1.11.0/twine/__init__.py 2018-03-19 23:24:08.000000000 +0000 @@ -20,9 +20,9 @@ __title__ = "twine" __summary__ = "Collection of utilities for publishing packages on PyPI" -__uri__ = "http://twine.readthedocs.io/" +__uri__ = "https://twine.readthedocs.io/" -__version__ = "1.10.0" +__version__ = "1.11.0" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" diff -Nru twine-1.10.0/twine/package.py twine-1.11.0/twine/package.py --- twine-1.10.0/twine/package.py 2018-01-24 20:27:30.000000000 +0000 +++ twine-1.11.0/twine/package.py 2018-03-18 18:04:08.000000000 +0000 @@ -148,6 +148,10 @@ "requires_dist": meta.requires_dist, "requires_external": meta.requires_external, "requires_python": meta.requires_python, + + # Metadata 2.1 + "provides_extras": meta.provides_extras, + "description_content_type": meta.description_content_type, } if self.gpg_signature is not None: diff -Nru twine-1.10.0/twine/repository.py twine-1.11.0/twine/repository.py --- twine-1.10.0/twine/repository.py 2018-03-02 21:44:01.000000000 +0000 +++ twine-1.11.0/twine/repository.py 2018-03-18 18:04:08.000000000 +0000 @@ -31,6 +31,7 @@ KEYWORDS_TO_NOT_FLATTEN = set(["gpg_signature", "content"]) LEGACY_PYPI = 'https://pypi.python.org/' +LEGACY_TEST_PYPI = 'https://testpypi.python.org/' WAREHOUSE = 'https://upload.pypi.org/' OLD_WAREHOUSE = 'https://upload.pypi.io/' diff -Nru twine-1.10.0/twine/utils.py twine-1.11.0/twine/utils.py --- twine-1.10.0/twine/utils.py 2018-03-07 20:33:28.000000000 +0000 +++ twine-1.11.0/twine/utils.py 2018-03-19 16:58:11.000000000 +0000 @@ -32,6 +32,8 @@ except ImportError: from urllib.parse import urlparse, urlunparse +import twine.exceptions + # Shim for raw_input in python3 if sys.version_info > (3,): input_func = input @@ -63,11 +65,17 @@ parser.read(path) # Get a list of repositories from the config file + # format: https://docs.python.org/3/distutils/packageindex.html#pypirc if (parser.has_section("distutils") and parser.has_option("distutils", "index-servers")): repositories = parser.get("distutils", "index-servers").split() + elif parser.has_section("pypi"): + # Special case: if the .pypirc file has a 'pypi' section, + # even if there's no list of index servers, + # be lenient and include that in our list of repositories. + repositories = ['pypi'] else: - repositories = ["pypi"] + repositories = [] config = {} @@ -103,15 +111,19 @@ def get_repository_from_config(config_file, repository, repository_url=None): - # Get our config from the .pypirc file + # Get our config from, if provided, command-line values for the + # repository name and URL, or the .pypirc file if repository_url and "://" in repository_url: - # assume that the repository is actually an URL and just sent - # them a dummy with the repo set + # prefer CLI `repository_url` over `repository` or .pypirc return { "repository": repository_url, "username": None, "password": None, } + if repository_url and "://" not in repository_url: + raise twine.exceptions.UnreachableRepositoryURLDetected( + "Repository URL {0} has no protocol. Please add " + "'https://'. \n".format(repository_url)) try: return get_config(config_file)[repository] except KeyError: @@ -140,14 +152,21 @@ def check_status_code(response): - if (response.status_code == 500 and + """ + Shouldn't happen, thanks to the UploadToDeprecatedPyPIDetected + exception, but this is in case that breaks and it does. + """ + if (response.status_code == 410 and response.url.startswith(("https://pypi.python.org", "https://testpypi.python.org"))): - print("It appears you're uploading to pypi.python.org (or testpypi) " - "you've received a 500 error response. PyPI is being phased " - "out for pypi.org. Try using https://upload.pypi.org/legacy/ " + print("It appears you're uploading to pypi.python.org (or " + "testpypi.python.org). You've received a 410 error response. " + "Uploading to those sites is deprecated. The new sites are " + "pypi.org and test.pypi.org. Try using " + "https://upload.pypi.org/legacy/ " "(or https://test.pypi.org/legacy/) to upload your packages " - "instead. These are the default URLs for Twine now.") + "instead. These are the default URLs for Twine now. More at " + "https://packaging.python.org/guides/migrating-to-pypi-org/ ") response.raise_for_status() diff -Nru twine-1.10.0/twine.egg-info/PKG-INFO twine-1.11.0/twine.egg-info/PKG-INFO --- twine-1.10.0/twine.egg-info/PKG-INFO 2018-03-07 20:54:11.000000000 +0000 +++ twine-1.11.0/twine.egg-info/PKG-INFO 2018-03-19 23:30:58.000000000 +0000 @@ -1,15 +1,14 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: twine -Version: 1.10.0 +Version: 1.11.0 Summary: Collection of utilities for publishing packages on PyPI -Home-page: http://twine.readthedocs.io/ +Home-page: https://twine.readthedocs.io/ Author: Donald Stufft and individual contributors Author-email: donald@stufft.io License: Apache License, Version 2.0 Project-URL: Packaging tutorial, https://packaging.python.org/tutorials/distributing-packages/ -Project-URL: Twine source, https://github.com/pypa/twine/ Project-URL: Twine documentation, https://twine.readthedocs.io/en/latest/ -Description-Content-Type: UNKNOWN +Project-URL: Twine source, https://github.com/pypa/twine/ Description: twine ===== @@ -26,13 +25,14 @@ The goal of ``twine`` is to improve PyPI interaction by improving security and testability. - 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`` `only - recently stopped using HTTP `_ 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. + 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`` `only recently stopped using HTTP + `_ 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. ``python setup.py upload`` only allows you to upload something that you've @@ -40,12 +40,12 @@ exact file you're going to upload to PyPI to ensure that it works before uploading it. - Finally, it allows you to pre-sign your files and pass the ``.asc`` - files into the command line invocation (``twine upload - twine-1.0.1.tar.gz twine-1.0.1.tar.gz.asc``). This enables you to be - assured that you're typing your ``gpg`` passphrase into ``gpg`` itself - and not anything else, since *you* will be the one directly executing - ``gpg --detach-sign -a ``. + Finally, ``twine`` allows you to pre-sign your files and pass the + ``.asc`` files into the command line invocation (``twine upload + myproject-1.0.1.tar.gz myproject-1.0.1.tar.gz.asc``). This enables you + to be assured that you're typing your ``gpg`` passphrase into ``gpg`` + itself and not anything else, since *you* will be the one directly + executing ``gpg --detach-sign -a ``. Features @@ -55,7 +55,7 @@ - Uploading doesn't require executing ``setup.py`` - Uploading files that have already been created, allowing testing of distributions before release - - Supports uploading any packaging format (including wheels) + - Supports uploading any packaging format (including `wheels`_) Installation @@ -66,8 +66,8 @@ $ pip install twine - Usage - ----- + Using Twine + ----------- 1. Create some distributions in the normal way: @@ -75,20 +75,78 @@ $ python setup.py sdist bdist_wheel - 2. Upload with ``twine``: + 2. Upload with ``twine`` to `Test PyPI`_ and verify things look right. Twine will automatically prompt for your username and password: + + .. code-block:: console + + $ twine upload --repository-url https://test.pypi.org/legacy/ dist/* + username: ... + password: + ... + + 3. Upload to `PyPI`_: .. code-block:: console $ twine upload dist/* - 3. Done! + 4. Done! More documentation on using ``twine`` to upload packages to PyPI is in the `Python Packaging User Guide`_. + Keyring Support + --------------- + + Instead of typing in your password every time you upload a distribution, Twine + allows you to store your username and password securely using `keyring`_. + + To use the keyring, you must first install the keyring packages: + + - On Windows and MacOS you just need to install ``keyring``, for example, + ``pip install --user keyring``. + - On Linux, in addition to the ``keyring`` package you also need to ensure the + ``python3-dbus`` system package is installed. For example, ``apt install + python3-dbus``. See `Keyring's installation instructions`_ for more details. + + Once keyring is installed you can use the ``keyring`` program to set your + username and password to use for each package index (repository) you want to + upload to using Twine. + + To set your username and password for test PyPI run the following command. + ``keyring`` will prompt you for your password: + + .. code-block:: console + + $ keyring set https://test.pypi.org/legacy/ your-username + # or + $ python3 -m keyring set https://test.pypi.org/legacy/ your-username + + To set your username and password for PyPI run this command, again, ``keyring`` + will prompt for the password: + + .. code-block:: console + + $ keyring set https://upload.pypi.org/legacy/ your-username + # or + $ python3 -m keyring set https://upload.pypi.org/legacy/ your-username + + + The next time you run ``twine`` it will prompt you for a username and will grab the appropriate password from the keyring. + + .. Note:: If you are using Linux in a headless environment (such as on a + server) you'll need to do some additional steps to ensure that Keyring can + store secrets securely. See `Using Keyring on headless systems`_. + + .. _`keyring`: https://pypi.org/project/keyring/ + .. _`Keyring's installation instructions`: + https://keyring.readthedocs.io/en/latest#installation-instructions + .. _`Using Keyring on headless systems`: + https://keyring.readthedocs.io/en/latest/#using-keyring-on-headless-linux-systems + Options - ^^^^^^^ + ------- .. code-block:: console @@ -101,48 +159,47 @@ dist [dist ...] positional arguments: - dist The distribution files to upload to the repository, - may additionally contain a .asc file to include an - existing signature with the file upload + dist The distribution files to upload to the repository + (package index). Usually dist/* . May additionally + contain a .asc file to include an existing signature + with the file upload. optional arguments: -h, --help show this help message and exit -r REPOSITORY, --repository REPOSITORY - The repository to upload the package to. Can be a - section in the config file or a full URL to the - repository (default: pypi). (Can also be set via - TWINE_REPOSITORY environment variable) + The repository (package index) to upload the package + to. Should be a section in the config file (default: + pypi). (Can also be set via TWINE_REPOSITORY + environment variable.) --repository-url REPOSITORY_URL - The repository URL to upload the package to. This can - be specified with --repository because it will be used - if there is no configuration for the value passed to - --repository. (Can also be set via - TWINE_REPOSITORY_URL environment variable.) - -s, --sign Sign files to upload using gpg + The repository (package index) URL to upload the + package to. This overrides --repository. (Can also be + set via TWINE_REPOSITORY_URL environment variable.) + -s, --sign Sign files to upload using GPG. --sign-with SIGN_WITH - GPG program used to sign uploads (default: gpg) + GPG program used to sign uploads (default: gpg). -i IDENTITY, --identity IDENTITY - GPG identity used to sign files + GPG identity used to sign files. -u USERNAME, --username USERNAME - The username to authenticate to the repository as (can - also be set via TWINE_USERNAME environment variable) + The username to authenticate to the repository + (package index) as. (Can also be set via + TWINE_USERNAME environment variable.) -p PASSWORD, --password PASSWORD - The password to authenticate to the repository with - (can also be set via TWINE_PASSWORD environment - variable) + The password to authenticate to the repository + (package index) with. (Can also be set via + TWINE_PASSWORD environment variable.) -c COMMENT, --comment COMMENT - The comment to include with the distribution file + The comment to include with the distribution file. --config-file CONFIG_FILE - The .pypirc config file to use + The .pypirc config file to use. --skip-existing Continue uploading files if one already exists. (Only valid when uploading to PyPI. Other implementations may not support this.) --cert path Path to alternate CA bundle (can also be set via - TWINE_CERT environment variable) + TWINE_CERT environment variable). --client-cert path Path to SSL client certificate, a single file containing the private key and the certificate in PEM - format - + format. Twine also includes a ``register`` command. @@ -160,45 +217,47 @@ .. code-block:: console $ twine register -h - usage: twine register [-h] [-r REPOSITORY] [--repository-url REPOSITORY_URL] + + usage: twine register [-h] -r REPOSITORY [--repository-url REPOSITORY_URL] [-u USERNAME] [-p PASSWORD] [-c COMMENT] [--config-file CONFIG_FILE] [--cert path] [--client-cert path] package positional arguments: - package File from which we read the package metadata + package File from which we read the package metadata. optional arguments: -h, --help show this help message and exit -r REPOSITORY, --repository REPOSITORY - The repository to register the package to. Can be a - section in the config file or a full URL to the - repository (default: pypi). (Can also be set via - TWINE_REPOSITORY environment variable) + The repository (package index) to register the package + to. Should be a section in the config file. (Can also + be set via TWINE_REPOSITORY environment variable.) + Initial package registration no longer necessary on + pypi.org: + https://packaging.python.org/guides/migrating-to-pypi- + org/ --repository-url REPOSITORY_URL - The repository URL to upload the package to. This can - be specified with --repository because it will be used - if there is no configuration for the value passed to - --repository. (Can also be set via - TWINE_REPOSITORY_URL environment variable.) + The repository (package index) URL to register the + package to. This overrides --repository. (Can also be + set via TWINE_REPOSITORY_URL environment variable.) -u USERNAME, --username USERNAME - The username to authenticate to the repository as (can - also be set via TWINE_USERNAME environment variable) + The username to authenticate to the repository + (package index) as. (Can also be set via + TWINE_USERNAME environment variable.) -p PASSWORD, --password PASSWORD - The password to authenticate to the repository with - (can also be set via TWINE_PASSWORD environment - variable) + The password to authenticate to the repository + (package index) with. (Can also be set via + TWINE_PASSWORD environment variable.) -c COMMENT, --comment COMMENT - The comment to include with the distribution file + The comment to include with the distribution file. --config-file CONFIG_FILE - The .pypirc config file to use + The .pypirc config file to use. --cert path Path to alternate CA bundle (can also be set via - TWINE_CERT environment variable) + TWINE_CERT environment variable). --client-cert path Path to SSL client certificate, a single file containing the private key and the certificate in PEM - format - + format. Environment Variables ^^^^^^^^^^^^^^^^^^^^^ @@ -209,18 +268,18 @@ it is not convenient to create a `.pypirc` file, such as a CI/build server, for example. - * ``TWINE_USERNAME`` - the username to use for authentication to the repository - * ``TWINE_PASSWORD`` - the password to use for authentication to the repository + * ``TWINE_USERNAME`` - the username to use for authentication to the repository. + * ``TWINE_PASSWORD`` - the password to use for authentication to the repository. * ``TWINE_REPOSITORY`` - the repository configuration, either defined as a - section in `.pypirc` or provided as a full URL - * ``TWINE_REPOSITORY_URL`` - the repository URL to use + section in `.pypirc` or provided as a full URL. + * ``TWINE_REPOSITORY_URL`` - the repository URL to use. * ``TWINE_CERT`` - custom CA certificate to use for repositories with - self-signed or untrusted certificates + self-signed or untrusted certificates. Resources --------- - * `IRC `_ + * `IRC `_ (``#pypa`` - irc.freenode.net) * `GitHub repository `_ * User and developer `documentation`_ @@ -242,13 +301,15 @@ .. _`a utility`: https://pypi.org/project/twine/ .. _`publishing`: https://packaging.python.org/tutorials/distributing-packages/ .. _`PyPI`: https://pypi.org + .. _`Test PyPI`: https://packaging.python.org/guides/using-testpypi/ .. _`Python Packaging User Guide`: https://packaging.python.org/tutorials/distributing-packages/ - .. _`documentation`: http://twine.readthedocs.io/ + .. _`documentation`: https://twine.readthedocs.io/ .. _`developer documentation`: https://twine.readthedocs.io/en/latest/contributing.html .. _`projects`: https://packaging.python.org/glossary/#term-project .. _`distributions`: https://packaging.python.org/glossary/#term-distribution-package .. _`PyPA Code of Conduct`: https://www.pypa.io/en/latest/code-of-conduct/ .. _`Warehouse`: https://github.com/pypa/warehouse + .. _`wheels`: https://packaging.python.org/glossary/#term-wheel Platform: UNKNOWN Classifier: Intended Audience :: Developers @@ -269,3 +330,5 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +Provides-Extra: keyring +Provides-Extra: with-blake2 diff -Nru twine-1.10.0/twine.egg-info/requires.txt twine-1.11.0/twine.egg-info/requires.txt --- twine-1.10.0/twine.egg-info/requires.txt 2018-03-07 20:54:11.000000000 +0000 +++ twine-1.11.0/twine.egg-info/requires.txt 2018-03-19 23:30:58.000000000 +0000 @@ -1,5 +1,5 @@ tqdm>=4.14 -pkginfo>=1.0 +pkginfo>=1.4.2 requests!=2.15,!=2.16,>=2.5.0 requests-toolbelt>=0.8.0 setuptools>=0.7.0 @@ -8,4 +8,3 @@ keyring [with-blake2] -pyblake2