13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Implementation of Transport that traces transport operations.
19
This does not change the transport behaviour at all, merely records every call
19
This does not change the transport behaviour at allmerely records every call
20
20
and then delegates it.
23
from bzrlib.transport import decorator
26
class TransportTraceDecorator(decorator.TransportDecorator):
23
from bzrlib.transport.decorator import TransportDecorator, DecoratorServer
26
class TransportTraceDecorator(TransportDecorator):
27
27
"""A tracing decorator for Transports.
29
29
Calls that potentially perform IO are logged to self._activity. The
30
30
_activity attribute is shared as the transport is cloned, but not if a new
31
31
transport is created without cloning.
33
Not all operations are logged at this point, if you need an unlogged
34
operation please add a test to the tests of this transport, for the logging
35
of the operation you want logged.
37
See also TransportLogDecorator, that records a machine-readable log in
38
memory for eg testing.
41
34
def __init__(self, url, _decorated=None, _from_transport=None):
42
35
"""Set the 'base' path where files will be stored.
44
37
_decorated is a private parameter for cloning.
46
super(TransportTraceDecorator, self).__init__(url, _decorated)
39
TransportDecorator.__init__(self, url, _decorated)
47
40
if _from_transport is None:
49
42
self._activity = []
62
55
def delete(self, relpath):
63
56
"""See Transport.delete()."""
64
self._activity.append(('delete', relpath))
65
57
return self._decorated.delete(relpath)
67
59
def delete_tree(self, relpath):
76
68
def get(self, relpath):
77
69
"""See Transport.get()."""
78
self._trace(('get', relpath))
70
self._activity.append(('get', relpath))
79
71
return self._decorated.get(relpath)
81
73
def get_smart_client(self):
92
84
def mkdir(self, relpath, mode=None):
93
85
"""See Transport.mkdir()."""
94
self._trace(('mkdir', relpath, mode))
95
86
return self._decorated.mkdir(relpath, mode)
97
88
def open_write_stream(self, relpath, mode=None):
101
92
def put_file(self, relpath, f, mode=None):
102
93
"""See Transport.put_file()."""
103
94
return self._decorated.put_file(relpath, f, mode)
105
96
def put_bytes(self, relpath, bytes, mode=None):
106
97
"""See Transport.put_bytes()."""
107
self._trace(('put_bytes', relpath, len(bytes), mode))
98
self._activity.append(('put_bytes', relpath, len(bytes), mode))
108
99
return self._decorated.put_bytes(relpath, bytes, mode)
110
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
111
create_parent_dir=False, dir_mode=None):
112
"""See Transport.put_bytes_non_atomic."""
113
self._trace(('put_bytes_non_atomic', relpath, len(bytes), mode,
114
create_parent_dir, dir_mode))
115
return self._decorated.put_bytes_non_atomic(relpath, bytes, mode=mode,
116
create_parent_dir=create_parent_dir, dir_mode=dir_mode)
118
101
def listable(self):
119
102
"""See Transport.listable."""
120
103
return self._decorated.listable()
122
105
def iter_files_recursive(self):
123
106
"""See Transport.iter_files_recursive()."""
124
107
return self._decorated.iter_files_recursive()
126
109
def list_dir(self, relpath):
127
110
"""See Transport.list_dir()."""
128
111
return self._decorated.list_dir(relpath)
130
113
def readv(self, relpath, offsets, adjust_for_latency=False,
131
114
upper_limit=None):
132
# we override at the readv() level rather than _readv() so that any
133
# latency adjustments will be done by the underlying transport
134
self._trace(('readv', relpath, offsets, adjust_for_latency,
115
"""See Transport.readv."""
116
self._activity.append(('readv', relpath, offsets, adjust_for_latency,
136
118
return self._decorated.readv(relpath, offsets, adjust_for_latency,
141
123
return self._decorated.recommended_page_size()
143
125
def rename(self, rel_from, rel_to):
144
self._activity.append(('rename', rel_from, rel_to))
145
126
return self._decorated.rename(rel_from, rel_to)
147
128
def rmdir(self, relpath):
148
129
"""See Transport.rmdir."""
149
self._trace(('rmdir', relpath))
150
130
return self._decorated.rmdir(relpath)
152
132
def stat(self, relpath):
161
141
"""See Transport.lock_write."""
162
142
return self._decorated.lock_write(relpath)
164
def _trace(self, operation_tuple):
165
"""Record that a transport operation occured.
167
:param operation: Tuple of transport call name and arguments.
169
self._activity.append(operation_tuple)
145
class TraceServer(DecoratorServer):
146
"""Server for the TransportTraceDecorator for testing with."""
148
def get_decorator_class(self):
149
return TransportTraceDecorator
172
152
def get_test_permutations():
173
"""Return the permutations to be used in testing."""
174
from bzrlib.tests import test_server
175
return [(TransportTraceDecorator, test_server.TraceServer)]
153
"""Return the permutations to be used in testing.
155
The Decorator class is not directly usable, and testing it would not have
156
any benefit - its the concrete classes which need to be tested.
158
return [(TransportTraceDecorator, TraceServer)]