##// END OF EJS Templates
httprepo: long arguments support (issue2126)...
httprepo: long arguments support (issue2126) Send the command arguments in the HTTP headers. The command is still part of the URL. If the server does not have the 'httpheader' capability, the client will send the command arguments in the URL as it did previously. Web servers typically allow more data to be placed within the headers than in the URL, so this approach will: - Avoid HTTP errors due to using a URL that is too large. - Allow Mercurial to implement a more efficient wire protocol. An alternate approach is to send the arguments as part of the request body. This approach has been rejected because it requires the use of POST requests, so it would break any existing configuration that relies on the request type for authentication or caching. Extensibility: - The header size is provided by the server, which makes it possible to introduce an hgrc setting for it. - The client ignores the capability value after the first comma, which allows more information to be included in the future.

File last commit:

r14077:c285bdb0 default
r14093:ce99d887 default
Show More
util.py
1567 lines | 48.5 KiB | text/x-python | PythonLexer
Martin Geisler
put license and copyright info into comment blocks
r8226 # util.py - Mercurial utility functions and platform specfic implementations
#
# Copyright 2005 K. Thananchayan <thananck@yahoo.com>
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
#
# This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
mpm@selenic.com
Update util.py docstrings, fix walk test
r1082
Martin Geisler
turn some comments back into module docstrings
r8227 """Mercurial utility functions and platform specfic implementations.
mpm@selenic.com
Update util.py docstrings, fix walk test
r1082
Martin Geisler
turn some comments back into module docstrings
r8227 This contains helper routines that are independent of the SCM core and
hide platform-specific details from the core.
mpm@selenic.com
Update util.py docstrings, fix walk test
r1082 """
mpm@selenic.com
[PATCH] file seperator handling for the other 'OS'...
r419
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Martin Geisler
util: do not corrupt multi-byte characters in wrap
r9480 import error, osutil, encoding
Matt Mackall
chunkbuffer: use += rather than cStringIO to reduce memory footprint...
r11758 import errno, re, shutil, sys, tempfile, traceback
Adrian Buehlmann
move path_auditor from util to scmutil
r13972 import os, time, calendar, textwrap, unicodedata, signal
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076 import imp, socket, urllib
Matt Mackall
Add encoding detection
r3769
Dirkjan Ochtman
python 2.6 compatibility: compatibility wrappers for hash functions
r6470 # Python compatibility
Matt Mackall
Add encoding detection
r3769
Dirkjan Ochtman
python 2.6 compatibility: compatibility wrappers for hash functions
r6470 def sha1(s):
Martin Geisler
util: stop overwriting sha1, overwrite _fastsha1 instead...
r8297 return _fastsha1(s)
def _fastsha1(s):
# This function will import sha1 from hashlib or sha (whichever is
# available) and overwrite itself with it on the first call.
# Subsequent calls will go directly to the imported function.
Sol Jerome
util: avoid using hashlib on Python < 2.5 (issue2278)...
r12051 if sys.version_info >= (2, 5):
Martin Geisler
util: stop overwriting sha1, overwrite _fastsha1 instead...
r8297 from hashlib import sha1 as _sha1
Sol Jerome
util: avoid using hashlib on Python < 2.5 (issue2278)...
r12051 else:
Sune Foldager
util: remove warnings when importing md5 and sha
r8295 from sha import sha as _sha1
Simon Heimberg
util: overwrite sha1 and _fastsha1
r8309 global _fastsha1, sha1
_fastsha1 = sha1 = _sha1
Dirkjan Ochtman
python 2.6 compatibility: compatibility wrappers for hash functions
r6470 return _sha1(s)
Ronny Pfannschmidt
util: fake the builtin buffer if it's missing (jython)
r10756 import __builtin__
Renato Cunha
util: use fakebuffer as buffer in py3k...
r11566 if sys.version_info[0] < 3:
def fakebuffer(sliceable, offset=0):
return sliceable[offset:]
else:
def fakebuffer(sliceable, offset=0):
return memoryview(sliceable)[offset:]
Renato Cunha
util: improved the check for the existence of the 'buffer' builtin...
r11565 try:
buffer
except NameError:
Ronny Pfannschmidt
util: fake the builtin buffer if it's missing (jython)
r10756 __builtin__.buffer = fakebuffer
Martin Geisler
util: always use subprocess
r8280 import subprocess
closefds = os.name == 'posix'
Patrick Mezard
subrepo: normalize svn output line-endings
r10197
Patrick Mezard
subrepo: force en_US.UTF-8 locale when calling svn...
r10199 def popen2(cmd, env=None, newlines=False):
Martin Geisler
util: remove unused bufsize argument...
r9089 # Setting bufsize to -1 lets the system decide the buffer size.
# The default for bufsize is 0, meaning unbuffered. This leads to
# poor performance on Mac OS X: http://bugs.python.org/issue4194
p = subprocess.Popen(cmd, shell=True, bufsize=-1,
Bryan O'Sullivan
Backed out changeset fce065538bcf: it caused a 5x performance regression on OS X
r9083 close_fds=closefds,
Patrick Mezard
subrepo: normalize svn output line-endings
r10197 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
Patrick Mezard
subrepo: force en_US.UTF-8 locale when calling svn...
r10199 universal_newlines=newlines,
env=env)
Martin Geisler
util: always use subprocess
r8280 return p.stdin, p.stdout
Patrick Mezard
subrepo: normalize svn output line-endings
r10197
Patrick Mezard
subrepo: force en_US.UTF-8 locale when calling svn...
r10199 def popen3(cmd, env=None, newlines=False):
Martin Geisler
util: remove unused bufsize argument...
r9089 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
Bryan O'Sullivan
Backed out changeset fce065538bcf: it caused a 5x performance regression on OS X
r9083 close_fds=closefds,
Martin Geisler
util: always use subprocess
r8280 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
Patrick Mezard
subrepo: normalize svn output line-endings
r10197 stderr=subprocess.PIPE,
Patrick Mezard
subrepo: force en_US.UTF-8 locale when calling svn...
r10199 universal_newlines=newlines,
env=env)
Martin Geisler
util: always use subprocess
r8280 return p.stdin, p.stdout, p.stderr
Dirkjan Ochtman
python2.6: use subprocess if available
r7106
Matt Mackall
refactor version code...
r7632 def version():
"""Return version information if available."""
try:
import __version__
return __version__.version
except ImportError:
return 'unknown'
Chris Mason
util.parsedate should understand dates from hg export
r2609 # used by parsedate
Matt Mackall
improve date parsing for numerous new date formats...
r3808 defaultdateformats = (
'%Y-%m-%d %H:%M:%S',
'%Y-%m-%d %I:%M:%S%p',
'%Y-%m-%d %H:%M',
'%Y-%m-%d %I:%M%p',
'%Y-%m-%d',
'%m-%d',
'%m/%d',
'%m/%d/%y',
'%m/%d/%Y',
'%a %b %d %H:%M:%S %Y',
'%a %b %d %I:%M:%S%p %Y',
Markus F.X.J. Oberhumer
Add support for RFC2822 to util.parsedate().
r4708 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
Matt Mackall
improve date parsing for numerous new date formats...
r3808 '%b %d %H:%M:%S %Y',
Matt Mackall
Add date matching support...
r3812 '%b %d %I:%M:%S%p %Y',
'%b %d %H:%M:%S',
Matt Mackall
improve date parsing for numerous new date formats...
r3808 '%b %d %I:%M:%S%p',
'%b %d %H:%M',
'%b %d %I:%M%p',
'%b %d %Y',
'%b %d',
'%H:%M:%S',
Carey Evans
util: Fix date format for 12-hour time.
r9383 '%I:%M:%S%p',
Matt Mackall
improve date parsing for numerous new date formats...
r3808 '%H:%M',
'%I:%M%p',
)
Chris Mason
util.parsedate should understand dates from hg export
r2609
Matt Mackall
Add date matching support...
r3812 extendeddateformats = defaultdateformats + (
"%Y",
"%Y-%m",
"%b",
"%b %Y",
)
Chris Mason
util.parsedate should understand dates from hg export
r2609
Brendan Cully
Add cachefunc to abstract function call cache
r3145 def cachefunc(func):
'''cache the result of function calls'''
Benoit Boissinot
add comments in cachefunc
r3147 # XXX doesn't handle keywords args
Brendan Cully
Add cachefunc to abstract function call cache
r3145 cache = {}
if func.func_code.co_argcount == 1:
Benoit Boissinot
add comments in cachefunc
r3147 # we gain a small amount of time because
# we don't need to pack/unpack the list
Brendan Cully
Add cachefunc to abstract function call cache
r3145 def f(arg):
if arg not in cache:
cache[arg] = func(arg)
return cache[arg]
else:
def f(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return f
Matt Mackall
fix memory usage of revlog caches by limiting cache size [issue1639]
r9097 def lrucachefunc(func):
'''cache most recent results of function calls'''
cache = {}
order = []
if func.func_code.co_argcount == 1:
def f(arg):
if arg not in cache:
if len(cache) > 20:
del cache[order.pop(0)]
cache[arg] = func(arg)
else:
order.remove(arg)
order.append(arg)
return cache[arg]
else:
def f(*args):
if args not in cache:
if len(cache) > 20:
del cache[order.pop(0)]
cache[args] = func(*args)
else:
order.remove(args)
order.append(args)
return cache[args]
return f
Matt Mackall
util: take propertycache from context.py
r8207 class propertycache(object):
def __init__(self, func):
self.func = func
self.name = func.__name__
def __get__(self, obj, type=None):
result = self.func(obj)
setattr(obj, self.name, result)
return result
Bryan O'Sullivan
Enhance the file filtering capabilities....
r1293 def pipefilter(s, cmd):
'''filter string S through command CMD, returning its output'''
Martin Geisler
util: simplify pipefilter and avoid subprocess race...
r8302 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
pout, perr = p.communicate(s)
return pout
mpm@selenic.com
[PATCH] file seperator handling for the other 'OS'...
r419
Bryan O'Sullivan
Enhance the file filtering capabilities....
r1293 def tempfilter(s, cmd):
'''filter string S through a pair of temporary files with CMD.
CMD is used as a template to create the real command to be run,
with the strings INFILE and OUTFILE replaced by the real names of
the temporary files generated.'''
inname, outname = None, None
try:
Thomas Arendsen Hein
Use better names (hg-{usage}-{random}.{suffix}) for temporary files.
r2165 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
Bryan O'Sullivan
Enhance the file filtering capabilities....
r1293 fp = os.fdopen(infd, 'wb')
fp.write(s)
fp.close()
Thomas Arendsen Hein
Use better names (hg-{usage}-{random}.{suffix}) for temporary files.
r2165 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
Bryan O'Sullivan
Enhance the file filtering capabilities....
r1293 os.close(outfd)
cmd = cmd.replace('INFILE', inname)
cmd = cmd.replace('OUTFILE', outname)
code = os.system(cmd)
Jean-Francois PIERONNE
OpenVMS patches
r4720 if sys.platform == 'OpenVMS' and code & 1:
code = 0
Matt Mackall
many, many trivial check-code fixups
r10282 if code:
raise Abort(_("command '%s' failed: %s") %
(cmd, explain_exit(code)))
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 fp = open(outname, 'rb')
r = fp.read()
fp.close()
return r
Bryan O'Sullivan
Enhance the file filtering capabilities....
r1293 finally:
try:
Matt Mackall
many, many trivial check-code fixups
r10282 if inname:
os.unlink(inname)
Idan Kamara
eliminate various naked except clauses
r14004 except OSError:
Matt Mackall
many, many trivial check-code fixups
r10282 pass
Bryan O'Sullivan
Enhance the file filtering capabilities....
r1293 try:
Matt Mackall
many, many trivial check-code fixups
r10282 if outname:
os.unlink(outname)
Idan Kamara
eliminate various naked except clauses
r14004 except OSError:
Matt Mackall
many, many trivial check-code fixups
r10282 pass
Bryan O'Sullivan
Enhance the file filtering capabilities....
r1293
filtertable = {
'tempfile:': tempfilter,
'pipe:': pipefilter,
}
def filter(s, cmd):
"filter a string through a command that transforms its input to its output"
for name, fn in filtertable.iteritems():
if cmd.startswith(name):
return fn(s, cmd[len(name):].lstrip())
return pipefilter(s, cmd)
mpm@selenic.com
Add automatic binary file detection to diff and export...
r1015 def binary(s):
Christian Ebert
Let util.binary check entire data for \0 (issue1066, issue1079)
r6507 """return true if a string is binary data"""
Martin Geisler
util: return boolean result directly in util.binary
r8118 return bool(s and '\0' in s)
Matt Mackall
util: add sort helper
r6762
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 def increasingchunks(source, min=1024, max=65536):
'''return no less than min bytes per chunk while data remains,
doubling min after each chunk until it reaches max'''
def log2(x):
if not x:
return 0
i = 0
while x:
x >>= 1
i += 1
return i - 1
buf = []
blen = 0
for chunk in source:
buf.append(chunk)
blen += len(chunk)
if blen >= min:
if min < max:
min = min << 1
nmin = 1 << log2(blen)
if nmin > min:
min = nmin
if min > max:
min = max
yield ''.join(buf)
blen = 0
buf = []
if buf:
yield ''.join(buf)
Matt Mackall
move util.Abort to error.py
r7947 Abort = error.Abort
mpm@selenic.com
[PATCH] Harden os.system...
r508
Matt Mackall
many, many trivial check-code fixups
r10282 def always(fn):
return True
def never(fn):
return False
Bryan O'Sullivan
Get add and locate to use new repo and dirstate walk code....
r724
Alexis S. L. Carvalho
pass repo.root to util.pathto() in preparation for the next patch
r4229 def pathto(root, n1, n2):
Bryan O'Sullivan
Fix walk path handling on Windows
r886 '''return the relative path from one place to another.
Alexis S. L. Carvalho
pass repo.root to util.pathto() in preparation for the next patch
r4229 root should use os.sep to separate directories
Alexis S. L. Carvalho
fix util.pathto...
r3669 n1 should use os.sep to separate directories
n2 should use "/" to separate directories
returns an os.sep-separated path.
Alexis S. L. Carvalho
pass repo.root to util.pathto() in preparation for the next patch
r4229
If n1 is a relative path, it's assumed it's
relative to root.
n2 should always be relative to root.
Alexis S. L. Carvalho
fix util.pathto...
r3669 '''
Matt Mackall
many, many trivial check-code fixups
r10282 if not n1:
return localpath(n2)
Alexis S. L. Carvalho
Fix handling of paths when run outside the repo....
r4230 if os.path.isabs(n1):
if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
return os.path.join(root, localpath(n2))
n2 = '/'.join((pconvert(root), n2))
Shun-ichi GOTO
Add util.splitpath() and use it instead of using os.sep directly....
r5844 a, b = splitpath(n1), n2.split('/')
twaldmann@thinkmo.de
fixed some stuff pychecker shows, marked unclear/wrong stuff with XXX
r1541 a.reverse()
b.reverse()
Bryan O'Sullivan
Fix walk code for files that do not exist anywhere, and unhandled types....
r884 while a and b and a[-1] == b[-1]:
twaldmann@thinkmo.de
fixed some stuff pychecker shows, marked unclear/wrong stuff with XXX
r1541 a.pop()
b.pop()
Bryan O'Sullivan
Fix walk code for files that do not exist anywhere, and unhandled types....
r884 b.reverse()
Alexis S. L. Carvalho
util.pathto: return '.' instead of an empty string...
r6111 return os.sep.join((['..'] * len(a)) + b) or '.'
Bryan O'Sullivan
Fix walk code for files that do not exist anywhere, and unhandled types....
r884
Thomas Arendsen Hein
Simplify/correct finding the hg executable (fixes issue644)...
r5062 _hgexecutable = None
"Paul Moore "
Added hgexecutable support for py2exe/frozen scripts
r6499 def main_is_frozen():
"""return True if we are a frozen executable.
The code supports py2exe (most common, Windows only) and tools/freeze
(portable, not much used).
"""
return (hasattr(sys, "frozen") or # new py2exe
hasattr(sys, "importers") or # old py2exe
imp.is_frozen("__main__")) # tools/freeze
Thomas Arendsen Hein
Simplify/correct finding the hg executable (fixes issue644)...
r5062 def hgexecutable():
"""return location of the 'hg' executable.
Defaults to $HG or 'hg' in the search path.
"""
if _hgexecutable is None:
Bryan O'Sullivan
Tidy code, fix typo
r6500 hg = os.environ.get('HG')
if hg:
set_hgexecutable(hg)
"Paul Moore "
Added hgexecutable support for py2exe/frozen scripts
r6499 elif main_is_frozen():
set_hgexecutable(sys.executable)
else:
Nicolas Dumazet
util: use sys.argv[0] if $HG is unset and 'hg' is not in PATH...
r9632 exe = find_exe('hg') or os.path.basename(sys.argv[0])
set_hgexecutable(exe)
Thomas Arendsen Hein
Simplify/correct finding the hg executable (fixes issue644)...
r5062 return _hgexecutable
Thomas Arendsen Hein
Remember path to 'hg' executable and pass to external tools and hooks as $HG.
r4686
def set_hgexecutable(path):
Thomas Arendsen Hein
Simplify/correct finding the hg executable (fixes issue644)...
r5062 """set location of the 'hg' executable"""
Thomas Arendsen Hein
Remember path to 'hg' executable and pass to external tools and hooks as $HG.
r4686 global _hgexecutable
Thomas Arendsen Hein
Simplify/correct finding the hg executable (fixes issue644)...
r5062 _hgexecutable = path
Thomas Arendsen Hein
Remember path to 'hg' executable and pass to external tools and hooks as $HG.
r4686
Maxim Khitrov
http: deliver hook output to client
r11469 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
Vadim Gelfer
merge util.esystem and util.system.
r1882 '''enhanced shell command execution.
run with environment maybe modified, maybe in different dir.
mpm@selenic.com
[PATCH] Harden os.system...
r508
Vadim Gelfer
merge util.esystem and util.system.
r1882 if command fails and onerr is None, return status. if ui object,
print error message and return status, else raise onerr object as
Maxim Khitrov
http: deliver hook output to client
r11469 exception.
if out is specified, it is assumed to be a file-like object that has a
write() method. stdout and stderr will be redirected to out.'''
Mads Kiilerich
util: flush stdout before calling external processes...
r13439 try:
sys.stdout.flush()
except Exception:
pass
Vadim Gelfer
move most of tag code to localrepository class.
r2601 def py2shell(val):
'convert python object into string that is useful to shell'
Martin Geisler
util: use "is" for True/False/None comparisons
r8534 if val is None or val is False:
Vadim Gelfer
move most of tag code to localrepository class.
r2601 return '0'
Martin Geisler
util: use "is" for True/False/None comparisons
r8534 if val is True:
Vadim Gelfer
move most of tag code to localrepository class.
r2601 return '1'
return str(val)
Alexis S. L. Carvalho
util.system: fix quoting on windows
r3905 origcmd = cmd
Steve Borho
util: concentrate quoting knowledge to windows.py quotecommand()...
r13188 cmd = quotecommand(cmd)
Mads Kiilerich
util.system: Use subprocess instead of os.system...
r9517 env = dict(os.environ)
env.update((k, py2shell(v)) for k, v in environ.iteritems())
env['HG'] = hgexecutable()
Maxim Khitrov
http: deliver hook output to client
r11469 if out is None:
rc = subprocess.call(cmd, shell=True, close_fds=closefds,
env=env, cwd=cwd)
else:
proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
env=env, cwd=cwd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in proc.stdout:
out.write(line)
proc.wait()
rc = proc.returncode
Mads Kiilerich
util.system: Use subprocess instead of os.system...
r9517 if sys.platform == 'OpenVMS' and rc & 1:
rc = 0
if rc and onerr:
errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
explain_exit(rc)[0])
if errprefix:
errmsg = '%s: %s' % (errprefix, errmsg)
try:
onerr.warn(errmsg + '\n')
except AttributeError:
raise onerr(errmsg)
return rc
Vadim Gelfer
fix broken environment save/restore when a hook runs....
r1880
Matt Mackall
dispatch: generalize signature checking for extension command wrapping
r7388 def checksignature(func):
'''wrap a function with code to check for calling errors'''
def check(*args, **kwargs):
try:
return func(*args, **kwargs)
except TypeError:
if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
Matt Mackall
error: move SignatureError
r7646 raise error.SignatureError
Matt Mackall
dispatch: generalize signature checking for extension command wrapping
r7388 raise
return check
Adrian Buehlmann
set NOT_CONTENT_INDEXED on .hg dir (issue2694)...
r13795 def makedir(path, notindexed):
os.mkdir(path)
Adrian Buehlmann
rename util.unlink to unlinkpath
r13235 def unlinkpath(f):
Benoit Boissinot
refactor some unlink/remove code and make sure we prune empty dir
r1415 """unlink and remove the directory if it is empty"""
os.unlink(f)
# try removing directories that might now be empty
Vadim Gelfer
util.unlink should only catch OSError.
r2064 try:
os.removedirs(os.path.dirname(f))
except OSError:
pass
Benoit Boissinot
refactor some unlink/remove code and make sure we prune empty dir
r1415
Matt Mackall
util: add copyfile function
r3629 def copyfile(src, dest):
Will Maier
Use shutil.copystat in copyfile().
r7767 "copy a file, preserving mode and atime/mtime"
Eric St-Jean
fix util.copyfile to deal with symlinks
r4271 if os.path.islink(src):
try:
os.unlink(dest)
Idan Kamara
eliminate various naked except clauses
r14004 except OSError:
Eric St-Jean
fix util.copyfile to deal with symlinks
r4271 pass
os.symlink(os.readlink(src), dest)
else:
try:
shutil.copyfile(src, dest)
Brodie Rao
record: move copystat() hack out of util.copyfile() and into record...
r13099 shutil.copymode(src, dest)
Eric St-Jean
fix util.copyfile to deal with symlinks
r4271 except shutil.Error, inst:
raise Abort(str(inst))
Matt Mackall
util: add copyfile function
r3629
Stephen Darnell
Add support for cloning with hardlinks on windows....
r1241 def copyfiles(src, dst, hardlink=None):
"""Copy a directory tree using hardlinks if possible"""
if hardlink is None:
hardlink = (os.stat(src).st_dev ==
os.stat(os.path.dirname(dst)).st_dev)
Thomas Arendsen Hein
Use python function instead of external 'cp' command when cloning repos....
r698
Adrian Buehlmann
clone: print number of linked/copied files on --debug
r11251 num = 0
mpm@selenic.com
Rewrite copytree as copyfiles...
r1207 if os.path.isdir(src):
os.mkdir(dst)
Bryan O'Sullivan
Add osutil module, containing a listdir function....
r5396 for name, kind in osutil.listdir(src):
mpm@selenic.com
Rewrite copytree as copyfiles...
r1207 srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
Adrian Buehlmann
clone: print number of linked/copied files on --debug
r11251 hardlink, n = copyfiles(srcname, dstname, hardlink)
num += n
mpm@selenic.com
Rewrite copytree as copyfiles...
r1207 else:
Stephen Darnell
Add support for cloning with hardlinks on windows....
r1241 if hardlink:
try:
os_link(src, dst)
Vadim Gelfer
util.copyfiles: only switch to copy if hardlink raises IOError or OSError....
r2050 except (IOError, OSError):
Stephen Darnell
Add support for cloning with hardlinks on windows....
r1241 hardlink = False
Benoit Boissinot
do not copy atime and mtime in util.copyfiles...
r1591 shutil.copy(src, dst)
Stephen Darnell
Add support for cloning with hardlinks on windows....
r1241 else:
Benoit Boissinot
do not copy atime and mtime in util.copyfiles...
r1591 shutil.copy(src, dst)
Adrian Buehlmann
clone: print number of linked/copied files on --debug
r11251 num += 1
Thomas Arendsen Hein
Use python function instead of external 'cp' command when cloning repos....
r698
Adrian Buehlmann
clone: print number of linked/copied files on --debug
r11251 return hardlink, num
Adrian Buehlmann
util.copyfiles: don't try os_link() again if it failed before...
r11254
Adrian Buehlmann
path_auditor: check filenames for basic platform validity (issue2755)...
r13916 _windows_reserved_filenames = '''con prn aux nul
com1 com2 com3 com4 com5 com6 com7 com8 com9
lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
_windows_reserved_chars = ':*?"<>|'
def checkwinfilename(path):
'''Check that the base-relative path is a valid filename on Windows.
Returns None if the path is ok, or a UI string describing the problem.
>>> checkwinfilename("just/a/normal/path")
>>> checkwinfilename("foo/bar/con.xml")
"filename contains 'con', which is reserved on Windows"
>>> checkwinfilename("foo/con.xml/bar")
"filename contains 'con', which is reserved on Windows"
>>> checkwinfilename("foo/bar/xml.con")
>>> checkwinfilename("foo/bar/AUX/bla.txt")
"filename contains 'AUX', which is reserved on Windows"
>>> checkwinfilename("foo/bar/bla:.txt")
"filename contains ':', which is reserved on Windows"
>>> checkwinfilename("foo/bar/b\07la.txt")
Adrian Buehlmann
checkwinfilename: use %r in format string
r13947 "filename contains '\\\\x07', which is invalid on Windows"
Adrian Buehlmann
path_auditor: check filenames for basic platform validity (issue2755)...
r13916 >>> checkwinfilename("foo/bar/bla ")
"filename ends with ' ', which is not allowed on Windows"
'''
for n in path.replace('\\', '/').split('/'):
if not n:
continue
for c in n:
if c in _windows_reserved_chars:
return _("filename contains '%s', which is reserved "
"on Windows") % c
if ord(c) <= 31:
Adrian Buehlmann
checkwinfilename: use %r in format string
r13947 return _("filename contains %r, which is invalid "
Adrian Buehlmann
path_auditor: check filenames for basic platform validity (issue2755)...
r13916 "on Windows") % c
base = n.split('.')[0]
if base and base.lower() in _windows_reserved_filenames:
return _("filename contains '%s', which is reserved "
"on Windows") % base
t = n[-1]
if t in '. ':
return _("filename ends with '%s', which is not allowed "
"on Windows") % t
Sune Foldager
util: don't overwrite os-specific functions with general ones
r8011 def lookup_reg(key, name=None, scope=None):
return None
Patrick Mezard
cmdutil: hide child window created by win32 spawndetached()...
r10240 def hidewindow():
"""Hide current shell window.
Used to hide the window opened when starting asynchronous
child process under Windows, unneeded on other systems.
"""
pass
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 if os.name == 'nt':
Adrian Buehlmann
path_auditor: check filenames for basic platform validity (issue2755)...
r13916 checkosfilename = checkwinfilename
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 from windows import *
else:
from posix import *
def makelock(info, pathname):
try:
return os.symlink(info, pathname)
except OSError, why:
if why.errno == errno.EEXIST:
raise
except AttributeError: # no symlink in os
pass
Thomas Arendsen Hein
Make makelock and readlock work on filesystems without symlink support....
r704 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
os.write(ld, info)
os.close(ld)
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 def readlock(pathname):
try:
return os.readlink(pathname)
except OSError, why:
if why.errno not in (errno.EINVAL, errno.ENOSYS):
raise
except AttributeError: # no symlink in os
pass
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 fp = posixfile(pathname)
r = fp.read()
fp.close()
return r
Thomas Arendsen Hein
Make makelock and readlock work on filesystems without symlink support....
r704
Vadim Gelfer
fix file handling bugs on windows....
r2176 def fstat(fp):
'''stat file object that may not have fileno method.'''
try:
return os.fstat(fp.fileno())
except AttributeError:
return os.stat(fp.name)
Matt Mackall
imported patch folding
r3784 # File system features
Matt Mackall
rename checkfolding to checkcase
r6746 def checkcase(path):
Matt Mackall
imported patch folding
r3784 """
Check whether the given path is on a case-sensitive filesystem
Requires a path (like /foo/.hg) ending with a foldable final
directory component.
"""
s1 = os.stat(path)
d, b = os.path.split(path)
p2 = os.path.join(d, b.upper())
if path == p2:
p2 = os.path.join(d, b.lower())
try:
s2 = os.stat(p2)
if s2 == s1:
return False
return True
Idan Kamara
eliminate various naked except clauses
r14004 except OSError:
Matt Mackall
imported patch folding
r3784 return True
Paul Moore
Add a new function, fspath...
r6676 _fspathcache = {}
def fspath(name, root):
'''Get name in the case stored in the filesystem
The name is either relative to root, or it is an absolute path starting
with root. Note that this function is unnecessary, and should not be
called, for case-sensitive filesystems (simply because it's expensive).
'''
# If name is absolute, make it relative
if name.lower().startswith(root.lower()):
l = len(root)
Matt Mackall
backout most of 4f8067c94729
r12401 if name[l] == os.sep or name[l] == os.altsep:
Paul Moore
Add a new function, fspath...
r6676 l = l + 1
name = name[l:]
Patrick Mezard
Use lexists() instead of exists() where appropriate
r12344 if not os.path.lexists(os.path.join(root, name)):
Paul Moore
Add a new function, fspath...
r6676 return None
seps = os.sep
if os.altsep:
seps = seps + os.altsep
# Protect backslashes. This gets silly very quickly.
seps.replace('\\','\\\\')
pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
dir = os.path.normcase(os.path.normpath(root))
result = []
for part, sep in pattern.findall(name):
if sep:
result.append(sep)
continue
if dir not in _fspathcache:
_fspathcache[dir] = os.listdir(dir)
contents = _fspathcache[dir]
lpart = part.lower()
Simon Heimberg
util: quicker fspath, do not lower names when the length is different
r9397 lenp = len(part)
Paul Moore
Add a new function, fspath...
r6676 for n in contents:
Simon Heimberg
util: quicker fspath, do not lower names when the length is different
r9397 if lenp == len(n) and n.lower() == lpart:
Paul Moore
Add a new function, fspath...
r6676 result.append(n)
break
else:
# Cannot happen, as the file exists!
result.append(part)
dir = os.path.join(dir, lpart)
return ''.join(result)
Adrian Buehlmann
opener: check hardlink count reporting (issue1866)...
r12938 def checknlink(testfile):
'''check whether hardlink count reporting works properly'''
Adrian Buehlmann
checknlink: use two testfiles (issue2543)...
r13204 # testfile may be open, so we need a separate file for checking to
# work around issue2543 (or testfile may get lost on Samba shares)
f1 = testfile + ".hgtmp1"
if os.path.lexists(f1):
return False
Adrian Buehlmann
opener: check hardlink count reporting (issue1866)...
r12938 try:
Adrian Buehlmann
checknlink: use two testfiles (issue2543)...
r13204 posixfile(f1, 'w').close()
except IOError:
Adrian Buehlmann
opener: check hardlink count reporting (issue1866)...
r12938 return False
Adrian Buehlmann
checknlink: use two testfiles (issue2543)...
r13204 f2 = testfile + ".hgtmp2"
fd = None
Adrian Buehlmann
opener: check hardlink count reporting (issue1866)...
r12938 try:
Adrian Buehlmann
checknlink: use two testfiles (issue2543)...
r13204 try:
os_link(f1, f2)
except OSError:
return False
Adrian Buehlmann
opener: check hardlink count reporting (issue1866)...
r12938 # nlinks() may behave differently for files on Windows shares if
# the file is open.
Adrian Buehlmann
opener: use posixfile to hold file open when calling nlinks()...
r13342 fd = posixfile(f2)
Adrian Buehlmann
checknlink: use two testfiles (issue2543)...
r13204 return nlinks(f2) > 1
Adrian Buehlmann
opener: check hardlink count reporting (issue1866)...
r12938 finally:
Adrian Buehlmann
checknlink: use two testfiles (issue2543)...
r13204 if fd is not None:
fd.close()
for f in (f1, f2):
try:
os.unlink(f)
except OSError:
pass
Adrian Buehlmann
opener: check hardlink count reporting (issue1866)...
r12938
return False
Shun-ichi GOTO
Add endswithsep() and use it instead of using os.sep and os.altsep directly....
r5843 def endswithsep(path):
'''Check path ends with os.sep or os.altsep.'''
return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
Shun-ichi GOTO
Add util.splitpath() and use it instead of using os.sep directly....
r5844 def splitpath(path):
'''Split path by os.sep.
Note that this function does not use os.altsep because this is
an alternative of simple "xxx.split(os.sep)".
It is recommended to use os.path.normpath() before using this
function if need.'''
return path.split(os.sep)
Matt Mackall
filemerge: add config item for GUI tools...
r6007 def gui():
'''Are we running in a GUI?'''
Dan Villiom Podlaski Christiansen
util: add Mac-specific check whether we're in a GUI session (issue2553)...
r13734 if sys.platform == 'darwin':
if 'SSH_CONNECTION' in os.environ:
# handle SSH access to a box where the user is logged in
return False
elif getattr(osutil, 'isgui', None):
# check if a CoreGraphics session is available
return osutil.isgui()
else:
# pure build; use a safe default
return True
else:
return os.name == "nt" or os.environ.get("DISPLAY")
Matt Mackall
filemerge: add config item for GUI tools...
r6007
Alexis S. L. Carvalho
Make files in .hg inherit the permissions from .hg/store
r6062 def mktempcopy(name, emptyok=False, createmode=None):
Alexis S. L. Carvalho
turn util.opener into a class
r4827 """Create a temporary file with the same contents from name
The permission bits are copied from the original file.
If the temporary file is going to be truncated immediately, you
can use emptyok=True as an optimization.
Returns the name of the temporary file.
Vadim Gelfer
fix file handling bugs on windows....
r2176 """
Alexis S. L. Carvalho
turn util.opener into a class
r4827 d, fn = os.path.split(name)
fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
os.close(fd)
# Temporary files are created with mode 0600, which is usually not
# what we want. If the original file already exists, just copy
# its mode. Otherwise, manually obey umask.
try:
Matt Mackall
atomictempfile: avoid chmod weirdness on Linux vfat
r5740 st_mode = os.lstat(name).st_mode & 0777
Alexis S. L. Carvalho
turn util.opener into a class
r4827 except OSError, inst:
if inst.errno != errno.ENOENT:
raise
Alexis S. L. Carvalho
Make files in .hg inherit the permissions from .hg/store
r6062 st_mode = createmode
if st_mode is None:
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 st_mode = ~umask
Alexis S. L. Carvalho
Make files in .hg inherit the permissions from .hg/store
r6062 st_mode &= 0666
Alexis S. L. Carvalho
turn util.opener into a class
r4827 os.chmod(temp, st_mode)
if emptyok:
return temp
try:
try:
ifp = posixfile(name, "rb")
except IOError, inst:
if inst.errno == errno.ENOENT:
return temp
if not getattr(inst, 'filename', None):
inst.filename = name
raise
ofp = posixfile(temp, "wb")
for chunk in filechunkiter(ifp):
ofp.write(chunk)
ifp.close()
ofp.close()
except:
try: os.unlink(temp)
except: pass
raise
return temp
Vadim Gelfer
fix file handling bugs on windows....
r2176
Benoit Boissinot
use new style classes
r8778 class atomictempfile(object):
Greg Ward
atomictempfile: rewrite docstring to clarify rename() vs. close().
r14008 '''writeable file object that atomically updates a file
Alexis S. L. Carvalho
turn util.opener into a class
r4827
Greg Ward
atomictempfile: rewrite docstring to clarify rename() vs. close().
r14008 All writes will go to a temporary copy of the original file. Call
rename() when you are done writing, and atomictempfile will rename
the temporary copy to the original name, making the changes visible.
Unlike other file-like objects, close() discards your writes by
simply deleting the temporary file.
'''
Yuya Nishihara
util: give appropriate default args to atomictempfile()...
r11212 def __init__(self, name, mode='w+b', createmode=None):
Greg Ward
atomictempfile: avoid infinite recursion in __del__()....
r14007 self.__name = name # permanent name
self._tempname = mktempcopy(name, emptyok=('w' in mode),
createmode=createmode)
self._fp = posixfile(self._tempname, mode)
Bryan O'Sullivan
atomictempfile: delegate to posixfile instead of inheriting from it
r8327
Greg Ward
atomictempfile: avoid infinite recursion in __del__()....
r14007 # delegated methods
self.write = self._fp.write
self.fileno = self._fp.fileno
Alexis S. L. Carvalho
turn util.opener into a class
r4827
def rename(self):
Benoit Boissinot
atomictempfile: fix exception in __del__ if mktempcopy fails (self._fp is None)...
r8785 if not self._fp.closed:
Bryan O'Sullivan
atomictempfile: delegate to posixfile instead of inheriting from it
r8327 self._fp.close()
Greg Ward
atomictempfile: avoid infinite recursion in __del__()....
r14007 rename(self._tempname, localpath(self.__name))
Alexis S. L. Carvalho
turn util.opener into a class
r4827
Dan Villiom Podlaski Christiansen
util: make atomicfiles closable
r13098 def close(self):
Benoit Boissinot
atomictempfile: fix exception in __del__ if mktempcopy fails (self._fp is None)...
r8785 if not self._fp.closed:
Alexis S. L. Carvalho
turn util.opener into a class
r4827 try:
Greg Ward
atomictempfile: avoid infinite recursion in __del__()....
r14007 os.unlink(self._tempname)
except OSError:
pass
Bryan O'Sullivan
atomictempfile: delegate to posixfile instead of inheriting from it
r8327 self._fp.close()
Alexis S. L. Carvalho
turn util.opener into a class
r4827
Dan Villiom Podlaski Christiansen
util: make atomicfiles closable
r13098 def __del__(self):
Greg Ward
atomictempfile: avoid infinite recursion in __del__()....
r14007 if hasattr(self, '_fp'): # constructor actually did something
self.close()
Dan Villiom Podlaski Christiansen
util: make atomicfiles closable
r13098
Alexis S. L. Carvalho
Make files in .hg inherit the permissions from .hg/store
r6062 def makedirs(name, mode=None):
"""recursive directory creation with parent mode inheritance"""
Adrian Buehlmann
makedirs: abort if parent == name (issue2531)...
r13068 parent = os.path.abspath(os.path.dirname(name))
Alexis S. L. Carvalho
Make files in .hg inherit the permissions from .hg/store
r6062 try:
os.mkdir(name)
if mode is not None:
os.chmod(name, mode)
return
except OSError, err:
if err.errno == errno.EEXIST:
return
Adrian Buehlmann
makedirs: abort if parent == name (issue2531)...
r13068 if not name or parent == name or err.errno != errno.ENOENT:
Alexis S. L. Carvalho
Make files in .hg inherit the permissions from .hg/store
r6062 raise
makedirs(parent, mode)
makedirs(name, mode)
Eric Hopper
Created a class in util called chunkbuffer that buffers reads from an...
r1199 class chunkbuffer(object):
"""Allow arbitrary sized chunks of data to be efficiently read from an
iterator over chunks of arbitrary size."""
Bryan O'Sullivan
Minor cleanups.
r1200
Matt Mackall
chunkbuffer: removed unused method and arg
r5446 def __init__(self, in_iter):
Eric Hopper
Created a class in util called chunkbuffer that buffers reads from an...
r1199 """in_iter is the iterator that's iterating over the input chunks.
targetsize is how big a buffer to try to maintain."""
Benoit Boissinot
chunkbuffer: split big strings directly in chunkbuffer
r11670 def splitbig(chunks):
for chunk in chunks:
if len(chunk) > 2**20:
pos = 0
while pos < len(chunk):
end = pos + 2 ** 18
yield chunk[pos:end]
pos = end
else:
yield chunk
self.iter = splitbig(in_iter)
Matt Mackall
chunkbuffer: use += rather than cStringIO to reduce memory footprint...
r11758 self._queue = []
Bryan O'Sullivan
Minor cleanups.
r1200
Eric Hopper
Created a class in util called chunkbuffer that buffers reads from an...
r1199 def read(self, l):
Bryan O'Sullivan
Minor cleanups.
r1200 """Read L bytes of data from the iterator of chunks of data.
Thomas Arendsen Hein
Cleanup of tabs and trailing spaces.
r1308 Returns less than L bytes if the iterator runs dry."""
Matt Mackall
chunkbuffer: use += rather than cStringIO to reduce memory footprint...
r11758 left = l
buf = ''
queue = self._queue
while left > 0:
# refill the queue
if not queue:
target = 2**18
for chunk in self.iter:
queue.append(chunk)
target -= len(chunk)
if target <= 0:
break
if not queue:
Eric Hopper
Created a class in util called chunkbuffer that buffers reads from an...
r1199 break
Matt Mackall
chunkbuffer: use += rather than cStringIO to reduce memory footprint...
r11758
chunk = queue.pop(0)
left -= len(chunk)
if left < 0:
queue.insert(0, chunk[left:])
buf += chunk[:left]
else:
buf += chunk
return buf
Vadim Gelfer
util: add limit to amount filechunkiter will read
r2462 def filechunkiter(f, size=65536, limit=None):
"""Create a generator that produces the data in the file size
(default 65536) bytes at a time, up to optional limit (default is
to read all data). Chunks may be less than size bytes if the
chunk is the last chunk in the file, or the file is a socket or
some other type of file that sometimes reads less data than is
requested."""
assert size >= 0
assert limit is None or limit >= 0
while True:
Matt Mackall
many, many trivial check-code fixups
r10282 if limit is None:
nbytes = size
else:
nbytes = min(limit, size)
Vadim Gelfer
util: add limit to amount filechunkiter will read
r2462 s = nbytes and f.read(nbytes)
Matt Mackall
many, many trivial check-code fixups
r10282 if not s:
break
if limit:
limit -= len(s)
Eric Hopper
Created a class in util called chunkbuffer that buffers reads from an...
r1199 yield s
Bryan O'Sullivan
Fix up representation of dates in hgweb....
r1320
Bryan O'Sullivan
Clean up date and timezone handling....
r1321 def makedate():
Benoit Boissinot
fix handling of daylight saving time
r1482 lt = time.localtime()
if lt[8] == 1 and time.daylight:
tz = time.altzone
else:
tz = time.timezone
Adrian Buehlmann
makedate: abort on negative timestamps (issue2513)...
r13063 t = time.mktime(lt)
if t < 0:
hint = _("check your clock")
raise Abort(_("negative timestamp: %d") % t, hint=hint)
return t, tz
Bryan O'Sullivan
Allow files to be opened in text mode, even on Windows.
r1329
Matt Mackall
dates: improve timezone handling...
r6229 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
Bryan O'Sullivan
Clean up date and timezone handling....
r1321 """represent a (unixtime, offset) tuple as a localized time.
unixtime is seconds since the epoch, and offset is the time zone's
Vadim Gelfer
add changelog style to command line template....
r1987 number of seconds away from UTC. if timezone is false, do not
append time zone to string."""
Bryan O'Sullivan
Clean up date and timezone handling....
r1321 t, tz = date or makedate()
Adrian Buehlmann
util.datestr: do not crash on revisions with negative timestamp (issue2513)...
r13039 if t < 0:
t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
tz = 0
Matt Mackall
dates: improve timezone handling...
r6229 if "%1" in format or "%2" in format:
sign = (tz > 0) and "-" or "+"
Alejandro Santos
compat: use // for integer division
r9029 minutes = abs(tz) // 60
format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
Matt Mackall
dates: improve timezone handling...
r6229 format = format.replace("%2", "%02d" % (minutes % 60))
Vadim Gelfer
add changelog style to command line template....
r1987 s = time.strftime(format, time.gmtime(float(t) - tz))
return s
Vadim Gelfer
hgwebdir: export collections of repos...
r1829
Thomas Arendsen Hein
Make annotae/grep print short dates with -q/--quiet....
r6134 def shortdate(date=None):
"""turn (timestamp, tzoff) tuple into iso 8631 date."""
Matt Mackall
dates: improve timezone handling...
r6229 return datestr(date, format='%Y-%m-%d')
Thomas Arendsen Hein
Make annotae/grep print short dates with -q/--quiet....
r6134
Bryan O'Sullivan
util: add default argument to strdate
r5357 def strdate(string, format, defaults=[]):
Jose M. Prieto
Allow the use of human readable dates (issue 251)
r2522 """parse a localized time string and return a (unixtime, offset) tuple.
if the string cannot be parsed, ValueError is raised."""
Matt Mackall
parsedate: add UTC and GMT timezones
r3809 def timezone(string):
tz = string.split()[-1]
if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
Matt Mackall
dates: improve timezone handling...
r6229 sign = (tz[0] == "+") and 1 or -1
hours = int(tz[1:3])
minutes = int(tz[3:5])
return -sign * (hours * 60 + minutes) * 60
Matt Mackall
backout most of 4f8067c94729
r12401 if tz == "GMT" or tz == "UTC":
Matt Mackall
parsedate: add UTC and GMT timezones
r3809 return 0
return None
Jose M. Prieto
Allow the use of human readable dates (issue 251)
r2522
Jose M. Prieto
util.strdate: compute timestamp using UTC, not local timezone
r3255 # NOTE: unixtime = localunixtime + offset
Matt Mackall
parsedate: add UTC and GMT timezones
r3809 offset, date = timezone(string), string
Martin Geisler
code style: prefer 'is' and 'is not' tests with singletons
r13031 if offset is not None:
Matt Mackall
parsedate: add UTC and GMT timezones
r3809 date = " ".join(string.split()[:-1])
Matt Mackall
improve date parsing for numerous new date formats...
r3808
Matt Mackall
Add date matching support...
r3812 # add missing elements from defaults
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 usenow = False # default to using biased defaults
for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
Matt Mackall
Add date matching support...
r3812 found = [True for p in part if ("%"+p) in format]
if not found:
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 date += "@" + defaults[part][usenow]
Matt Mackall
Add date matching support...
r3812 format += "@%" + part[0]
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 else:
# We've found a specific time element, less specific time
# elements are relative to today
usenow = True
Matt Mackall
improve date parsing for numerous new date formats...
r3808
Jose M. Prieto
util.strdate: assume local time when no timezone specified
r3256 timetuple = time.strptime(date, format)
localunixtime = int(calendar.timegm(timetuple))
if offset is None:
# local timezone
unixtime = int(time.mktime(timetuple))
offset = unixtime - localunixtime
else:
unixtime = localunixtime + offset
Jose M. Prieto
util.strdate: compute timestamp using UTC, not local timezone
r3255 return unixtime, offset
Jose M. Prieto
Allow the use of human readable dates (issue 251)
r2522
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 def parsedate(date, formats=None, bias={}):
"""parse a localized date/time and return a (unixtime, offset) tuple.
Thomas Arendsen Hein
Fix bad behaviour when specifying an invalid date (issue700)...
r6139
Jose M. Prieto
Allow the use of human readable dates (issue 251)
r2522 The date may be a "unixtime offset" string or in one of the specified
Thomas Arendsen Hein
Fix bad behaviour when specifying an invalid date (issue700)...
r6139 formats. If the date already is a (unixtime, offset) tuple, it is returned.
"""
if not date:
Matt Mackall
parsedate: allow '' for epoch
r3807 return 0, 0
Matt Mackall
dates: Fix bare times to be relative to "today"
r6230 if isinstance(date, tuple) and len(date) == 2:
Thomas Arendsen Hein
Fix bad behaviour when specifying an invalid date (issue700)...
r6139 return date
Chris Mason
util.parsedate should understand dates from hg export
r2609 if not formats:
formats = defaultdateformats
Thomas Arendsen Hein
Fix bad behaviour when specifying an invalid date (issue700)...
r6139 date = date.strip()
Jose M. Prieto
Allow the use of human readable dates (issue 251)
r2522 try:
Thomas Arendsen Hein
Fix bad behaviour when specifying an invalid date (issue700)...
r6139 when, offset = map(int, date.split(' '))
Benoit Boissinot
validate the resulting date in parsedate
r2523 except ValueError:
Matt Mackall
Add date matching support...
r3812 # fill out defaults
now = makedate()
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 defaults = {}
David Soria Parra
avoid .split() in for loops and use tuples instead...
r13200 for part in ("d", "mb", "yY", "HI", "M", "S"):
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 # this piece is for rounding the specific end of unknowns
b = bias.get(part)
if b is None:
Matt Mackall
Add date matching support...
r3812 if part[0] in "HMS":
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 b = "00"
Matt Mackall
Add date matching support...
r3812 else:
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 b = "0"
# this piece is for matching the generic end to today's date
n = datestr(now, "%" + part[0])
defaults[part] = (b, n)
Matt Mackall
Add date matching support...
r3812
Benoit Boissinot
validate the resulting date in parsedate
r2523 for format in formats:
try:
Thomas Arendsen Hein
Fix bad behaviour when specifying an invalid date (issue700)...
r6139 when, offset = strdate(date, format, defaults)
Dirkjan Ochtman
better handle errors with date parsing (issue983)...
r6087 except (ValueError, OverflowError):
Benoit Boissinot
validate the resulting date in parsedate
r2523 pass
else:
break
else:
Nicolas Dumazet
util: get rid of extra trailing whitespace in parsedate abort message
r12105 raise Abort(_('invalid date: %r') % date)
Benoit Boissinot
validate the resulting date in parsedate
r2523 # validate explicit (probably user-specified) date and
# time zone offset. values must fit in signed 32 bits for
# current 32-bit linux runtimes. timezones go from UTC-12
# to UTC+14
if abs(when) > 0x7fffffff:
Matt Mackall
parsedate: use Abort rather than ValueError
r3806 raise Abort(_('date exceeds 32 bits: %d') % when)
Adrian Buehlmann
parsedate: abort on negative dates (issue2513)...
r13062 if when < 0:
raise Abort(_('negative date value: %d') % when)
Benoit Boissinot
validate the resulting date in parsedate
r2523 if offset < -50400 or offset > 43200:
Matt Mackall
parsedate: use Abort rather than ValueError
r3806 raise Abort(_('impossible time zone offset: %d') % offset)
Benoit Boissinot
validate the resulting date in parsedate
r2523 return when, offset
Jose M. Prieto
Allow the use of human readable dates (issue 251)
r2522
Matt Mackall
Add date matching support...
r3812 def matchdate(date):
"""Return a function that matches a given date match specifier
Formats include:
'{date}' match a given date to the accuracy provided
'<{date}' on or before a given date
'>{date}' on or after a given date
Matt Mackall
date: fix matching of underspecified date ranges...
r13212 >>> p1 = parsedate("10:29:59")
>>> p2 = parsedate("10:30:00")
>>> p3 = parsedate("10:30:59")
>>> p4 = parsedate("10:31:00")
>>> p5 = parsedate("Sep 15 10:30:00 1999")
>>> f = matchdate("10:30")
>>> f(p1[0])
False
>>> f(p2[0])
True
>>> f(p3[0])
True
>>> f(p4[0])
False
>>> f(p5[0])
False
Matt Mackall
Add date matching support...
r3812 """
def lower(date):
Matt Mackall
dates: Fix bare times to be relative to "today"
r6230 d = dict(mb="1", d="1")
return parsedate(date, extendeddateformats, d)[0]
Matt Mackall
Add date matching support...
r3812
def upper(date):
d = dict(mb="12", HI="23", M="59", S="59")
David Soria Parra
avoid .split() in for loops and use tuples instead...
r13200 for days in ("31", "30", "29"):
Matt Mackall
Add date matching support...
r3812 try:
d["d"] = days
return parsedate(date, extendeddateformats, d)[0]
except:
pass
d["d"] = "28"
return parsedate(date, extendeddateformats, d)[0]
Justin Peng
Correct a bug on date formats with '>' or '<' accompanied by space characters.
r7953 date = date.strip()
Idan Kamara
util: dates cannot consist entirely of whitespace (issue2732)
r13780
if not date:
raise Abort(_("dates cannot consist entirely of whitespace"))
elif date[0] == "<":
Matt Mackall
date: fixup breakage from ">" fix
r13869 if not date[1:]:
Martin Geisler
help/dates: use DATE as place-holder in help and abort texts...
r13886 raise Abort(_("invalid day spec, use '<DATE'"))
Matt Mackall
Add date matching support...
r3812 when = upper(date[1:])
return lambda x: x <= when
elif date[0] == ">":
Matt Mackall
date: fixup breakage from ">" fix
r13869 if not date[1:]:
Martin Geisler
help/dates: use DATE as place-holder in help and abort texts...
r13886 raise Abort(_("invalid day spec, use '>DATE'"))
Matt Mackall
Add date matching support...
r3812 when = lower(date[1:])
return lambda x: x >= when
elif date[0] == "-":
try:
days = int(date[1:])
except ValueError:
raise Abort(_("invalid day spec: %s") % date[1:])
Yun Lee
util: make 'hg log -d --2' abort (issue2734)
r13889 if days < 0:
raise Abort(_("%s must be nonnegative (see 'hg help dates')")
% date[1:])
Matt Mackall
Add date matching support...
r3812 when = makedate()[0] - days * 3600 * 24
Matt Mackall
Add --date support to log...
r3813 return lambda x: x >= when
Matt Mackall
Add date matching support...
r3812 elif " to " in date:
a, b = date.split(" to ")
start, stop = lower(a), upper(b)
return lambda x: x >= start and x <= stop
else:
start, stop = lower(date), upper(date)
return lambda x: x >= start and x <= stop
Vadim Gelfer
move shortuser into util module.
r1903 def shortuser(user):
"""Return a short representation of a user name or email address."""
f = user.find('@')
if f >= 0:
user = user[:f]
f = user.find('<')
if f >= 0:
Matt Mackall
many, many trivial check-code fixups
r10282 user = user[f + 1:]
Thomas Arendsen Hein
shortuser should stop before the first space character....
r3176 f = user.find(' ')
if f >= 0:
user = user[:f]
Matt Mackall
shortname: truncate at '.' too
r3533 f = user.find('.')
if f >= 0:
user = user[:f]
Vadim Gelfer
move shortuser into util module.
r1903 return user
Vadim Gelfer
merge with crew.
r1920
Matt Mackall
templater: move email function to util
r5975 def email(author):
'''get email of author.'''
r = author.find('>')
Matt Mackall
many, many trivial check-code fixups
r10282 if r == -1:
r = None
return author[author.find('<') + 1:r]
Matt Mackall
templater: move email function to util
r5975
Yuya Nishihara
util: fix ellipsis() not to break multi-byte sequence (issue2564)...
r13225 def _ellipsis(text, maxlength):
if len(text) <= maxlength:
return text, False
else:
return "%s..." % (text[:maxlength - 3]), True
Thomas Arendsen Hein
Move ellipsis code to util.ellipsis() and improve maxlength handling.
r3767 def ellipsis(text, maxlength=400):
"""Trim string to at most maxlength (default: 400) characters."""
Yuya Nishihara
util: fix ellipsis() not to break multi-byte sequence (issue2564)...
r13225 try:
# use unicode not to split at intermediate multi-byte sequence
utext, truncated = _ellipsis(text.decode(encoding.encoding),
maxlength)
if not truncated:
return text
return utext.encode(encoding.encoding)
except (UnicodeDecodeError, UnicodeEncodeError):
return _ellipsis(text, maxlength)[0]
Thomas Arendsen Hein
Move ellipsis code to util.ellipsis() and improve maxlength handling.
r3767
Vadim Gelfer
add support for streaming clone....
r2612 def bytecount(nbytes):
'''return byte count formatted as readable string, with units'''
units = (
Matt Mackall
many, many trivial check-code fixups
r10282 (100, 1 << 30, _('%.0f GB')),
(10, 1 << 30, _('%.1f GB')),
(1, 1 << 30, _('%.2f GB')),
(100, 1 << 20, _('%.0f MB')),
(10, 1 << 20, _('%.1f MB')),
(1, 1 << 20, _('%.2f MB')),
(100, 1 << 10, _('%.0f KB')),
(10, 1 << 10, _('%.1f KB')),
(1, 1 << 10, _('%.2f KB')),
Vadim Gelfer
add support for streaming clone....
r2612 (1, 1, _('%.0f bytes')),
)
for multiplier, divisor, format in units:
if nbytes >= divisor * multiplier:
return format % (nbytes / float(divisor))
return units[-1][2] % nbytes
Vadim Gelfer
clean up hg.py: move repo constructor code into each repo module
r2740
Patrick Mezard
extdiff: avoid repr() doubling paths backslashes under Windows
r5291 def uirepr(s):
# Avoid double backslash in Windows path repr()
return repr(s).replace('\\\\', '\\')
Alexander Solovyov
python implementation of diffstat...
r7547
Matt Mackall
util: delay loading of textwrap
r13316 # delay import of textwrap
def MBTextWrapper(**kwargs):
class tw(textwrap.TextWrapper):
"""
Extend TextWrapper for double-width characters.
Nicolas Dumazet
util: clarify purpose of MBTextWrapper class...
r12957
Matt Mackall
util: delay loading of textwrap
r13316 Some Asian characters use two terminal columns instead of one.
A good example of this behavior can be seen with u'\u65e5\u672c',
the two Japanese characters for "Japan":
len() returns 2, but when printed to a terminal, they eat 4 columns.
Nicolas Dumazet
util: clarify purpose of MBTextWrapper class...
r12957
Matt Mackall
util: delay loading of textwrap
r13316 (Note that this has nothing to do whatsoever with unicode
representation, or encoding of the underlying string)
"""
def __init__(self, **kwargs):
textwrap.TextWrapper.__init__(self, **kwargs)
FUJIWARA Katsunori
replace Python standard textwrap by MBCS sensitive one for i18n text...
r11297
Matt Mackall
util: delay loading of textwrap
r13316 def _cutdown(self, str, space_left):
l = 0
ucstr = unicode(str, encoding.encoding)
colwidth = unicodedata.east_asian_width
for i in xrange(len(ucstr)):
l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
if space_left < l:
return (ucstr[:i].encode(encoding.encoding),
ucstr[i:].encode(encoding.encoding))
return str, ''
FUJIWARA Katsunori
replace Python standard textwrap by MBCS sensitive one for i18n text...
r11297
Matt Mackall
util: delay loading of textwrap
r13316 # overriding of base class
def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
space_left = max(width - cur_len, 1)
FUJIWARA Katsunori
replace Python standard textwrap by MBCS sensitive one for i18n text...
r11297
Matt Mackall
util: delay loading of textwrap
r13316 if self.break_long_words:
cut, res = self._cutdown(reversed_chunks[-1], space_left)
cur_line.append(cut)
reversed_chunks[-1] = res
elif not cur_line:
cur_line.append(reversed_chunks.pop())
FUJIWARA Katsunori
replace Python standard textwrap by MBCS sensitive one for i18n text...
r11297
Matt Mackall
util: delay loading of textwrap
r13316 global MBTextWrapper
MBTextWrapper = tw
return tw(**kwargs)
FUJIWARA Katsunori
replace Python standard textwrap by MBCS sensitive one for i18n text...
r11297
Matt Mackall
util: make wrap() require a width argument...
r12698 def wrap(line, width, initindent='', hangindent=''):
FUJIWARA Katsunori
replace Python standard textwrap by MBCS sensitive one for i18n text...
r11297 maxindent = max(len(hangindent), len(initindent))
if width <= maxindent:
Martin Geisler
util, minirst: do not crash with COLUMNS=0
r9417 # adjust for weird terminal size
FUJIWARA Katsunori
replace Python standard textwrap by MBCS sensitive one for i18n text...
r11297 width = max(78, maxindent + 1)
wrapper = MBTextWrapper(width=width,
initial_indent=initindent,
subsequent_indent=hangindent)
return wrapper.fill(line)
Martin Geisler
commands: wrap short descriptions in 'hg help'...
r8938
Alexander Solovyov <piranha at piranha.org.ua>
templater: ability to display diffstat for log-like commands
r7879 def iterlines(iterator):
for chunk in iterator:
for line in chunk.splitlines():
yield line
Alexander Solovyov
make path expanding more consistent...
r9610
def expandpath(path):
return os.path.expanduser(os.path.expandvars(path))
Patrick Mezard
Find right hg command for detached process...
r10239
def hgcmd():
"""Return the command used to execute current hg
This is different from hgexecutable() because on Windows we want
to avoid things opening new shell windows like batch files, so we
get either the python call or current executable.
"""
if main_is_frozen():
return [sys.executable]
return gethgcmd()
Patrick Mezard
util: make spawndetached() handle subprocess early terminations...
r10344
def rundetached(args, condfn):
"""Execute the argument list in a detached process.
Augie Fackler
util: fix trailing whitespace found by check-code
r10422
Patrick Mezard
util: make spawndetached() handle subprocess early terminations...
r10344 condfn is a callable which is called repeatedly and should return
True once the child process is known to have started successfully.
At this point, the child process PID is returned. If the child
process fails to start or finishes before condfn() evaluates to
True, return -1.
"""
# Windows case is easier because the child process is either
# successfully starting and validating the condition or exiting
# on failure. We just poll on its PID. On Unix, if the child
# process fails to start, it will be left in a zombie state until
# the parent wait on it, which we cannot do since we expect a long
# running process on success. Instead we listen for SIGCHLD telling
# us our child process terminated.
terminated = set()
def handler(signum, frame):
terminated.add(os.wait())
prevhandler = None
if hasattr(signal, 'SIGCHLD'):
prevhandler = signal.signal(signal.SIGCHLD, handler)
try:
pid = spawndetached(args)
while not condfn():
if ((pid in terminated or not testpid(pid))
and not condfn()):
return -1
time.sleep(0.1)
return pid
finally:
if prevhandler is not None:
signal.signal(signal.SIGCHLD, prevhandler)
Steve Losh
util: add any() and all() functions for Python 2.4 compatibility...
r10438
Steve Losh
util: use the built-in any() and all() methods if they are available
r10487 try:
any, all = any, all
except NameError:
def any(iterable):
for i in iterable:
if i:
return True
return False
Steve Losh
util: add any() and all() functions for Python 2.4 compatibility...
r10438
Steve Losh
util: use the built-in any() and all() methods if they are available
r10487 def all(iterable):
for i in iterable:
if not i:
return False
return True
Patrick Mezard
util: fix default termwidth() under Windows...
r11010
Roman Sokolov
dispatch: support for $ escaping in shell-alias definition...
r13392 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
Steve Losh
util: add an interpolate() function to for replacing multiple values...
r11988 """Return the result of interpolating items in the mapping into string s.
prefix is a single character string, or a two character string with
a backslash as the first character if the prefix needs to be escaped in
a regular expression.
fn is an optional function that will be applied to the replacement text
just before replacement.
Roman Sokolov
dispatch: support for $ escaping in shell-alias definition...
r13392
escape_prefix is an optional flag that allows using doubled prefix for
its escaping.
Steve Losh
util: add an interpolate() function to for replacing multiple values...
r11988 """
fn = fn or (lambda s: s)
Roman Sokolov
dispatch: support for $ escaping in shell-alias definition...
r13392 patterns = '|'.join(mapping.keys())
if escape_prefix:
patterns += '|' + prefix
if len(prefix) > 1:
prefix_char = prefix[1:]
else:
prefix_char = prefix
mapping[prefix_char] = prefix_char
r = re.compile(r'%s(%s)' % (prefix, patterns))
Steve Losh
util: add an interpolate() function to for replacing multiple values...
r11988 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
Brodie Rao
mail/hgweb: support service names for ports (issue2350)...
r12076 def getport(port):
"""Return the port for a given network service.
If port is an integer, it's returned as is. If it's a string, it's
looked up using socket.getservbyname(). If there's no matching
service, util.Abort is raised.
"""
try:
return int(port)
except ValueError:
pass
try:
return socket.getservbyname(port)
except socket.error:
raise Abort(_("no port number associated with service '%s'") % port)
Augie Fackler
parsebool: create new function and use it for config parsing
r12087
Augie Fackler
parsebool: accept always as true and never as false
r12088 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
'0': False, 'no': False, 'false': False, 'off': False,
'never': False}
Augie Fackler
parsebool: create new function and use it for config parsing
r12087
def parsebool(s):
"""Parse s into a boolean.
If s is not a valid boolean, returns None.
"""
return _booleans.get(s.lower(), None)
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076
Brodie Rao
util.url: copy urllib.unquote() into util to improve startup times...
r14077 _hexdig = '0123456789ABCDEFabcdef'
_hextochr = dict((a + b, chr(int(a + b, 16)))
for a in _hexdig for b in _hexdig)
def _urlunquote(s):
"""unquote('abc%20def') -> 'abc def'."""
res = s.split('%')
# fastpath
if len(res) == 1:
return s
s = res[0]
for item in res[1:]:
try:
s += _hextochr[item[:2]] + item[2:]
except KeyError:
s += '%' + item
except UnicodeDecodeError:
s += unichr(int(item[:2], 16)) + item[2:]
return s
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076 class url(object):
"""Reliable URL parser.
This parses URLs and provides attributes for the following
components:
<scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
Missing components are set to None. The only exception is
fragment, which is set to '' if present but empty.
If parsefragment is False, fragment is included in query. If
parsequery is False, query is included in path. If both are
False, both fragment and query are included in path.
See http://www.ietf.org/rfc/rfc2396.txt for more information.
Note that for backward compatibility reasons, bundle URLs do not
take host names. That means 'bundle://../' has a path of '../'.
Examples:
>>> url('http://www.ietf.org/rfc/rfc2396.txt')
<url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
>>> url('ssh://[::1]:2200//home/joe/repo')
<url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
>>> url('file:///home/joe/repo')
<url scheme: 'file', path: '/home/joe/repo'>
>>> url('bundle:foo')
<url scheme: 'bundle', path: 'foo'>
>>> url('bundle://../foo')
<url scheme: 'bundle', path: '../foo'>
>>> url('c:\\\\foo\\\\bar')
<url path: 'c:\\\\foo\\\\bar'>
Authentication credentials:
>>> url('ssh://joe:xyz@x/repo')
<url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
>>> url('ssh://joe@x/repo')
<url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
Query strings and fragments:
>>> url('http://host/a?b#c')
<url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
>>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
<url scheme: 'http', host: 'host', path: 'a?b#c'>
"""
_safechars = "!~*'()+"
_safepchars = "/!~*'()+"
_matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
def __init__(self, path, parsequery=True, parsefragment=True):
# We slowly chomp away at path until we have only the path left
self.scheme = self.user = self.passwd = self.host = None
self.port = self.path = self.query = self.fragment = None
self._localpath = True
self._hostport = ''
self._origpath = path
# special case for Windows drive letters
if hasdriveletter(path):
self.path = path
return
# For compatibility reasons, we can't handle bundle paths as
# normal URLS
if path.startswith('bundle:'):
self.scheme = 'bundle'
path = path[7:]
if path.startswith('//'):
path = path[2:]
self.path = path
return
if self._matchscheme(path):
parts = path.split(':', 1)
if parts[0]:
self.scheme, path = parts
self._localpath = False
if not path:
path = None
if self._localpath:
self.path = ''
return
else:
if parsefragment and '#' in path:
path, self.fragment = path.split('#', 1)
if not path:
path = None
if self._localpath:
self.path = path
return
if parsequery and '?' in path:
path, self.query = path.split('?', 1)
if not path:
path = None
if not self.query:
self.query = None
# // is required to specify a host/authority
if path and path.startswith('//'):
parts = path[2:].split('/', 1)
if len(parts) > 1:
self.host, path = parts
path = path
else:
self.host = parts[0]
path = None
if not self.host:
self.host = None
if path:
path = '/' + path
if self.host and '@' in self.host:
self.user, self.host = self.host.rsplit('@', 1)
if ':' in self.user:
self.user, self.passwd = self.user.split(':', 1)
if not self.host:
self.host = None
# Don't split on colons in IPv6 addresses without ports
if (self.host and ':' in self.host and
not (self.host.startswith('[') and self.host.endswith(']'))):
self._hostport = self.host
self.host, self.port = self.host.rsplit(':', 1)
if not self.host:
self.host = None
if (self.host and self.scheme == 'file' and
self.host not in ('localhost', '127.0.0.1', '[::1]')):
raise Abort(_('file:// URLs can only refer to localhost'))
self.path = path
for a in ('user', 'passwd', 'host', 'port',
'path', 'query', 'fragment'):
v = getattr(self, a)
if v is not None:
Brodie Rao
util.url: copy urllib.unquote() into util to improve startup times...
r14077 setattr(self, a, _urlunquote(v))
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076
def __repr__(self):
attrs = []
for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
'query', 'fragment'):
v = getattr(self, a)
if v is not None:
attrs.append('%s: %r' % (a, v))
return '<url %s>' % ', '.join(attrs)
def __str__(self):
"""Join the URL's components back into a URL string.
Examples:
>>> str(url('http://user:pw@host:80/?foo#bar'))
'http://user:pw@host:80/?foo#bar'
>>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
'ssh://user:pw@[::1]:2200//home/joe#'
>>> str(url('http://localhost:80//'))
'http://localhost:80//'
>>> str(url('http://localhost:80/'))
'http://localhost:80/'
>>> str(url('http://localhost:80'))
'http://localhost:80/'
>>> str(url('bundle:foo'))
'bundle:foo'
>>> str(url('bundle://../foo'))
'bundle:../foo'
>>> str(url('path'))
'path'
"""
if self._localpath:
s = self.path
if self.scheme == 'bundle':
s = 'bundle:' + s
if self.fragment:
s += '#' + self.fragment
return s
s = self.scheme + ':'
if (self.user or self.passwd or self.host or
self.scheme and not self.path):
s += '//'
if self.user:
s += urllib.quote(self.user, safe=self._safechars)
if self.passwd:
s += ':' + urllib.quote(self.passwd, safe=self._safechars)
if self.user or self.passwd:
s += '@'
if self.host:
if not (self.host.startswith('[') and self.host.endswith(']')):
s += urllib.quote(self.host)
else:
s += self.host
if self.port:
s += ':' + urllib.quote(self.port)
if self.host:
s += '/'
if self.path:
s += urllib.quote(self.path, safe=self._safepchars)
if self.query:
s += '?' + urllib.quote(self.query, safe=self._safepchars)
if self.fragment is not None:
s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
return s
def authinfo(self):
user, passwd = self.user, self.passwd
try:
self.user, self.passwd = None, None
s = str(self)
finally:
self.user, self.passwd = user, passwd
if not self.user:
return (s, None)
return (s, (None, (str(self), self.host),
self.user, self.passwd or ''))
def localpath(self):
if self.scheme == 'file' or self.scheme == 'bundle':
path = self.path or '/'
# For Windows, we need to promote hosts containing drive
# letters to paths with drive letters.
if hasdriveletter(self._hostport):
path = self._hostport + '/' + self.path
elif self.host is not None and self.path:
path = '/' + path
# We also need to handle the case of file:///C:/, which
# should return C:/, not /C:/.
elif hasdriveletter(path):
# Strip leading slash from paths with drive names
return path[1:]
return path
return self._origpath
def hasscheme(path):
return bool(url(path).scheme)
def hasdriveletter(path):
return path[1:2] == ':' and path[0:1].isalpha()
def localpath(path):
return url(path, parsequery=False, parsefragment=False).localpath()
def hidepassword(u):
'''hide user credential in a url string'''
u = url(u)
if u.passwd:
u.passwd = '***'
return str(u)
def removeauth(u):
'''remove all authentication information from a url string'''
u = url(u)
u.user = u.passwd = None
return str(u)