expr-tracker 0.3.1__tar.gz → 0.3.2__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.
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/PKG-INFO +2 -1
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/cli.md +14 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/pyproject.toml +1 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/cli.py +17 -2
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_cli.py +79 -1
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/uv.lock +2 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/.github/workflows/docs.yaml +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/.github/workflows/release.yaml +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/.gitignore +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/LICENSE +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/README.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/architecture.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/design.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/examples.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/getting-started.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/alerts.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/artifacts.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/backends.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/distributed.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/history.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/logging.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/spans.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/guide/streams.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/index.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/reference/api.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/reference/configuration.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/docs/reference/expressions.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/examples/README.md +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/examples/alert_rules.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/examples/checkpoints.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/examples/early_stopping.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/examples/multiprocess_pipeline.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/examples/profile_step.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/examples/quickstart.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/mkdocs.yml +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/__init__.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/_compat.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/__init__.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/backends/__init__.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/backends/base.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/backends/cards.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/dispatch.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/engine.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/expr/__init__.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/expr/eval.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/expr/functions.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/expr/lexer.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/expr/nodes.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/expr/parser.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/expr/rule.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/alerts/models.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/artifacts.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/encoders.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/__init__.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/codec.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/frame.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/naming.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/reader.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/series.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/store.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/history/writer.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/plugins.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/py.typed +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/run.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/spans.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/summary.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/trace.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/tracker.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/src/expr_tracker/types.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/conftest.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_alert_backends.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_alert_delivery.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_alert_dispatch.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_alert_engine.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_alert_models.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_alert_routing.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_artifacts.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_benchmark.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_cache.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_correctness.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_distributed.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_e2e.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_examples.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_expr_builder.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_expr_eval.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_expr_functions.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_expr_parser.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_expr_properties.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_failure_modes.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_features.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_frame_codec_summary.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_history.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_hot_paths.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_integration.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_lark.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_perf.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_public_surfaces.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_review_regressions.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_rule_lifecycle.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_run_backends.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_run_dir.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_scenarios.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_span_plugins.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_spans.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_step_commit.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_streams.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_stress.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_trace.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_trackio.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_value_encoding.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_wandb.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_writer_buffer.py +0 -0
- {expr_tracker-0.3.1 → expr_tracker-0.3.2}/tests/test_writer_durability.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: expr_tracker
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Local-first experiment tracking with queryable history and expression-based alerts on your training metrics
|
|
5
5
|
Project-URL: Homepage, https://hspk.github.io/expr_tracker/
|
|
6
6
|
Project-URL: Documentation, https://hspk.github.io/expr_tracker/
|
|
@@ -27,6 +27,7 @@ Requires-Python: >=3.10
|
|
|
27
27
|
Requires-Dist: click>=8.1.0
|
|
28
28
|
Requires-Dist: loguru>=0.7.3
|
|
29
29
|
Requires-Dist: pydantic>=2.0
|
|
30
|
+
Requires-Dist: python-dotenv>=1.0
|
|
30
31
|
Provides-Extra: all
|
|
31
32
|
Requires-Dist: nvidia-ml-py>=12.0; extra == 'all'
|
|
32
33
|
Requires-Dist: pandas>=1.5; extra == 'all'
|
|
@@ -89,3 +89,17 @@ et alert "node 3 is on fire" --level critical --channel oncall
|
|
|
89
89
|
|
|
90
90
|
Channels come from the same configuration the library uses: `ET_ALERT_CONFIG` or the
|
|
91
91
|
`ET_LARK_WEBHOOK_URL` environment variable. See [Alerts](alerts.md).
|
|
92
|
+
|
|
93
|
+
`et alert` automatically loads `.env` from the directory where you invoke it,
|
|
94
|
+
before resolving alert configuration. Existing environment variables take
|
|
95
|
+
precedence, including empty values. It does not search parent directories; a
|
|
96
|
+
missing `.env` is fine. Use `--no-dotenv` to disable loading.
|
|
97
|
+
|
|
98
|
+
```dotenv
|
|
99
|
+
# .env
|
|
100
|
+
ET_LARK_WEBHOOK_URL=https://example.com/your-webhook
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Quoted values, comments, and `${VAR}` expansion are supported; shell commands are
|
|
104
|
+
not executed. Keep secrets out of version control. This loading applies only to
|
|
105
|
+
`et alert`, not other CLI commands or the Python API.
|
|
@@ -4,8 +4,10 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
import time
|
|
7
|
+
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
import click
|
|
10
|
+
from dotenv import load_dotenv
|
|
9
11
|
|
|
10
12
|
from .alerts import alert as send_alert
|
|
11
13
|
from .alerts.dispatch import Dispatcher
|
|
@@ -26,8 +28,21 @@ def main():
|
|
|
26
28
|
@click.option("--title", default="Alert", help="Title of the alert")
|
|
27
29
|
@click.option("--level", default="info", help="info | warning | error | critical")
|
|
28
30
|
@click.option("--channel", "channels", multiple=True, help="Restrict to these channels")
|
|
29
|
-
|
|
30
|
-
"""
|
|
31
|
+
@click.option(
|
|
32
|
+
"--no-dotenv", is_flag=True, help="Skip loading .env from the current directory"
|
|
33
|
+
)
|
|
34
|
+
def alert(
|
|
35
|
+
msg: str, title: str, level: str, channels: tuple[str, ...], no_dotenv: bool
|
|
36
|
+
):
|
|
37
|
+
"""Send a manual alert, loading the current directory's .env by default."""
|
|
38
|
+
if not no_dotenv:
|
|
39
|
+
try:
|
|
40
|
+
with Path(".env").open(encoding="utf-8") as stream:
|
|
41
|
+
load_dotenv(stream=stream, override=False)
|
|
42
|
+
except FileNotFoundError:
|
|
43
|
+
pass
|
|
44
|
+
except (OSError, UnicodeError) as exc:
|
|
45
|
+
raise click.ClickException(f"Could not read .env: {exc}") from exc
|
|
31
46
|
send_alert(title=title, text=msg, level=level, channels=list(channels) or None)
|
|
32
47
|
|
|
33
48
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""The full CLI surface: every command, every format, and their error paths."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
6
7
|
from click.testing import CliRunner
|
|
@@ -10,7 +11,9 @@ from expr_tracker.history import HistoryStore
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@pytest.fixture
|
|
13
|
-
def runner():
|
|
14
|
+
def runner(monkeypatch, tmp_path):
|
|
15
|
+
monkeypatch.chdir(tmp_path)
|
|
16
|
+
monkeypatch.setattr(os, "environ", os.environ.copy())
|
|
14
17
|
return CliRunner()
|
|
15
18
|
|
|
16
19
|
|
|
@@ -237,6 +240,81 @@ def test_alert_without_a_message_fails(runner, channel):
|
|
|
237
240
|
assert runner.invoke(cli.main, ["alert"]).exit_code != 0
|
|
238
241
|
|
|
239
242
|
|
|
243
|
+
def test_alert_loads_dotenv_before_resolving_channels(runner, monkeypatch, tmp_path):
|
|
244
|
+
from expr_tracker.alerts import load_config
|
|
245
|
+
|
|
246
|
+
monkeypatch.delenv("ET_LARK_WEBHOOK_URL", raising=False)
|
|
247
|
+
monkeypatch.delenv("ET_ALERT_CONFIG", raising=False)
|
|
248
|
+
(tmp_path / ".env").write_text(
|
|
249
|
+
'# Alert settings\nexport ET_LARK_WEBHOOK_URL="https://example.test/hook"\n',
|
|
250
|
+
encoding="utf-8",
|
|
251
|
+
)
|
|
252
|
+
configs = []
|
|
253
|
+
monkeypatch.setattr(cli, "send_alert", lambda **kwargs: configs.append(load_config()))
|
|
254
|
+
|
|
255
|
+
invoke(runner, "alert", "hello")
|
|
256
|
+
|
|
257
|
+
assert any(
|
|
258
|
+
c.type == "lark" and c.resolve_url() == "https://example.test/hook"
|
|
259
|
+
for c in configs[0].channels
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@pytest.mark.parametrize("existing", ["from-shell", ""])
|
|
264
|
+
def test_alert_dotenv_preserves_existing_environment(
|
|
265
|
+
runner, channel, monkeypatch, tmp_path, existing
|
|
266
|
+
):
|
|
267
|
+
monkeypatch.setenv("ET_TEST_DOTENV", existing)
|
|
268
|
+
(tmp_path / ".env").write_text("ET_TEST_DOTENV=from-file\n", encoding="utf-8")
|
|
269
|
+
|
|
270
|
+
invoke(runner, "alert", "hello")
|
|
271
|
+
|
|
272
|
+
assert os.environ["ET_TEST_DOTENV"] == existing
|
|
273
|
+
assert len(channel) == 1
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_alert_does_not_search_parent_dotenv(runner, channel, monkeypatch, tmp_path):
|
|
277
|
+
monkeypatch.delenv("ET_TEST_DOTENV", raising=False)
|
|
278
|
+
(tmp_path / ".env").write_text("ET_TEST_DOTENV=parent\n", encoding="utf-8")
|
|
279
|
+
child = tmp_path / "child"
|
|
280
|
+
child.mkdir()
|
|
281
|
+
monkeypatch.chdir(child)
|
|
282
|
+
|
|
283
|
+
invoke(runner, "alert", "hello")
|
|
284
|
+
|
|
285
|
+
assert "ET_TEST_DOTENV" not in os.environ
|
|
286
|
+
assert len(channel) == 1
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def test_alert_can_skip_dotenv(runner, channel, monkeypatch, tmp_path):
|
|
290
|
+
monkeypatch.delenv("ET_TEST_DOTENV", raising=False)
|
|
291
|
+
(tmp_path / ".env").write_text("ET_TEST_DOTENV=from-file\n", encoding="utf-8")
|
|
292
|
+
|
|
293
|
+
invoke(runner, "alert", "hello", "--no-dotenv")
|
|
294
|
+
|
|
295
|
+
assert "ET_TEST_DOTENV" not in os.environ
|
|
296
|
+
assert len(channel) == 1
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def test_alert_reports_unreadable_dotenv(runner, channel, tmp_path):
|
|
300
|
+
(tmp_path / ".env").mkdir()
|
|
301
|
+
|
|
302
|
+
result = runner.invoke(cli.main, ["alert", "hello"])
|
|
303
|
+
|
|
304
|
+
assert result.exit_code != 0
|
|
305
|
+
assert "Could not read .env" in result.output
|
|
306
|
+
assert not channel
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def test_other_commands_do_not_load_dotenv(runner, monkeypatch, tmp_path):
|
|
310
|
+
monkeypatch.delenv("ET_TEST_DOTENV", raising=False)
|
|
311
|
+
(tmp_path / ".env").write_text("ET_TEST_DOTENV=from-file\n", encoding="utf-8")
|
|
312
|
+
|
|
313
|
+
invoke(runner, "rules", "explain", "loss > 1")
|
|
314
|
+
|
|
315
|
+
assert "ET_TEST_DOTENV" not in os.environ
|
|
316
|
+
|
|
317
|
+
|
|
240
318
|
# ------------------------------------------------------------------ rules
|
|
241
319
|
|
|
242
320
|
|
|
@@ -463,6 +463,7 @@ dependencies = [
|
|
|
463
463
|
{ name = "click" },
|
|
464
464
|
{ name = "loguru" },
|
|
465
465
|
{ name = "pydantic" },
|
|
466
|
+
{ name = "python-dotenv" },
|
|
466
467
|
]
|
|
467
468
|
|
|
468
469
|
[package.optional-dependencies]
|
|
@@ -517,6 +518,7 @@ requires-dist = [
|
|
|
517
518
|
{ name = "polars", marker = "extra == 'all'", specifier = ">=0.20" },
|
|
518
519
|
{ name = "polars", marker = "extra == 'polars'", specifier = ">=0.20" },
|
|
519
520
|
{ name = "pydantic", specifier = ">=2.0" },
|
|
521
|
+
{ name = "python-dotenv", specifier = ">=1.0" },
|
|
520
522
|
{ name = "trackio", marker = "extra == 'all'", specifier = ">=0.4.0" },
|
|
521
523
|
{ name = "trackio", marker = "extra == 'trackio'", specifier = ">=0.4.0" },
|
|
522
524
|
{ name = "wandb", marker = "extra == 'all'", specifier = ">=0.21.0" },
|
|
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
|
|
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
|