Repository: pypa/twine

Issue:
notice obsolete repository settings and warn user more usefully
Following up on @tdicola's and others' comments in https://github.com/pypa/twine/issues/200#issuecomment-346537836 , #270, #258, here's a feature request:

If Twine notices that it's getting an obsolete `repository` setting (in particular `https://pypi.python.org/pypi`) from a `[repository]` flag in `.pypirc`, 

>  It would be really, really nice for twine to catch this case and give an actionable error since previous packaging instructions told people to add that repository line, etc.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pypa/twine/pull/322?src=pr&el=h1) Report
> Merging [#322](https://codecov.io/gh/pypa/twine/pull/322?src=pr&el=desc) into [master](https://codecov.io/gh/pypa/twine/commit/5199edbdce443ff8e0942424157820854c1cebf4?src=pr&el=desc) will **increase** coverage by `1.07%`.
> The diff coverage is `75%`.

[![Impacted file tree graph](https://codecov.io/gh/pypa/twine/pull/322/graphs/tree.svg?width=650&height=150&src=pr&token=NrvN4iFbj6)](https://codecov.io/gh/pypa/twine/pull/322?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #322      +/-   ##
==========================================
+ Coverage   68.51%   69.58%   +1.07%     
==========================================
  Files          12       12              
  Lines         578      582       +4     
  Branches       91       91              
==========================================
+ Hits          396      405       +9     
+ Misses        156      150       -6     
- Partials       26       27       +1
```


| [Impacted Files](https://codecov.io/gh/pypa/twine/pull/322?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [twine/utils.py](https://codecov.io/gh/pypa/twine/pull/322/diff?src=pr&el=tree#diff-dHdpbmUvdXRpbHMucHk=) | `87.12% <0%> (+1.98%)` | :arrow_up: |
| [twine/repository.py](https://codecov.io/gh/pypa/twine/pull/322/diff?src=pr&el=tree#diff-dHdpbmUvcmVwb3NpdG9yeS5weQ==) | `60.57% <100%> (+0.38%)` | :arrow_up: |
| [twine/exceptions.py](https://codecov.io/gh/pypa/twine/pull/322/diff?src=pr&el=tree#diff-dHdpbmUvZXhjZXB0aW9ucy5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [twine/commands/upload.py](https://codecov.io/gh/pypa/twine/pull/322/diff?src=pr&el=tree#diff-dHdpbmUvY29tbWFuZHMvdXBsb2FkLnB5) | `65.47% <66.66%> (+3.57%)` | :arrow_up: |
| [twine/wininst.py](https://codecov.io/gh/pypa/twine/pull/322/diff?src=pr&el=tree#diff-dHdpbmUvd2luaW5zdC5weQ==) | `29.72% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pypa/twine/pull/322?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pypa/twine/pull/322?src=pr&el=footer). Last update [5199edb...2697917](https://codecov.io/gh/pypa/twine/pull/322?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 8c88876..4885afa 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,8 @@
 Changelog
 =========
 
+* :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.
diff --git a/twine/commands/upload.py b/twine/commands/upload.py
index 9766893..e2ebdba 100644
--- a/twine/commands/upload.py
+++ b/twine/commands/upload.py
@@ -21,7 +21,7 @@ import sys
 
 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 @@ def upload(dists, repository, sign, identity, username, password, comment,
 
     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(
diff --git a/twine/exceptions.py b/twine/exceptions.py
index 5783a9d..af0401c 100644
--- a/twine/exceptions.py
+++ b/twine/exceptions.py
@@ -30,3 +30,9 @@ class PackageNotFound(Exception):
     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
diff --git a/twine/repository.py b/twine/repository.py
index 7d87d2d..74e1968 100644
--- a/twine/repository.py
+++ b/twine/repository.py
@@ -31,6 +31,7 @@ import twine
 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 --git a/twine/utils.py b/twine/utils.py
index 6f2ac30..5989de0 100644
--- a/twine/utils.py
+++ b/twine/utils.py
@@ -140,11 +140,15 @@ def normalize_repository_url(url):
 
 
 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.python.org). You've received a 500 error response. "
+              "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/ "


Test patch:
diff --git a/tests/test_upload.py b/tests/test_upload.py
index 12ad867..55c20df 100644
--- a/tests/test_upload.py
+++ b/tests/test_upload.py
@@ -20,7 +20,7 @@ import pretend
 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 @@ def test_get_config_old_format(tmpdir):
         ).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,