aiomisc 17.5.31__tar.gz → 17.6.3__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.
Files changed (69) hide show
  1. {aiomisc-17.5.31 → aiomisc-17.6.3}/PKG-INFO +3 -3
  2. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/aggregate.py +5 -12
  3. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/backoff.py +1 -1
  4. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/entrypoint.py +5 -2
  5. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/log.py +100 -71
  6. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/uvicorn.py +1 -1
  7. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/timeout.py +1 -1
  8. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/version.py +2 -2
  9. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/__init__.py +0 -1
  10. {aiomisc-17.5.31 → aiomisc-17.6.3}/pyproject.toml +1 -1
  11. {aiomisc-17.5.31 → aiomisc-17.6.3}/COPYING +0 -0
  12. {aiomisc-17.5.31 → aiomisc-17.6.3}/README.rst +0 -0
  13. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/__init__.py +0 -0
  14. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/_context_vars.py +0 -0
  15. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/circuit_breaker.py +0 -0
  16. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/compat.py +0 -0
  17. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/context.py +0 -0
  18. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/counters.py +0 -0
  19. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/cron.py +0 -0
  20. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/io.py +0 -0
  21. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/iterator_wrapper.py +0 -0
  22. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/periodic.py +0 -0
  23. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/plugins/__init__.py +0 -0
  24. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/plugins/__main__.py +0 -0
  25. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/pool.py +0 -0
  26. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/process_pool.py +0 -0
  27. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/py.typed +0 -0
  28. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/recurring.py +0 -0
  29. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/__init__.py +0 -0
  30. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/aiohttp.py +0 -0
  31. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/asgi.py +0 -0
  32. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/base.py +0 -0
  33. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/carbon.py +0 -0
  34. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/cron.py +0 -0
  35. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/dns/__init__.py +0 -0
  36. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/dns/records.py +0 -0
  37. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/dns/service.py +0 -0
  38. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/dns/store.py +0 -0
  39. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/dns/tree.py +0 -0
  40. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/dns/zone.py +0 -0
  41. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/grpc_server.py +0 -0
  42. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/periodic.py +0 -0
  43. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/process.py +0 -0
  44. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/profiler.py +0 -0
  45. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/raven.py +0 -0
  46. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/sdwatchdog.py +0 -0
  47. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/tcp.py +0 -0
  48. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/tls.py +0 -0
  49. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/tracer.py +0 -0
  50. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/service/udp.py +0 -0
  51. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/signal.py +0 -0
  52. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/thread_pool.py +0 -0
  53. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/utils.py +0 -0
  54. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc/worker_pool.py +0 -0
  55. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/enum.py +0 -0
  56. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/formatter/__init__.py +0 -0
  57. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/formatter/color.py +0 -0
  58. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/formatter/journald.py +0 -0
  59. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/formatter/json.py +0 -0
  60. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/formatter/rich.py +0 -0
  61. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_log/py.typed +0 -0
  62. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/__init__.py +0 -0
  63. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/__main__.py +0 -0
  64. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/forking.py +0 -0
  65. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/process.py +0 -0
  66. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/process_inner.py +0 -0
  67. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/protocol.py +0 -0
  68. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/py.typed +0 -0
  69. {aiomisc-17.5.31 → aiomisc-17.6.3}/aiomisc_worker/worker.py +0 -0
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: aiomisc
3
- Version: 17.5.31
3
+ Version: 17.6.3
4
4
  Summary: aiomisc - miscellaneous utils for asyncio
5
- Home-page: https://github.com/aiokitchen/aiomisc
6
5
  License: MIT
7
6
  Author: Dmitry Orlov
8
7
  Author-email: me@mosquito.su
@@ -70,6 +69,7 @@ Requires-Dist: uvicorn (>=0.27,<0.28) ; extra == "uvicorn"
70
69
  Requires-Dist: uvloop (>=0.19,<1) ; extra == "uvloop"
