[Notes] [Git][BuildStream/buildstream][jonathan/pickle-yaml] YamlCache: Fix the cache being mutated by changes to nodes since the node was loaded



Title: GitLab

Jonathan Maw pushed to branch jonathan/pickle-yaml at BuildStream / buildstream

Commits:

1 changed file:

Changes:

  • buildstream/_pickler.py
    ... ... @@ -45,6 +45,13 @@ class BstPickler(pickle.Pickler):
    45 45
             else:
    
    46 46
                 return None
    
    47 47
     
    
    48
    +    @staticmethod
    
    49
    +    def dumps(obj):
    
    50
    +        stream = io.BytesIO()
    
    51
    +        BstPickler(stream).dump(obj)
    
    52
    +        stream.seek(0)
    
    53
    +        return stream.read()
    
    54
    +
    
    48 55
     
    
    49 56
     class BstUnpickler(pickle.Unpickler):
    
    50 57
         def __init__(self, file, context):
    
    ... ... @@ -63,6 +70,12 @@ class BstUnpickler(pickle.Unpickler):
    63 70
             else:
    
    64 71
                 raise pickle.UnpicklingError("Unsupported persistent object")
    
    65 72
     
    
    73
    +    @staticmethod
    
    74
    +    def loads(text, context):
    
    75
    +        stream = io.BytesIO()
    
    76
    +        stream.write(bytes(text))
    
    77
    +        stream.seek(0)
    
    78
    +        return BstUnpickler(stream, context).load()
    
    66 79
     
    
    67 80
     CachedProject = namedtuple('CachedProject', ['path', 'project_sum', 'elements'])
    
    68 81
     
    
    ... ... @@ -70,21 +83,17 @@ CachedProject = namedtuple('CachedProject', ['path', 'project_sum', 'elements'])
    70 83
     class CachedYaml():
    
    71 84
         def __init__(self, key, contents):
    
    72 85
             self.key = key
    
    86
    +        self.set_contents(contents)
    
    87
    +
    
    88
    +    def set_contents(self, contents):
    
    73 89
             self.contents = contents
    
    74
    -        self.pickled_contents = None
    
    90
    +        self._pickled_contents = BstPickler.dumps(contents)
    
    75 91
     
    
    76
    -    # Intercept and return the pickled object
    
    92
    +    # Do not store non-pickled contents when pickling
    
    93
    +    # The contents will be unpickled on-demand
    
    77 94
         def __getstate__(self):
    
    78 95
             data = self.__dict__.copy()
    
    79
    -        if not self.pickled_contents:
    
    80
    -            # Pickle self.contents and store it as self.pickled_contents
    
    81
    -            picklestream = io.BytesIO()
    
    82
    -            BstPickler(picklestream).dump(self.contents)
    
    83
    -            provenance = _yaml.node_get_provenance(self.contents)
    
    84
    -            project = provenance.filename.project
    
    85
    -            data['pickled_contents'] = picklestream
    
    86 96
             data['contents'] = None
    
    87
    -
    
    88 97
             return data
    
    89 98
     
    
    90 99
     
    
    ... ... @@ -105,10 +114,9 @@ class YamlCache():
    105 114
                 if filepath in project_cache.elements:
    
    106 115
                     cachedyaml = project_cache.elements[filepath]
    
    107 116
                     if cachedyaml.key == key:
    
    108
    -                    if not cachedyaml.contents:
    
    109
    -                        cachedyaml.pickled_contents.seek(0)
    
    110
    -                        cachedyaml.contents = BstUnpickler(cachedyaml.pickled_contents, project._context).load()
    
    111
    -                    return _yaml.node_copy(cachedyaml.contents)
    
    117
    +                    if cachedyaml.contents is None:
    
    118
    +                        cachedyaml.contents = BstUnpickler.loads(cachedyaml._pickled_contents, project._context)
    
    119
    +                    return cachedyaml.contents
    
    112 120
             return None
    
    113 121
     
    
    114 122
         def put(self, project, filepath, key, value):
    
    ... ... @@ -119,9 +127,9 @@ class YamlCache():
    119 127
                 project_cache = self.project_caches[project.name] = CachedProject(project.directory, project.shasum, {})
    
    120 128
     
    
    121 129
             if filepath in project_cache.elements and project_cache.elements[filepath].key == key:
    
    122
    -            project_cache.elements[filepath].contents = _yaml.node_copy(value)
    
    130
    +            project_cache.elements[filepath].set_contents(value)
    
    123 131
             else:
    
    124
    -            project_cache.elements[filepath] = CachedYaml(key, _yaml.node_copy(value))
    
    132
    +            project_cache.elements[filepath] = CachedYaml(key, value)
    
    125 133
     
    
    126 134
         @staticmethod
    
    127 135
         @contextmanager
    



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]