File: url.py

package info (click to toggle)
devpi-common 3.2.2-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 248 kB
  • sloc: python: 1,552; makefile: 4
file content (197 lines) | stat: -rw-r--r-- 5,448 bytes parent folder | download | duplicates (2)
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)