def check_status_code(response: requests.Response, verbose: bool) -> None: """Generate a helpful message based on the response from the repository. Raise a custom exception for recognized errors. Otherwise, print the response content (based on the verbose option) before re-raising the HTTPError. """ if response.status_code == 410 and "pypi.python.org" in response.url: raise exceptions.UploadToDeprecatedPyPIDetected( f"It appears you're uploading to pypi.python.org (or " f"testpypi.python.org). You've received a 410 error response. " f"Uploading to those sites is deprecated. The new sites are " f"pypi.org and test.pypi.org. Try using {DEFAULT_REPOSITORY} (or " f"{TEST_REPOSITORY}) to upload your packages instead. These are " f"the default URLs for Twine now. More at " f"https://packaging.python.org/guides/migrating-to-pypi-org/.") elif response.status_code == 405 and "pypi.org" in response.url: raise exceptions.InvalidPyPIUploadURL( f"It appears you're trying to upload to pypi.org but have an " f"invalid URL. You probably want one of these two URLs: " f"{DEFAULT_REPOSITORY} or {TEST_REPOSITORY}. Check your " f"--repository-url value.") try: response.raise_for_status() except requests.HTTPError as err: if response.text: logger.info("Content received from server:\n{}".format( response.text)) if not verbose: logger.warning("NOTE: Try --verbose to see response content.") raise err
def upload(dists, repository, sign, identity, username, password, comment, sign_with, config_file, skip_existing, cert, client_cert, repository_url): # Check that a nonsensical option wasn't given if not sign and identity: raise ValueError("sign must be given along with identity") dists = find_dists(dists) # Determine if the user has passed in pre-signed distributions signatures = dict( (os.path.basename(d), d) for d in dists if d.endswith(".asc") ) uploads = [i for i in dists if not i.endswith(".asc")] config = utils.get_repository_from_config( config_file, repository, repository_url, ) config["repository"] = utils.normalize_repository_url( config["repository"] ) print("Uploading distributions to {0}".format(config["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( config["repository"], username, password, config, ) ca_cert = utils.get_cacert(cert, config) client_cert = utils.get_clientcert(client_cert, config) repository = Repository(config["repository"], username, password) repository.set_certificate_authority(ca_cert) repository.set_client_certificate(client_cert) for filename in uploads: package = PackageFile.from_filename(filename, comment) skip_message = ( " Skipping {0} because it appears to already exist".format( package.basefilename) ) # Note: The skip_existing check *needs* to be first, because otherwise # we're going to generate extra HTTP requests against a hardcoded # URL for no reason. if skip_existing and repository.package_is_uploaded(package): print(skip_message) continue signed_name = package.signed_basefilename if signed_name in signatures: package.add_gpg_signature(signatures[signed_name], signed_name) elif sign: package.sign(sign_with, identity) resp = repository.upload(package) # Bug 92. If we get a redirect we should abort because something seems # funky. The behaviour is not well defined and redirects being issued # by PyPI should never happen in reality. This should catch malicious # redirects as well. if resp.is_redirect: raise exc.RedirectDetected( ('"{0}" attempted to redirect to "{1}" during upload.' ' Aborting...').format(config["repository"], resp.headers["location"])) if skip_upload(resp, skip_existing, package): print(skip_message) continue utils.check_status_code(resp) # Bug 28. Try to silence a ResourceWarning by clearing the connection # pool. repository.close()