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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
#!/usr/bin/env python3
#
# Copyright (C) 2018 Bloomberg Finance LP
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# Jim MacArthur <jim.macarthur@codethink.co.uk>
"""
Directory
=========
This is a virtual Directory class to isolate the rest of BuildStream
from the backing store implementation. Sandboxes are allowed to read
from and write to the underlying storage, but all others must use this
Directory class to access files and directories in the sandbox.
See also: :ref:`sandboxing`.
"""
from typing import Callable, Optional, Union, List
from .._exceptions import BstError
from ..exceptions import ErrorDomain
from ..types import FastEnum
from ..utils import BST_ARBITRARY_TIMESTAMP, FileListResult
class VirtualDirectoryError(BstError):
"""Raised by Directory functions when system calls fail.
This will be handled internally by the BuildStream core,
if you need to handle this error, then it should be reraised,
or either of the :class:`.ElementError` or :class:`.SourceError`
exceptions should be raised from this error.
"""
def __init__(self, message, reason=None):
super().__init__(message, domain=ErrorDomain.VIRTUAL_FS, reason=reason)
class Directory:
def __init__(self, external_directory=None):
raise NotImplementedError()
def descend(self, *paths: str, create: bool = False, follow_symlinks: bool = False):
"""Descend one or more levels of directory hierarchy and return a new
Directory object for that directory.
Args:
*paths: A list of strings which are all directory names.
create: If this is true, the directories will be created if
they don't already exist.
Yields:
A Directory object representing the found directory.
Raises:
VirtualDirectoryError: if any of the components in subdirectory_spec
cannot be found, or are files, or symlinks to files.
"""
raise NotImplementedError()
# Import and export of files and links
def import_files(
self,
external_pathspec: Union["Directory", str],
*,
filter_callback: Optional[Callable[[str], bool]] = None,
report_written: bool = True,
update_mtime: Optional[float] = None,
can_link: bool = False,
properties: Optional[List[str]] = None
) -> FileListResult:
"""Imports some or all files from external_path into this directory.
Args:
external_pathspec: Either a string containing a pathname, or a
Directory object, to use as the source.
filter_callback: Optional filter callback. Called with the
relative path as argument for every file in the source directory.
The file is imported only if the callable returns True.
If no filter callback is specified, all files will be imported.
report_written: Return the full list of files
written. Defaults to true. If false, only a list of
overwritten files is returned.
update_mtime: Update the access and modification time
of each file copied to the time specified in seconds.
can_link: Whether it's OK to create a hard link to the
original content, meaning the stored copy will change when the
original files change. Setting this doesn't guarantee hard
links will be made.
properties: Optional list of strings representing file properties
to capture when importing.
Yields:
A report of files imported and overwritten.
"""
raise NotImplementedError()
def import_single_file(self, external_pathspec, properties=None):
"""Imports a single file from an external path"""
raise NotImplementedError()
def export_files(self, to_directory, *, can_link=False, can_destroy=False):
"""Copies everything from this into to_directory.
Args:
to_directory (string): a path outside this directory object
where the contents will be copied to.
can_link (bool): Whether we can create hard links in to_directory
instead of copying. Setting this does not guarantee hard links will be used.
can_destroy (bool): Can we destroy the data already in this
directory when exporting? If set, this may allow data to be
moved rather than copied which will be quicker.
"""
raise NotImplementedError()
def export_to_tar(self, tarfile, destination_dir, mtime=BST_ARBITRARY_TIMESTAMP):
""" Exports this directory into the given tar file.
Args:
tarfile (TarFile): A Python TarFile object to export into.
destination_dir (str): The prefix for all filenames inside the archive.
mtime (int): mtimes of all files in the archive are set to this.
"""
raise NotImplementedError()
# Convenience functions
def is_empty(self):
""" Return true if this directory has no files, subdirectories or links in it.
"""
raise NotImplementedError()
def set_deterministic_mtime(self):
""" Sets a static modification time for all regular files in this directory.
The magic number for timestamps is 2011-11-11 11:11:11.
"""
raise NotImplementedError()
def set_deterministic_user(self):
""" Sets all files in this directory to the current user's euid/egid.
"""
raise NotImplementedError()
def mark_unmodified(self):
""" Marks all files in this directory (recursively) as unmodified.
"""
raise NotImplementedError()
def list_modified_paths(self):
"""Provide a list of relative paths which have been modified since the
last call to mark_unmodified. Includes directories only if
they are empty.
Yields:
(List(str)) - list of all modified files with relative paths.
"""
raise NotImplementedError()
def list_relative_paths(self):
"""Provide a list of all relative paths in this directory. Includes
directories only if they are empty.
Yields:
(List(str)) - list of all files with relative paths.
"""
raise NotImplementedError()
def _mark_changed(self):
"""Internal function to mark this directory as having been changed
outside this API. This normally can only happen by calling the
Sandbox's `run` method. This does *not* mark everything as modified
(i.e. list_modified_paths will not necessarily return the same results
as list_relative_paths after calling this.)
"""
raise NotImplementedError()
def get_size(self):
""" Get an approximation of the storage space in bytes used by this directory
and all files and subdirectories in it. Storage space varies by implementation
and effective space used may be lower than this number due to deduplication. """
raise NotImplementedError()
# FileType:
#
# Type of file or directory entry.
#
class _FileType(FastEnum):
# Directory
DIRECTORY = 1
# Regular file
REGULAR_FILE = 2
# Symbolic link
SYMLINK = 3
# Special file (FIFO, character device, block device, or socket)
SPECIAL_FILE = 4
def __str__(self):
# https://github.com/PyCQA/pylint/issues/2062
return self.name.lower().replace("_", " ") # pylint: disable=no-member
|