Module detection cleaner and more reliable
Stolen from mercurial.lsprof
# keyword.py - keyword expansion for mercurial# $Id$'''keyword expansion hack against the grain of a DSCMThis extension lets you expand RCS/CVS-like keywords in a Mercurialrepository.There are many good reasons why this is not needed in a distributedSCM, still it may be useful in very small projects based on singlefiles (like LaTeX packages), that are mostly addressed to an audiencenot running a version control system.Supported $keywords$ and their $keyword: substition $ are: Revision: changeset id Author: full username Date: %a %b %d %H:%M:%S %Y %z $ RCSFile: basename,v Source: /path/to/basename,v Id: basename,v csetid %Y-%m-%d %H:%M:%S %z shortname Header: /path/to/basename,v csetid %Y-%m-%d %H:%M:%S %z shortnameSimple setup in hgrc: # enable extension hgext.keyword = # or, if script not in hgext folder: # hgext.keyword = /full/path/to/script # filename patterns for expansion are configured in this section [keyword] **.py = expand ...'''frommercurialimportcontext,utilimportos.path,re,sysre_kw=re.compile(r'\$(Id|Header|Author|Date|Revision|RCSFile|Source)[^$]*?\$')defkwexpand(matchobj,repo,path,changeid=None,fileid=None,filelog=None):'''Called by kwfilelog.read and pretxnkw. Sets supported keywords as local variables and evaluates them to their expansion if matchobj is equal to string representation.'''c=context.filectx(repo,path,changeid=changeid,fileid=fileid,filelog=filelog)date=c.date()Revision=c.changectx()Author=c.user()RCSFile=os.path.basename(path)+',v'Source=repo.wjoin(path)+',v'Date=util.datestr(date=date)revdateauth='%s%s%s'%(Revision,util.datestr(date=date,format=util.defaultdateformats[0]),util.shortuser(Author))Header='%s%s'%(Source,revdateauth)Id='%s%s'%(RCSFile,revdateauth)return'$%s: %s $'%(matchobj.group(1),eval(matchobj.group(1)))defkwfmatches(ui,repo,files):'''Selects candidates for keyword substitution configured in keyword section in hgrc.'''files=[fforfinfilesifnotf.startswith('.hg')]ifnotfiles:return[]candidates=[]kwfmatchers=[util.matcher(repo.root,'',[pat],[],[])[1]forpat,optinui.configitems('keyword')ifopt=='expand']forfinfiles:formfinkwfmatchers:ifmf(f):candidates.append(f)breakreturncandidatesdefreposetup(ui,repo):frommercurialimportfilelog,revlogifnotrepo.local():returnclasskwrepo(repo.__class__):deffile(self,f):iff[0]=='/':f=f[1:]returnfilelog.filelog(self.sopener,f,self,self.revlogversion)classkwfilelog(filelog.filelog):''' Superclass over filelog to customize it's read, add, cmp methods. Keywords are "stored" unexpanded, and expanded on reading. '''def__init__(self,opener,path,repo,defversion=revlog.REVLOG_DEFAULT_VERSION):super(kwfilelog,self).__init__(opener,path,defversion)self._repo=repoself._path=pathdefiskwcandidate(self,data):'''Decides whether to act on keywords.'''return(kwfmatches(ui,self._repo,[self._path])andnotutil.binary(data))defread(self,node):'''Substitutes keywords when reading filelog.'''data=super(kwfilelog,self).read(node)ifself.iskwcandidate(data):returnre_kw.sub(lambdam:kwexpand(m,self._repo,self._path,fileid=node,filelog=self),data)returndatadefadd(self,text,meta,tr,link,p1=None,p2=None):'''Removes keyword substitutions when adding to filelog.'''ifself.iskwcandidate(text):text=re_kw.sub(r'$\1$',text)returnsuper(kwfilelog,self).add(text,meta,tr,link,p1=None,p2=None)defcmp(self,node,text):'''Removes keyword substitutions for comparison.'''ifself.iskwcandidate(text):text=re_kw.sub(r'$\1$',text)returnsuper(kwfilelog,self).cmp(node,text)filelog.filelog=kwfilelogrepo.__class__=kwrepo# make pretxncommit hook import kwmodule regardless of where it's locatedfork,vinsys.modules.iteritems():ifvisNone:continueifnothasattr(v,'__file__'):continueifv.__file__.startswith(__file__):mod=kbreakelse:sys.path.insert(0,os.path.abspath(os.path.dirname(__file__)))mod=os.path.splitext(os.path.basename(__file__))[0]ui.setconfig('hooks','pretxncommit.keyword','python:%s.pretxnkw'%mod)delmoddefpretxnkw(ui,repo,hooktype,**args):'''pretxncommit hook that collects candidates for keyword expansion on commit and expands keywords in working dir.'''frommercurial.i18nimportgettextas_# above line for backwards compatibility; can be changed to# from mercurial.i18n import _# some dayfrommercurialimportcmdutil,commandsifhooktype!='pretxncommit':returnTruecmd,sysargs,globalopts,cmdopts=commands.parse(ui,sys.argv[1:])[1:]ifrepr(cmd).split()[1]in('tag','import_'):returnFalsefiles,match,anypats=cmdutil.matchpats(repo,sysargs,cmdopts)modified,added=repo.status(files=files,match=match)[:2]forfinkwfmatches(ui,repo,modified+added):data=repo.wfile(f).read()ifnotutil.binary(data):data,kwct=re_kw.subn(lambdam:kwexpand(m,repo,f,changeid=args['node']),data)ifkwct:ui.debug(_('overwriting %s expanding keywords\n'%f))repo.wfile(f,'w').write(data)