1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Class that contains paths classified into four types."""
import os
class Error(Exception):
pass
class PathSet(object):
'''Container for paths classified into four types.
Paths in this container are broken into four separate collections, each
accessible as a property (see the "Attributes" section, below).
Attributes:
files: A set of plain files, keys are platform-specific normalized path
names. Might be empty.
dirs: A set of directories, keys are platform-specific normalized path
names. Might be empty.
symlinks: A dictionary of symbolic links - these are not followed. Keys
are platform-specific normalized path names. The value of each key is
the source file of the link (also a platform-specific normalized path).
Might be empty.
links: A dictionary of hard links. Keys are platform-specific normalized
path names. The value of each key is the source file of the link (also
a platform-specific normalized path). Might be empty.
'''
def __init__(self):
self.Reset()
def __ior__(self, other):
'''Override |= to merge |other| into this path set.'''
(self._files, self._dirs,
self._symlinks, self._links) = self._MergeWithPathSet(other)
return self
def __or__(self, other):
'''Override | to produce the merger of |self| and |other|.'''
merged_path_set = PathSet()
(merged_path_set.files,
merged_path_set.dirs,
merged_path_set.symlinks,
merged_path_set.links) = self._MergeWithPathSet(other)
return merged_path_set
def _MergeWithPathSet(self, path_set):
'''Merge this path set with |path_set|.
Forms the union of the |_files| and |_dirs| sets, then merges the
|_symlinks| and |_links| dicts. The dictionaries are merged in such that
keys are not duplicated: the values of the keys in |path_set| take
precedence in the returned dictionaries.
Any keys in either the symlink or links dictionaries that also exist in
either of the files or dicts sets are removed from the latter, meaning that
symlinks or links which overlap file or directory entries take precedence.
Args:
path_set: The other path set. Must be an object with four properties:
files (a set), dirs (a set), symlinks (a dict), links (a dict).
Returns:
A four-tuple (files, dirs, symlinks, links) which is the result of merging
the two PathSets.
'''
def DiscardOverlappingLinks(links_dict):
'''Discard all overlapping keys from files and dirs.'''
for link in links_dict:
self._files.discard(link)
self._dirs.discard(link)
DiscardOverlappingLinks(path_set.symlinks)
DiscardOverlappingLinks(path_set.links)
return (self._files | path_set.files,
self._dirs | path_set.dirs,
dict(self._symlinks.items() + path_set.symlinks.items()),
dict(self._links.items() + path_set.links.items()))
@property
def files(self):
'''The set of plain files.'''
return self._files
@files.setter
def files(self, new_file_set):
if isinstance(new_file_set, set):
self._files = new_file_set
else:
raise Error('files property must be a set')
@property
def dirs(self):
'''The set of directories.'''
return self._dirs
@dirs.setter
def dirs(self, new_dir_set):
if isinstance(new_dir_set, set):
self._dirs = new_dir_set
else:
raise Error('dirs property must be a set')
@property
def symlinks(self):
'''The dictionary of symbolic links.'''
return self._symlinks
@symlinks.setter
def symlinks(self, new_symlinks_dict):
if isinstance(new_symlinks_dict, dict):
self._symlinks = new_symlinks_dict
else:
raise Error('symlinks property must be a dict')
@property
def links(self):
'''The dictionary of hard links.'''
return self._links
@links.setter
def links(self, new_links_dict):
if isinstance(new_links_dict, dict):
self._links = new_links_dict
else:
raise Error('links property must be a dict')
def Reset(self):
'''Reset all attributes to empty containers.'''
self._files = set()
self._dirs = set()
self._symlinks = dict()
self._links = dict()
def PrependPath(self, path_prefix):
'''Prepend paths in all collections with |path_prefix|.
All the keys in all the colletions get prepended with |path_prefix|. The
resulting path is an os-specific path.
Args:
path_prefix: The path to prepend to all collection keys.
'''
prepend_path = lambda p: os.path.join(path_prefix, p)
def PrependToLinkDict(link_dict):
return dict([prepend_path(p), link] for p, link in link_dict.items())
self._files = set(map(prepend_path, self._files))
self._dirs = set(map(prepend_path, self._dirs))
self._symlinks = PrependToLinkDict(self._symlinks)
self._links = PrependToLinkDict(self._links)
|