1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
import sys
import posixpath
from devpi_common.types import cached_property, ensure_unicode, parse_hash_spec
from requests.models import parse_url
if sys.version_info >= (3, 0):
from urllib.parse import urlparse, urlunsplit, urljoin, unquote
else:
from urlparse import urlparse, urlunsplit, urljoin
from urllib import unquote
def _joinpath(url, args, asdir=False):
new = url
for arg in args[:-1]:
new = urljoin(new, arg.rstrip("/")) + "/"
new = urljoin(new, args[-1])
if asdir:
new = new.rstrip("/") + "/"
return new
class URL:
def __init__(self, url="", *args, **kwargs):
if isinstance(url, URL):
url = url.url
if args:
url = _joinpath(url, args, **kwargs)
if not url:
url = ""
self.url = ensure_unicode(url)
def __nonzero__(self):
return bool(self.url)
__bool__ = __nonzero__
def __repr__(self):
c = repr(self.url.encode("utf8"))
if sys.version_info >= (3,0):
c = c.lstrip("b")
return "<URL %s>" % c
def __eq__(self, other):
return self.url == getattr(other, "url", other)
def __ne__(self, other):
return not (self == other)
def geturl_nofragment(self):
""" return url without fragment """
scheme, netloc, url, params, query, ofragment = self._parsed
return URL(urlunsplit((scheme, netloc, url, query, "")))
@property
def hash_spec(self):
hashalgo, hash_value = parse_hash_spec(self._parsed[-1])
if hashalgo:
hashtype = self._parsed[-1].split("=")[0]
return "%s=%s" %(hashtype, hash_value)
return ""
@property
def hash_algo(self):
return parse_hash_spec(self._parsed[-1])[0]
@property
def hash_value(self):
return parse_hash_spec(self._parsed[-1])[1]
def replace(self, **kwargs):
_parsed = self._parsed
url = []
for field in ('scheme', 'netloc', 'path', 'query', 'fragment'):
url.append(kwargs.get(field, getattr(_parsed, field)))
return URL(urlunsplit(url))
@property
def netloc(self):
return self._parsed.netloc
@property
def username(self):
return self._parsed.username
@property
def password(self):
return self._parsed.password
@property
def hostname(self):
return self._parsed.hostname
@property
def port(self):
return self._parsed.port
@property
def scheme(self):
return self._parsed.scheme
@property
def url_nofrag(self):
return self.geturl_nofragment().url
def __hash__(self):
return hash(self.url)
@cached_property
def _parsed(self):
return urlparse(self.url)
def is_valid_http_url(self):
try:
x = parse_url(self.url)
except Exception:
return False
return x.scheme in ("http", "https")
@property
def path(self):
return self._parsed.path
@property
def basename(self):
return posixpath.basename(unquote(self._parsed.path))
@property
def parentbasename(self):
return posixpath.basename(posixpath.dirname(unquote(self._parsed.path)))
@property
def eggfragment(self):
frag = self._parsed.fragment
if frag.startswith("egg="):
return frag[4:]
@property
def md5(self):
val = self._parsed.fragment
if val.startswith("md5="):
return ensure_unicode(val[4:])
@property
def sha256(self):
val = self._parsed.fragment
if val.startswith("sha256="):
return ensure_unicode(val[4:])
def joinpath(self, *args, **kwargs):
newurl = _joinpath(self.url, args, **kwargs)
return URL(newurl)
def addpath(self, *args, **kwargs):
url = self.url.rstrip("/") + "/"
return URL(_joinpath(url, args, **kwargs))
def relpath(self, target):
""" return a relative path which will point to the target resource."""
parts1 = self.path.split("/")
parts2 = target.split("/")
if not parts2 or parts2[0]:
raise ValueError("not an absolute target: %s" % (target,))
for i, part in enumerate(parts1):
if parts2[i] == part:
continue
prefix = "../" * (len(parts1)-i-1)
return prefix + "/".join(parts2[i:])
rest = parts2[len(parts1):]
if parts1[-1]: # ends not in slash
rest.insert(0, parts1[-1])
return "/".join(rest)
def asdir(self):
if self.url[-1:] == "/":
return self
return self.__class__(self.url + "/")
def asfile(self):
if self.url[-1:] == "/":
return self.__class__(self.url.rstrip("/"))
return self
def torelpath(self):
""" return scheme/netloc/path/fragment into a canonical relative
filepath. Only the scheme, netlocation and path are mapped,
fragments and queries are ignored.
"""
parsed = self._parsed
assert parsed.scheme in ("http", "https")
return "%s/%s%s" % (parsed.scheme, parsed.netloc, parsed.path)
@classmethod
def fromrelpath(cls, relpath):
""" return url from canonical relative path. """
scheme, netlocpath = relpath.split("/", 1)
return cls(scheme + "://" + netlocpath)
|