comparison mercurial/fileset.py @ 27461:afa76585c955

fileset: use decorator to mark a predicate as "status caller" Before this patch, predicates calling 'matchctx.status()' are listed up by immediate list value in 'getfileset()'. This prevents 3rd party extensions from adding specific predicate calling 'matchctx.status()'. This uses decorator to mark a predicate as "status caller". This can also localize changes for adding (or removing) a "status caller" predicate function in source code.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Mon, 21 Dec 2015 22:31:16 +0900
parents 11286ac374f3
children 470ea34ba593
comparison
equal deleted inserted replaced
27460:11286ac374f3 27461:afa76585c955
135 # with: 135 # with:
136 # mctx - current matchctx instance 136 # mctx - current matchctx instance
137 # x - argument in tree form 137 # x - argument in tree form
138 symbols = {} 138 symbols = {}
139 139
140 def predicate(decl): 140 # filesets using matchctx.status()
141 _statuscallers = []
142
143 def predicate(decl, callstatus=False):
141 """Return a decorator for fileset predicate function 144 """Return a decorator for fileset predicate function
142 145
143 'decl' argument is the declaration (including argument list like 146 'decl' argument is the declaration (including argument list like
144 'adds(pattern)') or the name (for internal use only) of predicate. 147 'adds(pattern)') or the name (for internal use only) of predicate.
148
149 Optional 'callstatus' argument indicates whether predicate implies
150 'matchctx.status()' at runtime or not (False, by default).
145 """ 151 """
146 def decorator(func): 152 def decorator(func):
147 i = decl.find('(') 153 i = decl.find('(')
148 if i > 0: 154 if i > 0:
149 name = decl[:i] 155 name = decl[:i]
150 else: 156 else:
151 name = decl 157 name = decl
152 symbols[name] = func 158 symbols[name] = func
159 if callstatus:
160 _statuscallers.append(name)
153 if func.__doc__: 161 if func.__doc__:
154 func.__doc__ = "``%s``\n %s" % (decl, func.__doc__.strip()) 162 func.__doc__ = "``%s``\n %s" % (decl, func.__doc__.strip())
155 return func 163 return func
156 return decorator 164 return decorator
157 165
158 @predicate('modified()') 166 @predicate('modified()', callstatus=True)
159 def modified(mctx, x): 167 def modified(mctx, x):
160 """File that is modified according to :hg:`status`. 168 """File that is modified according to :hg:`status`.
161 """ 169 """
162 # i18n: "modified" is a keyword 170 # i18n: "modified" is a keyword
163 getargs(x, 0, 0, _("modified takes no arguments")) 171 getargs(x, 0, 0, _("modified takes no arguments"))
164 s = mctx.status().modified 172 s = mctx.status().modified
165 return [f for f in mctx.subset if f in s] 173 return [f for f in mctx.subset if f in s]
166 174
167 @predicate('added()') 175 @predicate('added()', callstatus=True)
168 def added(mctx, x): 176 def added(mctx, x):
169 """File that is added according to :hg:`status`. 177 """File that is added according to :hg:`status`.
170 """ 178 """
171 # i18n: "added" is a keyword 179 # i18n: "added" is a keyword
172 getargs(x, 0, 0, _("added takes no arguments")) 180 getargs(x, 0, 0, _("added takes no arguments"))
173 s = mctx.status().added 181 s = mctx.status().added
174 return [f for f in mctx.subset if f in s] 182 return [f for f in mctx.subset if f in s]
175 183
176 @predicate('removed()') 184 @predicate('removed()', callstatus=True)
177 def removed(mctx, x): 185 def removed(mctx, x):
178 """File that is removed according to :hg:`status`. 186 """File that is removed according to :hg:`status`.
179 """ 187 """
180 # i18n: "removed" is a keyword 188 # i18n: "removed" is a keyword
181 getargs(x, 0, 0, _("removed takes no arguments")) 189 getargs(x, 0, 0, _("removed takes no arguments"))
182 s = mctx.status().removed 190 s = mctx.status().removed
183 return [f for f in mctx.subset if f in s] 191 return [f for f in mctx.subset if f in s]
184 192
185 @predicate('deleted()') 193 @predicate('deleted()', callstatus=True)
186 def deleted(mctx, x): 194 def deleted(mctx, x):
187 """Alias for ``missing()``. 195 """Alias for ``missing()``.
188 """ 196 """
189 # i18n: "deleted" is a keyword 197 # i18n: "deleted" is a keyword
190 getargs(x, 0, 0, _("deleted takes no arguments")) 198 getargs(x, 0, 0, _("deleted takes no arguments"))
191 s = mctx.status().deleted 199 s = mctx.status().deleted
192 return [f for f in mctx.subset if f in s] 200 return [f for f in mctx.subset if f in s]
193 201
194 @predicate('missing()') 202 @predicate('missing()', callstatus=True)
195 def missing(mctx, x): 203 def missing(mctx, x):
196 """File that is missing according to :hg:`status`. 204 """File that is missing according to :hg:`status`.
197 """ 205 """
198 # i18n: "missing" is a keyword 206 # i18n: "missing" is a keyword
199 getargs(x, 0, 0, _("missing takes no arguments")) 207 getargs(x, 0, 0, _("missing takes no arguments"))
200 s = mctx.status().deleted 208 s = mctx.status().deleted
201 return [f for f in mctx.subset if f in s] 209 return [f for f in mctx.subset if f in s]
202 210
203 @predicate('unknown()') 211 @predicate('unknown()', callstatus=True)
204 def unknown(mctx, x): 212 def unknown(mctx, x):
205 """File that is unknown according to :hg:`status`. These files will only be 213 """File that is unknown according to :hg:`status`. These files will only be
206 considered if this predicate is used. 214 considered if this predicate is used.
207 """ 215 """
208 # i18n: "unknown" is a keyword 216 # i18n: "unknown" is a keyword
209 getargs(x, 0, 0, _("unknown takes no arguments")) 217 getargs(x, 0, 0, _("unknown takes no arguments"))
210 s = mctx.status().unknown 218 s = mctx.status().unknown
211 return [f for f in mctx.subset if f in s] 219 return [f for f in mctx.subset if f in s]
212 220
213 @predicate('ignored()') 221 @predicate('ignored()', callstatus=True)
214 def ignored(mctx, x): 222 def ignored(mctx, x):
215 """File that is ignored according to :hg:`status`. These files will only be 223 """File that is ignored according to :hg:`status`. These files will only be
216 considered if this predicate is used. 224 considered if this predicate is used.
217 """ 225 """
218 # i18n: "ignored" is a keyword 226 # i18n: "ignored" is a keyword
219 getargs(x, 0, 0, _("ignored takes no arguments")) 227 getargs(x, 0, 0, _("ignored takes no arguments"))
220 s = mctx.status().ignored 228 s = mctx.status().ignored
221 return [f for f in mctx.subset if f in s] 229 return [f for f in mctx.subset if f in s]
222 230
223 @predicate('clean()') 231 @predicate('clean()', callstatus=True)
224 def clean(mctx, x): 232 def clean(mctx, x):
225 """File that is clean according to :hg:`status`. 233 """File that is clean according to :hg:`status`.
226 """ 234 """
227 # i18n: "clean" is a keyword 235 # i18n: "clean" is a keyword
228 getargs(x, 0, 0, _("clean takes no arguments")) 236 getargs(x, 0, 0, _("clean takes no arguments"))
521 529
522 def getfileset(ctx, expr): 530 def getfileset(ctx, expr):
523 tree = parse(expr) 531 tree = parse(expr)
524 532
525 # do we need status info? 533 # do we need status info?
526 if (_intree(['modified', 'added', 'removed', 'deleted', 534 if (_intree(_statuscallers, tree) or
527 'missing', 'unknown', 'ignored', 'clean'], tree) or
528 # Using matchctx.existing() on a workingctx requires us to check 535 # Using matchctx.existing() on a workingctx requires us to check
529 # for deleted files. 536 # for deleted files.
530 (ctx.rev() is None and _intree(_existingcallers, tree))): 537 (ctx.rev() is None and _intree(_existingcallers, tree))):
531 unknown = _intree(['unknown'], tree) 538 unknown = _intree(['unknown'], tree)
532 ignored = _intree(['ignored'], tree) 539 ignored = _intree(['ignored'], tree)