osn-selenium 0.0.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.
Files changed (57) hide show
  1. osn_selenium/__init__.py +1 -0
  2. osn_selenium/browsers_handler/__init__.py +70 -0
  3. osn_selenium/browsers_handler/_windows.py +130 -0
  4. osn_selenium/browsers_handler/types.py +20 -0
  5. osn_selenium/captcha_workers/__init__.py +26 -0
  6. osn_selenium/dev_tools/__init__.py +1 -0
  7. osn_selenium/dev_tools/_types.py +22 -0
  8. osn_selenium/dev_tools/domains/__init__.py +63 -0
  9. osn_selenium/dev_tools/domains/abstract.py +378 -0
  10. osn_selenium/dev_tools/domains/fetch.py +1295 -0
  11. osn_selenium/dev_tools/domains_default/__init__.py +1 -0
  12. osn_selenium/dev_tools/domains_default/fetch.py +155 -0
  13. osn_selenium/dev_tools/errors.py +89 -0
  14. osn_selenium/dev_tools/logger.py +558 -0
  15. osn_selenium/dev_tools/manager.py +1551 -0
  16. osn_selenium/dev_tools/utils.py +509 -0
  17. osn_selenium/errors.py +16 -0
  18. osn_selenium/types.py +118 -0
  19. osn_selenium/webdrivers/BaseDriver/__init__.py +1 -0
  20. osn_selenium/webdrivers/BaseDriver/_utils.py +37 -0
  21. osn_selenium/webdrivers/BaseDriver/flags.py +644 -0
  22. osn_selenium/webdrivers/BaseDriver/protocols.py +2135 -0
  23. osn_selenium/webdrivers/BaseDriver/trio_wrapper.py +71 -0
  24. osn_selenium/webdrivers/BaseDriver/webdriver.py +2626 -0
  25. osn_selenium/webdrivers/Blink/__init__.py +1 -0
  26. osn_selenium/webdrivers/Blink/flags.py +1349 -0
  27. osn_selenium/webdrivers/Blink/protocols.py +330 -0
  28. osn_selenium/webdrivers/Blink/webdriver.py +637 -0
  29. osn_selenium/webdrivers/Chrome/__init__.py +1 -0
  30. osn_selenium/webdrivers/Chrome/flags.py +192 -0
  31. osn_selenium/webdrivers/Chrome/protocols.py +228 -0
  32. osn_selenium/webdrivers/Chrome/webdriver.py +394 -0
  33. osn_selenium/webdrivers/Edge/__init__.py +1 -0
  34. osn_selenium/webdrivers/Edge/flags.py +192 -0
  35. osn_selenium/webdrivers/Edge/protocols.py +228 -0
  36. osn_selenium/webdrivers/Edge/webdriver.py +394 -0
  37. osn_selenium/webdrivers/Yandex/__init__.py +1 -0
  38. osn_selenium/webdrivers/Yandex/flags.py +192 -0
  39. osn_selenium/webdrivers/Yandex/protocols.py +211 -0
  40. osn_selenium/webdrivers/Yandex/webdriver.py +350 -0
  41. osn_selenium/webdrivers/__init__.py +1 -0
  42. osn_selenium/webdrivers/_functions.py +504 -0
  43. osn_selenium/webdrivers/js_scripts/check_element_in_viewport.js +18 -0
  44. osn_selenium/webdrivers/js_scripts/get_document_scroll_size.js +4 -0
  45. osn_selenium/webdrivers/js_scripts/get_element_css.js +6 -0
  46. osn_selenium/webdrivers/js_scripts/get_element_rect_in_viewport.js +9 -0
  47. osn_selenium/webdrivers/js_scripts/get_random_element_point_in_viewport.js +59 -0
  48. osn_selenium/webdrivers/js_scripts/get_viewport_position.js +4 -0
  49. osn_selenium/webdrivers/js_scripts/get_viewport_rect.js +6 -0
  50. osn_selenium/webdrivers/js_scripts/get_viewport_size.js +4 -0
  51. osn_selenium/webdrivers/js_scripts/open_new_tab.js +1 -0
  52. osn_selenium/webdrivers/js_scripts/stop_window_loading.js +1 -0
  53. osn_selenium/webdrivers/types.py +390 -0
  54. osn_selenium-0.0.0.dist-info/METADATA +710 -0
  55. osn_selenium-0.0.0.dist-info/RECORD +57 -0
  56. osn_selenium-0.0.0.dist-info/WHEEL +5 -0
  57. osn_selenium-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,558 @@
