def run(self): name = self.distribution.metadata['Name'] version = self.distribution.metadata['Version'] zip_file = zip_dir(self.upload_dir) fields = [(':action', 'doc_upload'), ('name', name), ('version', version)] files = [('content', name, zip_file.getvalue())] content_type, body = encode_multipart(fields, files) credentials = self.username + ':' + self.password # FIXME should use explicit encoding auth = b"Basic " + base64.encodebytes(credentials.encode()).strip() logger.info("Submitting documentation to %s", self.repository) scheme, netloc, url, params, query, fragments = urllib.parse.urlparse( self.repository) if scheme == "http": conn = http.client.HTTPConnection(netloc) elif scheme == "https": conn = http.client.HTTPSConnection(netloc) else: raise AssertionError("unsupported scheme %r" % scheme) try: conn.connect() conn.putrequest("POST", url) conn.putheader('Content-type', content_type) conn.putheader('Content-length', str(len(body))) conn.putheader('Authorization', auth) conn.endheaders() conn.send(body) except socket.error as e: logger.error(e) return r = conn.getresponse() if r.status == 200: logger.info('Server response (%s): %s', r.status, r.reason) elif r.status == 301: location = r.getheader('Location') if location is None: location = 'http://packages.python.org/%s/' % name logger.info('Upload successful. Visit %s', location) else: logger.error('Upload failed (%s): %s', r.status, r.reason) if self.show_response and logger.isEnabledFor(logging.INFO): sep = '-' * 75 logger.info('%s\n%s\n%s', sep, r.read().decode('utf-8'), sep)
def install(project): """Installs a project. Returns True on success, False on failure """ if is_python_build(): # Python would try to install into the site-packages directory under # $PREFIX, but when running from an uninstalled code checkout we don't # want to create directories under the installation root message = ('installing third-party projects from an uninstalled ' 'Python is not supported') logger.error(message) return False logger.info('Checking the installation location...') purelib_path = get_path('purelib') # trying to write a file there try: with tempfile.NamedTemporaryFile(suffix=project, dir=purelib_path) as testfile: testfile.write(b'test') except OSError: # FIXME this should check the errno, or be removed altogether (race # condition: the directory permissions could be changed between here # and the actual install) logger.info('Unable to write in "%s". Do you have the permissions ?' % purelib_path) return False logger.info('Getting information about %r...', project) try: info = get_infos(project) except InstallationException: logger.info('Cound not find %r', project) return False if info['install'] == []: logger.info('Nothing to install') return False install_path = get_config_var('base') try: install_from_infos(install_path, info['install'], info['remove'], info['conflict']) except InstallationConflict as e: if logger.isEnabledFor(logging.INFO): projects = ('%r %s' % (p.name, p.version) for p in e.args[0]) logger.info('%r conflicts with %s', project, ','.join(projects)) return True
def find_config_files(self): """Find as many configuration files as should be processed for this platform, and return a list of filenames in the order in which they should be parsed. The filenames returned are guaranteed to exist (modulo nasty race conditions). There are three possible config files: packaging.cfg in the Packaging installation directory (ie. where the top-level Packaging __inst__.py file lives), a file in the user's home directory named .pydistutils.cfg on Unix and pydistutils.cfg on Windows/Mac; and setup.cfg in the current directory. The file in the user's home directory can be disabled with the --no-user-cfg option. """ files = [] check_environ() # Where to look for the system-wide Packaging config file sys_dir = os.path.dirname(sys.modules['packaging'].__file__) # Look for the system config file sys_file = os.path.join(sys_dir, "packaging.cfg") if os.path.isfile(sys_file): files.append(sys_file) # What to call the per-user config file if os.name == 'posix': user_filename = ".pydistutils.cfg" else: user_filename = "pydistutils.cfg" # And look for the user config file if self.dist.want_user_cfg: user_file = os.path.join(os.path.expanduser('~'), user_filename) if os.path.isfile(user_file): files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file): files.append(local_file) if logger.isEnabledFor(logging.DEBUG): logger.debug("using config files: %s", ', '.join(files)) return files
def set(self, name, value): """Control then set a metadata field.""" name = self._convert_name(name) if ((name in _ELEMENTSFIELD or name == 'Platform') and not isinstance(value, (list, tuple))): if isinstance(value, str): value = [v.strip() for v in value.split(',')] else: value = [] elif (name in _LISTFIELDS and not isinstance(value, (list, tuple))): if isinstance(value, str): value = [value] else: value = [] if logger.isEnabledFor(logging.WARNING): project_name = self['Name'] if name in _PREDICATE_FIELDS and value is not None: for v in value: # check that the values are valid predicates if not is_valid_predicate(v.split(';')[0]): logger.warning( '%r: %r is not a valid predicate (field %r)', project_name, v, name) # FIXME this rejects UNKNOWN, is that right? elif name in _VERSIONS_FIELDS and value is not None: if not is_valid_versions(value): logger.warning('%r: %r is not a valid version (field %r)', project_name, value, name) elif name in _VERSION_FIELDS and value is not None: if not is_valid_version(value): logger.warning('%r: %r is not a valid version (field %r)', project_name, value, name) if name in _UNICODEFIELDS: if name == 'Description': value = self._remove_line_prefix(value) self._fields[name] = value self._set_best_version()
def upload_file(self, command, pyversion, filename): # Makes sure the repository URL is compliant scheme, netloc, url, params, query, fragments = \ urllib.parse.urlparse(self.repository) if params or query or fragments: raise AssertionError("Incompatible url %s" % self.repository) if scheme not in ('http', 'https'): raise AssertionError("unsupported scheme " + scheme) # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] if self.identity: gpg_args[2:2] = ["--local-user", self.identity] spawn(gpg_args, dry_run=self.dry_run) # Fill in the data - send all the metadata in case we need to # register a new release with open(filename, 'rb') as f: content = f.read() data = self.distribution.metadata.todict() # extra upload infos data[':action'] = 'file_upload' data['protcol_version'] = '1' data['content'] = (os.path.basename(filename), content) data['filetype'] = command data['pyversion'] = pyversion data['md5_digest'] = md5(content).hexdigest() if command == 'bdist_dumb': data['comment'] = 'built for %s' % platform.platform(terse=True) if self.sign: with open(filename + '.asc') as fp: sig = fp.read() data['gpg_signature'] = [ (os.path.basename(filename) + ".asc", sig)] # set up the authentication # The exact encoding of the authentication string is debated. # Anyway PyPI only accepts ascii for both username or password. user_pass = (self.username + ":" + self.password).encode('ascii') auth = b"Basic " + standard_b64encode(user_pass) # Build up the MIME payload for the POST data files = [] for key in ('content', 'gpg_signature'): if key in data: filename_, value = data.pop(key) files.append((key, filename_, value)) content_type, body = encode_multipart(data.items(), files) logger.info("Submitting %s to %s", filename, self.repository) # build the Request headers = {'Content-type': content_type, 'Content-length': str(len(body)), 'Authorization': auth} request = Request(self.repository, body, headers) # send the data try: result = urlopen(request) status = result.code reason = result.msg except socket.error as e: logger.error(e) return except HTTPError as e: status = e.code reason = e.msg if status == 200: logger.info('Server response (%s): %s', status, reason) else: logger.error('Upload failed (%s): %s', status, reason) if self.show_response and logger.isEnabledFor(logging.INFO): sep = '-' * 75 logger.info('%s\n%s\n%s', sep, result.read().decode(), sep)