[PATCH] store: break up reference cycle introduced in 9cbff8a39a2a

Adrian Buehlmann adrian at cadifra.com
Wed May 4 18:45:27 CDT 2011


On 2011-05-05 01:02, Matt Mackall wrote:
> On Wed, 2011-05-04 at 14:22 +0200, Adrian Buehlmann wrote:
>> On 2011-05-04 13:42, Adrian Buehlmann wrote:
>>> # HG changeset patch
>>> # User Adrian Buehlmann <adrian at cadifra.com>
>>> # Date 1304506739 -7200
>>> # Node ID 6aa9981d0f3dbdeaa14ed5a12f68f49fd171a5f8
>>> # Parent  bf951d58b9172e3dee1be36032784956e5775636
>>> store: break up reference cycle introduced in 9cbff8a39a2a
>>>
>>> see also 996c1cd8f530
>>>
>>> diff --git a/mercurial/store.py b/mercurial/store.py
>>> --- a/mercurial/store.py
>>> +++ b/mercurial/store.py
>>> @@ -365,26 +365,27 @@
>>>              self._load()
>>>          return iter(self.entries)
>>>  
>>> +class _fncacheopener(scmutil.abstractopener):
>>> +    def __init__(self, op, fnc, encode):
>>> +        self.opener = op
>>> +        self.fncache = fnc
>>> +        self.encode = encode
>>> +
>>> +    def __call__(self, path, mode='r', *args, **kw):
>>> +        if mode not in ('r', 'rb') and path.startswith('data/'):
>>> +            self.fncache.add(path)
>>> +        return self.opener(self.encode(path), mode, *args, **kw)
>>> +
>>>  class fncachestore(basicstore):
>>>      def __init__(self, path, openertype, encode):
>>>          self.encode = encode
>>>          self.path = path + '/store'
>>>          self.createmode = _calcmode(self.path)
>>> -
>>> -        storeself = self
>>> -
>>> -        class fncacheopener(openertype):
>>> -            def __call__(self, path, mode='r', *args, **kw):
>>> -                if mode not in ('r', 'rb') and path.startswith('data/'):
>>> -                    fnc.add(path)
>>> -                return openertype.__call__(self, storeself.encode(path), mode,
>>> -                                           *args, **kw)
>>> -
>>> -        op = fncacheopener(self.path)
>>> +        op = openertype(self.path)
>>>          op.createmode = self.createmode
>>>          fnc = fncache(op)
>>>          self.fncache = fnc
>>> -        self.opener = op
>>> +        self.opener = _fncacheopener(op, fnc, encode)
>>>  
>>>      def join(self, f):
>>>          return self.path + '/' + self.encode(f)
>>
>> I think the reference cycle in 9cbff8a39a2a goes like this:
>>
>>       --> fncacheopener object
>>      |        |
>>      |    local scope of function fncachestore.__init__
>>      |        |
>>       --- fncache object bound to name fnc
>>
>> Not sure if that's really a problem though.
> 
> Yes, there's a reference cycle here (not ideal). But it's not clear to
> me that it's anything new though:
> 
> http://www.selenic.com/hg/file/222c8ec7a274/mercurial/store.py#l369
> 
> Here the nested fncacheopener function holds a reference to its parent
> scope, which contains self, and then self.opener takes a reference to
> that, creating a loop.
> 
>> With my patch, the references go like this:
>>
>>        -- fncachestore object
>>       |       |
>>       |   _fncacheopener object  --
>>       |       |                    |
>>        -> fncache object           |
>>               |                    |
>>           object of openertype  <--
> 
> And this should indeed fix it, so I'm going to queue it up.
> 
> But it's not ideal as it's kind of a hybrid inheritance-by-wrapping
> thing again. More thought is needed here. Ideally, we'd come up with a
> way to avoid wrapping openers in openers or dynamically build classes. 
> 
> Off the top of my head, the base opener might allow stacking name
> transformations on top of the basic open method:
> 
> Then fncachestore could do something like:
> 
>   self.opener = openertype(self.path)
>   self.fncache = fncache(openertype(self.path))
>   self.opener.addfilter(self.encode)
>   self.opener.addfilter(self.fncache.add)
> 

It's unlikely that I will do anything further here. So whoever would
like to jump in here, feel free to try making Matt happier :-).


More information about the Mercurial-devel mailing list