1
+ import json
2
+ import trio
3
+ from pathlib import Path
4
+ from datetime import datetime
5
+ from dataclasses import dataclass
6
+ from osn_selenium.dev_tools._types import LogLevelsType
7
+ from osn_selenium.dev_tools.errors import trio_end_exceptions
8
+ from osn_selenium.dev_tools.utils import (
9
+ _validate_log_filter,
10
+ log_exception
11
+ )
12
+ from typing import (
13
+ Any,
14
+ Literal,
15
+ Optional,
16
+ Sequence,
17
+ TYPE_CHECKING,
18
+ Union
19
+ )
20
+
21
+
22
+ if TYPE_CHECKING:
23
+ from osn_selenium.dev_tools.utils import TargetData
24
+
25
+
26
+ @dataclass
27
+ class LoggerSettings:
28
+ """
29
+ Settings for configuring the LoggerManager.
30
+
31
+ Attributes:
32
+ log_dir_path (Optional[Path]): The base directory where log files will be stored.
33
+ If None, logging will only happen in memory and not be written to files.
34
+ Defaults to None.
35
+ renew_log (bool): If True and `log_dir_path` is provided, the log directory
36
+ will be cleared (recreated) on initialization. Defaults to True.
37
+ log_level_filter_mode (Literal["exclude", "include"]): The mode for filtering log levels.
38
+ "exclude" means log levels in `log_level_filter` will be excluded.
39
+ "include" means only log levels in `log_level_filter` will be included.
40
+ Defaults to "exclude".
41
+ log_level_filter (Optional[Union[LogLevelsType, Sequence[LogLevelsType]]]):
42
+ A single log level or a sequence of log levels to filter by.
43
+ Used in conjunction with `log_level_filter_mode`. Defaults to None.
44
+ target_type_filter_mode (Literal["exclude", "include"]): The mode for filtering target types.
45
+ "exclude" means target types in `target_type_filter` will be excluded.
46
+ "include" means only target types in `target_type_filter` will be included.
47
+ Defaults to "exclude".
48
+ target_type_filter (Optional[Union[str, Sequence[str]]]):
49
+ A single target type string or a sequence of target type strings to filter by.
50
+ Used in conjunction with `target_type_filter_mode`. Defaults to None.
51
+ """
52
+
53
+ log_dir_path: Optional[Path] = None
54
+ renew_log: bool = True
55
+ log_level_filter_mode: Literal["exclude", "include"] = "exclude"
56
+ log_level_filter: Optional[Union[LogLevelsType, Sequence[LogLevelsType]]] = None
57
+ target_type_filter_mode: Literal["exclude", "include"] = "exclude"
58
+ target_type_filter: Optional[Union[str, Sequence[str]]] = None
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class LogEntry:
63
+ """
64
+ Represents a single log entry with detailed information.
65
+
66
+ Attributes:
67
+ target_data ("TargetData"): Data about the target (browser tab/session) from which the log originated.
68
+ message (str): The main log message.
69
+ level (LogLevelsType): The severity level of the log (e.g., "INFO", "ERROR").
70
+ timestamp (datetime): The exact time when the log entry was created.
71
+ source_function (str): The name of the function that generated the log.
72
+ extra_data (Optional[dict[str, Any]]): Optional additional data associated with the log.
73
+ """
74
+
75
+ target_data: "TargetData"
76
+ message: str
77
+ level: LogLevelsType
78
+ timestamp: datetime
79
+ source_function: str
80
+ extra_data: Optional[dict[str, Any]] = None
81
+
82
+ def to_json(self) -> dict[str, Any]:
83
+ """
84
+ Converts the log entry to a JSON-serializable dictionary.
85
+
86
+ Returns:
87
+ dict[str, Any]: A dictionary representation of the log entry.
88
+ """
89
+
90
+ log_dict = {
91
+ "target_data": self.target_data.to_json(),
92
+ "timestamp": self.timestamp.isoformat(),
93
+ "level": self.level,
94
+ "source_function": self.source_function,
95
+ "message": self.message,
96
+ }
97
+
98
+ if self.extra_data is not None:
99
+ log_dict["extra_data"] = {key: str(value) for key, value in self.extra_data.items()}
100
+
101
+ return log_dict
102
+
103
+ def to_string(self) -> str:
104
+ """
105
+ Converts the log entry to a human-readable string format.
106
+
107
+ Returns:
108
+ str: A multi-line string representation of the log entry.
109
+ """
110
+
111
+ return "\n\n".join(
112
+ f"{key}: {json.dumps(value, indent=4, ensure_ascii=False)}"
113
+ for key, value in self.to_json().items()
114
+ )
115
+
116
+
117
+ class TargetLogger:
118
+ """
119
+ Manages logging for a specific browser target (e.g., a tab or iframe).
120
+
121
+ Each `TargetLogger` instance is responsible for writing log entries
122
+ related to its associated `TargetData` to a dedicated file.
123
+
124
+ Attributes:
125
+ _target_data ("TargetData"): The data of the browser target this logger is associated with.
126
+ _nursery_object (trio.Nursery): The Trio nursery for managing concurrent tasks.
127
+ _receive_channel (trio.MemoryReceiveChannel[LogEntry]): The receive channel for log entries specific to this target.
128
+ _log_level_filter (Callable[[Any], bool]): Filter function for log levels.
129
+ _target_type_filter (Callable[[Any], bool]): Filter function for target types.
130
+ _file_writing_stopped (Optional[trio.Event]): An event set when file writing task stops.
131
+ _file_path (Optional[Path]): The path to the target-specific log file.
132
+ _is_active (bool): Flag indicating if the target logger is active.
133
+ """
134
+
135
+ def __init__(
136
+ self,
137
+ target_data: "TargetData",
138
+ nursery_object: trio.Nursery,
139
+ receive_channel: trio.MemoryReceiveChannel[LogEntry],
140
+ logger_settings: LoggerSettings,
141
+ ):
142
+ """
143
+ Initializes the TargetLogger.
144
+
145
+ Args:
146
+ target_data ("TargetData"): The data of the browser target this logger will log for.
147
+ nursery_object (trio.Nursery): The Trio nursery to spawn background tasks.
148
+ """
149
+
150
+ self._target_data = target_data
151
+ self._nursery_object = nursery_object
152
+ self._receive_channel = receive_channel
153
+
154
+ self._log_level_filter = _validate_log_filter(
155
+ logger_settings.log_level_filter_mode,
156
+ logger_settings.log_level_filter
157
+ )
158
+
159
+ self._target_type_filter = _validate_log_filter(
160
+ logger_settings.target_type_filter_mode,
161
+ logger_settings.target_type_filter
162
+ )
163
+
164
+ self._file_writing_stopped: Optional[trio.Event] = None
165
+
166
+ if logger_settings.log_dir_path is None:
167
+ self._file_path = None
168
+ else:
169
+ self._file_path = logger_settings.log_dir_path.joinpath(f"{target_data.target_id}.txt")
170
+
171
+ if self._file_path.exists():
172
+ with open(self._file_path, "w", encoding="utf-8") as file:
173
+ file.write("")
174
+
175
+ self._is_active = False
176
+
177
+ @property
178
+ def is_active(self) -> bool:
179
+ """
180
+ Checks if the target logger is currently active.
181
+
182
+ Returns:
183
+ bool: True if the logger is active and running, False otherwise.
184
+ """
185
+
186
+ return self._is_active
187
+
188
+ async def close(self):
189
+ """
190
+ Closes the target logger, including its receive channel.
191
+ """
192
+
193
+ if self._receive_channel is not None:
194
+ await self._receive_channel.aclose()
195
+ self._receive_channel = None
196
+
197
+ if self._file_writing_stopped is not None:
198
+ await self._file_writing_stopped.wait()
199
+ self._file_writing_stopped = None
200
+
201
+ self._is_active = False
202
+
203
+ async def _write_file(self):
204
+ """
205
+ Asynchronously writes log entries to the target-specific file.
206
+
207
+ This method continuously receives `LogEntry` objects from its channel
208
+ and appends their string representation to the configured file,
209
+ applying log level and target type filters.
210
+ It runs as a background task.
211
+
212
+ Raises:
213
+ BaseException: If an unexpected error occurs during file writing.
214
+ """
215
+
216
+ try:
217
+ end_of_entry = "\n\n" + "=" * 100 + "\n\n"
218
+
219
+ async with await trio.open_file(self._file_path, "a+", encoding="utf-8") as file:
220
+ async for log_entry in self._receive_channel:
221
+ if self._log_level_filter(log_entry.level) and self._target_type_filter(log_entry.target_data.type_):
222
+ await file.write(log_entry.to_string() + end_of_entry)
223
+ except* trio_end_exceptions:
224
+ pass
225
+ except* BaseException as error:
226
+ log_exception(error)
227
+ finally:
228
+ if self._file_writing_stopped is not None:
229
+ self._file_writing_stopped.set()
230
+
231
+ async def run(self):
232
+ """Starts the target logger, setting up its receive channel and file writing task."""
233
+
234
+ try:
235
+ if not self._is_active:
236
+ self._file_writing_stopped = trio.Event()
237
+
238
+ if self._file_path is not None:
239
+ self._nursery_object.start_soon(self._write_file,)
240
+
241
+ self._is_active = True
242
+ except* trio_end_exceptions:
243
+ await self.close()
244
+ except* BaseException as error:
245
+ log_exception(error)
246
+ await self.close()
247
+
248
+
249
+ def build_target_logger(
250
+ target_data: "TargetData",
251
+ nursery_object: trio.Nursery,
252
+ logger_settings: LoggerSettings
253
+ ) -> tuple[trio.MemorySendChannel[LogEntry], TargetLogger]:
254
+ """
255
+ Builds and initializes a `TargetLogger` instance along with its send channel.
256
+
257
+ Args:
258
+ target_data ("TargetData"): The data for the target this logger will serve.
259
+ nursery_object (trio.Nursery): The Trio nursery to associate with the logger for background tasks.
260
+ logger_settings (LoggerSettings): The logger configuration settings.
261
+
262
+ Returns:
263
+ tuple[trio.MemorySendChannel[LogEntry], TargetLogger]: A tuple containing
264
+ the send channel for `LogEntry` objects and the initialized `TargetLogger` instance.
265
+ """
266
+
267
+ send_channel, receive_channel = trio.open_memory_channel(1000)
268
+ target_logger = TargetLogger(target_data, nursery_object, receive_channel, logger_settings)
269
+
270
+ return send_channel, target_logger
271
+
272
+
273
+ @dataclass
274
+ class LogLevelStats:
275
+ """
276
+ Dataclass to store statistics for a specific log level.
277
+
278
+ Attributes:
279
+ num_logs (int): The total number of logs recorded for this level.
280
+ last_log_time (datetime): The timestamp of the most recent log entry for this level.
281
+ """
282
+
283
+ num_logs: int
284
+ last_log_time: datetime
285
+
286
+ def to_json(self) -> dict[str, Any]:
287
+ """
288
+ Converts the statistics to a JSON-serializable dictionary.
289
+
290
+ Returns:
291
+ dict[str, Any]: A dictionary representation of the log level statistics.
292
+ """
293
+
294
+ return {
295
+ "num_logs": self.num_logs,
296
+ "last_log_time": self.last_log_time.isoformat()
297
+ }
298
+
299
+
300
+ @dataclass
301
+ class LoggerChannelStats:
302
+ """
303
+ Dataclass to store statistics for a specific logging channel (per target).
304
+
305
+ Attributes:
306
+ target_id (str): The unique ID of the target associated with this channel.
307
+ title (str): The title of the target (e.g., page title).
308
+ url (str): The URL of the target.
309
+ num_logs (int): The total number of log entries for this channel.
310
+ last_log_time (datetime): The timestamp of the most recent log entry for this channel.
311
+ log_level_stats (dict[str, LogLevelStats]): A dictionary mapping log levels to their specific statistics.
312
+ """
313
+
314
+ target_id: str
315
+ title: str
316
+ url: str
317
+ num_logs: int
318
+ last_log_time: datetime
319
+ log_level_stats: dict[str, LogLevelStats]
320
+
321
+ async def add_log(self, log_entry: LogEntry):
322
+ """
323
+ Updates the channel statistics based on a new log entry.
324
+
325
+ Args:
326
+ log_entry (LogEntry): The new log entry to incorporate into the statistics.
327
+ """
328
+
329
+ self.num_logs += 1
330
+ self.last_log_time = log_entry.timestamp
331
+
332
+ if log_entry.level not in self.log_level_stats:
333
+ self.log_level_stats[log_entry.level] = LogLevelStats(num_logs=1, last_log_time=log_entry.timestamp)
334
+ else:
335
+ self.log_level_stats[log_entry.level].num_logs += 1
336
+ self.log_level_stats[log_entry.level].last_log_time = log_entry.timestamp
337
+
338
+ def to_json(self) -> dict[str, Any]:
339
+ """
340
+ Converts the channel statistics to a JSON-serializable dictionary.
341
+
342
+ Nested statistics objects are converted to their JSON representations.
343
+
344
+ Returns:
345
+ dict[str, Any]: A dictionary representation of the logger channel statistics.
346
+ """
347
+
348
+ return {
349
+ "target_id": self.target_id,
350
+ "title": self.title,
351
+ "url": self.url,
352
+ "num_logs": self.num_logs,
353
+ "last_log_time": self.last_log_time.isoformat(),
354
+ "log_level_stats": {
355
+ log_level: log_level_stats.to_json()
356
+ for log_level, log_level_stats in self.log_level_stats.items()
357
+ }
358
+ }
359
+
360
+
361
+ @dataclass
362
+ class TargetTypeStats:
363
+ """
364
+ Dataclass to store statistics for a specific target type.
365
+
366
+ Attributes:
367
+ num_targets (int): The count of targets of this type.
368
+ """
369
+
370
+ num_targets: int
371
+
372
+ def to_json(self) -> dict[str, Any]:
373
+ """
374
+ Converts the statistics to a JSON-serializable dictionary.
375
+
376
+ Returns:
377
+ dict[str, Any]: A dictionary representation of the target type statistics.
378
+ """
379
+
380
+ return {"num_targets": self.num_targets}
381
+
382
+
383
+ @dataclass(frozen=True)
384
+ class MainLogEntry:
385
+ """
386
+ Represents a summary log entry for the entire logging system.
387
+
388
+ Attributes:
389
+ num_channels (int): The total number of active logging channels (targets).
390
+ targets_types_stats (dict[str, TargetTypeStats]): Statistics grouped by target type.
391
+ num_logs (int): The total number of log entries across all channels.
392
+ log_level_stats (dict[str, LogLevelStats]): Overall statistics for each log level.
393
+ channels_stats (Sequence[LoggerChannelStats]): A list of statistics for each active logging channel.
394
+ """
395
+
396
+ num_channels: int
397
+ targets_types_stats: dict[str, TargetTypeStats]
398
+ num_logs: int
399
+ log_level_stats: dict[str, LogLevelStats]
400
+ channels_stats: Sequence[LoggerChannelStats]
401
+
402
+ def to_json(self) -> dict[str, Any]:
403
+ """
404
+ Converts the main log entry to a JSON-serializable dictionary.
405
+
406
+ Nested statistics objects are converted to their JSON representations.
407
+
408
+ Returns:
409
+ dict[str, Any]: A dictionary representation of the main log entry.
410
+ """
411
+
412
+ return {
413
+ "num_channels": self.num_channels,
414
+ "targets_types_stats": {
415
+ type_: target_type_stats.to_json()
416
+ for type_, target_type_stats in self.targets_types_stats.items()
417
+ },
418
+ "num_logs": self.num_logs,
419
+ "log_level_stats": {
420
+ log_level: log_level_stats.to_json()
421
+ for log_level, log_level_stats in self.log_level_stats.items()
422
+ },
423
+ "channels_stats": [channel_stats.to_json() for channel_stats in self.channels_stats]
424
+ }
425
+
426
+ def to_string(self) -> str:
427
+ """
428
+ Converts the main log entry to a human-readable string format.
429
+
430
+ Returns:
431
+ str: A multi-line string representation of the main log entry.
432
+ """
433
+
434
+ return "\n\n".join(
435
+ f"{key}: {json.dumps(value, indent=4, ensure_ascii=False)}"
436
+ for key, value in self.to_json().items()
437
+ )
438
+
439
+
440
+ class MainLogger:
441
+ """
442
+ Manages the main log file, summarizing overall logging activity.
443
+
444
+ This logger is responsible for writing aggregated statistics about all active
445
+ logging channels and target types to a designated file.
446
+
447
+ Attributes:
448
+ _nursery_object (trio.Nursery): The Trio nursery for managing concurrent tasks.
449
+ _receive_channel (trio.MemoryReceiveChannel[MainLogEntry]): The receive channel for main log entries.
450
+ _file_writing_stopped (Optional[trio.Event]): An event set when the file writing task stops.
451
+ _is_active (bool): Flag indicating if the main logger is active.
452
+ _file_path (Optional[Path]): The path to the main log file.
453
+ """
454
+
455
+ def __init__(
456
+ self,
457
+ logger_settings: LoggerSettings,
458
+ nursery_object: trio.Nursery,
459
+ receive_channel: trio.MemoryReceiveChannel[MainLogEntry]
460
+ ):
461
+ """
462
+ Initializes the MainLogger.
463
+
464
+ Args:
465
+ logger_settings (LoggerSettings): The settings for logging, including log directory.
466
+ nursery_object (trio.Nursery): The Trio nursery to spawn background tasks.
467
+ receive_channel (trio.MemoryReceiveChannel[MainLogEntry]): The channel from which main log entries are received.
468
+ """
469
+
470
+ self._nursery_object = nursery_object
471
+ self._receive_channel = receive_channel
472
+ self._file_writing_stopped: Optional[trio.Event] = None
473
+ self._is_active = False
474
+
475
+ if logger_settings.log_dir_path is None:
476
+ self._file_path = None
477
+ else:
478
+ self._file_path = logger_settings.log_dir_path.joinpath("__MAIN__.txt")
479
+
480
+ async def close(self):
481
+ """
482
+ Closes the main logger, including its receive channel.
483
+ """
484
+
485
+ if self._receive_channel is not None:
486
+ await self._receive_channel.aclose()
487
+ self._receive_channel = None
488
+
489
+ if self._file_writing_stopped is not None:
490
+ await self._file_writing_stopped.wait()
491
+
492
+ self._is_active = False
493
+
494
+ async def _write_file(self):
495
+ """
496
+ Asynchronously writes main log entries to the file.
497
+
498
+ This method continuously receives `MainLogEntry` objects from its channel
499
+ and overwrites the configured file with their string representation.
500
+ It runs as a background task.
501
+
502
+ Raises:
503
+ BaseException: If an unexpected error occurs during file writing.
504
+ """
505
+
506
+ try:
507
+ async with await trio.open_file(self._file_path, "w+", encoding="utf-8") as file:
508
+ async for log_entry in self._receive_channel:
509
+ await file.write(log_entry.to_string())
510
+ await file.seek(0)
511
+ except* trio_end_exceptions:
512
+ pass
513
+ except* BaseException as error:
514
+ log_exception(error)
515
+ finally:
516
+ if self._file_writing_stopped is not None:
517
+ self._file_writing_stopped.set()
518
+
519
+ async def run(self):
520
+ """
521
+ Starts the main logger, setting up its receive channel and file writing task.
522
+
523
+ Raises:
524
+ BaseException: If an error occurs during setup or if the logger fails to activate.
525
+ """
526
+
527
+ try:
528
+ if not self._is_active:
529
+ self._file_writing_stopped = trio.Event()
530
+
531
+ if self._file_path is not None:
532
+ self._nursery_object.start_soon(self._write_file,)
533
+
534
+ self._is_active = True
535
+ except* trio_end_exceptions:
536
+ await self.close()
537
+ except* BaseException as error:
538
+ log_exception(error)
539
+ await self.close()
540
+
541
+
542
+ def build_main_logger(nursery_object: trio.Nursery, logger_settings: LoggerSettings) -> tuple[trio.MemorySendChannel[MainLogEntry], MainLogger]:
543
+ """
544
+ Builds and initializes a `MainLogger` instance along with its send channel.
545
+
546
+ Args:
547
+ nursery_object (trio.Nursery): The Trio nursery to associate with the logger for background tasks.
548
+ logger_settings (LoggerSettings): The logger configuration settings.
549
+
550
+ Returns:
551
+ tuple[trio.MemorySendChannel[MainLogEntry], MainLogger]: A tuple containing
552
+ the send channel for `MainLogEntry` objects and the initialized `MainLogger` instance.
553
+ """
554
+
555
+ send_channel, receive_channel = trio.open_memory_channel(1000)
556
+ target_logger = MainLogger(logger_settings, nursery_object, receive_channel)
557
+
558
+ return send_channel, target_logger