Show More
@@ -9,9 +9,9 b'' | |||
|
9 | 9 | # Lesser General Public License for more details. |
|
10 | 10 | # |
|
11 | 11 | # You should have received a copy of the GNU Lesser General Public |
|
12 |
# License along with this library; if not, write to the |
|
|
13 |
# Free Software Foundation, Inc., |
|
|
14 |
# 59 Temple Place, Suite 330, |
|
|
12 | # License along with this library; if not, write to the | |
|
13 | # Free Software Foundation, Inc., | |
|
14 | # 59 Temple Place, Suite 330, | |
|
15 | 15 | # Boston, MA 02111-1307 USA |
|
16 | 16 | |
|
17 | 17 | # This file is part of urlgrabber, a high-level cross-protocol url-grabber |
@@ -25,59 +25,59 b' import urllib' | |||
|
25 | 25 | import urllib2 |
|
26 | 26 | import rfc822 |
|
27 | 27 | |
|
28 |
try: |
|
|
28 | try: | |
|
29 | 29 | from cStringIO import StringIO |
|
30 |
except ImportError, msg: |
|
|
30 | except ImportError, msg: | |
|
31 | 31 | from StringIO import StringIO |
|
32 | 32 | |
|
33 | 33 | class RangeError(IOError): |
|
34 | 34 | """Error raised when an unsatisfiable range is requested.""" |
|
35 | 35 | pass |
|
36 | ||
|
36 | ||
|
37 | 37 | class HTTPRangeHandler(urllib2.BaseHandler): |
|
38 | 38 | """Handler that enables HTTP Range headers. |
|
39 | ||
|
39 | ||
|
40 | 40 | This was extremely simple. The Range header is a HTTP feature to |
|
41 |
begin with so all this class does is tell urllib2 that the |
|
|
42 |
"206 Partial Content" reponse from the HTTP server is what we |
|
|
41 | begin with so all this class does is tell urllib2 that the | |
|
42 | "206 Partial Content" reponse from the HTTP server is what we | |
|
43 | 43 | expected. |
|
44 | ||
|
44 | ||
|
45 | 45 | Example: |
|
46 | 46 | import urllib2 |
|
47 | 47 | import byterange |
|
48 | ||
|
48 | ||
|
49 | 49 | range_handler = range.HTTPRangeHandler() |
|
50 | 50 | opener = urllib2.build_opener(range_handler) |
|
51 | ||
|
51 | ||
|
52 | 52 | # install it |
|
53 | 53 | urllib2.install_opener(opener) |
|
54 | ||
|
54 | ||
|
55 | 55 | # create Request and set Range header |
|
56 | 56 | req = urllib2.Request('http://www.python.org/') |
|
57 | 57 | req.header['Range'] = 'bytes=30-50' |
|
58 | 58 | f = urllib2.urlopen(req) |
|
59 | 59 | """ |
|
60 | ||
|
60 | ||
|
61 | 61 | def http_error_206(self, req, fp, code, msg, hdrs): |
|
62 | 62 | # 206 Partial Content Response |
|
63 | 63 | r = urllib.addinfourl(fp, hdrs, req.get_full_url()) |
|
64 | 64 | r.code = code |
|
65 | 65 | r.msg = msg |
|
66 | 66 | return r |
|
67 | ||
|
67 | ||
|
68 | 68 | def http_error_416(self, req, fp, code, msg, hdrs): |
|
69 | 69 | # HTTP's Range Not Satisfiable error |
|
70 | 70 | raise RangeError('Requested Range Not Satisfiable') |
|
71 | 71 | |
|
72 | 72 | class RangeableFileObject: |
|
73 | 73 | """File object wrapper to enable raw range handling. |
|
74 |
This was implemented primarilary for handling range |
|
|
75 |
specifications for file:// urls. This object effectively makes |
|
|
76 |
a file object look like it consists only of a range of bytes in |
|
|
74 | This was implemented primarilary for handling range | |
|
75 | specifications for file:// urls. This object effectively makes | |
|
76 | a file object look like it consists only of a range of bytes in | |
|
77 | 77 | the stream. |
|
78 | ||
|
78 | ||
|
79 | 79 | Examples: |
|
80 |
# expose 10 bytes, starting at byte position 20, from |
|
|
80 | # expose 10 bytes, starting at byte position 20, from | |
|
81 | 81 | # /etc/aliases. |
|
82 | 82 | >>> fo = RangeableFileObject(file('/etc/passwd', 'r'), (20,30)) |
|
83 | 83 | # seek seeks within the range (to position 23 in this case) |
@@ -89,11 +89,11 b' class RangeableFileObject:' | |||
|
89 | 89 | # byte in the range. the following will return only 7 bytes. |
|
90 | 90 | >>> fo.read(30) |
|
91 | 91 | """ |
|
92 | ||
|
92 | ||
|
93 | 93 | def __init__(self, fo, rangetup): |
|
94 | 94 | """Create a RangeableFileObject. |
|
95 |
fo -- a file like object. only the read() method need be |
|
|
96 |
supported but supporting an optimized seek() is |
|
|
95 | fo -- a file like object. only the read() method need be | |
|
96 | supported but supporting an optimized seek() is | |
|
97 | 97 | preferable. |
|
98 | 98 | rangetup -- a (firstbyte,lastbyte) tuple specifying the range |
|
99 | 99 | to work over. |
@@ -103,7 +103,7 b' class RangeableFileObject:' | |||
|
103 | 103 | (self.firstbyte, self.lastbyte) = range_tuple_normalize(rangetup) |
|
104 | 104 | self.realpos = 0 |
|
105 | 105 | self._do_seek(self.firstbyte) |
|
106 | ||
|
106 | ||
|
107 | 107 | def __getattr__(self, name): |
|
108 | 108 | """This effectively allows us to wrap at the instance level. |
|
109 | 109 | Any attribute not found in _this_ object will be searched for |
@@ -111,16 +111,16 b' class RangeableFileObject:' | |||
|
111 | 111 | if hasattr(self.fo, name): |
|
112 | 112 | return getattr(self.fo, name) |
|
113 | 113 | raise AttributeError, name |
|
114 | ||
|
114 | ||
|
115 | 115 | def tell(self): |
|
116 | 116 | """Return the position within the range. |
|
117 |
This is different from fo.seek in that position 0 is the |
|
|
117 | This is different from fo.seek in that position 0 is the | |
|
118 | 118 | first byte position of the range tuple. For example, if |
|
119 | 119 | this object was created with a range tuple of (500,899), |
|
120 | 120 | tell() will return 0 when at byte position 500 of the file. |
|
121 | 121 | """ |
|
122 | 122 | return (self.realpos - self.firstbyte) |
|
123 | ||
|
123 | ||
|
124 | 124 | def seek(self,offset,whence=0): |
|
125 | 125 | """Seek within the byte range. |
|
126 | 126 | Positioning is identical to that described under tell(). |
@@ -133,13 +133,13 b' class RangeableFileObject:' | |||
|
133 | 133 | elif whence == 2: # absolute from end of file |
|
134 | 134 | # XXX: are we raising the right Error here? |
|
135 | 135 | raise IOError('seek from end of file not supported.') |
|
136 | ||
|
136 | ||
|
137 | 137 | # do not allow seek past lastbyte in range |
|
138 | 138 | if self.lastbyte and (realoffset >= self.lastbyte): |
|
139 | 139 | realoffset = self.lastbyte |
|
140 | ||
|
140 | ||
|
141 | 141 | self._do_seek(realoffset - self.realpos) |
|
142 | ||
|
142 | ||
|
143 | 143 | def read(self, size=-1): |
|
144 | 144 | """Read within the range. |
|
145 | 145 | This method will limit the size read based on the range. |
@@ -148,7 +148,7 b' class RangeableFileObject:' | |||
|
148 | 148 | rslt = self.fo.read(size) |
|
149 | 149 | self.realpos += len(rslt) |
|
150 | 150 | return rslt |
|
151 | ||
|
151 | ||
|
152 | 152 | def readline(self, size=-1): |
|
153 | 153 | """Read lines within the range. |
|
154 | 154 | This method will limit the size read based on the range. |
@@ -157,7 +157,7 b' class RangeableFileObject:' | |||
|
157 | 157 | rslt = self.fo.readline(size) |
|
158 | 158 | self.realpos += len(rslt) |
|
159 | 159 | return rslt |
|
160 | ||
|
160 | ||
|
161 | 161 | def _calc_read_size(self, size): |
|
162 | 162 | """Handles calculating the amount of data to read based on |
|
163 | 163 | the range. |
@@ -169,7 +169,7 b' class RangeableFileObject:' | |||
|
169 | 169 | else: |
|
170 | 170 | size = (self.lastbyte - self.realpos) |
|
171 | 171 | return size |
|
172 | ||
|
172 | ||
|
173 | 173 | def _do_seek(self,offset): |
|
174 | 174 | """Seek based on whether wrapped object supports seek(). |
|
175 | 175 | offset is relative to the current position (self.realpos). |
@@ -180,7 +180,7 b' class RangeableFileObject:' | |||
|
180 | 180 | else: |
|
181 | 181 | self.fo.seek(self.realpos + offset) |
|
182 | 182 | self.realpos+= offset |
|
183 | ||
|
183 | ||
|
184 | 184 | def _poor_mans_seek(self,offset): |
|
185 | 185 | """Seek by calling the wrapped file objects read() method. |
|
186 | 186 | This is used for file like objects that do not have native |
@@ -188,7 +188,7 b' class RangeableFileObject:' | |||
|
188 | 188 | to manually seek to the desired position. |
|
189 | 189 | offset -- read this number of bytes from the wrapped |
|
190 | 190 | file object. |
|
191 |
raise RangeError if we encounter EOF before reaching the |
|
|
191 | raise RangeError if we encounter EOF before reaching the | |
|
192 | 192 | specified offset. |
|
193 | 193 | """ |
|
194 | 194 | pos = 0 |
@@ -237,10 +237,10 b' class FileRangeHandler(urllib2.FileHandl' | |||
|
237 | 237 | return urllib.addinfourl(fo, headers, 'file:'+file) |
|
238 | 238 | |
|
239 | 239 | |
|
240 |
# FTP Range Support |
|
|
240 | # FTP Range Support | |
|
241 | 241 | # Unfortunately, a large amount of base FTP code had to be copied |
|
242 | 242 | # from urllib and urllib2 in order to insert the FTP REST command. |
|
243 |
# Code modifications for range support have been commented as |
|
|
243 | # Code modifications for range support have been commented as | |
|
244 | 244 | # follows: |
|
245 | 245 | # -- range support modifications start/end here |
|
246 | 246 | |
@@ -271,7 +271,7 b' class FTPRangeHandler(urllib2.FTPHandler' | |||
|
271 | 271 | host = unquote(host) |
|
272 | 272 | user = unquote(user or '') |
|
273 | 273 | passwd = unquote(passwd or '') |
|
274 | ||
|
274 | ||
|
275 | 275 | try: |
|
276 | 276 | host = socket.gethostbyname(host) |
|
277 | 277 | except socket.error, msg: |
@@ -290,22 +290,22 b' class FTPRangeHandler(urllib2.FTPHandler' | |||
|
290 | 290 | if attr.lower() == 'type' and \ |
|
291 | 291 | value in ('a', 'A', 'i', 'I', 'd', 'D'): |
|
292 | 292 | type = value.upper() |
|
293 | ||
|
293 | ||
|
294 | 294 | # -- range support modifications start here |
|
295 | 295 | rest = None |
|
296 |
range_tup = range_header_to_tuple(req.headers.get('Range',None)) |
|
|
296 | range_tup = range_header_to_tuple(req.headers.get('Range',None)) | |
|
297 | 297 | assert range_tup != () |
|
298 | 298 | if range_tup: |
|
299 | 299 | (fb,lb) = range_tup |
|
300 | 300 | if fb > 0: rest = fb |
|
301 | 301 | # -- range support modifications end here |
|
302 | ||
|
302 | ||
|
303 | 303 | fp, retrlen = fw.retrfile(file, type, rest) |
|
304 | ||
|
304 | ||
|
305 | 305 | # -- range support modifications start here |
|
306 | 306 | if range_tup: |
|
307 | 307 | (fb,lb) = range_tup |
|
308 |
if lb == '': |
|
|
308 | if lb == '': | |
|
309 | 309 | if retrlen is None or retrlen == 0: |
|
310 | 310 | raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.') |
|
311 | 311 | lb = retrlen |
@@ -317,7 +317,7 b' class FTPRangeHandler(urllib2.FTPHandler' | |||
|
317 | 317 | retrlen = lb - fb |
|
318 | 318 | fp = RangeableFileObject(fp, (0,retrlen)) |
|
319 | 319 | # -- range support modifications end here |
|
320 | ||
|
320 | ||
|
321 | 321 | headers = "" |
|
322 | 322 | mtype = mimetypes.guess_type(req.get_full_url())[0] |
|
323 | 323 | if mtype: |
@@ -389,17 +389,17 b' class ftpwrapper(urllib.ftpwrapper):' | |||
|
389 | 389 | _rangere = None |
|
390 | 390 | def range_header_to_tuple(range_header): |
|
391 | 391 | """Get a (firstbyte,lastbyte) tuple from a Range header value. |
|
392 | ||
|
392 | ||
|
393 | 393 | Range headers have the form "bytes=<firstbyte>-<lastbyte>". This |
|
394 | 394 | function pulls the firstbyte and lastbyte values and returns |
|
395 | 395 | a (firstbyte,lastbyte) tuple. If lastbyte is not specified in |
|
396 | 396 | the header value, it is returned as an empty string in the |
|
397 | 397 | tuple. |
|
398 | ||
|
398 | ||
|
399 | 399 | Return None if range_header is None |
|
400 |
Return () if range_header does not conform to the range spec |
|
|
400 | Return () if range_header does not conform to the range spec | |
|
401 | 401 | pattern. |
|
402 | ||
|
402 | ||
|
403 | 403 | """ |
|
404 | 404 | global _rangere |
|
405 | 405 | if range_header is None: return None |
@@ -407,9 +407,9 b' def range_header_to_tuple(range_header):' | |||
|
407 | 407 | import re |
|
408 | 408 | _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)') |
|
409 | 409 | match = _rangere.match(range_header) |
|
410 |
if match: |
|
|
410 | if match: | |
|
411 | 411 | tup = range_tuple_normalize(match.group(1,2)) |
|
412 |
if tup and tup[1]: |
|
|
412 | if tup and tup[1]: | |
|
413 | 413 | tup = (tup[0],tup[1]+1) |
|
414 | 414 | return tup |
|
415 | 415 | return () |
@@ -422,14 +422,14 b' def range_tuple_to_header(range_tup):' | |||
|
422 | 422 | if range_tup is None: return None |
|
423 | 423 | range_tup = range_tuple_normalize(range_tup) |
|
424 | 424 | if range_tup: |
|
425 |
if range_tup[1]: |
|
|
425 | if range_tup[1]: | |
|
426 | 426 | range_tup = (range_tup[0],range_tup[1] - 1) |
|
427 | 427 | return 'bytes=%s-%s' % range_tup |
|
428 | ||
|
428 | ||
|
429 | 429 | def range_tuple_normalize(range_tup): |
|
430 | 430 | """Normalize a (first_byte,last_byte) range tuple. |
|
431 | 431 | Return a tuple whose first element is guaranteed to be an int |
|
432 |
and whose second element will be '' (meaning: the last byte) or |
|
|
432 | and whose second element will be '' (meaning: the last byte) or | |
|
433 | 433 | an int. Finally, return None if the normalized tuple == (0,'') |
|
434 | 434 | as that is equivelant to retrieving the entire file. |
|
435 | 435 | """ |
@@ -441,7 +441,7 b' def range_tuple_normalize(range_tup):' | |||
|
441 | 441 | # handle last byte |
|
442 | 442 | try: lb = range_tup[1] |
|
443 | 443 | except IndexError: lb = '' |
|
444 |
else: |
|
|
444 | else: | |
|
445 | 445 | if lb is None: lb = '' |
|
446 | 446 | elif lb != '': lb = int(lb) |
|
447 | 447 | # check if range is over the entire file |
@@ -449,4 +449,3 b' def range_tuple_normalize(range_tup):' | |||
|
449 | 449 | # check that the range is valid |
|
450 | 450 | if lb < fb: raise RangeError('Invalid byte range: %s-%s' % (fb,lb)) |
|
451 | 451 | return (fb,lb) |
|
452 |
@@ -647,7 +647,7 b' class localrepository:' | |||
|
647 | 647 | if cp: |
|
648 | 648 | meta["copy"] = cp |
|
649 | 649 | meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid))) |
|
650 |
self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"])) |
|
|
650 | self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"])) | |
|
651 | 651 | |
|
652 | 652 | r = self.file(f) |
|
653 | 653 | fp1 = m1.get(f, nullid) |
@@ -813,7 +813,7 b' class localrepository:' | |||
|
813 | 813 | while n != bottom: |
|
814 | 814 | p = self.changelog.parents(n)[0] |
|
815 | 815 | if i == f: |
|
816 |
l.append(n) |
|
|
816 | l.append(n) | |
|
817 | 817 | f = f * 2 |
|
818 | 818 | n = p |
|
819 | 819 | i += 1 |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | # hgweb.py - web interface to a mercurial repository |
|
2 | 2 | # |
|
3 | 3 | # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> |
|
4 |
# Copyright 2005 Matt Mackall <mpm@selenic.com> |
|
|
4 | # Copyright 2005 Matt Mackall <mpm@selenic.com> | |
|
5 | 5 | # |
|
6 | 6 | # This software may be used and distributed according to the terms |
|
7 | 7 | # of the GNU General Public License, incorporated herein by reference. |
@@ -657,7 +657,7 b' class hgweb:' | |||
|
657 | 657 | except KeyError: |
|
658 | 658 | write(self.search(hi)) |
|
659 | 659 | return |
|
660 | ||
|
660 | ||
|
661 | 661 | write(self.changelog(hi)) |
|
662 | 662 | |
|
663 | 663 | elif args['cmd'][0] == 'changeset': |
@@ -1,6 +1,6 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | ||
|
3 |
# This is the mercurial setup script. |
|
|
2 | # | |
|
3 | # This is the mercurial setup script. | |
|
4 | 4 | # |
|
5 | 5 | # './setup.py install', or |
|
6 | 6 | # './setup.py --help' for more options |
@@ -35,7 +35,7 b' try:' | |||
|
35 | 35 | data_files=[('mercurial/templates', |
|
36 | 36 | ['templates/map'] + |
|
37 | 37 | glob.glob('templates/map-*') + |
|
38 |
glob.glob('templates/*.tmpl'))], |
|
|
38 | glob.glob('templates/*.tmpl'))], | |
|
39 | 39 | cmdclass = { 'install_data' : install_package_data }, |
|
40 | 40 | scripts=['hg', 'hgmerge']) |
|
41 | 41 | finally: |
General Comments 0
You need to be logged in to leave comments.
Login now