blissdata 1.0.2__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {blissdata-1.0.2 → blissdata-1.0.4}/PKG-INFO +3 -3
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/__init__.py +1 -1
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/bliss.py +2 -5
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/hdf5.py +52 -24
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/scan.py +7 -6
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/stream.py +8 -1
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/conftest.py +2 -2
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/scanner.py +2 -2
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_streaming.py +16 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/PKG-INFO +3 -3
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/requires.txt +2 -2
- {blissdata-1.0.2 → blissdata-1.0.4}/setup.cfg +2 -2
- {blissdata-1.0.2 → blissdata-1.0.4}/LICENSE +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/README.md +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/_base.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/config.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/data.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/beacon/files.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/abstract.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/dynamic_hdf5.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/file_arguments.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/static_hdf5.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/hdf5_retry.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/lima.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/h5api/utils/types.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/lima/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/lima/client.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/lima/image_utils.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/encoding/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/encoding/json.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/encoding/numeric.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/exceptions.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/identities.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/memory_tracker.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/sink.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/redis_engine/store.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/scan.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/schemas/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/schemas/scan_info.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/schemas/scan_info_display.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/stream.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/test_config.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/test_data.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/beacon/test_files.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/test_dynamic_files.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/h5api/test_static_files.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/conftest.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/encoding/__init__.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/encoding/test_numeric.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_memory_tracker.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_scan.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/test_search.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata/tests/redis_engine/utils.py +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/SOURCES.txt +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/dependency_links.txt +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/entry_points.txt +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/blissdata.egg-info/top_level.txt +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/pyproject.toml +0 -0
- {blissdata-1.0.2 → blissdata-1.0.4}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: blissdata
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Bliss data streaming client
|
|
5
5
|
Home-page: https://gitlab.esrf.fr/bliss/bliss/-/tree/master/blissdata
|
|
6
6
|
Author: BCU (ESRF)
|
|
@@ -15,8 +15,8 @@ Requires-Dist: beartype
|
|
|
15
15
|
Requires-Dist: h5py<3.10,>=2.9
|
|
16
16
|
Requires-Dist: msgpack
|
|
17
17
|
Requires-Dist: numpy>=1.13
|
|
18
|
-
Requires-Dist: redis
|
|
19
|
-
Requires-Dist: redis-om
|
|
18
|
+
Requires-Dist: redis<5,>=4.6.0
|
|
19
|
+
Requires-Dist: redis-om<0.3
|
|
20
20
|
Requires-Dist: silx>=1.1.0
|
|
21
21
|
Provides-Extra: tango
|
|
22
22
|
Requires-Dist: pytango>=9.3.1; extra == "tango"
|
|
@@ -102,7 +102,7 @@ class BlissDynamicHDF5Handler(hdf5.DynamicHDF5Handler):
|
|
|
102
102
|
if h5item.name == hdf5.SEP:
|
|
103
103
|
return False
|
|
104
104
|
scan = [s for s in h5item.name.split(hdf5.SEP) if s][0]
|
|
105
|
-
nxentry = self.file_obj
|
|
105
|
+
nxentry = self.file_obj[scan]
|
|
106
106
|
except AttributeError:
|
|
107
107
|
raise RetryError("file is closed")
|
|
108
108
|
if "end_time" in nxentry:
|
|
@@ -119,10 +119,7 @@ class BlissDynamicHDF5Handler(hdf5.DynamicHDF5Handler):
|
|
|
119
119
|
nxentry = self.file_obj[scan]
|
|
120
120
|
except AttributeError:
|
|
121
121
|
raise RetryError("file is closed")
|
|
122
|
-
|
|
123
|
-
# Last dataset written to the file
|
|
124
|
-
return True
|
|
125
|
-
return self._get_writer_status(nxentry) in ("SUCCEEDED", "FAILED")
|
|
122
|
+
return "end_time" in nxentry # Last dataset written to the file
|
|
126
123
|
|
|
127
124
|
def _get_writer_status(self, nxentry: h5py.Dataset) -> Optional[str]:
|
|
128
125
|
nxnote = nxentry.get("writer", None)
|
|
@@ -146,19 +146,22 @@ class DynamicHDF5Handler:
|
|
|
146
146
|
return item
|
|
147
147
|
try:
|
|
148
148
|
item = self.file_obj[name]
|
|
149
|
-
except KeyError
|
|
150
|
-
parent_name = name.rpartition(SEP)
|
|
149
|
+
except KeyError:
|
|
150
|
+
parent_name, _, item_name = name.rpartition(SEP)
|
|
151
151
|
if not parent_name:
|
|
152
152
|
parent_name = SEP
|
|
153
|
-
if parent_name
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
153
|
+
if parent_name == name:
|
|
154
|
+
raise
|
|
155
|
+
parent_item = self._get_item(parent_name)
|
|
156
|
+
if not self._is_finished(parent_item):
|
|
157
|
+
raise # item could still be created
|
|
158
|
+
try:
|
|
159
|
+
item = parent_item[item_name] # try one more time
|
|
160
|
+
except KeyError as e:
|
|
161
|
+
if not _key_does_not_exist(e):
|
|
162
|
+
raise
|
|
163
|
+
# item will never be created
|
|
164
|
+
raise RetryTimeoutError(str(e)) from e
|
|
162
165
|
self._native_items[name] = item
|
|
163
166
|
return item
|
|
164
167
|
|
|
@@ -174,19 +177,26 @@ class DynamicHDF5Handler:
|
|
|
174
177
|
item = self._get_item(name)
|
|
175
178
|
try:
|
|
176
179
|
return item.attrs[key]
|
|
177
|
-
except KeyError
|
|
178
|
-
if self._is_finished(item):
|
|
180
|
+
except KeyError:
|
|
181
|
+
if not self._is_finished(item):
|
|
182
|
+
raise # attribute could still be created
|
|
183
|
+
try:
|
|
184
|
+
return item.attrs[key] # try one more time
|
|
185
|
+
except KeyError as e:
|
|
186
|
+
if not _key_does_not_exist(e):
|
|
187
|
+
raise
|
|
188
|
+
# attribute will never be created
|
|
179
189
|
raise RetryTimeoutError(str(e)) from e
|
|
180
|
-
raise
|
|
181
190
|
|
|
182
191
|
def _iter_attrs(self, name: str, start_index: int = 0) -> Iterator[str]:
|
|
183
192
|
item = self._get_item(name)
|
|
193
|
+
is_complete = self._is_initialized(item)
|
|
184
194
|
if start_index == 0:
|
|
185
195
|
yield from item.attrs
|
|
186
196
|
else:
|
|
187
197
|
yield from list(item.attrs)[start_index:]
|
|
188
|
-
if not
|
|
189
|
-
raise RetryError("
|
|
198
|
+
if not is_complete:
|
|
199
|
+
raise RetryError("attributes could still be added")
|
|
190
200
|
|
|
191
201
|
def _len_attrs(self, name: str) -> int:
|
|
192
202
|
item = self._get_item(name)
|
|
@@ -198,19 +208,19 @@ class DynamicHDF5Handler:
|
|
|
198
208
|
item = self._get_item(name)
|
|
199
209
|
self._check_dataset_before_read(item)
|
|
200
210
|
if self.is_group(item):
|
|
201
|
-
|
|
211
|
+
is_complete = self._is_initialized(item)
|
|
202
212
|
for key in self._iter_group(item, start_index):
|
|
203
|
-
|
|
213
|
+
is_complete = False
|
|
204
214
|
yield key
|
|
205
|
-
if not
|
|
206
|
-
raise RetryError("
|
|
215
|
+
if not is_complete:
|
|
216
|
+
raise RetryError("children could still be added to the group")
|
|
207
217
|
else:
|
|
208
|
-
|
|
218
|
+
is_complete = self._is_finished(item)
|
|
209
219
|
for data in self._iter_dataset(item, start_index):
|
|
210
|
-
|
|
220
|
+
is_complete = False
|
|
211
221
|
yield data
|
|
212
|
-
if not
|
|
213
|
-
raise RetryError("
|
|
222
|
+
if not is_complete:
|
|
223
|
+
raise RetryError("dataset could still grow")
|
|
214
224
|
|
|
215
225
|
@staticmethod
|
|
216
226
|
def is_group(h5item: HDF5Item):
|
|
@@ -250,3 +260,21 @@ class DynamicHDF5Handler:
|
|
|
250
260
|
|
|
251
261
|
def _is_finished(self, h5item: HDF5Item) -> bool:
|
|
252
262
|
return True
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _key_does_not_exist(exc: KeyError) -> bool:
|
|
266
|
+
"""
|
|
267
|
+
A real KeyError (meaning that the key does not exist) gives the following error message:
|
|
268
|
+
|
|
269
|
+
.. code
|
|
270
|
+
|
|
271
|
+
KeyError: "Unable to synchronously open object (object 'end_time' doesn't exist)"
|
|
272
|
+
|
|
273
|
+
An example of a KeyError caused by another reason
|
|
274
|
+
|
|
275
|
+
.. code
|
|
276
|
+
|
|
277
|
+
KeyError: 'Unable to synchronously open object (addr overflow, addr = 64392369, size = 96, eoa = 64365353)'
|
|
278
|
+
|
|
279
|
+
"""
|
|
280
|
+
return "doesn't exist" in str(exc)
|
|
@@ -73,6 +73,8 @@ class Scan:
|
|
|
73
73
|
for prop_name in scan._data_store._scan_model.id.field.type_.schema()[
|
|
74
74
|
"properties"
|
|
75
75
|
].keys():
|
|
76
|
+
if prop_name == "pk":
|
|
77
|
+
continue
|
|
76
78
|
|
|
77
79
|
def get_id_field(self, field=prop_name):
|
|
78
80
|
return getattr(self._model.id, field, None)
|
|
@@ -109,6 +111,8 @@ class Scan:
|
|
|
109
111
|
for prop_name in scan._data_store._scan_model.id.field.type_.schema()[
|
|
110
112
|
"properties"
|
|
111
113
|
].keys():
|
|
114
|
+
if prop_name == "pk":
|
|
115
|
+
continue
|
|
112
116
|
|
|
113
117
|
def get_id_field(self, field=prop_name):
|
|
114
118
|
return getattr(self._model.id, field, None)
|
|
@@ -278,10 +282,7 @@ class Scan:
|
|
|
278
282
|
the still running writers to fail.
|
|
279
283
|
"""
|
|
280
284
|
for stream_writer in self._writer_streams.values():
|
|
281
|
-
|
|
282
|
-
stream_writer.seal()
|
|
283
|
-
else:
|
|
284
|
-
stream_writer._sink.stop()
|
|
285
|
+
stream_writer.seal()
|
|
285
286
|
self._writer_streams = {}
|
|
286
287
|
|
|
287
288
|
@needs_write_permission
|
|
@@ -301,15 +302,15 @@ class Scan:
|
|
|
301
302
|
@needs_write_permission
|
|
302
303
|
def stop(self):
|
|
303
304
|
if self.state is ScanState.STARTED:
|
|
304
|
-
self._set_state(ScanState.STOPPED)
|
|
305
305
|
self._close_stream_writers()
|
|
306
|
+
self._set_state(ScanState.STOPPED)
|
|
306
307
|
else:
|
|
307
308
|
raise UnauthorizeStateTransition(self.state, ScanState.STOPPED)
|
|
308
309
|
|
|
309
310
|
@needs_write_permission
|
|
310
311
|
def close(self):
|
|
311
|
-
self._set_state(ScanState.CLOSED)
|
|
312
312
|
self._close_stream_writers()
|
|
313
|
+
self._set_state(ScanState.CLOSED)
|
|
313
314
|
|
|
314
315
|
@needs_write_permission
|
|
315
316
|
def _set_state(self, state):
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Copyright (c) Beamline Control Unit, ESRF
|
|
6
6
|
# Distributed under the GNU LGPLv3. See LICENSE for more info.
|
|
7
|
+
import redis
|
|
7
8
|
from functools import wraps
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from collections.abc import Mapping
|
|
@@ -270,8 +271,14 @@ class Stream:
|
|
|
270
271
|
|
|
271
272
|
@needs_write_permission
|
|
272
273
|
def seal(self):
|
|
273
|
-
self._sink.fcall("seal_stream", 1, self.key)
|
|
274
274
|
self._sink.stop()
|
|
275
|
+
try:
|
|
276
|
+
return self._data_store._redis.fcall("seal_stream", 1, self.key)
|
|
277
|
+
except redis.exceptions.ResponseError as e:
|
|
278
|
+
if "is already sealed" in str(e):
|
|
279
|
+
return len(self)
|
|
280
|
+
else:
|
|
281
|
+
raise
|
|
275
282
|
|
|
276
283
|
def __del__(self):
|
|
277
284
|
# Thread based sinks can't be garbage collected, because their internal thread
|
|
@@ -3,7 +3,7 @@ import subprocess
|
|
|
3
3
|
from redis import Redis
|
|
4
4
|
from socket import socket
|
|
5
5
|
from pytest import fixture
|
|
6
|
-
from pytest_redis import
|
|
6
|
+
from pytest_redis.factories.noproc import redis_noproc
|
|
7
7
|
|
|
8
8
|
from blissdata.redis_engine.store import DataStore
|
|
9
9
|
|
|
@@ -15,7 +15,7 @@ def _find_available_port():
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
redis_port = _find_available_port()
|
|
18
|
-
redis_db =
|
|
18
|
+
redis_db = redis_noproc(host="localhost", port=redis_port, startup_timeout=1)
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def pytest_configure(config):
|
|
@@ -268,7 +268,7 @@ def writer_context(filename: str, scan_number: int) -> Iterator[h5py.Group]:
|
|
|
268
268
|
|
|
269
269
|
try:
|
|
270
270
|
entry.attrs["NX_class"] = "NXentry"
|
|
271
|
-
entry
|
|
271
|
+
entry["start_time"] = timestamp()
|
|
272
272
|
writer = entry.create_group("writer")
|
|
273
273
|
writer.attrs["NX_class"] = "NXnote"
|
|
274
274
|
writer["status"] = "STARTING"
|
|
@@ -279,7 +279,7 @@ def writer_context(filename: str, scan_number: int) -> Iterator[h5py.Group]:
|
|
|
279
279
|
else:
|
|
280
280
|
writer["status"][()] = "SUCCEEDED"
|
|
281
281
|
finally:
|
|
282
|
-
entry
|
|
282
|
+
entry["end_time"] = timestamp()
|
|
283
283
|
|
|
284
284
|
|
|
285
285
|
@contextmanager
|
|
@@ -80,6 +80,22 @@ def test_stream_unknown_encoding(data_store):
|
|
|
80
80
|
Stream.create(data_store, "my_stream", model, None)
|
|
81
81
|
|
|
82
82
|
|
|
83
|
+
def test_multiple_calls_to_seal(data_store):
|
|
84
|
+
input = np.arange(1000)
|
|
85
|
+
rw_stream, _ = stream_pair(data_store, input.dtype)
|
|
86
|
+
rw_stream.send(input)
|
|
87
|
+
|
|
88
|
+
# make a second writing stream to test re-sealing from an unaware object
|
|
89
|
+
external_writer = Stream.create(
|
|
90
|
+
data_store, rw_stream.name, rw_stream._model, rw_stream._encoder
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
assert rw_stream.seal() == 1000
|
|
94
|
+
assert rw_stream.seal() == 1000
|
|
95
|
+
assert external_writer.seal() == 1000
|
|
96
|
+
assert external_writer.seal() == 1000
|
|
97
|
+
|
|
98
|
+
|
|
83
99
|
def test_stream_chunk_slicing(data_store):
|
|
84
100
|
data = np.arange(100)
|
|
85
101
|
rw_stream, ro_stream = stream_pair(data_store, data.dtype)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: blissdata
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Bliss data streaming client
|
|
5
5
|
Home-page: https://gitlab.esrf.fr/bliss/bliss/-/tree/master/blissdata
|
|
6
6
|
Author: BCU (ESRF)
|
|
@@ -15,8 +15,8 @@ Requires-Dist: beartype
|
|
|
15
15
|
Requires-Dist: h5py<3.10,>=2.9
|
|
16
16
|
Requires-Dist: msgpack
|
|
17
17
|
Requires-Dist: numpy>=1.13
|
|
18
|
-
Requires-Dist: redis
|
|
19
|
-
Requires-Dist: redis-om
|
|
18
|
+
Requires-Dist: redis<5,>=4.6.0
|
|
19
|
+
Requires-Dist: redis-om<0.3
|
|
20
20
|
Requires-Dist: silx>=1.1.0
|
|
21
21
|
Provides-Extra: tango
|
|
22
22
|
Requires-Dist: pytango>=9.3.1; extra == "tango"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|