##// END OF EJS Templates
eliminate backtrace when piping output on windows....
Vadim Gelfer -
r1609:c50bddfb default
parent child Browse files
Show More
@@ -1,155 +1,155 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, ConfigParser
8 import os, ConfigParser
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "re socket sys util")
11 demandload(globals(), "re socket sys util")
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True):
15 interactive=True):
16 self.overlay = {}
16 self.overlay = {}
17 self.cdata = ConfigParser.SafeConfigParser()
17 self.cdata = ConfigParser.SafeConfigParser()
18 self.readconfig(util.rcpath)
18 self.readconfig(util.rcpath)
19
19
20 self.quiet = self.configbool("ui", "quiet")
20 self.quiet = self.configbool("ui", "quiet")
21 self.verbose = self.configbool("ui", "verbose")
21 self.verbose = self.configbool("ui", "verbose")
22 self.debugflag = self.configbool("ui", "debug")
22 self.debugflag = self.configbool("ui", "debug")
23 self.interactive = self.configbool("ui", "interactive", True)
23 self.interactive = self.configbool("ui", "interactive", True)
24
24
25 self.updateopts(verbose, debug, quiet, interactive)
25 self.updateopts(verbose, debug, quiet, interactive)
26
26
27 def updateopts(self, verbose=False, debug=False, quiet=False,
27 def updateopts(self, verbose=False, debug=False, quiet=False,
28 interactive=True):
28 interactive=True):
29 self.quiet = (self.quiet or quiet) and not verbose and not debug
29 self.quiet = (self.quiet or quiet) and not verbose and not debug
30 self.verbose = (self.verbose or verbose) or debug
30 self.verbose = (self.verbose or verbose) or debug
31 self.debugflag = (self.debugflag or debug)
31 self.debugflag = (self.debugflag or debug)
32 self.interactive = (self.interactive and interactive)
32 self.interactive = (self.interactive and interactive)
33
33
34 def readconfig(self, fn):
34 def readconfig(self, fn):
35 if isinstance(fn, basestring):
35 if isinstance(fn, basestring):
36 fn = [fn]
36 fn = [fn]
37 for f in fn:
37 for f in fn:
38 try:
38 try:
39 self.cdata.read(f)
39 self.cdata.read(f)
40 except ConfigParser.ParsingError, inst:
40 except ConfigParser.ParsingError, inst:
41 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
41 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
42
42
43 def setconfig(self, section, name, val):
43 def setconfig(self, section, name, val):
44 self.overlay[(section, name)] = val
44 self.overlay[(section, name)] = val
45
45
46 def config(self, section, name, default=None):
46 def config(self, section, name, default=None):
47 if self.overlay.has_key((section, name)):
47 if self.overlay.has_key((section, name)):
48 return self.overlay[(section, name)]
48 return self.overlay[(section, name)]
49 if self.cdata.has_option(section, name):
49 if self.cdata.has_option(section, name):
50 return self.cdata.get(section, name)
50 return self.cdata.get(section, name)
51 return default
51 return default
52
52
53 def configbool(self, section, name, default=False):
53 def configbool(self, section, name, default=False):
54 if self.overlay.has_key((section, name)):
54 if self.overlay.has_key((section, name)):
55 return self.overlay[(section, name)]
55 return self.overlay[(section, name)]
56 if self.cdata.has_option(section, name):
56 if self.cdata.has_option(section, name):
57 return self.cdata.getboolean(section, name)
57 return self.cdata.getboolean(section, name)
58 return default
58 return default
59
59
60 def configitems(self, section):
60 def configitems(self, section):
61 if self.cdata.has_section(section):
61 if self.cdata.has_section(section):
62 return self.cdata.items(section)
62 return self.cdata.items(section)
63 return []
63 return []
64
64
65 def walkconfig(self):
65 def walkconfig(self):
66 seen = {}
66 seen = {}
67 for (section, name), value in self.overlay.iteritems():
67 for (section, name), value in self.overlay.iteritems():
68 yield section, name, value
68 yield section, name, value
69 seen[section, name] = 1
69 seen[section, name] = 1
70 for section in self.cdata.sections():
70 for section in self.cdata.sections():
71 for name, value in self.cdata.items(section):
71 for name, value in self.cdata.items(section):
72 if (section, name) in seen: continue
72 if (section, name) in seen: continue
73 yield section, name, value.replace('\n', '\\n')
73 yield section, name, value.replace('\n', '\\n')
74 seen[section, name] = 1
74 seen[section, name] = 1
75
75
76 def extensions(self):
76 def extensions(self):
77 return self.configitems("extensions")
77 return self.configitems("extensions")
78
78
79 def username(self):
79 def username(self):
80 return (os.environ.get("HGUSER") or
80 return (os.environ.get("HGUSER") or
81 self.config("ui", "username") or
81 self.config("ui", "username") or
82 os.environ.get("EMAIL") or
82 os.environ.get("EMAIL") or
83 (os.environ.get("LOGNAME",
83 (os.environ.get("LOGNAME",
84 os.environ.get("USERNAME", "unknown"))
84 os.environ.get("USERNAME", "unknown"))
85 + '@' + socket.getfqdn()))
85 + '@' + socket.getfqdn()))
86
86
87 def shortuser(self, user):
87 def shortuser(self, user):
88 """Return a short representation of a user name or email address."""
88 """Return a short representation of a user name or email address."""
89 if not self.verbose:
89 if not self.verbose:
90 f = user.find('@')
90 f = user.find('@')
91 if f >= 0:
91 if f >= 0:
92 user = user[:f]
92 user = user[:f]
93 f = user.find('<')
93 f = user.find('<')
94 if f >= 0:
94 if f >= 0:
95 user = user[f+1:]
95 user = user[f+1:]
96 return user
96 return user
97
97
98 def expandpath(self, loc, root=""):
98 def expandpath(self, loc, root=""):
99 paths = {}
99 paths = {}
100 for name, path in self.configitems("paths"):
100 for name, path in self.configitems("paths"):
101 m = path.find("://")
101 m = path.find("://")
102 if m == -1:
102 if m == -1:
103 path = os.path.join(root, path)
103 path = os.path.join(root, path)
104 paths[name] = path
104 paths[name] = path
105
105
106 return paths.get(loc, loc)
106 return paths.get(loc, loc)
107
107
108 def write(self, *args):
108 def write(self, *args):
109 for a in args:
109 for a in args:
110 sys.stdout.write(str(a))
110 sys.stdout.write(str(a))
111
111
112 def write_err(self, *args):
112 def write_err(self, *args):
113 sys.stdout.flush()
113 if not sys.stdout.closed: sys.stdout.flush()
114 for a in args:
114 for a in args:
115 sys.stderr.write(str(a))
115 sys.stderr.write(str(a))
116
116
117 def readline(self):
117 def readline(self):
118 return sys.stdin.readline()[:-1]
118 return sys.stdin.readline()[:-1]
119 def prompt(self, msg, pat, default="y"):
119 def prompt(self, msg, pat, default="y"):
120 if not self.interactive: return default
120 if not self.interactive: return default
121 while 1:
121 while 1:
122 self.write(msg, " ")
122 self.write(msg, " ")
123 r = self.readline()
123 r = self.readline()
124 if re.match(pat, r):
124 if re.match(pat, r):
125 return r
125 return r
126 else:
126 else:
127 self.write(_("unrecognized response\n"))
127 self.write(_("unrecognized response\n"))
128 def status(self, *msg):
128 def status(self, *msg):
129 if not self.quiet: self.write(*msg)
129 if not self.quiet: self.write(*msg)
130 def warn(self, *msg):
130 def warn(self, *msg):
131 self.write_err(*msg)
131 self.write_err(*msg)
132 def note(self, *msg):
132 def note(self, *msg):
133 if self.verbose: self.write(*msg)
133 if self.verbose: self.write(*msg)
134 def debug(self, *msg):
134 def debug(self, *msg):
135 if self.debugflag: self.write(*msg)
135 if self.debugflag: self.write(*msg)
136 def edit(self, text):
136 def edit(self, text):
137 import tempfile
137 import tempfile
138 (fd, name) = tempfile.mkstemp("hg")
138 (fd, name) = tempfile.mkstemp("hg")
139 f = os.fdopen(fd, "w")
139 f = os.fdopen(fd, "w")
140 f.write(text)
140 f.write(text)
141 f.close()
141 f.close()
142
142
143 editor = (os.environ.get("HGEDITOR") or
143 editor = (os.environ.get("HGEDITOR") or
144 self.config("ui", "editor") or
144 self.config("ui", "editor") or
145 os.environ.get("EDITOR", "vi"))
145 os.environ.get("EDITOR", "vi"))
146
146
147 os.environ["HGUSER"] = self.username()
147 os.environ["HGUSER"] = self.username()
148 util.system("%s \"%s\"" % (editor, name), errprefix=_("edit failed"))
148 util.system("%s \"%s\"" % (editor, name), errprefix=_("edit failed"))
149
149
150 t = open(name).read()
150 t = open(name).read()
151 t = re.sub("(?m)^HG:.*\n", "", t)
151 t = re.sub("(?m)^HG:.*\n", "", t)
152
152
153 os.unlink(name)
153 os.unlink(name)
154
154
155 return t
155 return t
@@ -1,664 +1,689 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8
8
9 This contains helper routines that are independent of the SCM core and hide
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
10 platform-specific details from the core.
11 """
11 """
12
12
13 import os, errno
13 import os, errno
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import *
15 from demandload import *
16 demandload(globals(), "re cStringIO shutil popen2 sys tempfile threading time")
16 demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
17 demandload(globals(), "threading time")
17
18
18 def pipefilter(s, cmd):
19 def pipefilter(s, cmd):
19 '''filter string S through command CMD, returning its output'''
20 '''filter string S through command CMD, returning its output'''
20 (pout, pin) = popen2.popen2(cmd, -1, 'b')
21 (pout, pin) = popen2.popen2(cmd, -1, 'b')
21 def writer():
22 def writer():
22 pin.write(s)
23 pin.write(s)
23 pin.close()
24 pin.close()
24
25
25 # we should use select instead on UNIX, but this will work on most
26 # we should use select instead on UNIX, but this will work on most
26 # systems, including Windows
27 # systems, including Windows
27 w = threading.Thread(target=writer)
28 w = threading.Thread(target=writer)
28 w.start()
29 w.start()
29 f = pout.read()
30 f = pout.read()
30 pout.close()
31 pout.close()
31 w.join()
32 w.join()
32 return f
33 return f
33
34
34 def tempfilter(s, cmd):
35 def tempfilter(s, cmd):
35 '''filter string S through a pair of temporary files with CMD.
36 '''filter string S through a pair of temporary files with CMD.
36 CMD is used as a template to create the real command to be run,
37 CMD is used as a template to create the real command to be run,
37 with the strings INFILE and OUTFILE replaced by the real names of
38 with the strings INFILE and OUTFILE replaced by the real names of
38 the temporary files generated.'''
39 the temporary files generated.'''
39 inname, outname = None, None
40 inname, outname = None, None
40 try:
41 try:
41 infd, inname = tempfile.mkstemp(prefix='hgfin')
42 infd, inname = tempfile.mkstemp(prefix='hgfin')
42 fp = os.fdopen(infd, 'wb')
43 fp = os.fdopen(infd, 'wb')
43 fp.write(s)
44 fp.write(s)
44 fp.close()
45 fp.close()
45 outfd, outname = tempfile.mkstemp(prefix='hgfout')
46 outfd, outname = tempfile.mkstemp(prefix='hgfout')
46 os.close(outfd)
47 os.close(outfd)
47 cmd = cmd.replace('INFILE', inname)
48 cmd = cmd.replace('INFILE', inname)
48 cmd = cmd.replace('OUTFILE', outname)
49 cmd = cmd.replace('OUTFILE', outname)
49 code = os.system(cmd)
50 code = os.system(cmd)
50 if code: raise Abort(_("command '%s' failed: %s") %
51 if code: raise Abort(_("command '%s' failed: %s") %
51 (cmd, explain_exit(code)))
52 (cmd, explain_exit(code)))
52 return open(outname, 'rb').read()
53 return open(outname, 'rb').read()
53 finally:
54 finally:
54 try:
55 try:
55 if inname: os.unlink(inname)
56 if inname: os.unlink(inname)
56 except: pass
57 except: pass
57 try:
58 try:
58 if outname: os.unlink(outname)
59 if outname: os.unlink(outname)
59 except: pass
60 except: pass
60
61
61 filtertable = {
62 filtertable = {
62 'tempfile:': tempfilter,
63 'tempfile:': tempfilter,
63 'pipe:': pipefilter,
64 'pipe:': pipefilter,
64 }
65 }
65
66
66 def filter(s, cmd):
67 def filter(s, cmd):
67 "filter a string through a command that transforms its input to its output"
68 "filter a string through a command that transforms its input to its output"
68 for name, fn in filtertable.iteritems():
69 for name, fn in filtertable.iteritems():
69 if cmd.startswith(name):
70 if cmd.startswith(name):
70 return fn(s, cmd[len(name):].lstrip())
71 return fn(s, cmd[len(name):].lstrip())
71 return pipefilter(s, cmd)
72 return pipefilter(s, cmd)
72
73
73 def patch(strip, patchname, ui):
74 def patch(strip, patchname, ui):
74 """apply the patch <patchname> to the working directory.
75 """apply the patch <patchname> to the working directory.
75 a list of patched files is returned"""
76 a list of patched files is returned"""
76 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
77 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
77 files = {}
78 files = {}
78 for line in fp:
79 for line in fp:
79 line = line.rstrip()
80 line = line.rstrip()
80 ui.status("%s\n" % line)
81 ui.status("%s\n" % line)
81 if line.startswith('patching file '):
82 if line.startswith('patching file '):
82 pf = parse_patch_output(line)
83 pf = parse_patch_output(line)
83 files.setdefault(pf, 1)
84 files.setdefault(pf, 1)
84 code = fp.close()
85 code = fp.close()
85 if code:
86 if code:
86 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
87 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
87 return files.keys()
88 return files.keys()
88
89
89 def binary(s):
90 def binary(s):
90 """return true if a string is binary data using diff's heuristic"""
91 """return true if a string is binary data using diff's heuristic"""
91 if s and '\0' in s[:4096]:
92 if s and '\0' in s[:4096]:
92 return True
93 return True
93 return False
94 return False
94
95
95 def unique(g):
96 def unique(g):
96 """return the uniq elements of iterable g"""
97 """return the uniq elements of iterable g"""
97 seen = {}
98 seen = {}
98 for f in g:
99 for f in g:
99 if f not in seen:
100 if f not in seen:
100 seen[f] = 1
101 seen[f] = 1
101 yield f
102 yield f
102
103
103 class Abort(Exception):
104 class Abort(Exception):
104 """Raised if a command needs to print an error and exit."""
105 """Raised if a command needs to print an error and exit."""
105
106
106 def always(fn): return True
107 def always(fn): return True
107 def never(fn): return False
108 def never(fn): return False
108
109
109 def patkind(name, dflt_pat='glob'):
110 def patkind(name, dflt_pat='glob'):
110 """Split a string into an optional pattern kind prefix and the
111 """Split a string into an optional pattern kind prefix and the
111 actual pattern."""
112 actual pattern."""
112 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
113 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
113 if name.startswith(prefix + ':'): return name.split(':', 1)
114 if name.startswith(prefix + ':'): return name.split(':', 1)
114 return dflt_pat, name
115 return dflt_pat, name
115
116
116 def globre(pat, head='^', tail='$'):
117 def globre(pat, head='^', tail='$'):
117 "convert a glob pattern into a regexp"
118 "convert a glob pattern into a regexp"
118 i, n = 0, len(pat)
119 i, n = 0, len(pat)
119 res = ''
120 res = ''
120 group = False
121 group = False
121 def peek(): return i < n and pat[i]
122 def peek(): return i < n and pat[i]
122 while i < n:
123 while i < n:
123 c = pat[i]
124 c = pat[i]
124 i = i+1
125 i = i+1
125 if c == '*':
126 if c == '*':
126 if peek() == '*':
127 if peek() == '*':
127 i += 1
128 i += 1
128 res += '.*'
129 res += '.*'
129 else:
130 else:
130 res += '[^/]*'
131 res += '[^/]*'
131 elif c == '?':
132 elif c == '?':
132 res += '.'
133 res += '.'
133 elif c == '[':
134 elif c == '[':
134 j = i
135 j = i
135 if j < n and pat[j] in '!]':
136 if j < n and pat[j] in '!]':
136 j += 1
137 j += 1
137 while j < n and pat[j] != ']':
138 while j < n and pat[j] != ']':
138 j += 1
139 j += 1
139 if j >= n:
140 if j >= n:
140 res += '\\['
141 res += '\\['
141 else:
142 else:
142 stuff = pat[i:j].replace('\\','\\\\')
143 stuff = pat[i:j].replace('\\','\\\\')
143 i = j + 1
144 i = j + 1
144 if stuff[0] == '!':
145 if stuff[0] == '!':
145 stuff = '^' + stuff[1:]
146 stuff = '^' + stuff[1:]
146 elif stuff[0] == '^':
147 elif stuff[0] == '^':
147 stuff = '\\' + stuff
148 stuff = '\\' + stuff
148 res = '%s[%s]' % (res, stuff)
149 res = '%s[%s]' % (res, stuff)
149 elif c == '{':
150 elif c == '{':
150 group = True
151 group = True
151 res += '(?:'
152 res += '(?:'
152 elif c == '}' and group:
153 elif c == '}' and group:
153 res += ')'
154 res += ')'
154 group = False
155 group = False
155 elif c == ',' and group:
156 elif c == ',' and group:
156 res += '|'
157 res += '|'
157 else:
158 else:
158 res += re.escape(c)
159 res += re.escape(c)
159 return head + res + tail
160 return head + res + tail
160
161
161 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
162 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
162
163
163 def pathto(n1, n2):
164 def pathto(n1, n2):
164 '''return the relative path from one place to another.
165 '''return the relative path from one place to another.
165 this returns a path in the form used by the local filesystem, not hg.'''
166 this returns a path in the form used by the local filesystem, not hg.'''
166 if not n1: return localpath(n2)
167 if not n1: return localpath(n2)
167 a, b = n1.split('/'), n2.split('/')
168 a, b = n1.split('/'), n2.split('/')
168 a.reverse()
169 a.reverse()
169 b.reverse()
170 b.reverse()
170 while a and b and a[-1] == b[-1]:
171 while a and b and a[-1] == b[-1]:
171 a.pop()
172 a.pop()
172 b.pop()
173 b.pop()
173 b.reverse()
174 b.reverse()
174 return os.sep.join((['..'] * len(a)) + b)
175 return os.sep.join((['..'] * len(a)) + b)
175
176
176 def canonpath(root, cwd, myname):
177 def canonpath(root, cwd, myname):
177 """return the canonical path of myname, given cwd and root"""
178 """return the canonical path of myname, given cwd and root"""
178 if root == os.sep:
179 if root == os.sep:
179 rootsep = os.sep
180 rootsep = os.sep
180 else:
181 else:
181 rootsep = root + os.sep
182 rootsep = root + os.sep
182 name = myname
183 name = myname
183 if not name.startswith(os.sep):
184 if not name.startswith(os.sep):
184 name = os.path.join(root, cwd, name)
185 name = os.path.join(root, cwd, name)
185 name = os.path.normpath(name)
186 name = os.path.normpath(name)
186 if name.startswith(rootsep):
187 if name.startswith(rootsep):
187 return pconvert(name[len(rootsep):])
188 return pconvert(name[len(rootsep):])
188 elif name == root:
189 elif name == root:
189 return ''
190 return ''
190 else:
191 else:
191 raise Abort('%s not under root' % myname)
192 raise Abort('%s not under root' % myname)
192
193
193 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
194 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
194 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob')
195 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob')
195
196
196 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
197 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
197 if os.name == 'nt':
198 if os.name == 'nt':
198 dflt_pat = 'glob'
199 dflt_pat = 'glob'
199 else:
200 else:
200 dflt_pat = 'relpath'
201 dflt_pat = 'relpath'
201 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat)
202 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat)
202
203
203 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat):
204 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat):
204 """build a function to match a set of file patterns
205 """build a function to match a set of file patterns
205
206
206 arguments:
207 arguments:
207 canonroot - the canonical root of the tree you're matching against
208 canonroot - the canonical root of the tree you're matching against
208 cwd - the current working directory, if relevant
209 cwd - the current working directory, if relevant
209 names - patterns to find
210 names - patterns to find
210 inc - patterns to include
211 inc - patterns to include
211 exc - patterns to exclude
212 exc - patterns to exclude
212 head - a regex to prepend to patterns to control whether a match is rooted
213 head - a regex to prepend to patterns to control whether a match is rooted
213
214
214 a pattern is one of:
215 a pattern is one of:
215 'glob:<rooted glob>'
216 'glob:<rooted glob>'
216 're:<rooted regexp>'
217 're:<rooted regexp>'
217 'path:<rooted path>'
218 'path:<rooted path>'
218 'relglob:<relative glob>'
219 'relglob:<relative glob>'
219 'relpath:<relative path>'
220 'relpath:<relative path>'
220 'relre:<relative regexp>'
221 'relre:<relative regexp>'
221 '<rooted path or regexp>'
222 '<rooted path or regexp>'
222
223
223 returns:
224 returns:
224 a 3-tuple containing
225 a 3-tuple containing
225 - list of explicit non-pattern names passed in
226 - list of explicit non-pattern names passed in
226 - a bool match(filename) function
227 - a bool match(filename) function
227 - a bool indicating if any patterns were passed in
228 - a bool indicating if any patterns were passed in
228
229
229 todo:
230 todo:
230 make head regex a rooted bool
231 make head regex a rooted bool
231 """
232 """
232
233
233 def contains_glob(name):
234 def contains_glob(name):
234 for c in name:
235 for c in name:
235 if c in _globchars: return True
236 if c in _globchars: return True
236 return False
237 return False
237
238
238 def regex(kind, name, tail):
239 def regex(kind, name, tail):
239 '''convert a pattern into a regular expression'''
240 '''convert a pattern into a regular expression'''
240 if kind == 're':
241 if kind == 're':
241 return name
242 return name
242 elif kind == 'path':
243 elif kind == 'path':
243 return '^' + re.escape(name) + '(?:/|$)'
244 return '^' + re.escape(name) + '(?:/|$)'
244 elif kind == 'relglob':
245 elif kind == 'relglob':
245 return head + globre(name, '(?:|.*/)', tail)
246 return head + globre(name, '(?:|.*/)', tail)
246 elif kind == 'relpath':
247 elif kind == 'relpath':
247 return head + re.escape(name) + tail
248 return head + re.escape(name) + tail
248 elif kind == 'relre':
249 elif kind == 'relre':
249 if name.startswith('^'):
250 if name.startswith('^'):
250 return name
251 return name
251 return '.*' + name
252 return '.*' + name
252 return head + globre(name, '', tail)
253 return head + globre(name, '', tail)
253
254
254 def matchfn(pats, tail):
255 def matchfn(pats, tail):
255 """build a matching function from a set of patterns"""
256 """build a matching function from a set of patterns"""
256 if not pats:
257 if not pats:
257 return
258 return
258 matches = []
259 matches = []
259 for k, p in pats:
260 for k, p in pats:
260 try:
261 try:
261 pat = '(?:%s)' % regex(k, p, tail)
262 pat = '(?:%s)' % regex(k, p, tail)
262 matches.append(re.compile(pat).match)
263 matches.append(re.compile(pat).match)
263 except re.error:
264 except re.error:
264 raise Abort("invalid pattern: %s:%s" % (k, p))
265 raise Abort("invalid pattern: %s:%s" % (k, p))
265
266
266 def buildfn(text):
267 def buildfn(text):
267 for m in matches:
268 for m in matches:
268 r = m(text)
269 r = m(text)
269 if r:
270 if r:
270 return r
271 return r
271
272
272 return buildfn
273 return buildfn
273
274
274 def globprefix(pat):
275 def globprefix(pat):
275 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
276 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
276 root = []
277 root = []
277 for p in pat.split(os.sep):
278 for p in pat.split(os.sep):
278 if contains_glob(p): break
279 if contains_glob(p): break
279 root.append(p)
280 root.append(p)
280 return '/'.join(root)
281 return '/'.join(root)
281
282
282 pats = []
283 pats = []
283 files = []
284 files = []
284 roots = []
285 roots = []
285 for kind, name in [patkind(p, dflt_pat) for p in names]:
286 for kind, name in [patkind(p, dflt_pat) for p in names]:
286 if kind in ('glob', 'relpath'):
287 if kind in ('glob', 'relpath'):
287 name = canonpath(canonroot, cwd, name)
288 name = canonpath(canonroot, cwd, name)
288 if name == '':
289 if name == '':
289 kind, name = 'glob', '**'
290 kind, name = 'glob', '**'
290 if kind in ('glob', 'path', 're'):
291 if kind in ('glob', 'path', 're'):
291 pats.append((kind, name))
292 pats.append((kind, name))
292 if kind == 'glob':
293 if kind == 'glob':
293 root = globprefix(name)
294 root = globprefix(name)
294 if root: roots.append(root)
295 if root: roots.append(root)
295 elif kind == 'relpath':
296 elif kind == 'relpath':
296 files.append((kind, name))
297 files.append((kind, name))
297 roots.append(name)
298 roots.append(name)
298
299
299 patmatch = matchfn(pats, '$') or always
300 patmatch = matchfn(pats, '$') or always
300 filematch = matchfn(files, '(?:/|$)') or always
301 filematch = matchfn(files, '(?:/|$)') or always
301 incmatch = always
302 incmatch = always
302 if inc:
303 if inc:
303 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
304 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
304 excmatch = lambda fn: False
305 excmatch = lambda fn: False
305 if exc:
306 if exc:
306 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
307 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
307
308
308 return (roots,
309 return (roots,
309 lambda fn: (incmatch(fn) and not excmatch(fn) and
310 lambda fn: (incmatch(fn) and not excmatch(fn) and
310 (fn.endswith('/') or
311 (fn.endswith('/') or
311 (not pats and not files) or
312 (not pats and not files) or
312 (pats and patmatch(fn)) or
313 (pats and patmatch(fn)) or
313 (files and filematch(fn)))),
314 (files and filematch(fn)))),
314 (inc or exc or (pats and pats != [('glob', '**')])) and True)
315 (inc or exc or (pats and pats != [('glob', '**')])) and True)
315
316
316 def system(cmd, errprefix=None):
317 def system(cmd, errprefix=None):
317 """execute a shell command that must succeed"""
318 """execute a shell command that must succeed"""
318 rc = os.system(cmd)
319 rc = os.system(cmd)
319 if rc:
320 if rc:
320 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
321 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
321 explain_exit(rc)[0])
322 explain_exit(rc)[0])
322 if errprefix:
323 if errprefix:
323 errmsg = "%s: %s" % (errprefix, errmsg)
324 errmsg = "%s: %s" % (errprefix, errmsg)
324 raise Abort(errmsg)
325 raise Abort(errmsg)
325
326
326 def rename(src, dst):
327 def rename(src, dst):
327 """forcibly rename a file"""
328 """forcibly rename a file"""
328 try:
329 try:
329 os.rename(src, dst)
330 os.rename(src, dst)
330 except:
331 except:
331 os.unlink(dst)
332 os.unlink(dst)
332 os.rename(src, dst)
333 os.rename(src, dst)
333
334
334 def unlink(f):
335 def unlink(f):
335 """unlink and remove the directory if it is empty"""
336 """unlink and remove the directory if it is empty"""
336 os.unlink(f)
337 os.unlink(f)
337 # try removing directories that might now be empty
338 # try removing directories that might now be empty
338 try: os.removedirs(os.path.dirname(f))
339 try: os.removedirs(os.path.dirname(f))
339 except: pass
340 except: pass
340
341
341 def copyfiles(src, dst, hardlink=None):
342 def copyfiles(src, dst, hardlink=None):
342 """Copy a directory tree using hardlinks if possible"""
343 """Copy a directory tree using hardlinks if possible"""
343
344
344 if hardlink is None:
345 if hardlink is None:
345 hardlink = (os.stat(src).st_dev ==
346 hardlink = (os.stat(src).st_dev ==
346 os.stat(os.path.dirname(dst)).st_dev)
347 os.stat(os.path.dirname(dst)).st_dev)
347
348
348 if os.path.isdir(src):
349 if os.path.isdir(src):
349 os.mkdir(dst)
350 os.mkdir(dst)
350 for name in os.listdir(src):
351 for name in os.listdir(src):
351 srcname = os.path.join(src, name)
352 srcname = os.path.join(src, name)
352 dstname = os.path.join(dst, name)
353 dstname = os.path.join(dst, name)
353 copyfiles(srcname, dstname, hardlink)
354 copyfiles(srcname, dstname, hardlink)
354 else:
355 else:
355 if hardlink:
356 if hardlink:
356 try:
357 try:
357 os_link(src, dst)
358 os_link(src, dst)
358 except:
359 except:
359 hardlink = False
360 hardlink = False
360 shutil.copy(src, dst)
361 shutil.copy(src, dst)
361 else:
362 else:
362 shutil.copy(src, dst)
363 shutil.copy(src, dst)
363
364
364 def opener(base):
365 def opener(base):
365 """
366 """
366 return a function that opens files relative to base
367 return a function that opens files relative to base
367
368
368 this function is used to hide the details of COW semantics and
369 this function is used to hide the details of COW semantics and
369 remote file access from higher level code.
370 remote file access from higher level code.
370 """
371 """
371 p = base
372 p = base
372
373
373 def mktempcopy(name):
374 def mktempcopy(name):
374 d, fn = os.path.split(name)
375 d, fn = os.path.split(name)
375 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
376 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
376 fp = os.fdopen(fd, "wb")
377 fp = os.fdopen(fd, "wb")
377 try:
378 try:
378 fp.write(file(name, "rb").read())
379 fp.write(file(name, "rb").read())
379 except:
380 except:
380 try: os.unlink(temp)
381 try: os.unlink(temp)
381 except: pass
382 except: pass
382 raise
383 raise
383 fp.close()
384 fp.close()
384 st = os.lstat(name)
385 st = os.lstat(name)
385 os.chmod(temp, st.st_mode)
386 os.chmod(temp, st.st_mode)
386 return temp
387 return temp
387
388
388 class atomicfile(file):
389 class atomicfile(file):
389 """the file will only be copied on close"""
390 """the file will only be copied on close"""
390 def __init__(self, name, mode, atomic=False):
391 def __init__(self, name, mode, atomic=False):
391 self.__name = name
392 self.__name = name
392 self.temp = mktempcopy(name)
393 self.temp = mktempcopy(name)
393 file.__init__(self, self.temp, mode)
394 file.__init__(self, self.temp, mode)
394 def close(self):
395 def close(self):
395 if not self.closed:
396 if not self.closed:
396 file.close(self)
397 file.close(self)
397 rename(self.temp, self.__name)
398 rename(self.temp, self.__name)
398 def __del__(self):
399 def __del__(self):
399 self.close()
400 self.close()
400
401
401 def o(path, mode="r", text=False, atomic=False):
402 def o(path, mode="r", text=False, atomic=False):
402 f = os.path.join(p, path)
403 f = os.path.join(p, path)
403
404
404 if not text:
405 if not text:
405 mode += "b" # for that other OS
406 mode += "b" # for that other OS
406
407
407 if mode[0] != "r":
408 if mode[0] != "r":
408 try:
409 try:
409 nlink = nlinks(f)
410 nlink = nlinks(f)
410 except OSError:
411 except OSError:
411 d = os.path.dirname(f)
412 d = os.path.dirname(f)
412 if not os.path.isdir(d):
413 if not os.path.isdir(d):
413 os.makedirs(d)
414 os.makedirs(d)
414 else:
415 else:
415 if atomic:
416 if atomic:
416 return atomicfile(f, mode)
417 return atomicfile(f, mode)
417 if nlink > 1:
418 if nlink > 1:
418 rename(mktempcopy(f), f)
419 rename(mktempcopy(f), f)
419 return file(f, mode)
420 return file(f, mode)
420
421
421 return o
422 return o
422
423
423 def _makelock_file(info, pathname):
424 def _makelock_file(info, pathname):
424 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
425 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
425 os.write(ld, info)
426 os.write(ld, info)
426 os.close(ld)
427 os.close(ld)
427
428
428 def _readlock_file(pathname):
429 def _readlock_file(pathname):
429 return file(pathname).read()
430 return file(pathname).read()
430
431
431 def nlinks(pathname):
432 def nlinks(pathname):
432 """Return number of hardlinks for the given file."""
433 """Return number of hardlinks for the given file."""
433 return os.stat(pathname).st_nlink
434 return os.stat(pathname).st_nlink
434
435
435 if hasattr(os, 'link'):
436 if hasattr(os, 'link'):
436 os_link = os.link
437 os_link = os.link
437 else:
438 else:
438 def os_link(src, dst):
439 def os_link(src, dst):
439 raise OSError(0, _("Hardlinks not supported"))
440 raise OSError(0, _("Hardlinks not supported"))
440
441
441 # Platform specific variants
442 # Platform specific variants
442 if os.name == 'nt':
443 if os.name == 'nt':
443 demandload(globals(), "msvcrt")
444 demandload(globals(), "msvcrt")
444 nulldev = 'NUL:'
445 nulldev = 'NUL:'
445
446
447 class winstdout:
448 '''stdout on windows misbehaves if sent through a pipe'''
449
450 def __init__(self, fp):
451 self.fp = fp
452
453 def __getattr__(self, key):
454 return getattr(self.fp, key)
455
456 def close(self):
457 try:
458 self.fp.close()
459 except: pass
460
461 def write(self, s):
462 try:
463 return self.fp.write(s)
464 except IOError, inst:
465 if inst.errno != 0: raise
466 self.close()
467 raise IOError(errno.EPIPE, 'Broken pipe')
468
469 sys.stdout = winstdout(sys.stdout)
470
446 try:
471 try:
447 import win32api, win32process
472 import win32api, win32process
448 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
473 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
449 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
474 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
450
475
451 except ImportError:
476 except ImportError:
452 systemrc = r'c:\mercurial\mercurial.ini'
477 systemrc = r'c:\mercurial\mercurial.ini'
453 pass
478 pass
454
479
455 rcpath = (systemrc,
480 rcpath = (systemrc,
456 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
481 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
457
482
458 def parse_patch_output(output_line):
483 def parse_patch_output(output_line):
459 """parses the output produced by patch and returns the file name"""
484 """parses the output produced by patch and returns the file name"""
460 pf = output_line[14:]
485 pf = output_line[14:]
461 if pf[0] == '`':
486 if pf[0] == '`':
462 pf = pf[1:-1] # Remove the quotes
487 pf = pf[1:-1] # Remove the quotes
463 return pf
488 return pf
464
489
465 try: # ActivePython can create hard links using win32file module
490 try: # ActivePython can create hard links using win32file module
466 import win32file
491 import win32file
467
492
468 def os_link(src, dst): # NB will only succeed on NTFS
493 def os_link(src, dst): # NB will only succeed on NTFS
469 win32file.CreateHardLink(dst, src)
494 win32file.CreateHardLink(dst, src)
470
495
471 def nlinks(pathname):
496 def nlinks(pathname):
472 """Return number of hardlinks for the given file."""
497 """Return number of hardlinks for the given file."""
473 try:
498 try:
474 fh = win32file.CreateFile(pathname,
499 fh = win32file.CreateFile(pathname,
475 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
500 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
476 None, win32file.OPEN_EXISTING, 0, None)
501 None, win32file.OPEN_EXISTING, 0, None)
477 res = win32file.GetFileInformationByHandle(fh)
502 res = win32file.GetFileInformationByHandle(fh)
478 fh.Close()
503 fh.Close()
479 return res[7]
504 return res[7]
480 except:
505 except:
481 return os.stat(pathname).st_nlink
506 return os.stat(pathname).st_nlink
482
507
483 except ImportError:
508 except ImportError:
484 pass
509 pass
485
510
486 def is_exec(f, last):
511 def is_exec(f, last):
487 return last
512 return last
488
513
489 def set_exec(f, mode):
514 def set_exec(f, mode):
490 pass
515 pass
491
516
492 def set_binary(fd):
517 def set_binary(fd):
493 msvcrt.setmode(fd.fileno(), os.O_BINARY)
518 msvcrt.setmode(fd.fileno(), os.O_BINARY)
494
519
495 def pconvert(path):
520 def pconvert(path):
496 return path.replace("\\", "/")
521 return path.replace("\\", "/")
497
522
498 def localpath(path):
523 def localpath(path):
499 return path.replace('/', '\\')
524 return path.replace('/', '\\')
500
525
501 def normpath(path):
526 def normpath(path):
502 return pconvert(os.path.normpath(path))
527 return pconvert(os.path.normpath(path))
503
528
504 makelock = _makelock_file
529 makelock = _makelock_file
505 readlock = _readlock_file
530 readlock = _readlock_file
506
531
507 def explain_exit(code):
532 def explain_exit(code):
508 return _("exited with status %d") % code, code
533 return _("exited with status %d") % code, code
509
534
510 else:
535 else:
511 nulldev = '/dev/null'
536 nulldev = '/dev/null'
512
537
513 def rcfiles(path):
538 def rcfiles(path):
514 rcs = [os.path.join(path, 'hgrc')]
539 rcs = [os.path.join(path, 'hgrc')]
515 rcdir = os.path.join(path, 'hgrc.d')
540 rcdir = os.path.join(path, 'hgrc.d')
516 try:
541 try:
517 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
542 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
518 if f.endswith(".rc")])
543 if f.endswith(".rc")])
519 except OSError, inst: pass
544 except OSError, inst: pass
520 return rcs
545 return rcs
521 rcpath = rcfiles(os.path.dirname(sys.argv[0]) + '/../etc/mercurial')
546 rcpath = rcfiles(os.path.dirname(sys.argv[0]) + '/../etc/mercurial')
522 rcpath.extend(rcfiles('/etc/mercurial'))
547 rcpath.extend(rcfiles('/etc/mercurial'))
523 rcpath.append(os.path.expanduser('~/.hgrc'))
548 rcpath.append(os.path.expanduser('~/.hgrc'))
524 rcpath = [os.path.normpath(f) for f in rcpath]
549 rcpath = [os.path.normpath(f) for f in rcpath]
525
550
526 def parse_patch_output(output_line):
551 def parse_patch_output(output_line):
527 """parses the output produced by patch and returns the file name"""
552 """parses the output produced by patch and returns the file name"""
528 pf = output_line[14:]
553 pf = output_line[14:]
529 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
554 if pf.startswith("'") and pf.endswith("'") and pf.find(" ") >= 0:
530 pf = pf[1:-1] # Remove the quotes
555 pf = pf[1:-1] # Remove the quotes
531 return pf
556 return pf
532
557
533 def is_exec(f, last):
558 def is_exec(f, last):
534 """check whether a file is executable"""
559 """check whether a file is executable"""
535 return (os.stat(f).st_mode & 0100 != 0)
560 return (os.stat(f).st_mode & 0100 != 0)
536
561
537 def set_exec(f, mode):
562 def set_exec(f, mode):
538 s = os.stat(f).st_mode
563 s = os.stat(f).st_mode
539 if (s & 0100 != 0) == mode:
564 if (s & 0100 != 0) == mode:
540 return
565 return
541 if mode:
566 if mode:
542 # Turn on +x for every +r bit when making a file executable
567 # Turn on +x for every +r bit when making a file executable
543 # and obey umask.
568 # and obey umask.
544 umask = os.umask(0)
569 umask = os.umask(0)
545 os.umask(umask)
570 os.umask(umask)
546 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
571 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
547 else:
572 else:
548 os.chmod(f, s & 0666)
573 os.chmod(f, s & 0666)
549
574
550 def set_binary(fd):
575 def set_binary(fd):
551 pass
576 pass
552
577
553 def pconvert(path):
578 def pconvert(path):
554 return path
579 return path
555
580
556 def localpath(path):
581 def localpath(path):
557 return path
582 return path
558
583
559 normpath = os.path.normpath
584 normpath = os.path.normpath
560
585
561 def makelock(info, pathname):
586 def makelock(info, pathname):
562 try:
587 try:
563 os.symlink(info, pathname)
588 os.symlink(info, pathname)
564 except OSError, why:
589 except OSError, why:
565 if why.errno == errno.EEXIST:
590 if why.errno == errno.EEXIST:
566 raise
591 raise
567 else:
592 else:
568 _makelock_file(info, pathname)
593 _makelock_file(info, pathname)
569
594
570 def readlock(pathname):
595 def readlock(pathname):
571 try:
596 try:
572 return os.readlink(pathname)
597 return os.readlink(pathname)
573 except OSError, why:
598 except OSError, why:
574 if why.errno == errno.EINVAL:
599 if why.errno == errno.EINVAL:
575 return _readlock_file(pathname)
600 return _readlock_file(pathname)
576 else:
601 else:
577 raise
602 raise
578
603
579 def explain_exit(code):
604 def explain_exit(code):
580 """return a 2-tuple (desc, code) describing a process's status"""
605 """return a 2-tuple (desc, code) describing a process's status"""
581 if os.WIFEXITED(code):
606 if os.WIFEXITED(code):
582 val = os.WEXITSTATUS(code)
607 val = os.WEXITSTATUS(code)
583 return _("exited with status %d") % val, val
608 return _("exited with status %d") % val, val
584 elif os.WIFSIGNALED(code):
609 elif os.WIFSIGNALED(code):
585 val = os.WTERMSIG(code)
610 val = os.WTERMSIG(code)
586 return _("killed by signal %d") % val, val
611 return _("killed by signal %d") % val, val
587 elif os.WIFSTOPPED(code):
612 elif os.WIFSTOPPED(code):
588 val = os.WSTOPSIG(code)
613 val = os.WSTOPSIG(code)
589 return _("stopped by signal %d") % val, val
614 return _("stopped by signal %d") % val, val
590 raise ValueError(_("invalid exit code"))
615 raise ValueError(_("invalid exit code"))
591
616
592 class chunkbuffer(object):
617 class chunkbuffer(object):
593 """Allow arbitrary sized chunks of data to be efficiently read from an
618 """Allow arbitrary sized chunks of data to be efficiently read from an
594 iterator over chunks of arbitrary size."""
619 iterator over chunks of arbitrary size."""
595
620
596 def __init__(self, in_iter, targetsize = 2**16):
621 def __init__(self, in_iter, targetsize = 2**16):
597 """in_iter is the iterator that's iterating over the input chunks.
622 """in_iter is the iterator that's iterating over the input chunks.
598 targetsize is how big a buffer to try to maintain."""
623 targetsize is how big a buffer to try to maintain."""
599 self.in_iter = iter(in_iter)
624 self.in_iter = iter(in_iter)
600 self.buf = ''
625 self.buf = ''
601 self.targetsize = int(targetsize)
626 self.targetsize = int(targetsize)
602 if self.targetsize <= 0:
627 if self.targetsize <= 0:
603 raise ValueError(_("targetsize must be greater than 0, was %d") %
628 raise ValueError(_("targetsize must be greater than 0, was %d") %
604 targetsize)
629 targetsize)
605 self.iterempty = False
630 self.iterempty = False
606
631
607 def fillbuf(self):
632 def fillbuf(self):
608 """Ignore target size; read every chunk from iterator until empty."""
633 """Ignore target size; read every chunk from iterator until empty."""
609 if not self.iterempty:
634 if not self.iterempty:
610 collector = cStringIO.StringIO()
635 collector = cStringIO.StringIO()
611 collector.write(self.buf)
636 collector.write(self.buf)
612 for ch in self.in_iter:
637 for ch in self.in_iter:
613 collector.write(ch)
638 collector.write(ch)
614 self.buf = collector.getvalue()
639 self.buf = collector.getvalue()
615 self.iterempty = True
640 self.iterempty = True
616
641
617 def read(self, l):
642 def read(self, l):
618 """Read L bytes of data from the iterator of chunks of data.
643 """Read L bytes of data from the iterator of chunks of data.
619 Returns less than L bytes if the iterator runs dry."""
644 Returns less than L bytes if the iterator runs dry."""
620 if l > len(self.buf) and not self.iterempty:
645 if l > len(self.buf) and not self.iterempty:
621 # Clamp to a multiple of self.targetsize
646 # Clamp to a multiple of self.targetsize
622 targetsize = self.targetsize * ((l // self.targetsize) + 1)
647 targetsize = self.targetsize * ((l // self.targetsize) + 1)
623 collector = cStringIO.StringIO()
648 collector = cStringIO.StringIO()
624 collector.write(self.buf)
649 collector.write(self.buf)
625 collected = len(self.buf)
650 collected = len(self.buf)
626 for chunk in self.in_iter:
651 for chunk in self.in_iter:
627 collector.write(chunk)
652 collector.write(chunk)
628 collected += len(chunk)
653 collected += len(chunk)
629 if collected >= targetsize:
654 if collected >= targetsize:
630 break
655 break
631 if collected < targetsize:
656 if collected < targetsize:
632 self.iterempty = True
657 self.iterempty = True
633 self.buf = collector.getvalue()
658 self.buf = collector.getvalue()
634 s, self.buf = self.buf[:l], buffer(self.buf, l)
659 s, self.buf = self.buf[:l], buffer(self.buf, l)
635 return s
660 return s
636
661
637 def filechunkiter(f, size = 65536):
662 def filechunkiter(f, size = 65536):
638 """Create a generator that produces all the data in the file size
663 """Create a generator that produces all the data in the file size
639 (default 65536) bytes at a time. Chunks may be less than size
664 (default 65536) bytes at a time. Chunks may be less than size
640 bytes if the chunk is the last chunk in the file, or the file is a
665 bytes if the chunk is the last chunk in the file, or the file is a
641 socket or some other type of file that sometimes reads less data
666 socket or some other type of file that sometimes reads less data
642 than is requested."""
667 than is requested."""
643 s = f.read(size)
668 s = f.read(size)
644 while len(s) > 0:
669 while len(s) > 0:
645 yield s
670 yield s
646 s = f.read(size)
671 s = f.read(size)
647
672
648 def makedate():
673 def makedate():
649 lt = time.localtime()
674 lt = time.localtime()
650 if lt[8] == 1 and time.daylight:
675 if lt[8] == 1 and time.daylight:
651 tz = time.altzone
676 tz = time.altzone
652 else:
677 else:
653 tz = time.timezone
678 tz = time.timezone
654 return time.mktime(lt), tz
679 return time.mktime(lt), tz
655
680
656 def datestr(date=None, format='%c'):
681 def datestr(date=None, format='%c'):
657 """represent a (unixtime, offset) tuple as a localized time.
682 """represent a (unixtime, offset) tuple as a localized time.
658 unixtime is seconds since the epoch, and offset is the time zone's
683 unixtime is seconds since the epoch, and offset is the time zone's
659 number of seconds away from UTC."""
684 number of seconds away from UTC."""
660 t, tz = date or makedate()
685 t, tz = date or makedate()
661 return ("%s %+03d%02d" %
686 return ("%s %+03d%02d" %
662 (time.strftime(format, time.gmtime(float(t) - tz)),
687 (time.strftime(format, time.gmtime(float(t) - tz)),
663 -tz / 3600,
688 -tz / 3600,
664 ((-tz % 3600) / 60)))
689 ((-tz % 3600) / 60)))
General Comments 0
You need to be logged in to leave comments. Login now