def test_get_repository_config_missing_file(repository): """Raise an exception when a custom config file doesn't exist.""" with pytest.raises( exceptions.InvalidConfiguration, match=r"No such file.*missing-file", ): utils.get_repository_from_config("missing-file", repository)
def test_get_repository_config_with_invalid_url(config_file, repo_url, message): """Raise an exception for a URL with an invalid/missing scheme and/or host.""" with pytest.raises( exceptions.UnreachableRepositoryURLDetected, match=message, ): utils.get_repository_from_config(config_file, "pypi", repo_url)
def test_get_repository_config_missing_repository(write_config_file): """Raise an exception when a custom repository isn't defined in .pypirc.""" config_file = write_config_file("") with pytest.raises( exceptions.InvalidConfiguration, match="Missing 'missing-repository'", ): utils.get_repository_from_config(config_file, "missing-repository")
def test_get_repository_config_missing_config(tmpdir): """ Test if a invalid section is being looked for in the config file """ pypirc = os.path.join(str(tmpdir), ".pypirc") with pytest.raises(exceptions.InvalidConfiguration): utils.get_repository_from_config(pypirc, "foobar")
def test_get_repository_config_with_invalid_url(tmpdir, repo_url, message): """Raise an exception for a URL with an invalid/missing scheme and/or host.""" pypirc = os.path.join(str(tmpdir), ".pypirc") with pytest.raises( exceptions.UnreachableRepositoryURLDetected, match=message, ): utils.get_repository_from_config(pypirc, "pypi", repo_url)
def test_get_repository_config_invalid_url(tmpdir): """ Test if we get an URL without a protocol """ pypirc = os.path.join(str(tmpdir), ".pypirc") repository_url = "foo.bar" with pytest.raises(exceptions.UnreachableRepositoryURLDetected): utils.get_repository_from_config(pypirc, "foo.bar", repository_url)
def test_get_repository_config_invalid_scheme(tmpdir): """Test if we get an URL with a invalid scheme""" pypirc = os.path.join(str(tmpdir), ".pypirc") with pytest.raises( exceptions.UnreachableRepositoryURLDetected, match=r"Invalid repository URL: " r"scheme was required to be one of \['http', 'https'\] but was 'ftp'.", ): utils.get_repository_from_config(pypirc, "foo.bar", "ftp://test.pypi.org")
def register(package, repository, username, password, comment, config_file, cert, client_cert): config = utils.get_repository_from_config(config_file, repository) config["repository"] = utils.normalize_repository_url(config["repository"]) print("Registering package to {0}".format(config["repository"])) username = utils.get_username(username, config) password = utils.get_password(password, config) ca_cert = utils.get_cacert(cert, config) client_cert = utils.get_clientcert(client_cert, config) repository = Repository(config["repository"], username, password) repository.set_certificate_authority(ca_cert) repository.set_client_certificate(client_cert) if not os.path.exists(package): raise exc.PackageNotFound( '"{0}" does not exist on the file system.'.format(package)) resp = repository.register(PackageFile.from_filename(package, comment)) repository.close() if resp.is_redirect: raise exc.RedirectDetected( ('"{0}" attempted to redirect to "{1}" during registration.' ' Aborting...').format(config["repository"], resp.headers["location"])) resp.raise_for_status()
def register(package, repository, username, password, comment, config_file, cert, client_cert): config = utils.get_repository_from_config(config_file, repository) config["repository"] = utils.normalize_repository_url( config["repository"] ) print("Registering package to {0}".format(config["repository"])) username = utils.get_username(username, config) password = utils.get_password(password, config) ca_cert = utils.get_cacert(cert, config) client_cert = utils.get_clientcert(client_cert, config) repository = Repository(config["repository"], username, password, ca_cert, client_cert) if not os.path.exists(package): raise exc.PackageNotFound( '"{0}" does not exist on the file system.'.format(package) ) resp = repository.register(PackageFile.from_filename(package, comment)) repository.close() if resp.is_redirect: raise exc.RedirectDetected( ('"{0}" attempted to redirect to "{1}" during upload.' ' Aborting...').format(config["respository"], resp.headers["location"])) resp.raise_for_status()
def upload(dists, repository, sign, identity, username, password, comment, sign_with, config_file, skip_existing, cert, client_cert): # Check that a nonsensical option wasn't given if not sign and identity: raise ValueError("sign must be given along with identity") 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) config["repository"] = utils.normalize_repository_url(config["repository"]) print("Uploading distributions to {0}".format(config["repository"])) username = utils.get_username(username, config) password = utils.get_password(password, config) ca_cert = utils.get_cacert(cert, config) client_cert = utils.get_clientcert(client_cert, config) repository = Repository(config["repository"], username, password) repository.set_certificate_authority(ca_cert) repository.set_client_certificate(client_cert) for filename in uploads: package = PackageFile.from_filename(filename, comment) 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"])) # Otherwise, raise an HTTPError based on the status code. if skip_upload(resp, skip_existing, package): print(" Skipping {0} because it appears to already exist".format( package.basefilename)) continue resp.raise_for_status() # Bug 28. Try to silence a ResourceWarning by clearing the connection # pool. repository.close()
def test_get_repository_config_missing(config_file): repository_url = "https://notexisting.python.org/pypi" exp = { "repository": repository_url, "username": None, "password": None, } assert utils.get_repository_from_config(config_file, "foo", repository_url) == exp assert utils.get_repository_from_config(config_file, "pypi", repository_url) == exp exp = { "repository": utils.DEFAULT_REPOSITORY, "username": None, "password": None, } assert utils.get_repository_from_config(config_file, "pypi") == exp
def upload(dists, repository, sign, identity, username, password, comment, sign_with, config_file, skip_existing): # 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) config["repository"] = utils.normalize_repository_url( config["repository"] ) print("Uploading distributions to {0}".format(config["repository"])) username = utils.get_username(username, config) password = utils.get_password(password, config) repository = Repository(config["repository"], username, password) for filename in uploads: package = PackageFile.from_filename(filename, comment) 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"])) # Otherwise, raise an HTTPError based on the status code. if skip_upload(resp, skip_existing, package): print(" Skipping {0} because it appears to already exist".format( package.basefilename)) continue resp.raise_for_status() # Bug 28. Try to silence a ResourceWarning by clearing the connection # pool. repository.close()
def _handle_repository_options(self, repository_name, repository_url): self.repository_config = utils.get_repository_from_config( self.config_file, repository_name, repository_url, ) self.repository_config['repository'] = utils.normalize_repository_url( self.repository_config['repository'], )
def test_get_repository_config_missing(tmpdir): pypirc = os.path.join(str(tmpdir), ".pypirc") repository_url = "https://notexisting.python.org/pypi" exp = { "repository": repository_url, "username": None, "password": None, } assert (utils.get_repository_from_config(pypirc, 'foo', repository_url) == exp) exp = { "repository": utils.DEFAULT_REPOSITORY, "username": None, "password": None, } assert utils.get_repository_from_config(pypirc, "pypi") == exp
def _handle_repository_options( self, repository_name: str, repository_url: Optional[str] ) -> None: self.repository_config = utils.get_repository_from_config( self.config_file, repository_name, repository_url, ) self.repository_config["repository"] = utils.normalize_repository_url( cast(str, self.repository_config["repository"]), )
def _handle_repository_options(self, repository_name, repository_url): self.repository_config = utils.get_repository_from_config( self.config_file, repository_name, repository_url, ) self.repository_config['repository'] = utils.normalize_repository_url( self.repository_config['repository'], )
def test_get_repository_config_url_precendence(tmpdir): pypirc = os.path.join(str(tmpdir), ".pypirc") repository_url = "https://notexisting.python.org/pypi" exp = { "repository": repository_url, "username": None, "password": None, } assert (utils.get_repository_from_config(pypirc, 'pypi', repository_url) == exp)
def test_get_repository_config_missing_components(tmpdir): """Test if we get an URL with missing components""" pypirc = os.path.join(str(tmpdir), ".pypirc") with pytest.raises( exceptions.UnreachableRepositoryURLDetected, match="Invalid repository URL: host was required but missing.", ): utils.get_repository_from_config(pypirc, "foo.bar", "https:/") with pytest.raises( exceptions.UnreachableRepositoryURLDetected, match="Invalid repository URL: scheme was required but missing.", ): utils.get_repository_from_config(pypirc, "foo.bar", "//test.pypi.org") with pytest.raises( exceptions.UnreachableRepositoryURLDetected, match= "Invalid repository URL: host, scheme were required but missing.", ): utils.get_repository_from_config(pypirc, "foo.bar", "foo.bar")
def test_get_repository_config_missing_config(tmpdir): """Raise an exception when a repository isn't defined in .pypirc.""" pypirc = os.path.join(str(tmpdir), ".pypirc") with pytest.raises(exceptions.InvalidConfiguration): utils.get_repository_from_config(pypirc, "foobar")
def main(args: List[str]) -> None: # TODO: repository auth parser = argparse.ArgumentParser( prog="twine exists", description="Check existence of distribution on the remote, compare " "and validate hashes.") parser.add_argument( "dists", nargs="+", metavar="dist", help="The distribution files to check, usually dist/*", ) parser.add_argument( "-r", "--repository", action=EnvironmentDefault, env="TWINE_REPOSITORY", default="pypi", help="The repository (package index) to check the package against. " "Should be a section in the config file (default: " "%(default)s). (Can also be set via %(env)s environment " "variable.)", ) parser.add_argument( "--repository-url", action=EnvironmentDefault, env="TWINE_REPOSITORY_URL", default=None, required=False, help="The repository (package index) URL to check the package against. " "This overrides --repository. " "(Can also be set via %(env)s environment variable.)", ) parser.add_argument( "--config-file", default="~/.pypirc", help="The .pypirc config file to use.", ) # TODO: reword below # TODO: formatting of the list below # TODO: mention the fact, that for exists and missing the API doesn't have # to return hashes. parser.add_argument( "--fail-when", choices=failure_conditions.keys(), default=default_failure_condition, required=False, action="store", help="Set the condition by which exit status different than 0 is " "returned. Possible choices are: " "missing - when a package is absent, " "exists - when a package is present, " "different - when cheksums doesn't match, " "miss (default) - when hashes doesn't match or the package is absent.") parser.add_argument("-q", "--quiet", default=False, required=False, action="store_true", help="Do not write anything to standard output.") parsed_args = parser.parse_args(args) repository_url = get_repository_from_config( parsed_args.config_file, parsed_args.repository, parsed_args.repository_url)["repository"] repository_url = cast(str, repository_url) repository_url = normalize_repository_url(repository_url) condition = failure_conditions[parsed_args.fail_when] if parsed_args.quiet: sys.stdout = open(os.devnull, 'a') sys.stderr = open(os.devnull, 'a') return exists(parsed_args.dists, repository_url, failure_checker=condition, quiet=parsed_args.quiet)
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()
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"])) 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()