locust 2.46.6.dev2__tar.gz → 2.46.6.dev8__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.
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/PKG-INFO +1 -1
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/_version.py +2 -2
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/clients.py +4 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/fasthttp.py +3 -67
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/stats.py +4 -15
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/user/users.py +79 -3
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/.gitignore +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/LICENSE +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/README.md +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/hatch_build.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/__init__.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/__main__.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/argument_parser.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/__init__.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/csv_request_logger.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/dns.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/milvus.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/mongodb.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/mqtt.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/oai.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/postgres.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/qdrant.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/contrib/socketio.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/debug.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/dispatch.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/env.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/event.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/exception.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/html.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/input_events.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/log.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/main.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/opentelemetry.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/py.typed +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/rpc/__init__.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/rpc/protocol.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/rpc/zmqrpc.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/runners.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/shape.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/user/__init__.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/user/inspectuser.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/user/markov_taskset.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/user/sequential_taskset.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/user/task.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/user/wait_time.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/__init__.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/cache.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/date.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/deprecation.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/directory.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/exception_handler.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/load_locustfile.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/rounding.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/timespan.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/util/url.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/web.py +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/favicon-dark.png +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/favicon-light.png +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/graphs-dark.png +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/graphs-light.png +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/src-pSlzKFX1.js +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/terminal.gif +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/testruns-dark.png +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/assets/testruns-light.png +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/auth.html +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/index.html +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/locust/webui/dist/report.html +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/pyproject.toml +0 -0
- {locust-2.46.6.dev2 → locust-2.46.6.dev8}/pytest_locust/plugin.py +0 -0
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.46.6.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 46, 6, '
|
|
21
|
+
__version__ = version = '2.46.6.dev8'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 46, 6, 'dev8')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -478,6 +478,10 @@ class ResponseContextManager(Response):
|
|
|
478
478
|
self._manual_result = exc
|
|
479
479
|
|
|
480
480
|
|
|
481
|
+
class RestResponseContextManager(ResponseContextManager):
|
|
482
|
+
js: dict # This is technically an Optional, but I dont want to force everyone to check it
|
|
483
|
+
|
|
484
|
+
|
|
481
485
|
class LocustHttpAdapter(HTTPAdapter):
|
|
482
486
|
def __init__(self, pool_manager: PoolManager | None, *args, **kwargs):
|
|
483
487
|
self.poolmanager = pool_manager
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from locust.exception import CatchResponseError, LocustError, ResponseError, StopTest
|
|
4
4
|
from locust.user import User
|
|
5
|
+
from locust.user.users import RestMixin
|
|
5
6
|
from locust.util.deprecation import DeprecatedFastHttpLocustClass as FastHttpLocust # noqa: F401
|
|
6
7
|
|
|
7
8
|
import json
|
|
@@ -9,14 +10,11 @@ import json as unshadowed_json # some methods take a named parameter called jso
|
|
|
9
10
|
import re
|
|
10
11
|
import socket
|
|
11
12
|
import time
|
|
12
|
-
import traceback
|
|
13
13
|
import zlib
|
|
14
14
|
from base64 import b64encode
|
|
15
|
-
from contextlib import contextmanager
|
|
16
15
|
from http.cookiejar import CookieJar
|
|
17
|
-
from json.decoder import JSONDecodeError
|
|
18
16
|
from ssl import SSLError
|
|
19
|
-
from typing import TYPE_CHECKING
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
20
18
|
from urllib.parse import urlparse, urlunparse
|
|
21
19
|
|
|
22
20
|
import gevent
|
|
@@ -355,7 +353,7 @@ class FastHttpSession:
|
|
|
355
353
|
return self.request("PUT", url, data=data, **kwargs)
|
|
356
354
|
|
|
357
355
|
|
|
358
|
-
class FastHttpUser(User):
|
|
356
|
+
class FastHttpUser(User, RestMixin["RestResponseContextManager"]):
|
|
359
357
|
"""
|
|
360
358
|
FastHttpUser provides the same API as HttpUser, but uses geventhttpclient instead of python-requests
|
|
361
359
|
as its underlying client. It uses considerably less CPU on the load generator, and should work
|
|
@@ -413,8 +411,6 @@ class FastHttpUser(User):
|
|
|
413
411
|
abstract = True
|
|
414
412
|
"""Dont register this as a User class that can be run by itself"""
|
|
415
413
|
|
|
416
|
-
_callstack_regex = re.compile(r' File "(\/.[^"]*)", line (\d*),(.*)')
|
|
417
|
-
|
|
418
414
|
def __init__(self, environment) -> None:
|
|
419
415
|
super().__init__(environment)
|
|
420
416
|
if self.host is None:
|
|
@@ -443,66 +439,6 @@ class FastHttpUser(User):
|
|
|
443
439
|
The client support cookies, and therefore keeps the session between HTTP requests.
|
|
444
440
|
"""
|
|
445
441
|
|
|
446
|
-
@contextmanager
|
|
447
|
-
def rest(self, method, url, headers: dict | None = None, **kwargs) -> Generator[RestResponseContextManager]:
|
|
448
|
-
"""
|
|
449
|
-
A wrapper for self.client.request that:
|
|
450
|
-
|
|
451
|
-
* Parses the JSON response to a dict called ``js`` in the response object. Marks the request as failed if the response was not valid JSON.
|
|
452
|
-
* Defaults ``Content-Type`` and ``Accept`` headers to ``application/json``
|
|
453
|
-
* Sets ``catch_response=True`` (so always use a :ref:`with-block <catch-response>`)
|
|
454
|
-
* Catches any unhandled exceptions thrown inside your with-block, marking the sample as failed (instead of exiting the task immediately without even firing the request event)
|
|
455
|
-
"""
|
|
456
|
-
headers = headers or {}
|
|
457
|
-
if not ("Content-Type" in headers or "content-type" in headers):
|
|
458
|
-
headers["Content-Type"] = "application/json"
|
|
459
|
-
if not ("Accept" in headers or "accept" in headers):
|
|
460
|
-
headers["Accept"] = "application/json"
|
|
461
|
-
with self.client.request(method, url, catch_response=True, headers=headers, **kwargs) as r:
|
|
462
|
-
resp = cast(RestResponseContextManager, r)
|
|
463
|
-
resp.js = None # type: ignore
|
|
464
|
-
if resp.content is None:
|
|
465
|
-
resp.failure(str(resp.error))
|
|
466
|
-
elif resp.text:
|
|
467
|
-
try:
|
|
468
|
-
resp.js = resp.json()
|
|
469
|
-
except JSONDecodeError as e:
|
|
470
|
-
resp.failure(
|
|
471
|
-
f"Could not parse response as JSON. {resp.text[:250]}, response code {resp.status_code}, error {e}"
|
|
472
|
-
)
|
|
473
|
-
try:
|
|
474
|
-
yield resp
|
|
475
|
-
except AssertionError as e:
|
|
476
|
-
if e.args:
|
|
477
|
-
if e.args[0].endswith(","):
|
|
478
|
-
short_resp = resp.text[:200] if resp.text else resp.text
|
|
479
|
-
resp.failure(f"{e.args[0][:-1]}, response was {short_resp}")
|
|
480
|
-
else:
|
|
481
|
-
resp.failure(e.args[0])
|
|
482
|
-
else:
|
|
483
|
-
resp.failure("Assertion failed")
|
|
484
|
-
|
|
485
|
-
except Exception as e:
|
|
486
|
-
error_lines = []
|
|
487
|
-
for l in traceback.format_exc().split("\n"):
|
|
488
|
-
if m := self._callstack_regex.match(l):
|
|
489
|
-
filename = re.sub(r"/((home|Users)/\w*)/", "~/", m.group(1))
|
|
490
|
-
error_lines.append(filename + ":" + m.group(2) + m.group(3))
|
|
491
|
-
short_resp = resp.text[:200] if resp.text else resp.text
|
|
492
|
-
resp.failure(f"{e.__class__.__name__}: {e} at {', '.join(error_lines)}. Response was {short_resp}")
|
|
493
|
-
|
|
494
|
-
@contextmanager
|
|
495
|
-
def rest_(self, method, url, name=None, **kwargs) -> Generator[RestResponseContextManager]:
|
|
496
|
-
"""
|
|
497
|
-
Some REST api:s use a timestamp as part of their query string (mainly to break through caches).
|
|
498
|
-
This is a convenience method for that, appending a _=<timestamp> parameter automatically
|
|
499
|
-
"""
|
|
500
|
-
separator = "&" if "?" in url else "?"
|
|
501
|
-
if name is None:
|
|
502
|
-
name = url + separator + "_=..."
|
|
503
|
-
with self.rest(method, f"{url}{separator}_={int(time.time() * 1000)}", name=name, **kwargs) as resp:
|
|
504
|
-
yield resp
|
|
505
|
-
|
|
506
442
|
|
|
507
443
|
class FastRequest(CompatRequest):
|
|
508
444
|
payload: str | None = None
|
|
@@ -435,7 +435,10 @@ class StatsEntry:
|
|
|
435
435
|
def median_response_time(self) -> int:
|
|
436
436
|
if not self.response_times:
|
|
437
437
|
return 0
|
|
438
|
-
median =
|
|
438
|
+
median = (
|
|
439
|
+
calculate_response_time_percentile(self.response_times, self.num_requests - self.num_none_requests, 0.5)
|
|
440
|
+
or 0
|
|
441
|
+
)
|
|
439
442
|
|
|
440
443
|
# Since we only use two digits of precision when calculating the median response time
|
|
441
444
|
# while still using the exact values for min and max response times, the following checks
|
|
@@ -812,20 +815,6 @@ def avg(values: list[float | int]) -> float:
|
|
|
812
815
|
return sum(values, 0.0) / max(len(values), 1)
|
|
813
816
|
|
|
814
817
|
|
|
815
|
-
def median_from_dict(total: int, count: dict[int, int]) -> int:
|
|
816
|
-
"""
|
|
817
|
-
total is the number of requests made
|
|
818
|
-
count is a dict {response_time: count}
|
|
819
|
-
"""
|
|
820
|
-
pos = (total - 1) / 2
|
|
821
|
-
for k in sorted(count.keys()):
|
|
822
|
-
if pos < count[k]:
|
|
823
|
-
return k
|
|
824
|
-
pos -= count[k]
|
|
825
|
-
|
|
826
|
-
return k
|
|
827
|
-
|
|
828
|
-
|
|
829
818
|
def setup_distributed_stats_event_listeners(events: Events, stats: RequestStats) -> None:
|
|
830
819
|
def on_report_to_master(client_id: str, data: dict[str, Any]) -> None:
|
|
831
820
|
data["stats"] = stats.serialize_stats()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from locust.clients import HttpSession
|
|
3
|
+
from locust.clients import HttpSession, RestResponseContextManager
|
|
4
4
|
from locust.exception import CatchResponseError, StopTest, StopUser
|
|
5
5
|
from locust.user.task import (
|
|
6
6
|
LOCUST_STATE_RUNNING,
|
|
@@ -14,11 +14,14 @@ from locust.user.wait_time import constant
|
|
|
14
14
|
from locust.util import deprecation
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
|
+
import re
|
|
17
18
|
import sys
|
|
18
19
|
import time
|
|
19
20
|
import traceback
|
|
20
21
|
from collections.abc import Callable
|
|
21
|
-
from
|
|
22
|
+
from contextlib import contextmanager
|
|
23
|
+
from json.decoder import JSONDecodeError
|
|
24
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar, final
|
|
22
25
|
|
|
23
26
|
from gevent import GreenletExit, greenlet
|
|
24
27
|
from gevent.pool import Group
|
|
@@ -32,6 +35,8 @@ else:
|
|
|
32
35
|
from typing_extensions import override
|
|
33
36
|
|
|
34
37
|
if TYPE_CHECKING:
|
|
38
|
+
from collections.abc import Generator
|
|
39
|
+
|
|
35
40
|
import pytest
|
|
36
41
|
|
|
37
42
|
logger = logging.getLogger(__name__)
|
|
@@ -250,7 +255,78 @@ class User(metaclass=UserMeta):
|
|
|
250
255
|
return ".".join(filter(lambda x: x != "<locals>", (cls.__module__ + "." + cls.__qualname__).split(".")))
|
|
251
256
|
|
|
252
257
|
|
|
253
|
-
|
|
258
|
+
RestResponseT = TypeVar("RestResponseT")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class RestMixin(Generic[RestResponseT]):
|
|
262
|
+
"""
|
|
263
|
+
Provides the ``rest`` and ``rest_`` methods for User classes that have an HTTP ``client`` (HttpUser and FastHttpUser)
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
_callstack_regex = re.compile(r' File "(\/.[^"]*)", line (\d*),(.*)')
|
|
267
|
+
|
|
268
|
+
@contextmanager
|
|
269
|
+
def rest(self, method, url, headers: dict | None = None, **kwargs) -> Generator[RestResponseT]:
|
|
270
|
+
"""
|
|
271
|
+
A wrapper for self.client.request that:
|
|
272
|
+
|
|
273
|
+
* Parses the JSON response to a dict called ``js`` in the response object. Marks the request as failed if the response was not valid JSON.
|
|
274
|
+
* Defaults ``Content-Type`` and ``Accept`` headers to ``application/json``
|
|
275
|
+
* Sets ``catch_response=True`` (so always use a :ref:`with-block <catch-response>`)
|
|
276
|
+
* Catches any unhandled exceptions thrown inside your with-block, marking the sample as failed (instead of exiting the task immediately without even firing the request event)
|
|
277
|
+
"""
|
|
278
|
+
headers = headers or {}
|
|
279
|
+
if not ("Content-Type" in headers or "content-type" in headers):
|
|
280
|
+
headers["Content-Type"] = "application/json"
|
|
281
|
+
if not ("Accept" in headers or "accept" in headers):
|
|
282
|
+
headers["Accept"] = "application/json"
|
|
283
|
+
with self.client.request(method, url, catch_response=True, headers=headers, **kwargs) as r: # type: ignore[attr-defined]
|
|
284
|
+
resp: Any = r
|
|
285
|
+
resp.js = None
|
|
286
|
+
if resp.content is None:
|
|
287
|
+
resp.failure(str(resp.error))
|
|
288
|
+
elif resp.text:
|
|
289
|
+
try:
|
|
290
|
+
resp.js = resp.json()
|
|
291
|
+
except JSONDecodeError as e:
|
|
292
|
+
resp.failure(
|
|
293
|
+
f"Could not parse response as JSON. {resp.text[:250]}, response code {resp.status_code}, error {e}"
|
|
294
|
+
)
|
|
295
|
+
try:
|
|
296
|
+
yield resp
|
|
297
|
+
except AssertionError as e:
|
|
298
|
+
if e.args:
|
|
299
|
+
if e.args[0].endswith(","):
|
|
300
|
+
short_resp = resp.text[:200] if resp.text else resp.text
|
|
301
|
+
resp.failure(f"{e.args[0][:-1]}, response was {short_resp}")
|
|
302
|
+
else:
|
|
303
|
+
resp.failure(e.args[0])
|
|
304
|
+
else:
|
|
305
|
+
resp.failure("Assertion failed")
|
|
306
|
+
|
|
307
|
+
except Exception as e:
|
|
308
|
+
error_lines = []
|
|
309
|
+
for l in traceback.format_exc().split("\n"):
|
|
310
|
+
if m := self._callstack_regex.match(l):
|
|
311
|
+
filename = re.sub(r"/((home|Users)/\w*)/", "~/", m.group(1))
|
|
312
|
+
error_lines.append(filename + ":" + m.group(2) + m.group(3))
|
|
313
|
+
short_resp = resp.text[:200] if resp.text else resp.text
|
|
314
|
+
resp.failure(f"{e.__class__.__name__}: {e} at {', '.join(error_lines)}. Response was {short_resp}")
|
|
315
|
+
|
|
316
|
+
@contextmanager
|
|
317
|
+
def rest_(self, method, url, name=None, **kwargs) -> Generator[RestResponseT]:
|
|
318
|
+
"""
|
|
319
|
+
Some REST api:s use a timestamp as part of their query string (mainly to break through caches).
|
|
320
|
+
This is a convenience method for that, appending a _=<timestamp> parameter automatically
|
|
321
|
+
"""
|
|
322
|
+
separator = "&" if "?" in url else "?"
|
|
323
|
+
if name is None:
|
|
324
|
+
name = url + separator + "_=..."
|
|
325
|
+
with self.rest(method, f"{url}{separator}_={int(time.time() * 1000)}", name=name, **kwargs) as resp:
|
|
326
|
+
yield resp
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class HttpUser(User, RestMixin[RestResponseContextManager]):
|
|
254
330
|
"""
|
|
255
331
|
Represents an HTTP "user" which is to be spawned and attack the system that is to be load tested.
|
|
256
332
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|