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 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 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 test_get_username_and_password_keyring_overrides_prompt(monkeypatch): import collections Credential = collections.namedtuple('Credential', 'username password') class MockKeyring: @staticmethod def get_credential(system, user): return Credential( 'real_user', 'real_user@{system} sekure pa55word'.format(**locals()) ) @staticmethod def get_password(system, user): cred = MockKeyring.get_credential(system, user) if user != cred.username: raise RuntimeError("unexpected username") return cred.password monkeypatch.setitem(sys.modules, 'keyring', MockKeyring) user = utils.get_username('system', None, {}) assert user == 'real_user' pw = utils.get_password('system', user, None, {}) assert pw == 'real_user@system sekure pa55word'
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_authentication(self, username, password): self.username = utils.get_username(username, self.repository_config) self.password = utils.get_password( self.repository_config['repository'], self.username, password, self.repository_config, )
def test_get_password_keyring_overrides_prompt(monkeypatch): class MockKeyring: @staticmethod def get_password(system, user): return '{user}@{system} sekure pa55word'.format(**locals()) monkeypatch.setitem(sys.modules, 'keyring', MockKeyring) pw = utils.get_password('system', 'user', None, {}) assert pw == 'user@system sekure pa55word'
def test_get_password_keyring_defers_to_prompt(monkeypatch): monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw') class MockKeyring: @staticmethod def get_password(system, user): return monkeypatch.setitem(sys.modules, 'keyring', MockKeyring) pw = utils.get_password('system', 'user', None, {}) assert pw == 'entered pw'
def _handle_authentication(self, username, password): self.username = utils.get_username( self.repository_config['repository'], username, self.repository_config ) self.password = utils.get_password( self.repository_config['repository'], self.username, password, self.repository_config, )
def _handle_authentication(self, username, password, non_interactive): self.username = utils.get_username( self.repository_config['repository'], username, self.repository_config, non_interactive, ) self.password = utils.get_password( self.repository_config['repository'], self.username, password, self.repository_config, non_interactive, )
def test_get_password_keyring_missing_non_interactive_aborts( entered_username, keyring_missing_get_credentials): with pytest.raises(exceptions.NonInteractive): utils.get_password('system', 'user', None, {}, True)
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()
def test_no_password_defers_to_prompt(monkeypatch): monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw') pw = utils.get_password('system', 'user', None, {'password': None}) assert pw == 'entered pw'
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): # Check that a nonsensical option wasn't given if not sign and identity: raise ValueError("sign must be given along with identity") # 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")) dists = [i for i in dists if not i.endswith(".asc")] # Get our config from ~/.pypirc try: config = get_config()[repository] except KeyError: raise KeyError( "Missing '{0}' section from the configuration file".format( repository, ), ) parsed = urlparse(config["repository"]) if parsed.netloc in ["pypi.python.org", "testpypi.python.org"]: config["repository"] = urlunparse(("https", ) + parsed[1:]) print("Uploading distributions to {0}".format(config["repository"])) username = get_username(username, config) password = get_password(password, config) session = requests.session() uploads = find_dists(dists) for filename in uploads: # Sign the dist if requested if sign: sign_file(sign_with, filename, identity) # Extract the metadata from the package for ext, dtype in DIST_EXTENSIONS.items(): if filename.endswith(ext): meta = DIST_TYPES[dtype](filename) break else: raise ValueError("Unknown distribution format: '%s'" % os.path.basename(filename)) if dtype == "bdist_egg": pkgd = pkg_resources.Distribution.from_filename(filename) py_version = pkgd.py_version elif dtype == "bdist_wheel": py_version = meta.py_version elif dtype == "bdist_wininst": py_version = meta.py_version else: py_version = None # Fill in the data - send all the meta-data in case we need to # register a new release data = { # action ":action": "file_upload", "protcol_version": "1", # identify release "name": pkg_resources.safe_name(meta.name), "version": meta.version, # file content "filetype": dtype, "pyversion": py_version, # additional meta-data "metadata_version": meta.metadata_version, "summary": meta.summary, "home_page": meta.home_page, "author": meta.author, "author_email": meta.author_email, "maintainer": meta.maintainer, "maintainer_email": meta.maintainer_email, "license": meta.license, "description": meta.description, "keywords": meta.keywords, "platform": meta.platforms, "classifiers": meta.classifiers, "download_url": meta.download_url, "supported_platform": meta.supported_platforms, "comment": comment, # PEP 314 "provides": meta.provides, "requires": meta.requires, "obsoletes": meta.obsoletes, # Metadata 1.2 "project_urls": meta.project_urls, "provides_dist": meta.provides_dist, "obsoletes_dist": meta.obsoletes_dist, "requires_dist": meta.requires_dist, "requires_external": meta.requires_external, "requires_python": meta.requires_python, } with open(filename, "rb") as fp: content = fp.read() filedata = { "content": (os.path.basename(filename), content), } data["md5_digest"] = hashlib.md5(content).hexdigest() signed_name = os.path.basename(filename) + ".asc" if signed_name in signatures: with open(signatures[signed_name], "rb") as gpg: filedata["gpg_signature"] = (signed_name, gpg.read()) elif sign: with open(filename + ".asc", "rb") as gpg: filedata["gpg_signature"] = (signed_name, gpg.read()) print("Uploading {0}".format(os.path.basename(filename))) resp = session.post( config["repository"], data=dict((k, v) for k, v in data.items() if v), files=filedata, auth=(username, password), ) # Bug 28. Try to silence a ResourceWarning by releasing the socket and # clearing the connection pool. resp.close() session.close() resp.raise_for_status()
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)
def upload(dists, repository, sign, identity, username, password, comment, sign_with, config_file): # Check that a nonsensical option wasn't given if not sign and identity: raise ValueError("sign must be given along with identity") # 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") ) dists = [i for i in dists if not i.endswith(".asc")] # Get our config from the .pypirc file try: config = get_config(config_file)[repository] except KeyError: 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"]: config["repository"] = urlunparse( ("https",) + parsed[1:] ) print("Uploading distributions to {0}".format(config["repository"])) username = get_username(username, config) password = get_password(password, config) session = requests.session() uploads = find_dists(dists) for filename in uploads: # Sign the dist if requested if sign: sign_file(sign_with, filename, identity) # Extract the metadata from the package for ext, dtype in DIST_EXTENSIONS.items(): if filename.endswith(ext): meta = DIST_TYPES[dtype](filename) break else: raise ValueError( "Unknown distribution format: '%s'" % os.path.basename(filename) ) if dtype == "bdist_egg": pkgd = pkg_resources.Distribution.from_filename(filename) py_version = pkgd.py_version elif dtype == "bdist_wheel": py_version = meta.py_version elif dtype == "bdist_wininst": py_version = meta.py_version else: py_version = None # Fill in the data - send all the meta-data in case we need to # register a new release data = { # action ":action": "file_upload", "protcol_version": "1", # identify release "name": pkg_resources.safe_name(meta.name), "version": meta.version, # file content "filetype": dtype, "pyversion": py_version, # additional meta-data "metadata_version": meta.metadata_version, "summary": meta.summary, "home_page": meta.home_page, "author": meta.author, "author_email": meta.author_email, "maintainer": meta.maintainer, "maintainer_email": meta.maintainer_email, "license": meta.license, "description": meta.description, "keywords": meta.keywords, "platform": meta.platforms, "classifiers": meta.classifiers, "download_url": meta.download_url, "supported_platform": meta.supported_platforms, "comment": comment, # PEP 314 "provides": meta.provides, "requires": meta.requires, "obsoletes": meta.obsoletes, # Metadata 1.2 "project_urls": meta.project_urls, "provides_dist": meta.provides_dist, "obsoletes_dist": meta.obsoletes_dist, "requires_dist": meta.requires_dist, "requires_external": meta.requires_external, "requires_python": meta.requires_python, } md5_hash = hashlib.md5() with open(filename, "rb") as fp: content = fp.read(4096) while content: md5_hash.update(content) content = fp.read(4096) data["md5_digest"] = md5_hash.hexdigest() signed_name = os.path.basename(filename) + ".asc" if signed_name in signatures: with open(signatures[signed_name], "rb") as gpg: data["gpg_signature"] = (signed_name, gpg.read()) elif sign: with open(filename + ".asc", "rb") as gpg: data["gpg_signature"] = (signed_name, gpg.read()) print("Uploading {0}".format(os.path.basename(filename))) data_to_send = [] for key, value in data.items(): if isinstance(value, (list, tuple)): for item in value: data_to_send.append((key, item)) else: data_to_send.append((key, value)) with open(filename, "rb") as fp: data_to_send.append(( "content", (os.path.basename(filename), fp, "application/octet-stream"), )) encoder = MultipartEncoder(data_to_send) resp = session.post( config["repository"], data=encoder, auth=(username, password), allow_redirects=False, headers={'Content-Type': encoder.content_type}, ) # Bug 28. Try to silence a ResourceWarning by releasing the socket and # clearing the connection pool. resp.close() session.close() # 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["respository"], resp.headers["location"])) # Otherwise, raise an HTTPError based on the status code. resp.raise_for_status()
def test_get_password_keyring_missing_prompts( entered_password, keyring_missing): assert utils.get_password('system', 'user', None, {}) == 'entered pw'
def test_empty_password_bypasses_prompt(monkeypatch): monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw') pw = utils.get_password('system', 'user', None, {'password': ''}) assert pw == ''
def test_get_password_keyring_missing_prompts(monkeypatch, keyring_missing): monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw') pw = utils.get_password('system', 'user', None, {}) assert pw == 'entered pw'
def test_no_password_non_interactive_aborts(): with pytest.raises(exceptions.NonInteractive): utils.get_password('system', 'user', None, {'password': None}, True)
def upload(dists, repository, sign, identity, username, password, comment, sign_with, config_file): # Check that a nonsensical option wasn't given if not sign and identity: raise ValueError("sign must be given along with identity") # 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") ) dists = [i for i in dists if not i.endswith(".asc")] # Get our config from the .pypirc file try: config = get_config(config_file)[repository] except KeyError: raise KeyError( "Missing '{0}' section from the configuration file".format( repository, ), ) parsed = urlparse(config["repository"]) if parsed.netloc in ["pypi.python.org", "testpypi.python.org"]: config["repository"] = urlunparse( ("https",) + parsed[1:] ) print("Uploading distributions to {0}".format(config["repository"])) username = get_username(username, config) password = get_password(password, config) session = requests.session() uploads = find_dists(dists) for filename in uploads: # Sign the dist if requested if sign: sign_file(sign_with, filename, identity) # Extract the metadata from the package for ext, dtype in DIST_EXTENSIONS.items(): if filename.endswith(ext): meta = DIST_TYPES[dtype](filename) break else: raise ValueError( "Unknown distribution format: '%s'" % os.path.basename(filename) ) if dtype == "bdist_egg": pkgd = pkg_resources.Distribution.from_filename(filename) py_version = pkgd.py_version elif dtype == "bdist_wheel": py_version = meta.py_version elif dtype == "bdist_wininst": py_version = meta.py_version else: py_version = None # Fill in the data - send all the meta-data in case we need to # register a new release data = { # action ":action": "file_upload", "protcol_version": "1", # identify release "name": pkg_resources.safe_name(meta.name), "version": meta.version, # file content "filetype": dtype, "pyversion": py_version, # additional meta-data "metadata_version": meta.metadata_version, "summary": meta.summary, "home_page": meta.home_page, "author": meta.author, "author_email": meta.author_email, "maintainer": meta.maintainer, "maintainer_email": meta.maintainer_email, "license": meta.license, "description": meta.description, "keywords": meta.keywords, "platform": meta.platforms, "classifiers": meta.classifiers, "download_url": meta.download_url, "supported_platform": meta.supported_platforms, "comment": comment, # PEP 314 "provides": meta.provides, "requires": meta.requires, "obsoletes": meta.obsoletes, # Metadata 1.2 "project_urls": meta.project_urls, "provides_dist": meta.provides_dist, "obsoletes_dist": meta.obsoletes_dist, "requires_dist": meta.requires_dist, "requires_external": meta.requires_external, "requires_python": meta.requires_python, } with open(filename, "rb") as fp: content = fp.read() filedata = { "content": (os.path.basename(filename), content), } data["md5_digest"] = hashlib.md5(content).hexdigest() signed_name = os.path.basename(filename) + ".asc" if signed_name in signatures: with open(signatures[signed_name], "rb") as gpg: filedata["gpg_signature"] = (signed_name, gpg.read()) elif sign: with open(filename + ".asc", "rb") as gpg: filedata["gpg_signature"] = (signed_name, gpg.read()) print("Uploading {0}".format(os.path.basename(filename))) resp = session.post( config["repository"], data=dict((k, v) for k, v in data.items() if v), files=filedata, auth=(username, password), ) # Bug 28. Try to silence a ResourceWarning by releasing the socket and # clearing the connection pool. resp.close() session.close() resp.raise_for_status()
def upload(dists, repository, sign, identity, username, password, comment, sign_with, config_file): # Check that a nonsensical option wasn't given if not sign and identity: raise ValueError("sign must be given along with identity") # 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")) dists = [i for i in dists if not i.endswith(".asc")] # Get our config from the .pypirc file try: config = get_config(config_file)[repository] except KeyError: raise KeyError( "Missing '{0}' section from the configuration file".format( repository, ), ) parsed = urlparse(config["repository"]) if parsed.netloc in ["pypi.python.org", "testpypi.python.org"]: config["repository"] = urlunparse(("https", ) + parsed[1:]) print("Uploading distributions to {0}".format(config["repository"])) username = get_username(username, config) password = get_password(password, config) session = requests.session() uploads = find_dists(dists) for filename in uploads: # Sign the dist if requested if sign: sign_file(sign_with, filename, identity) # Extract the metadata from the package for ext, dtype in DIST_EXTENSIONS.items(): if filename.endswith(ext): meta = DIST_TYPES[dtype](filename) break else: raise ValueError("Unknown distribution format: '%s'" % os.path.basename(filename)) if dtype == "bdist_egg": pkgd = pkg_resources.Distribution.from_filename(filename) py_version = pkgd.py_version elif dtype == "bdist_wheel": py_version = meta.py_version elif dtype == "bdist_wininst": py_version = meta.py_version else: py_version = None # Fill in the data - send all the meta-data in case we need to # register a new release data = { # action ":action": "file_upload", "protcol_version": "1", # identify release "name": pkg_resources.safe_name(meta.name), "version": meta.version, # file content "filetype": dtype, "pyversion": py_version, # additional meta-data "metadata_version": meta.metadata_version, "summary": meta.summary, "home_page": meta.home_page, "author": meta.author, "author_email": meta.author_email, "maintainer": meta.maintainer, "maintainer_email": meta.maintainer_email, "license": meta.license, "description": meta.description, "keywords": meta.keywords, "platform": meta.platforms, "classifiers": meta.classifiers, "download_url": meta.download_url, "supported_platform": meta.supported_platforms, "comment": comment, # PEP 314 "provides": meta.provides, "requires": meta.requires, "obsoletes": meta.obsoletes, # Metadata 1.2 "project_urls": meta.project_urls, "provides_dist": meta.provides_dist, "obsoletes_dist": meta.obsoletes_dist, "requires_dist": meta.requires_dist, "requires_external": meta.requires_external, "requires_python": meta.requires_python, } md5_hash = hashlib.md5() with open(filename, "rb") as fp: content = fp.read(4096) while content: md5_hash.update(content) content = fp.read(4096) data["md5_digest"] = md5_hash.hexdigest() signed_name = os.path.basename(filename) + ".asc" if signed_name in signatures: with open(signatures[signed_name], "rb") as gpg: data["gpg_signature"] = (signed_name, gpg.read()) elif sign: with open(filename + ".asc", "rb") as gpg: data["gpg_signature"] = (signed_name, gpg.read()) print("Uploading {0}".format(os.path.basename(filename))) data_to_send = [] for key, value in data.items(): if isinstance(value, (list, tuple)): for item in value: data_to_send.append((key, item)) else: data_to_send.append((key, value)) with open(filename, "rb") as fp: data_to_send.append(( "content", (os.path.basename(filename), fp, "application/octet-stream"), )) encoder = MultipartEncoder(data_to_send) resp = session.post( config["repository"], data=encoder, auth=(username, password), allow_redirects=False, headers={'Content-Type': encoder.content_type}, ) # Bug 28. Try to silence a ResourceWarning by releasing the socket and # clearing the connection pool. resp.close() session.close() # 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["respository"], resp.headers["location"])) # Otherwise, raise an HTTPError based on the status code. resp.raise_for_status()