71
70
  Project-URL: Changelog, https://github.com/aiokitchen/aiomisc/blob/master/CHANGELOG.md
72
71
  Project-URL: Documentation, https://docs.aiomisc.com/
72
+ Project-URL: Homepage, https://github.com/aiokitchen/aiomisc
73
73
  Project-URL: Source, https://github.com/aiokitchen/aiomisc
74
74
  Project-URL: Tracker, https://github.com/aiokitchen/aiomisc/issues
75
75
  Description-Content-Type: text/x-rst
@@ -6,14 +6,7 @@ from asyncio import CancelledError, Event, Future, Lock, wait_for
6
6
  from dataclasses import dataclass
7
7
  from inspect import Parameter
8
8
  from typing import (
9
- Any,
10
- Callable,
11
- Coroutine,
12
- Generic,
13
- Iterable,
14
- List,
15
- Optional,
16
- Protocol,
9
+ Any, Callable, Coroutine, Generic, Iterable, List, Optional, Protocol,
17
10
  TypeVar,
18
11
  )
19
12
 
@@ -245,7 +238,7 @@ class Aggregator(AggregatorAsync[V, R], Generic[V, R]):
245
238
 
246
239
 
247
240
  def aggregate(
248
- leeway_ms: float, max_count: Optional[int] = None
241
+ leeway_ms: float, max_count: Optional[int] = None,
249
242
  ) -> Callable[[AggregateFunc[V, R]], Callable[[V], Coroutine[Any, Any, R]]]:
250
243
  """
251
244
  Parametric decorator that aggregates multiple
@@ -276,7 +269,7 @@ def aggregate(
276
269
  :return:
277
270
  """
278
271
  def decorator(
279
- func: AggregateFunc[V, R]
272
+ func: AggregateFunc[V, R],
280
273
  ) -> Callable[[V], Coroutine[Any, Any, R]]:
