flru-parser 0.3.0__py3-none-any.whl

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.
flru/__init__.py ADDED
@@ -0,0 +1,138 @@
1
+ from .batch import BatchError, BatchResult
2
+ from .client import FLClient
3
+ from .config import (
4
+ CircuitBreakerConfig,
5
+ ClientConfig,
6
+ ProxyConfig,
7
+ RateLimitConfig,
8
+ RetryConfig,
9
+ TimeoutConfig,
10
+ )
11
+ from .easy import (
12
+ Client,
13
+ Pages,
14
+ fetch_freelancers,
15
+ fetch_project,
16
+ fetch_projects,
17
+ fetch_user,
18
+ )
19
+ from .exceptions import (
20
+ AuthenticationRequired,
21
+ BlockedError,
22
+ CircuitOpenError,
23
+ ConfigurationError,
24
+ EmptyPageError,
25
+ FLRUError,
26
+ HTTPStatusError,
27
+ ParseError,
28
+ RateLimitedError,
29
+ RobotsDeniedError,
30
+ SecurityError,
31
+ SelectorDriftError,
32
+ TransportError,
33
+ UnexpectedPageError,
34
+ )
35
+ from .filters import ProjectFilters, ProjectType
36
+ from .models import (
37
+ Attachment,
38
+ Category,
39
+ CrawlCheckpoint,
40
+ FreelancerPage,
41
+ FreelancerSummary,
42
+ Image,
43
+ Link,
44
+ Money,
45
+ PageData,
46
+ ParseDiagnostics,
47
+ PortfolioItem,
48
+ ProjectDetail,
49
+ ProjectKind,
50
+ ProjectPage,
51
+ ProjectRecord,
52
+ ProjectStatus,
53
+ ProjectSummary,
54
+ RequestMetrics,
55
+ Review,
56
+ SourceInfo,
57
+ UserProfile,
58
+ UserSummary,
59
+ )
60
+ from .observability import RequestEvent, StructuredLogHandler
61
+ from .state import (
62
+ CrawlStateStore,
63
+ MemoryStateStore,
64
+ PostgresStateStore,
65
+ RedisStateStore,
66
+ SQLiteStateStore,
67
+ project_content_hash,
68
+ )
69
+
70
+ __version__ = "0.3.0"
71
+
72
+ AsyncClient = Client
73
+
74
+ __all__ = [
75
+ "__version__",
76
+ "AsyncClient",
77
+ "Attachment",
78
+ "AuthenticationRequired",
79
+ "BatchError",
80
+ "BatchResult",
81
+ "BlockedError",
82
+ "Category",
83
+ "CircuitBreakerConfig",
84
+ "CircuitOpenError",
85
+ "Client",
86
+ "ClientConfig",
87
+ "ConfigurationError",
88
+ "CrawlCheckpoint",
89
+ "CrawlStateStore",
90
+ "EmptyPageError",
91
+ "FLClient",
92
+ "FLRUError",
93
+ "FreelancerPage",
94
+ "FreelancerSummary",
95
+ "HTTPStatusError",
96
+ "Image",
97
+ "Link",
98
+ "MemoryStateStore",
99
+ "Money",
100
+ "PageData",
101
+ "Pages",
102
+ "ParseDiagnostics",
103
+ "ParseError",
104
+ "PortfolioItem",
105
+ "PostgresStateStore",
106
+ "ProjectDetail",
107
+ "ProjectFilters",
108
+ "ProjectKind",
109
+ "ProjectPage",
110
+ "ProjectRecord",
111
+ "ProjectStatus",
112
+ "ProjectSummary",
113
+ "ProjectType",
114
+ "ProxyConfig",
115
+ "RateLimitConfig",
116
+ "RateLimitedError",
117
+ "RedisStateStore",
118
+ "RequestEvent",
119
+ "RequestMetrics",
120
+ "RetryConfig",
121
+ "Review",
122
+ "RobotsDeniedError",
123
+ "SQLiteStateStore",
124
+ "SecurityError",
125
+ "SelectorDriftError",
126
+ "SourceInfo",
127
+ "StructuredLogHandler",
128
+ "TimeoutConfig",
129
+ "TransportError",
130
+ "UnexpectedPageError",
131
+ "UserProfile",
132
+ "UserSummary",
133
+ "project_content_hash",
134
+ "fetch_freelancers",
135
+ "fetch_project",
136
+ "fetch_projects",
137
+ "fetch_user",
138
+ ]
flru/batch.py ADDED
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Generic, TypeVar
5
+
6
+ T = TypeVar("T")
7
+ K = TypeVar("K")
8
+
9
+
10
+ @dataclass(slots=True, frozen=True)
11
+ class BatchError(Generic[K]):
12
+ key: K
13
+ error: Exception
14
+
15
+
16
+ @dataclass(slots=True)
17
+ class BatchResult(Generic[K, T]):
18
+ successful: list[T] = field(default_factory=list)
19
+ failed: list[BatchError[K]] = field(default_factory=list)
20
+
21
+ @property
22
+ def ok(self) -> bool:
23
+ return not self.failed
24
+
25
+ def raise_for_errors(self) -> None:
26
+ if self.failed:
27
+ first = self.failed[0]
28
+ raise first.error
flru/canary.py ADDED
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import asyncio
5
+ import json
6
+
7
+ from .client import FLClient
8
+
9
+
10
+ async def run_canary() -> dict[str, object]:
11
+ async with FLClient() as client:
12
+ page = await client.get_projects_page(1)
13
+ first = page.items[0]
14
+ return {
15
+ "ok": True,
16
+ "items": len(page.items),
17
+ "first_id": first.id,
18
+ "first_title": first.title,
19
+ "fingerprint": page.diagnostics.page_fingerprint,
20
+ "confidence": page.diagnostics.confidence,
21
+ }
22
+
23
+
24
+ def main() -> None:
25
+ parser = argparse.ArgumentParser(description="Run a read-only FL.ru parser canary")
26
+ parser.add_argument("--pretty", action="store_true")
27
+ args = parser.parse_args()
28
+ result = asyncio.run(run_canary())
29
+ print(json.dumps(result, ensure_ascii=False, indent=2 if args.pretty else None))
30
+
31
+
32
+ if __name__ == "__main__":
33
+ main()