scraper-rust 0.5.0__tar.gz → 0.5.1__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.
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/Cargo.lock +47 -1
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/Cargo.toml +3 -2
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/PKG-INFO +2 -1
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/README.md +1 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/pyproject.toml +1 -1
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/scraper_rs/asyncio.py +41 -80
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/runtime.rs +6 -1
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/uv.lock +1 -1
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/.github/workflows/benchmark.yml +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/.github/workflows/bump-version.yml +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/.github/workflows/release.yml +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/.github/workflows/tests.yml +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/.gitignore +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/AGENTS.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/LICENSE +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/benchmarks/README.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/benchmarks/bench_parse_memory.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/benchmarks/bench_sync_async.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/benchmarks/bench_vs_markupever.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/docs/README.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/docs/api.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/docs/architecture.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/docs/async-api.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/docs/development.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/docs/limits-and-errors.md +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/examples/demo.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/examples/demo_async_document.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/examples/demo_asyncio.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/examples/demo_prettify_url.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/justfile +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/py.typed +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/scraper_rs/__init__.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/scraper_rs/asyncio.pyi +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/scraper_rs.pyi +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/async_core.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/cache.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/document.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/element.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/functions.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/html5_dict.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/lib.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/limits.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/prettify.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/selectors.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/tests.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/text.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/src/xpath.rs +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/tests/test_asyncio.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/tests/test_performance.py +0 -0
- {scraper_rust-0.5.0 → scraper_rust-0.5.1}/tests/test_scraper.py +0 -0
|
@@ -244,6 +244,31 @@ dependencies = [
|
|
|
244
244
|
"libm",
|
|
245
245
|
]
|
|
246
246
|
|
|
247
|
+
[[package]]
|
|
248
|
+
name = "crossbeam-deque"
|
|
249
|
+
version = "0.8.6"
|
|
250
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
251
|
+
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
|
252
|
+
dependencies = [
|
|
253
|
+
"crossbeam-epoch",
|
|
254
|
+
"crossbeam-utils",
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
[[package]]
|
|
258
|
+
name = "crossbeam-epoch"
|
|
259
|
+
version = "0.9.18"
|
|
260
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
261
|
+
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
|
262
|
+
dependencies = [
|
|
263
|
+
"crossbeam-utils",
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
[[package]]
|
|
267
|
+
name = "crossbeam-utils"
|
|
268
|
+
version = "0.8.21"
|
|
269
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
270
|
+
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
271
|
+
|
|
247
272
|
[[package]]
|
|
248
273
|
name = "cssparser"
|
|
249
274
|
version = "0.37.0"
|
|
@@ -1680,6 +1705,26 @@ dependencies = [
|
|
|
1680
1705
|
"rand_core",
|
|
1681
1706
|
]
|
|
1682
1707
|
|
|
1708
|
+
[[package]]
|
|
1709
|
+
name = "rayon"
|
|
1710
|
+
version = "1.12.0"
|
|
1711
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1712
|
+
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
|
|
1713
|
+
dependencies = [
|
|
1714
|
+
"either",
|
|
1715
|
+
"rayon-core",
|
|
1716
|
+
]
|
|
1717
|
+
|
|
1718
|
+
[[package]]
|
|
1719
|
+
name = "rayon-core"
|
|
1720
|
+
version = "1.13.0"
|
|
1721
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1722
|
+
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
|
1723
|
+
dependencies = [
|
|
1724
|
+
"crossbeam-deque",
|
|
1725
|
+
"crossbeam-utils",
|
|
1726
|
+
]
|
|
1727
|
+
|
|
1683
1728
|
[[package]]
|
|
1684
1729
|
name = "redox_syscall"
|
|
1685
1730
|
version = "0.5.18"
|
|
@@ -1832,11 +1877,12 @@ dependencies = [
|
|
|
1832
1877
|
|
|
1833
1878
|
[[package]]
|
|
1834
1879
|
name = "scraper-rs"
|
|
1835
|
-
version = "0.5.
|
|
1880
|
+
version = "0.5.1"
|
|
1836
1881
|
dependencies = [
|
|
1837
1882
|
"html5ever",
|
|
1838
1883
|
"pyo3",
|
|
1839
1884
|
"pyo3-async-runtimes",
|
|
1885
|
+
"rayon",
|
|
1840
1886
|
"scraper",
|
|
1841
1887
|
"tokio",
|
|
1842
1888
|
"xee-xpath",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "scraper-rs"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.1"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
|
|
@@ -15,7 +15,8 @@ extension-module = ["pyo3/extension-module"]
|
|
|
15
15
|
[dependencies]
|
|
16
16
|
pyo3 = { version = "0.28.3", features = ["abi3-py310"] }
|
|
17
17
|
pyo3-async-runtimes = { version = "0.28", features = ["tokio-runtime"] }
|
|
18
|
-
|
|
18
|
+
rayon = "1"
|
|
19
|
+
tokio = { version = "1", features = ["rt", "macros", "sync"] }
|
|
19
20
|
|
|
20
21
|
scraper = { version = "0.27.0", git = "https://github.com/rust-scraper/scraper", features = ["atomic"], rev = "9c1eff304e45a8bccb463968268ce6758703e821" }
|
|
21
22
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scraper-rust
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Classifier: Programming Language :: Python
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -103,6 +103,7 @@ asyncio.run(main())
|
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
All async functions accept the same keyword arguments as their sync counterparts (`max_size_bytes`, `truncate_on_limit`, etc.).
|
|
106
|
+
CPU-bound async parsing and selection work is offloaded through the Rust async core onto Rayon worker threads so concurrent coroutines do not run that work on the event-loop thread.
|
|
106
107
|
`AsyncDocument` supports `async with` for automatic cleanup in coroutine code, and `AsyncElement` / `AsyncDocument` both expose async `.prettify()` helpers.
|
|
107
108
|
|
|
108
109
|
### Large documents and memory safety
|
|
@@ -76,6 +76,7 @@ asyncio.run(main())
|
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
All async functions accept the same keyword arguments as their sync counterparts (`max_size_bytes`, `truncate_on_limit`, etc.).
|
|
79
|
+
CPU-bound async parsing and selection work is offloaded through the Rust async core onto Rayon worker threads so concurrent coroutines do not run that work on the event-loop thread.
|
|
79
80
|
`AsyncDocument` supports `async with` for automatic cleanup in coroutine code, and `AsyncElement` / `AsyncDocument` both expose async `.prettify()` helpers.
|
|
80
81
|
|
|
81
82
|
### Large documents and memory safety
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scraper-rust"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.1"
|
|
8
8
|
description = "Python bindings around rust-scraper/scraper with PyO3"
|
|
9
9
|
authors = [{ name = "Yehor Smoliakov", email = "egorsmkv@gmail.com" }]
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -2,44 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import asyncio
|
|
6
|
-
|
|
7
5
|
from .scraper_rs import (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
_AsyncDocumentCore,
|
|
7
|
+
_AsyncElementCore,
|
|
8
|
+
first_async as _first_async,
|
|
9
|
+
parse_async as _parse_async,
|
|
10
|
+
prettify_async as _prettify_async,
|
|
11
|
+
select_async as _select_async,
|
|
12
|
+
select_first_async as _select_first_async,
|
|
13
|
+
xpath_async as _xpath_async,
|
|
14
|
+
xpath_first_async as _xpath_first_async,
|
|
16
15
|
)
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
def _wrap_element(element:
|
|
18
|
+
def _wrap_element(element: _AsyncElementCore | None) -> "AsyncElement | None":
|
|
20
19
|
if element is None:
|
|
21
20
|
return None
|
|
22
21
|
return AsyncElement(element)
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
def _wrap_elements(elements: list[
|
|
24
|
+
def _wrap_elements(elements: list[_AsyncElementCore]) -> list["AsyncElement"]:
|
|
26
25
|
return [AsyncElement(element) for element in elements]
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
def _parse_state(html: str, kwargs: dict[str, int | bool | None]) -> tuple[str, str]:
|
|
30
|
-
document = _Document(html, **kwargs)
|
|
31
|
-
try:
|
|
32
|
-
return document.html, document.text
|
|
33
|
-
finally:
|
|
34
|
-
document.close()
|
|
35
|
-
|
|
36
|
-
|
|
37
28
|
class AsyncElement:
|
|
38
29
|
"""Async wrapper for immutable element snapshots."""
|
|
39
30
|
|
|
40
31
|
__slots__ = ("_element",)
|
|
41
32
|
|
|
42
|
-
def __init__(self, element:
|
|
33
|
+
def __init__(self, element: _AsyncElementCore) -> None:
|
|
43
34
|
self._element = element
|
|
44
35
|
|
|
45
36
|
@property
|
|
@@ -65,32 +56,25 @@ class AsyncElement:
|
|
|
65
56
|
return self._element.get(name, default)
|
|
66
57
|
|
|
67
58
|
async def select(self, css: str) -> list["AsyncElement"]:
|
|
68
|
-
await
|
|
69
|
-
return _wrap_elements(self._element.select(css))
|
|
59
|
+
return _wrap_elements(await self._element.select(css))
|
|
70
60
|
|
|
71
61
|
async def select_first(self, css: str) -> "AsyncElement | None":
|
|
72
|
-
await
|
|
73
|
-
return _wrap_element(self._element.select_first(css))
|
|
62
|
+
return _wrap_element(await self._element.select_first(css))
|
|
74
63
|
|
|
75
64
|
async def find(self, css: str) -> "AsyncElement | None":
|
|
76
|
-
await
|
|
77
|
-
return _wrap_element(self._element.find(css))
|
|
65
|
+
return _wrap_element(await self._element.find(css))
|
|
78
66
|
|
|
79
67
|
async def css(self, css: str) -> list["AsyncElement"]:
|
|
80
|
-
await
|
|
81
|
-
return _wrap_elements(self._element.css(css))
|
|
68
|
+
return _wrap_elements(await self._element.css(css))
|
|
82
69
|
|
|
83
70
|
async def xpath(self, expr: str) -> list["AsyncElement"]:
|
|
84
|
-
await
|
|
85
|
-
return _wrap_elements(self._element.xpath(expr))
|
|
71
|
+
return _wrap_elements(await self._element.xpath(expr))
|
|
86
72
|
|
|
87
73
|
async def xpath_first(self, expr: str) -> "AsyncElement | None":
|
|
88
|
-
await
|
|
89
|
-
return _wrap_element(self._element.xpath_first(expr))
|
|
74
|
+
return _wrap_element(await self._element.xpath_first(expr))
|
|
90
75
|
|
|
91
76
|
async def prettify(self) -> str:
|
|
92
|
-
await
|
|
93
|
-
return self._element.prettify()
|
|
77
|
+
return await self._element.prettify()
|
|
94
78
|
|
|
95
79
|
def to_dict(self) -> dict[str, str | dict[str, str]]:
|
|
96
80
|
return self._element.to_dict()
|
|
@@ -100,59 +84,44 @@ class AsyncElement:
|
|
|
100
84
|
|
|
101
85
|
|
|
102
86
|
class AsyncDocument:
|
|
103
|
-
"""Async wrapper
|
|
87
|
+
"""Async wrapper around Rust-owned document state."""
|
|
104
88
|
|
|
105
|
-
__slots__ = ("
|
|
89
|
+
__slots__ = ("_document",)
|
|
106
90
|
|
|
107
|
-
def __init__(self,
|
|
108
|
-
self.
|
|
109
|
-
self._text = text
|
|
110
|
-
self._closed = False
|
|
91
|
+
def __init__(self, document: _AsyncDocumentCore) -> None:
|
|
92
|
+
self._document = document
|
|
111
93
|
|
|
112
94
|
@property
|
|
113
95
|
def html(self) -> str:
|
|
114
|
-
return
|
|
96
|
+
return self._document.html
|
|
115
97
|
|
|
116
98
|
@property
|
|
117
99
|
def text(self) -> str:
|
|
118
|
-
return
|
|
100
|
+
return self._document.text
|
|
119
101
|
|
|
120
102
|
async def select(self, css: str) -> list[AsyncElement]:
|
|
121
|
-
|
|
122
|
-
return []
|
|
123
|
-
return await select(self._html, css)
|
|
103
|
+
return _wrap_elements(await self._document.select(css))
|
|
124
104
|
|
|
125
105
|
async def select_first(self, css: str) -> AsyncElement | None:
|
|
126
|
-
|
|
127
|
-
return None
|
|
128
|
-
return await select_first(self._html, css)
|
|
106
|
+
return _wrap_element(await self._document.select_first(css))
|
|
129
107
|
|
|
130
108
|
async def find(self, css: str) -> AsyncElement | None:
|
|
131
|
-
return await self.
|
|
109
|
+
return _wrap_element(await self._document.find(css))
|
|
132
110
|
|
|
133
111
|
async def css(self, css: str) -> list[AsyncElement]:
|
|
134
|
-
return await self.
|
|
112
|
+
return _wrap_elements(await self._document.css(css))
|
|
135
113
|
|
|
136
114
|
async def xpath(self, expr: str) -> list[AsyncElement]:
|
|
137
|
-
|
|
138
|
-
return []
|
|
139
|
-
return await xpath(self._html, expr)
|
|
115
|
+
return _wrap_elements(await self._document.xpath(expr))
|
|
140
116
|
|
|
141
117
|
async def xpath_first(self, expr: str) -> AsyncElement | None:
|
|
142
|
-
|
|
143
|
-
return None
|
|
144
|
-
return await xpath_first(self._html, expr)
|
|
118
|
+
return _wrap_element(await self._document.xpath_first(expr))
|
|
145
119
|
|
|
146
120
|
async def prettify(self) -> str:
|
|
147
|
-
|
|
148
|
-
return ""
|
|
149
|
-
await asyncio.sleep(0)
|
|
150
|
-
return _prettify_sync(self._html)
|
|
121
|
+
return await self._document.prettify()
|
|
151
122
|
|
|
152
123
|
def close(self) -> None:
|
|
153
|
-
self.
|
|
154
|
-
self._html = ""
|
|
155
|
-
self._text = ""
|
|
124
|
+
self._document.close()
|
|
156
125
|
|
|
157
126
|
def __enter__(self) -> "AsyncDocument":
|
|
158
127
|
return self
|
|
@@ -167,43 +136,35 @@ class AsyncDocument:
|
|
|
167
136
|
self.close()
|
|
168
137
|
|
|
169
138
|
def __repr__(self) -> str:
|
|
170
|
-
return
|
|
139
|
+
return repr(self._document)
|
|
171
140
|
|
|
172
141
|
|
|
173
142
|
async def parse(html: str, **kwargs) -> "AsyncDocument":
|
|
174
|
-
await
|
|
175
|
-
parsed_html, text = _parse_state(html, kwargs)
|
|
176
|
-
return AsyncDocument(parsed_html, text)
|
|
143
|
+
return AsyncDocument(await _parse_async(html, **kwargs))
|
|
177
144
|
|
|
178
145
|
|
|
179
146
|
async def select(html: str, css: str, **kwargs) -> list["AsyncElement"]:
|
|
180
|
-
await
|
|
181
|
-
return _wrap_elements(_select_sync(html, css, **kwargs))
|
|
147
|
+
return _wrap_elements(await _select_async(html, css, **kwargs))
|
|
182
148
|
|
|
183
149
|
|
|
184
150
|
async def select_first(html: str, css: str, **kwargs) -> "AsyncElement | None":
|
|
185
|
-
await
|
|
186
|
-
return _wrap_element(_select_first_sync(html, css, **kwargs))
|
|
151
|
+
return _wrap_element(await _select_first_async(html, css, **kwargs))
|
|
187
152
|
|
|
188
153
|
|
|
189
154
|
async def first(html: str, css: str, **kwargs) -> "AsyncElement | None":
|
|
190
|
-
await
|
|
191
|
-
return _wrap_element(_first_sync(html, css, **kwargs))
|
|
155
|
+
return _wrap_element(await _first_async(html, css, **kwargs))
|
|
192
156
|
|
|
193
157
|
|
|
194
158
|
async def xpath(html: str, expr: str, **kwargs) -> list["AsyncElement"]:
|
|
195
|
-
await
|
|
196
|
-
return _wrap_elements(_xpath_sync(html, expr, **kwargs))
|
|
159
|
+
return _wrap_elements(await _xpath_async(html, expr, **kwargs))
|
|
197
160
|
|
|
198
161
|
|
|
199
162
|
async def xpath_first(html: str, expr: str, **kwargs) -> "AsyncElement | None":
|
|
200
|
-
await
|
|
201
|
-
return _wrap_element(_xpath_first_sync(html, expr, **kwargs))
|
|
163
|
+
return _wrap_element(await _xpath_first_async(html, expr, **kwargs))
|
|
202
164
|
|
|
203
165
|
|
|
204
166
|
async def prettify(html: str, **kwargs) -> str:
|
|
205
|
-
await
|
|
206
|
-
return _prettify_sync(html, **kwargs)
|
|
167
|
+
return await _prettify_async(html, **kwargs)
|
|
207
168
|
|
|
208
169
|
|
|
209
170
|
__all__ = [
|
|
@@ -21,7 +21,12 @@ where
|
|
|
21
21
|
F: FnOnce() -> PyResult<T> + Send + 'static,
|
|
22
22
|
T: Send + 'static,
|
|
23
23
|
{
|
|
24
|
-
tokio::
|
|
24
|
+
let (sender, receiver) = tokio::sync::oneshot::channel();
|
|
25
|
+
rayon::spawn(move || {
|
|
26
|
+
let _ = sender.send(work());
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
receiver
|
|
25
30
|
.await
|
|
26
31
|
.map_err(|e| PyValueError::new_err(format!("Task join error: {e}")))?
|
|
27
32
|
}
|
|
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
|