281
274
  aggregator = Aggregator(
282
275
  func, max_count=max_count, leeway_ms=leeway_ms,
@@ -289,7 +282,7 @@ def aggregate_async(
289
282
  leeway_ms: float, max_count: Optional[int] = None,
290
283
  ) -> Callable[
291
284
  [AggregateAsyncFunc[V, R]],
292
- Callable[[V], Coroutine[Any, Any, R]]
285
+ Callable[[V], Coroutine[Any, Any, R]],
293
286
  ]:
294
287
  """
295
288
  Same as ``aggregate``, but with ``func`` arguments of type ``Arg``
@@ -302,7 +295,7 @@ def aggregate_async(
302
295
  :return:
303
296
  """
304
297
  def decorator(
305
- func: AggregateAsyncFunc[V, R]
298
+ func: AggregateAsyncFunc[V, R],
306
299
  ) -> Callable[[V], Coroutine[Any, Any, R]]:
307
300
  aggregator = AggregatorAsync(
308
301
  func, max_count=max_count, leeway_ms=leeway_ms,
@@ -89,7 +89,7 @@ def asyncbackoff(
89
89
  exceptions += asyncio.TimeoutError,
90
90
 
91
91
  def decorator(
92
- func: Callable[P, Coroutine[Any, Any, T]]
92
+ func: Callable[P, Coroutine[Any, Any, T]],
93
93
  ) -> Callable[P, Coroutine[Any, Any, T]]:
94
94
  if attempt_timeout is not None:
95
95
  func = timeout(attempt_timeout)(func)
@@ -6,8 +6,8 @@ import sys
6
6
  import threading
7
7
  from concurrent.futures import Executor
8
8
  from typing import (
9
- Any, Callable, Coroutine, FrozenSet, MutableSet, Optional, Set, Tuple,
10
- TypeVar, Union,
9
+ Any, Callable, Coroutine, FrozenSet, Iterable, MutableSet, Optional, Set,
10
+ Tuple, TypeVar, Union,
11
11
  )
12
12
  from weakref import WeakSet
13
13
 
@@ -115,6 +115,7 @@ class Entrypoint:
115
115
  date_format=self.log_date_format,
116
116
  buffered=self.log_buffering,
117
117
  loop=self.loop,
118
+ handlers=self.log_handlers,
118
119
  buffer_size=self.log_buffer_size,
119
120
  flush_interval=self.log_flush_interval,
120
121
  )
@@ -146,6 +147,7 @@ class Entrypoint:
146
147
  log_date_format: Optional[str] = DEFAULT_LOG_DATE_FORMAT,
147
148
  log_flush_interval: float = DEFAULT_AIOMISC_LOG_FLUSH,
148
149
  log_config: bool = DEFAULT_AIOMISC_LOG_CONFIG,
150
+ log_handlers: Iterable[logging.Handler] = (),
149
151
  policy: asyncio.AbstractEventLoopPolicy = event_loop_policy,
150
152
  debug: bool = DEFAULT_AIOMISC_DEBUG,
151
153
  catch_signals: Optional[Tuple[int, ...]] = None,
@@ -196,6 +198,7 @@ class Entrypoint:
196
198
  self.log_date_format = log_date_format
197
199
  self.log_flush_interval = log_flush_interval
198
200
  self.log_format = log_format
201
+ self.log_handlers = tuple(log_handlers)
199
202
  self.log_level = log_level
200
203
  self.policy = policy
201
204
 
@@ -1,45 +1,82 @@
1
1
  import asyncio
2
- import atexit
3
- import logging
4
2
  import logging.handlers
5
- import time
3
+ import threading
6
4
  import traceback
5
+ import warnings
7
6
  from contextlib import suppress
8
- from functools import partial
7
+ from queue import Empty, Queue
9
8
  from socket import socket
10
9
  from typing import (
11
10
  Any, Callable, Dict, Iterable, List, Optional, Tuple, Type, Union,
12
11
  )
13
- from weakref import finalize
14
12
 
15
13
  import aiomisc_log
16
14
  from aiomisc_log.enum import LogFormat, LogLevel
17
15
 
18
- from .thread_pool import run_in_new_thread
19
-
20
-
21
- def _thread_flusher(
22
- handler: logging.handlers.MemoryHandler,
23
- flush_interval: Union[float, int],
24
- loop: asyncio.AbstractEventLoop,
25
- ) -> None:
26
- def has_no_target() -> bool:
27
- return True
28
-
29
- def has_target() -> bool:
30
- return bool(handler.target)
31
-
32
- is_target = has_no_target
33
-
34
- if isinstance(handler, logging.handlers.MemoryHandler):
35
- is_target = has_target
36
-
37
- while not loop.is_closed() and is_target():
38
- with suppress(Exception):
39
- if handler.buffer:
40
- handler.flush()
41
-
42
- time.sleep(flush_interval)
16
+ from .counters import Statistic
17
+
18
+
19
+ class ThreadedHandlerStatistic(Statistic):
20
+ threads: int
21
+ records: int
22
+ errors: int
23
+ flushes: int
24
+
25
+
26
+ class ThreadedHandler(logging.Handler):
27
+ def __init__(
28
+ self, target: logging.Handler, flush_interval: float = 0.1,
29
+ buffered: bool = True, queue_size: int = 0,
30
+ ):
31
+ super().__init__()
32
+ self._buffered = buffered
33
+ self._target = target
34
+ self._flush_interval = flush_interval
35
+ self._flush_event = threading.Event()
36
+ self._queue: Queue[Optional[logging.LogRecord]] = Queue(queue_size)
37
+ self._close_event = threading.Event()
38
+ self._thread = threading.Thread(target=self._in_thread, daemon=True)
39
+ self._statistic = ThreadedHandlerStatistic()
40
+
41
+ def start(self) -> None:
42
+ self._statistic.threads += 1
43
+ self._thread.start()
44
+
45
+ def close(self) -> None:
46
+ self._queue.put(None)
47
+ del self._queue
48
+ self.flush()
49
+ self._close_event.set()
50
+ super().close()
51
+
52
+ def flush(self) -> None:
53
+ self._statistic.flushes += 1
54
+ self._flush_event.set()
55
+
56
+ def emit(self, record: logging.LogRecord) -> None:
57
+ if self._buffered:
58
+ self._queue.put_nowait(record)
59
+ else:
60
+ self._queue.put(record)
61
+ self._statistic.records += 1
62
+
63
+ def _in_thread(self) -> None:
64
+ queue = self._queue
65
+ while not self._close_event.is_set():
66
+ self._flush_event.wait(self._flush_interval)
67
+ try:
68
+ self.acquire()
69
+ while True:
70
+ record = queue.get(timeout=self._flush_interval)
71
+ if record is None:
72
+ return
73
+ with suppress(Exception):
74
+ self._target.handle(record)
75
+ except Empty:
76
+ pass
77
+ finally:
78
+ self.release()
79
+ self._statistic.threads -= 1
43
80
 
44
81
 
45
82
  def suppressor(
@@ -54,27 +91,18 @@ def suppressor(
54
91
 
55
92
  def wrap_logging_handler(
56
93
  handler: logging.Handler,
57
- loop: Optional[asyncio.AbstractEventLoop] = None,
58
94
  buffer_size: int = 1024,
59
95
  flush_interval: Union[float, int] = 0.1,
96
+ loop: Optional[asyncio.AbstractEventLoop] = None,
60
97
  ) -> logging.Handler:
61
- buffered_handler = logging.handlers.MemoryHandler(
62
- buffer_size,
98
+ warnings.warn("wrap_logging_handler is deprecated", DeprecationWarning)
99
+ handler = ThreadedHandler(
63
100
  target=handler,
64
- flushLevel=logging.CRITICAL,
65
- )
66
-
67
- run_in_new_thread(
68
- _thread_flusher, args=(
69
- buffered_handler, flush_interval, loop,
70
- ), no_return=True, statistic_name="logger",
101
+ queue_size=buffer_size,
102
+ flush_interval=flush_interval,
71
103
  )
72
-
73
- at_exit_flusher = suppressor(handler.flush)
74
- atexit.register(at_exit_flusher)
75
- finalize(buffered_handler, partial(atexit.unregister, at_exit_flusher))
76
-
77
- return buffered_handler
104
+ handler.start()
105
+ return handler
78
106
 
79
107
 
80
108
  class UnhandledLoopHook(aiomisc_log.UnhandledHook):
@@ -109,7 +137,7 @@ class UnhandledLoopHook(aiomisc_log.UnhandledHook):
109
137
  protocol: Optional[asyncio.Protocol] = context.pop("protocol", None)
110
138
  transport: Optional[asyncio.Transport] = context.pop("transport", None)
111
139
  sock: Optional[socket] = context.pop("socket", None)
112
- source_traceback: List[traceback.FrameSummary] = (
140
+ source_tb: List[traceback.FrameSummary] = (
113
141
  context.pop("source_traceback", None) or []
114
142
  )
115
143
 
@@ -129,51 +157,52 @@ class UnhandledLoopHook(aiomisc_log.UnhandledHook):
129
157
 
130
158
  self._fill_transport_extra(transport, extra)
131
159
  self.logger.exception(message, exc_info=exception, extra=extra)
132
- if source_traceback:
133
- self.logger.error(
134
- "".join(traceback.format_list(source_traceback)),
135
- )
160
+ if source_tb:
161
+ self.logger.error("".join(traceback.format_list(source_tb)))
136
162
 
137
163
 
138
164
  def basic_config(
139
165
  level: Union[int, str] = LogLevel.default(),
140
166
  log_format: Union[str, LogFormat] = LogFormat.default(),
141
- buffered: bool = True, buffer_size: int = 1024,
167
+ buffered: bool = True,
168
+ buffer_size: int = 0,
142
169
  flush_interval: Union[int, float] = 0.2,
143
170
  loop: Optional[asyncio.AbstractEventLoop] = None,
144
171
  handlers: Iterable[logging.Handler] = (),
145
172
  **kwargs: Any,
146
173
  ) -> None:
147
- loop = loop or asyncio.get_event_loop()
148
174
  unhandled_hook = UnhandledLoopHook(logger_name="asyncio.unhandled")
149
175
 
150
- def wrap_handler(handler: logging.Handler) -> logging.Handler:
151
- nonlocal buffer_size, buffered, loop, unhandled_hook
176
+ if loop is None:
177
+ loop = asyncio.get_event_loop()
152
178
 
153
- unhandled_hook.add_handler(handler)
179
+ forever_task = asyncio.gather(
180
+ loop.create_future(), return_exceptions=True,
181
+ )
182
+ loop.set_exception_handler(unhandled_hook)
154
183
 
155
- if buffered:
156
- return wrap_logging_handler(
157
- handler=handler,
158
- buffer_size=buffer_size,
159
- flush_interval=flush_interval,
160
- loop=loop,
161
- )
162
- return handler
184
+ log_handlers = []
185
+
186
+ for user_handler in handlers:
187
+ handler = ThreadedHandler(
188
+ buffered=buffered,
189
+ flush_interval=flush_interval,
190
+ queue_size=buffer_size,
191
+ target=user_handler,
192
+ )
193
+ unhandled_hook.add_handler(handler)
194
+ forever_task.add_done_callback(lambda _: handler.close())
195
+ log_handlers.append(handler)
196
+ handler.start()
163
197
 
164
198
  aiomisc_log.basic_config(
165
- level=level,
166
- log_format=log_format,
167
- handler_wrapper=wrap_handler,
168
- handlers=handlers,
169
- **kwargs,
199
+ level=level, log_format=log_format, handlers=log_handlers, **kwargs,
170
200
  )
171
201
 
172
- loop.set_exception_handler(unhandled_hook)
173
-
174
202
 
175
203
  __all__ = (
176
204
  "LogFormat",
177
205
  "LogLevel",
178
206
  "basic_config",
207
+ "ThreadedHandler",
179
208
  )
@@ -117,7 +117,7 @@ class UvicornService(Service, abc.ABC):
117
117
  self.sock = config.bind_socket()
118
118
  self.server = Server(config)
119
119
  self.serve_task = asyncio.create_task(
120
- self.server.serve(sockets=[self.sock])
120
+ self.server.serve(sockets=[self.sock]),
121
121
  )
122
122
 
123
123
  async def stop(self, exception: Optional[Exception] = None) -> None:
@@ -16,7 +16,7 @@ Number = Union[int, float]
16
16
 
17
17
 
18
18
  def timeout(
19
- value: Number
19
+ value: Number,
20
20
  ) -> Callable[
21
21
  [Callable[P, Coroutine[Any, Any, T]]],
22
22
  Callable[P, Coroutine[Any, Any, T]],
@@ -2,5 +2,5 @@
2
2
  # BY: poem-plugins "git" plugin
3
3
  # NEVER EDIT THIS FILE MANUALLY
4
4
 
5
- version_info = (17, 5, 31)
6
- __version__ = "17.5.31"
5
+ version_info = (17, 6, 3)
6
+ __version__ = "17.6.3"
@@ -125,7 +125,6 @@ def basic_config(
125
125
  handlers: Iterable[logging.Handler] = (),
126
126
  **kwargs: Any,
127
127
  ) -> None:
128
-
129
128
  if isinstance(level, str):
130
129
  level = LogLevel[level]
131
130
 
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "aiomisc"
3
3
  # This is a dummy version which will be rewritten with poem-plugins
4
- version = "17.5.31"
4
+ version = "17.6.3"
5
5
  description = "aiomisc - miscellaneous utils for asyncio"
6
6
  authors = ["Dmitry Orlov <me@mosquito.su>"]
7
7
  readme = "README.rst"
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