[PATCH] templater: add recursive template detection (issue4758)

Gregory Szorc gregory.szorc at gmail.com
Fri Jan 22 11:34:31 CST 2016



> On Jan 22, 2016, at 08:49, Kostia Balytskyi <ikostia at fb.com> wrote:
> 
> This is currently implemented using global variable and I'm aware that it's not the nicest option, but it feels like in order to store this state in some proper way, the templater would have to go through a significant refactoring. If I'm missing some easy way to fix this bug, please tell me.
> 

Turn _flatten into a closure.

> 
>> On 1/22/16, 4:45 PM, "Kostia Balytskyi" <ikostia at fb.com> wrote:
>> 
>> # HG changeset patch
>> # User Kostia Balytskyi <ikostia at fb.com>
>> # Date 1453481091 0
>> #      Fri Jan 22 16:44:51 2016 +0000
>> # Node ID bf244ba18a2ff50783a17fecf134118921892020
>> # Parent  d73a5ab18015f61ac61e6e77256512fd82b03818
>> templater: add recursive template detection (issue4758)
>> 
>> When mercurial is given a recursive template it currently fails with
>> 'maximum recursion depth' exception. To avoid that we need to
>> introduce a set of symbols currenly in resolution which would be added to
>> as we go deeper in call start and removed from when we move out, just
>> like the actual call stack. However, since we want to completely prohibit
>> recursion, on every addition to this set we need to check whether
>> it already contains the same value, in which case we have to abort.
>> 
>> diff --git a/mercurial/templater.py b/mercurial/templater.py
>> --- a/mercurial/templater.py
>> +++ b/mercurial/templater.py
>> @@ -760,8 +760,15 @@
>> 
>> stringify = templatefilters.stringify
>> 
>> -def _flatten(thing):
>> +namesinresolution = set([])
>> +def _flatten(thing, key=''):
>>    '''yield a single stream from a possibly nested set of iterators'''
>> +    if key and key in namesinresolution:
>> +        raise error.Abort("recursive template definition")
>> +
>> +    if key:
>> +        namesinresolution.add(key)
>> +
>>    if isinstance(thing, str):
>>        yield thing
>>    elif not util.safehasattr(thing, '__iter__'):
>> @@ -778,6 +785,9 @@
>>                for j in _flatten(i):
>>                    yield j
>> 
>> +    if key:
>> +        namesinresolution.remove(key)
>> +
>> def unquotestring(s):
>>    '''unwrap quotes'''
>>    if len(s) < 2 or s[0] != s[-1]:
>> @@ -824,7 +834,7 @@
>>        '''Perform expansion. t is name of map element to expand.
>>        mapping contains added elements for use during expansion. Is a
>>        generator.'''
>> -        return _flatten(runtemplate(self, mapping, self._load(t)))
>> +        return _flatten(runtemplate(self, mapping, self._load(t)), t)
>> 
>> engines = {'default': engine}
>> 
>> diff --git a/tests/test-template-engine.t b/tests/test-template-engine.t
>> --- a/tests/test-template-engine.t
>> +++ b/tests/test-template-engine.t
>> @@ -44,6 +44,10 @@
>>  0 97e5f848f0936960273bbf75be6388cd0350a32b -1 0000000000000000000000000000000000000000
>>  -1 0000000000000000000000000000000000000000 -1 0000000000000000000000000000000000000000
>> 
>> +  $ hg log -l 1 --template "{changeset}"
>> +  abort: recursive template definition
>> +  [255]
>> +
>> Fuzzing the unicode escaper to ensure it produces valid data
>> 
>> #if hypothesis
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel


More information about the Mercurial-devel mailing list