class URLParser(object): """A minimal URL parser and splitter.""" scheme_re = re.compile(r'^(\S+?)://|^(file):') git_ssh_re = re.compile(r'^(\S+?):(.*)') def __init__(self): self.scp = SCP() def get_scheme(self, url): match = self.scheme_re.match(url) if match is not None: return match.group(1) or match.group(2) return '' def is_url(self, url): return bool(self.get_scheme(url)) def is_git_ssh_url(self, url): return (not self.is_url(url) and self.git_ssh_re.match(url) is not None and self.scp.has_host(url)) def abspath(self, url): scheme = self.get_scheme(url) if scheme == 'file': ignored, user, host, path, qs, frag = self.split(url) if host in ('', 'localhost'): # Strip leading slash to allow tilde expansion if host and path.startswith('/~'): path = path[1:] path = abspath(expanduser(path)) host = self._hostunsplit(user, host) return urlunsplit((scheme, host, path, qs, frag)) return url def split(self, url): scheme = self.get_scheme(url) if scheme: ignored, host, path, qs, frag = urlsplit(url) user, host = self._hostsplit(host) return scheme, user, host, path, qs, frag return '', '', '', url, '', '' def _hostsplit(self, host): if '@' in host: return host.split('@', 1) return '', host def _hostunsplit(self, user, host): if user: return '%s@%s' % (user, host) return host
class Locations(object): def __init__(self, defaults): self.distbase = defaults.distbase self.distdefault = defaults.distdefault self.aliases = defaults.aliases self.servers = defaults.servers self.locations = [] self.scp = SCP() def __len__(self): """Return number of locations. """ return len(self.locations) def __iter__(self): """Iterate over locations. """ return iter(self.locations) def extend(self, location): """Extend list of locations. """ self.locations.extend(location) def is_server(self, location): """Return True if 'location' is an index server. """ return location in self.servers def has_host(self, location): """Return True if 'location' contains a host part. """ return self.scp.has_host(location) def join(self, distbase, location): """Join 'distbase' and 'location' in such way that the result is a valid scp destination. """ return self.scp.join(distbase, location) def get_location(self, location, depth=0): """Resolve aliases and apply distbase. """ if not location: return [] if location in self.aliases: res = [] if depth > MAXALIASDEPTH: err_exit('Maximum alias depth exceeded: %(location)s' % locals()) for loc in self.aliases[location]: res.extend(self.get_location(loc, depth+1)) return res if self.is_server(location): return [location] if location == 'pypi': err_exit('No configuration found for server: pypi\n' 'Please create a ~/.pypirc file') if not self.has_host(location) and self.distbase: return [self.join(self.distbase, location)] return [location] def get_default_location(self): """Return the default location. """ return self.get_location(self.distdefault) def check_valid_locations(self, locations=None): """Fail if 'locations' is empty or contains bad scp destinations. """ if locations is None: locations = self.locations if not locations: err_exit('mkrelease: option -d is required\n%s' % USAGE) for location in locations: if not self.is_server(location) and not self.has_host(location): err_exit('Scp destination must contain a host part: %(location)s' % locals())