nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: pypa/twine
Pull request #107 (2015-04-29 22:39:28)

Put wheel files first when uploading.
References https://github.com/pypa/twine/issues/106

This groups wheel ".whl" files first for upload order so that the metadata can be displayed immediately while other files are still uploading.

- Added a function upload.group_wheel_files_first
- Added a two tests.
- Ensured coverage for function



Patch:
diff --git a/AUTHORS b/AUTHORS
index fffe735..29b5d05 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -11,3 +11,4 @@ Ian Cordasco <graffatcolmingov@gmail.com>
 Marc Abramowitz <msabramo@gmail.com> (http://marc-abramowitz.com/)
 Tom Myers <tom.stephen.myers@gmail.com>
 Rodrigue Cloutier <rodcloutier@gmail.com>
+Tyrel Souza <tyrelsouza@gmail.com> (https://tyrelsouza.com)
diff --git a/twine/commands/upload.py b/twine/commands/upload.py
index 5979e62..bc89dd4 100644
--- a/twine/commands/upload.py
+++ b/twine/commands/upload.py
@@ -17,6 +17,7 @@ from __future__ import unicode_literals
 import argparse
 import glob
 import hashlib
+import itertools
 import os.path
 import subprocess
 import sys
@@ -53,6 +54,23 @@ DIST_EXTENSIONS = {
 }
 
 
+def group_wheel_files_first(dist_files):
+    if not any(fname for fname in dist_files if fname.endswith(".whl")):
+        # Return early if there's no wheel files
+        return dist_files
+
+    group_func = lambda x: x.endswith(".whl")
+    sorted_distfiles = sorted(dist_files, key=group_func)
+    wheels, not_wheels = [], []
+    for grp, files in itertools.groupby(sorted_distfiles, key=group_func):
+        if grp:
+            wheels.extend(files)
+        else:
+            not_wheels.extend(files)
+
+    return wheels + not_wheels
+
+
 def find_dists(dists):
     uploads = []
     for filename in dists:
@@ -68,7 +86,7 @@ def find_dists(dists):
                 )
         # Otherwise, files will be filenames that exist
         uploads.extend(files)
-    return uploads
+    return group_wheel_files_first(uploads)
 
 
 def sign_file(sign_with, filename, identity):


Test patch:
diff --git a/tests/test_upload.py b/tests/test_upload.py
index e84f1e7..f42c8bc 100644
--- a/tests/test_upload.py
+++ b/tests/test_upload.py
@@ -17,6 +17,26 @@ import pytest
 from twine.commands import upload
 
 
+def test_ensure_wheel_files_uploaded_first():
+    files = upload.group_wheel_files_first(["twine/foo.py",
+                                            "twine/first.whl",
+                                            "twine/bar.py",
+                                            "twine/second.whl"])
+    expected = ["twine/first.whl",
+                "twine/second.whl",
+                "twine/foo.py",
+                "twine/bar.py"]
+    assert expected == files
+
+
+def test_ensure_if_no_wheel_files():
+    files = upload.group_wheel_files_first(["twine/foo.py",
+                                            "twine/bar.py"])
+    expected = ["twine/foo.py",
+                "twine/bar.py"]
+    assert expected == files
+
+
 def test_find_dists_expands_globs():
     files = sorted(upload.find_dists(['twine/__*.py']))
     expected = ['twine/__init__.py', 'twine/__main__.py']
Repository: pypa/twine
Pull request #112 (2015-06-10 14:18:31)

helpful messages if ~/.pypirc format is out-dated
see also:
https://github.com/pypa/twine/issues/111

sigmavirus24: @jedie Travis is failing. Please fix it.

Patch:
diff --git a/AUTHORS b/AUTHORS
index 9f4cc91..74456d6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,3 +13,4 @@ Tom Myers <tom.stephen.myers@gmail.com>
 Rodrigue Cloutier <rodcloutier@gmail.com>
 Tyrel Souza <tyrelsouza@gmail.com> (https://tyrelsouza.com)
 Adam Talsma <adam@talsma.ca>
+Jens Diemer <github@jensdiemer.de> (http://jensdiemer.de/)
diff --git a/twine/commands/upload.py b/twine/commands/upload.py
index 8f87d50..4cc1d65 100644
--- a/twine/commands/upload.py
+++ b/twine/commands/upload.py
@@ -114,11 +114,16 @@ def upload(dists, repository, sign, identity, username, password, comment,
     try:
         config = get_config(config_file)[repository]
     except KeyError:
-        raise KeyError(
-            "Missing '{0}' section from the configuration file".format(
-                repository,
-            ),
+        msg = (
+            "Missing '{repo}' section from the configuration file.\n"
+            "Maybe you have a out-dated '{cfg}' format?\n"
+            "more info: "
+            "https://docs.python.org/distutils/packageindex.html#pypirc\n"
+        ).format(
+            repo=repository,
+            cfg=config_file
         )
+        raise KeyError(msg)
 
     parsed = urlparse(config["repository"])
     if parsed.netloc in ["pypi.python.org", "testpypi.python.org"]:


Test patch:
diff --git a/tests/test_upload.py b/tests/test_upload.py
index f42c8bc..a42f5f9 100644
--- a/tests/test_upload.py
+++ b/tests/test_upload.py
@@ -11,6 +11,11 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+from __future__ import unicode_literals
+
+import os
+import textwrap
+
 import pretend
 import pytest
 
@@ -72,3 +77,26 @@ def test_sign_file_with_identity(monkeypatch):
     args = ['gpg', '--detach-sign', '--local-user', 'identity', '-a',
             'my_file.tar.gz']
     assert replaced_check_call.calls == [pretend.call(args)]
+
+
+def test_get_config_old_format(tmpdir):
+    pypirc = os.path.join(str(tmpdir), ".pypirc")
+
+    with open(pypirc, "w") as fp:
+        fp.write(textwrap.dedent("""
+            [server-login]
+            username:foo
+            password:bar
+        """))
+
+    try:
+        upload.upload(dists="foo", repository="pypi", sign=None, identity=None,
+                      username=None, password=None, comment=None,
+                      sign_with=None, config_file=pypirc)
+    except KeyError as err:
+        assert err.args[0] == (
+            "Missing 'pypi' section from the configuration file.\n"
+            "Maybe you have a out-dated '{0}' format?\n"
+            "more info: "
+            "https://docs.python.org/distutils/packageindex.html#pypirc\n"
+        ).format(pypirc)
Repository: pypa/twine
Pull request #162 (2016-01-28 04:58:50)

Add 409 status code support to --skip-existing
twine only supported the 400 status code when deciding to skip an upload.  However, alternative implementations of pypi, for example [pypiserver](http://github.com/pypiserver/pypiserver), use a 409 status code.


Patch:
diff --git a/AUTHORS b/AUTHORS
index 74456d6..6392a19 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -14,3 +14,4 @@ Rodrigue Cloutier <rodcloutier@gmail.com>
 Tyrel Souza <tyrelsouza@gmail.com> (https://tyrelsouza.com)
 Adam Talsma <adam@talsma.ca>
 Jens Diemer <github@jensdiemer.de> (http://jensdiemer.de/)
+Andrew Watts <andrewwatts@gmail.com>
diff --git a/twine/commands/upload.py b/twine/commands/upload.py
index f194d8a..dc3a697 100644
--- a/twine/commands/upload.py
+++ b/twine/commands/upload.py
@@ -56,7 +56,7 @@ def find_dists(dists):
 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
+    return (response.status_code in [400, 409] and
             response.reason.startswith(msg) and
             skip_existing)
 


Test patch:
diff --git a/tests/test_upload.py b/tests/test_upload.py
index 596de80..da723d5 100644
--- a/tests/test_upload.py
+++ b/tests/test_upload.py
@@ -101,6 +101,19 @@ def test_skip_existing_skips_files_already_on_PyPI(monkeypatch):
                               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,
+                              package=pkg) is True
+
+
 def test_skip_upload_respects_skip_existing(monkeypatch):
     response = pretend.stub(
         status_code=400,
Repository: pypa/twine
Pull request #243 (2017-05-16 01:05:12)

Treat filenames from the Wheel file
Filenames in a Wheel are returned as bytestrings. When we check to find
METADATA files, we need to juggle the fact that strings are unicode by
default and that the filenames are bytestrings.

Closes gh-235


Patch:
diff --git a/twine/wheel.py b/twine/wheel.py
index c8291cb..cf6155d 100644
--- a/twine/wheel.py
+++ b/twine/wheel.py
@@ -38,6 +38,12 @@ wheel_file_re = re.compile(
     re.VERBOSE)
 
 
+def try_decode(s):
+    if isinstance(s, bytes):
+        return s.decode('utf8')
+    return s
+
+
 class Wheel(Distribution):
 
     def __init__(self, filename, metadata_version=None):
@@ -51,6 +57,15 @@ class Wheel(Distribution):
         wheel_info = wheel_file_re.match(self.basefilename)
         return wheel_info.group("pyver")
 
+    @staticmethod
+    def find_candidate_metadata_files(names):
+        """Filter files that may be METADATA files."""
+        tuples = [
+            x.split('/') for x in map(try_decode, names)
+            if 'METADATA' in x
+        ]
+        return [x[1] for x in sorted([(len(x), x) for x in tuples])]
+
     def read(self):
         fqn = os.path.abspath(os.path.normpath(self.filename))
         if not os.path.exists(fqn):
@@ -66,9 +81,7 @@ class Wheel(Distribution):
             raise ValueError('Not a known archive format: %s' % fqn)
 
         try:
-            tuples = [x.split('/') for x in names if 'METADATA' in x]
-            schwarz = sorted([(len(x), x) for x in tuples])
-            for path in [x[1] for x in schwarz]:
+            for path in self.find_candidate_metadata_files(names):
                 candidate = '/'.join(path)
                 data = read_file(candidate)
                 if b'Metadata-Version' in data:


Test patch:
diff --git a/tests/test_wheel.py b/tests/test_wheel.py
index 5834449..12fa3d1 100644
--- a/tests/test_wheel.py
+++ b/tests/test_wheel.py
@@ -28,3 +28,20 @@ def example_wheel(request):
 
 def test_version_parsing(example_wheel):
     assert example_wheel.py_version == 'py2.py3'
+
+
+def test_find_metadata_files():
+    names = [
+        b'package/lib/__init__.py',
+        b'package/lib/version.py',
+        b'package/METADATA.txt',
+        b'package/METADATA.json',
+        b'package/METADATA',
+    ]
+    expected = [
+        ['package', 'METADATA'],
+        ['package', 'METADATA.json'],
+        ['package', 'METADATA.txt'],
+    ]
+    candidates = wheel.Wheel.find_candidate_metadata_files(names)
+    assert expected == candidates
Repository: pypa/twine
Pull request #251 (2017-05-22 21:28:40)

Fix weirdness with --repository-url
Closes gh-206


Patch:
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 644577f..178a182 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -19,6 +19,9 @@ Changelog
   * Fix ``--skip-existing`` when used to upload a package for the first time.
     See also :bug:`220`
 
+  * Fix precedence of ``--repository-url`` over ``--repository``. See also
+    :bug:`206`
+
 * :release:`1.8.1 <2016-08-09>`
 
   * Check if a package exists if the URL is one of:
diff --git a/twine/utils.py b/twine/utils.py
index df85ac5..8f822b7 100644
--- a/twine/utils.py
+++ b/twine/utils.py
@@ -104,17 +104,17 @@ def get_config(path="~/.pypirc"):
 
 def get_repository_from_config(config_file, repository, repository_url=None):
     # Get our config from 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
+        return {
+            "repository": repository_url,
+            "username": None,
+            "password": None,
+        }
     try:
         return get_config(config_file)[repository]
     except KeyError:
-        if repository_url and "://" in repository_url:
-            # assume that the repsoitory is actually an URL and just sent
-            # them a dummy with the repo set
-            return {
-                "repository": repository_url,
-                "username": None,
-                "password": None,
-            }
         msg = (
             "Missing '{repo}' section from the configuration file\n"
             "or not a complete URL in --repository.\n"


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index c7d8214..8d70a51 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -121,6 +121,8 @@ def test_get_repository_config_missing(tmpdir):
     }
     assert (utils.get_repository_from_config(pypirc, 'foo', repository_url) ==
             exp)
+    assert (utils.get_repository_from_config(pypirc, 'pypi', repository_url) ==
+            exp)
     exp = {
             "repository": utils.DEFAULT_REPOSITORY,
             "username": None,
Repository: pypa/twine
Pull request #316 (2018-03-07 17:08:59)

Suppress Exceptions in keyring.get_password
Fixes issue reported in #315.


Patch:
diff --git a/twine/utils.py b/twine/utils.py
index 7318c99..218a37a 100644
--- a/twine/utils.py
+++ b/twine/utils.py
@@ -20,7 +20,7 @@ import functools
 import getpass
 import sys
 import argparse
-
+import warnings
 
 try:
     import configparser
@@ -196,7 +196,10 @@ def get_password_from_keyring(system, username):
     except ImportError:
         return
 
-    return keyring.get_password(system, username)
+    try:
+        return keyring.get_password(system, username)
+    except Exception as exc:
+        warnings.warn(str(exc))
 
 
 def password_from_keyring_or_prompt(system, username):


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 8d70a51..3d58ddf 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -222,8 +222,33 @@ def keyring_missing(monkeypatch):
     monkeypatch.setattr(builtins, '__import__', my_import)
 
 
-def test_get_password_keyring_missing_prompts(monkeypatch, keyring_missing):
+@pytest.fixture
+def entered_password(monkeypatch):
     monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw')
 
-    pw = utils.get_password('system', 'user', None, {})
-    assert pw == 'entered pw'
+
+def test_get_password_keyring_missing_prompts(
+        entered_password, keyring_missing):
+    assert utils.get_password('system', 'user', None, {}) == 'entered pw'
+
+
+@pytest.fixture
+def keyring_no_backends(monkeypatch):
+    """
+    Simulate that keyring has no available backends. When keyring
+    has no backends for the system, the backend will be a
+    fail.Keyring, which raises RuntimeError on get_password.
+    """
+    class FailKeyring(object):
+        @staticmethod
+        def get_password(system, username):
+            raise RuntimeError("fail!")
+    monkeypatch.setitem(sys.modules, 'keyring', FailKeyring())
+
+
+def test_get_password_runtime_error_suppressed(
+        entered_password, keyring_no_backends, recwarn):
+    assert utils.get_password('system', 'user', None, {}) == 'entered pw'
+    assert len(recwarn) == 1
+    warning = recwarn.pop(UserWarning)
+    assert 'fail!' in str(warning)
Repository: pypa/twine
Pull request #428 (2018-11-27 13:22:04)

Fixes --skip-existing with Nexus Repos
Small fix for the option `--skip-existing` when uploading to a [Nexus Repository](https://www.sonatype.com/nexus-repository-oss).

* [X] Ran `tox -e lint`
* [X] Ran tests

Thank you for your great work on _twine_!

theacodes: Thank you, @superwhoopy!

Patch:
diff --git a/twine/commands/upload.py b/twine/commands/upload.py
index b4d3f93..20bb6e0 100644
--- a/twine/commands/upload.py
+++ b/twine/commands/upload.py
@@ -26,10 +26,14 @@ from twine import utils
 
 def skip_upload(response, skip_existing, package):
     filename = package.basefilename
-    # NOTE(sigmavirus24): Old PyPI returns the first message while Warehouse
-    # returns the latter. This papers over the differences.
-    msg_400 = ('A file named "{0}" already exists for'.format(filename),
-               'File already exists')
+    msg_400 = (
+        # Old PyPI message:
+        'A file named "{0}" already exists for'.format(filename),
+        # Warehouse message:
+        'File already exists',
+        # Nexus Repository OSS message:
+        'Repository does not allow updating assets',
+    )
     msg_403 = 'Not enough permissions to overwrite artifact'
     # NOTE(sigmavirus24): PyPI presently returns a 400 status code with the
     # error message in the reason attribute. Other implementations return a
@@ -38,7 +42,7 @@ def skip_upload(response, skip_existing, package):
     #    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. OR
+    #       what we expect PyPI or Nexus OSS to return to us. OR
     # 2. c) The response status code is 403 AND the text matches what we
     #       expect Artifactory to return to us.
     return (skip_existing and (response.status_code == 409 or


Test patch:
diff --git a/tests/test_upload.py b/tests/test_upload.py
index 870a26e..c841400 100644
--- a/tests/test_upload.py
+++ b/tests/test_upload.py
@@ -137,6 +137,20 @@ def test_skip_existing_skips_files_already_on_PyPI(monkeypatch):
                               package=pkg) is True
 
 
+def test_skip_existing_skips_files_already_on_nexus(monkeypatch):
+    # Nexus Repository Manager (https://www.sonatype.com/nexus-repository-oss)
+    # responds with 400 when the file already exists
+    response = pretend.stub(
+        status_code=400,
+        reason="Repository does not allow updating assets: pypi for url: "
+               "http://www.foo.bar")
+
+    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.org/project/pypiserver) responds with a
     # 409 when the file already exists.