karton-core 5.7.0__py3-none-any.whl → 5.8.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.
- karton/core/__version__.py +1 -1
- karton/core/asyncio/__init__.py +21 -0
- karton/core/asyncio/backend.py +370 -0
- karton/core/asyncio/base.py +133 -0
- karton/core/asyncio/karton.py +359 -0
- karton/core/asyncio/logger.py +57 -0
- karton/core/asyncio/resource.py +384 -0
- karton/core/backend.py +150 -109
- karton/core/base.py +119 -93
- karton/core/config.py +5 -0
- karton/core/karton.py +13 -12
- karton/core/logger.py +33 -15
- karton/core/resource.py +32 -30
- karton/core/task.py +24 -2
- {karton_core-5.7.0.dist-info → karton_core-5.8.0.dist-info}/METADATA +3 -2
- karton_core-5.8.0.dist-info/RECORD +33 -0
- karton_core-5.7.0.dist-info/RECORD +0 -27
- /karton_core-5.7.0-nspkg.pth → /karton_core-5.8.0-nspkg.pth +0 -0
- {karton_core-5.7.0.dist-info → karton_core-5.8.0.dist-info}/LICENSE +0 -0
- {karton_core-5.7.0.dist-info → karton_core-5.8.0.dist-info}/WHEEL +0 -0
- {karton_core-5.7.0.dist-info → karton_core-5.8.0.dist-info}/entry_points.txt +0 -0
- {karton_core-5.7.0.dist-info → karton_core-5.8.0.dist-info}/namespace_packages.txt +0 -0
- {karton_core-5.7.0.dist-info → karton_core-5.8.0.dist-info}/top_level.txt +0 -0
karton/core/backend.py
CHANGED
@@ -21,6 +21,7 @@ from urllib3.response import HTTPResponse
|
|
21
21
|
|
22
22
|
from .config import Config
|
23
23
|
from .exceptions import InvalidIdentityError
|
24
|
+
from .resource import RemoteResource
|
24
25
|
from .task import Task, TaskPriority, TaskState
|
25
26
|
from .utils import chunks, chunks_iter
|
26
27
|
|
@@ -33,12 +34,20 @@ KARTON_OUTPUTS_NAMESPACE = "karton.outputs"
|
|
33
34
|
|
34
35
|
KartonBind = namedtuple(
|
35
36
|
"KartonBind",
|
36
|
-
[
|
37
|
+
[
|
38
|
+
"identity",
|
39
|
+
"info",
|
40
|
+
"version",
|
41
|
+
"persistent",
|
42
|
+
"filters",
|
43
|
+
"service_version",
|
44
|
+
"is_async",
|
45
|
+
],
|
37
46
|
)
|
38
47
|
|
39
48
|
|
40
49
|
KartonOutputs = namedtuple("KartonOutputs", ["identity", "outputs"])
|
41
|
-
logger = logging.getLogger(
|
50
|
+
logger = logging.getLogger(__name__)
|
42
51
|
|
43
52
|
|
44
53
|
class KartonMetrics(enum.Enum):
|
@@ -103,13 +112,13 @@ class KartonServiceInfo:
|
|
103
112
|
)
|
104
113
|
|
105
114
|
|
106
|
-
class
|
115
|
+
class KartonBackendBase:
|
107
116
|
def __init__(
|
108
117
|
self,
|
109
118
|
config: Config,
|
110
119
|
identity: Optional[str] = None,
|
111
120
|
service_info: Optional[KartonServiceInfo] = None,
|
112
|
-
)
|
121
|
+
):
|
113
122
|
self.config = config
|
114
123
|
|
115
124
|
if identity is not None:
|
@@ -117,59 +126,6 @@ class KartonBackend:
|
|
117
126
|
self.identity = identity
|
118
127
|
|
119
128
|
self.service_info = service_info
|
120
|
-
self.redis = self.make_redis(
|
121
|
-
config, identity=identity, service_info=service_info
|
122
|
-
)
|
123
|
-
|
124
|
-
endpoint = config.get("s3", "address")
|
125
|
-
access_key = config.get("s3", "access_key")
|
126
|
-
secret_key = config.get("s3", "secret_key")
|
127
|
-
iam_auth = config.getboolean("s3", "iam_auth")
|
128
|
-
|
129
|
-
if not endpoint:
|
130
|
-
raise RuntimeError("Attempting to get S3 client without an endpoint set")
|
131
|
-
|
132
|
-
if access_key and secret_key and iam_auth:
|
133
|
-
logger.warning(
|
134
|
-
"Warning: iam is turned on and both S3 access key and secret key are"
|
135
|
-
" provided"
|
136
|
-
)
|
137
|
-
|
138
|
-
if iam_auth:
|
139
|
-
s3_client = self.iam_auth_s3(endpoint)
|
140
|
-
if s3_client:
|
141
|
-
self.s3 = s3_client
|
142
|
-
return
|
143
|
-
|
144
|
-
if access_key is None or secret_key is None:
|
145
|
-
raise RuntimeError(
|
146
|
-
"Attempting to get S3 client without an access_key/secret_key set"
|
147
|
-
)
|
148
|
-
|
149
|
-
self.s3 = boto3.client(
|
150
|
-
"s3",
|
151
|
-
endpoint_url=endpoint,
|
152
|
-
aws_access_key_id=access_key,
|
153
|
-
aws_secret_access_key=secret_key,
|
154
|
-
)
|
155
|
-
|
156
|
-
def iam_auth_s3(self, endpoint: str):
|
157
|
-
boto_session = get_session()
|
158
|
-
iam_providers = [
|
159
|
-
ContainerProvider(),
|
160
|
-
InstanceMetadataProvider(
|
161
|
-
iam_role_fetcher=InstanceMetadataFetcher(timeout=1000, num_attempts=2)
|
162
|
-
),
|
163
|
-
]
|
164
|
-
|
165
|
-
for provider in iam_providers:
|
166
|
-
creds = provider.load()
|
167
|
-
if creds:
|
168
|
-
boto_session._credentials = creds # type: ignore
|
169
|
-
return boto3.Session(botocore_session=boto_session).client(
|
170
|
-
"s3",
|
171
|
-
endpoint_url=endpoint,
|
172
|
-
)
|
173
129
|
|
174
130
|
@staticmethod
|
175
131
|
def _validate_identity(identity: str):
|
@@ -179,48 +135,6 @@ class KartonBackend:
|
|
179
135
|
f"Karton identity should not contain {disallowed_chars}"
|
180
136
|
)
|
181
137
|
|
182
|
-
@staticmethod
|
183
|
-
def make_redis(
|
184
|
-
config,
|
185
|
-
identity: Optional[str] = None,
|
186
|
-
service_info: Optional[KartonServiceInfo] = None,
|
187
|
-
) -> StrictRedis:
|
188
|
-
"""
|
189
|
-
Create and test a Redis connection.
|
190
|
-
|
191
|
-
:param config: The karton configuration
|
192
|
-
:param identity: Karton service identity
|
193
|
-
:param service_info: Additional service identity metadata
|
194
|
-
:return: Redis connection
|
195
|
-
"""
|
196
|
-
if service_info is not None:
|
197
|
-
client_name: Optional[str] = service_info.make_client_name()
|
198
|
-
else:
|
199
|
-
client_name = identity
|
200
|
-
|
201
|
-
redis_args = {
|
202
|
-
"host": config["redis"]["host"],
|
203
|
-
"port": config.getint("redis", "port", 6379),
|
204
|
-
"db": config.getint("redis", "db", 0),
|
205
|
-
"username": config.get("redis", "username"),
|
206
|
-
"password": config.get("redis", "password"),
|
207
|
-
"client_name": client_name,
|
208
|
-
# set socket_timeout to None if set to 0
|
209
|
-
"socket_timeout": config.getint("redis", "socket_timeout", 30) or None,
|
210
|
-
"decode_responses": True,
|
211
|
-
}
|
212
|
-
try:
|
213
|
-
redis = StrictRedis(**redis_args)
|
214
|
-
redis.ping()
|
215
|
-
except AuthenticationError:
|
216
|
-
# Maybe we've sent a wrong password.
|
217
|
-
# Or maybe the server is not (yet) password protected
|
218
|
-
# To make smooth transition possible, try to login insecurely
|
219
|
-
del redis_args["password"]
|
220
|
-
redis = StrictRedis(**redis_args)
|
221
|
-
redis.ping()
|
222
|
-
return redis
|
223
|
-
|
224
138
|
@property
|
225
139
|
def default_bucket_name(self) -> str:
|
226
140
|
bucket_name = self.config.get("s3", "bucket")
|
@@ -270,6 +184,7 @@ class KartonBackend:
|
|
270
184
|
"filters": bind.filters,
|
271
185
|
"persistent": bind.persistent,
|
272
186
|
"service_version": bind.service_version,
|
187
|
+
"is_async": bind.is_async,
|
273
188
|
},
|
274
189
|
sort_keys=True,
|
275
190
|
)
|
@@ -294,6 +209,7 @@ class KartonBackend:
|
|
294
209
|
persistent=not identity.endswith(".test"),
|
295
210
|
filters=bind,
|
296
211
|
service_version=None,
|
212
|
+
is_async=False,
|
297
213
|
)
|
298
214
|
return KartonBind(
|
299
215
|
identity=identity,
|
@@ -302,6 +218,7 @@ class KartonBackend:
|
|
302
218
|
persistent=bind["persistent"],
|
303
219
|
filters=bind["filters"],
|
304
220
|
service_version=bind.get("service_version"),
|
221
|
+
is_async=bind.get("is_async", False),
|
305
222
|
)
|
306
223
|
|
307
224
|
@staticmethod
|
@@ -316,6 +233,126 @@ class KartonBackend:
|
|
316
233
|
output = [json.loads(output_type) for output_type in output_data]
|
317
234
|
return KartonOutputs(identity=identity, outputs=output)
|
318
235
|
|
236
|
+
@staticmethod
|
237
|
+
def _log_channel(logger_name: Optional[str], level: Optional[str]) -> str:
|
238
|
+
return ".".join(
|
239
|
+
[KARTON_LOG_CHANNEL, (level or "*").lower(), logger_name or "*"]
|
240
|
+
)
|
241
|
+
|
242
|
+
|
243
|
+
class KartonBackend(KartonBackendBase):
|
244
|
+
def __init__(
|
245
|
+
self,
|
246
|
+
config: Config,
|
247
|
+
identity: Optional[str] = None,
|
248
|
+
service_info: Optional[KartonServiceInfo] = None,
|
249
|
+
) -> None:
|
250
|
+
super().__init__(config, identity, service_info)
|
251
|
+
self.redis = self.make_redis(
|
252
|
+
config, identity=identity, service_info=service_info
|
253
|
+
)
|
254
|
+
|
255
|
+
endpoint = config.get("s3", "address")
|
256
|
+
access_key = config.get("s3", "access_key")
|
257
|
+
secret_key = config.get("s3", "secret_key")
|
258
|
+
iam_auth = config.getboolean("s3", "iam_auth")
|
259
|
+
|
260
|
+
if not endpoint:
|
261
|
+
raise RuntimeError("Attempting to get S3 client without an endpoint set")
|
262
|
+
|
263
|
+
if access_key and secret_key and iam_auth:
|
264
|
+
logger.warning(
|
265
|
+
"Warning: iam is turned on and both S3 access key and secret key are"
|
266
|
+
" provided"
|
267
|
+
)
|
268
|
+
|
269
|
+
if iam_auth:
|
270
|
+
s3_client = self.iam_auth_s3(endpoint)
|
271
|
+
if s3_client:
|
272
|
+
self.s3 = s3_client
|
273
|
+
return
|
274
|
+
|
275
|
+
if access_key is None or secret_key is None:
|
276
|
+
raise RuntimeError(
|
277
|
+
"Attempting to get S3 client without an access_key/secret_key set"
|
278
|
+
)
|
279
|
+
|
280
|
+
self.s3 = boto3.client(
|
281
|
+
"s3",
|
282
|
+
endpoint_url=endpoint,
|
283
|
+
aws_access_key_id=access_key,
|
284
|
+
aws_secret_access_key=secret_key,
|
285
|
+
)
|
286
|
+
|
287
|
+
def iam_auth_s3(self, endpoint: str):
|
288
|
+
boto_session = get_session()
|
289
|
+
iam_providers = [
|
290
|
+
ContainerProvider(),
|
291
|
+
InstanceMetadataProvider(
|
292
|
+
iam_role_fetcher=InstanceMetadataFetcher(timeout=1000, num_attempts=2)
|
293
|
+
),
|
294
|
+
]
|
295
|
+
|
296
|
+
for provider in iam_providers:
|
297
|
+
creds = provider.load()
|
298
|
+
if creds:
|
299
|
+
boto_session._credentials = creds # type: ignore
|
300
|
+
return boto3.Session(botocore_session=boto_session).client(
|
301
|
+
"s3",
|
302
|
+
endpoint_url=endpoint,
|
303
|
+
)
|
304
|
+
|
305
|
+
@staticmethod
|
306
|
+
def make_redis(
|
307
|
+
config,
|
308
|
+
identity: Optional[str] = None,
|
309
|
+
service_info: Optional[KartonServiceInfo] = None,
|
310
|
+
) -> StrictRedis:
|
311
|
+
"""
|
312
|
+
Create and test a Redis connection.
|
313
|
+
|
314
|
+
:param config: The karton configuration
|
315
|
+
:param identity: Karton service identity
|
316
|
+
:param service_info: Additional service identity metadata
|
317
|
+
:return: Redis connection
|
318
|
+
"""
|
319
|
+
if service_info is not None:
|
320
|
+
client_name: Optional[str] = service_info.make_client_name()
|
321
|
+
else:
|
322
|
+
client_name = identity
|
323
|
+
|
324
|
+
redis_args = {
|
325
|
+
"host": config["redis"]["host"],
|
326
|
+
"port": config.getint("redis", "port", 6379),
|
327
|
+
"db": config.getint("redis", "db", 0),
|
328
|
+
"username": config.get("redis", "username"),
|
329
|
+
"password": config.get("redis", "password"),
|
330
|
+
"client_name": client_name,
|
331
|
+
# set socket_timeout to None if set to 0
|
332
|
+
"socket_timeout": config.getint("redis", "socket_timeout", 30) or None,
|
333
|
+
"decode_responses": True,
|
334
|
+
}
|
335
|
+
try:
|
336
|
+
redis = StrictRedis(**redis_args)
|
337
|
+
redis.ping()
|
338
|
+
except AuthenticationError:
|
339
|
+
# Maybe we've sent a wrong password.
|
340
|
+
# Or maybe the server is not (yet) password protected
|
341
|
+
# To make smooth transition possible, try to login insecurely
|
342
|
+
del redis_args["password"]
|
343
|
+
redis = StrictRedis(**redis_args)
|
344
|
+
redis.ping()
|
345
|
+
return redis
|
346
|
+
|
347
|
+
def unserialize_resource(self, resource_spec: Dict[str, Any]) -> RemoteResource:
|
348
|
+
"""
|
349
|
+
Unserializes resource into a RemoteResource object bound with current backend
|
350
|
+
|
351
|
+
:param resource_spec: Resource specification
|
352
|
+
:return: RemoteResource object
|
353
|
+
"""
|
354
|
+
return RemoteResource.from_dict(resource_spec, backend=self)
|
355
|
+
|
319
356
|
def get_bind(self, identity: str) -> KartonBind:
|
320
357
|
"""
|
321
358
|
Get bind object for given identity
|
@@ -426,7 +463,9 @@ class KartonBackend:
|
|
426
463
|
task_data = self.redis.get(f"{KARTON_TASK_NAMESPACE}:{task_uid}")
|
427
464
|
if not task_data:
|
428
465
|
return None
|
429
|
-
return Task.unserialize(
|
466
|
+
return Task.unserialize(
|
467
|
+
task_data, resource_unserializer=self.unserialize_resource
|
468
|
+
)
|
430
469
|
|
431
470
|
def get_tasks(
|
432
471
|
self,
|
@@ -449,7 +488,11 @@ class KartonBackend:
|
|
449
488
|
chunk_size,
|
450
489
|
)
|
451
490
|
return [
|
452
|
-
Task.unserialize(
|
491
|
+
Task.unserialize(
|
492
|
+
task_data,
|
493
|
+
parse_resources=parse_resources,
|
494
|
+
resource_unserializer=self.unserialize_resource,
|
495
|
+
)
|
453
496
|
for chunk in keys
|
454
497
|
for task_data in self.redis.mget(chunk)
|
455
498
|
if task_data is not None
|
@@ -464,7 +507,9 @@ class KartonBackend:
|
|
464
507
|
for chunk in chunks_iter(task_keys, chunk_size):
|
465
508
|
yield from (
|
466
509
|
Task.unserialize(
|
467
|
-
task_data,
|
510
|
+
task_data,
|
511
|
+
parse_resources=parse_resources,
|
512
|
+
resource_unserializer=self.unserialize_resource,
|
468
513
|
)
|
469
514
|
for task_data in self.redis.mget(chunk)
|
470
515
|
if task_data is not None
|
@@ -550,7 +595,9 @@ class KartonBackend:
|
|
550
595
|
lambda task: task.root_uid == root_uid,
|
551
596
|
(
|
552
597
|
Task.unserialize(
|
553
|
-
task_data,
|
598
|
+
task_data,
|
599
|
+
parse_resources=parse_resources,
|
600
|
+
resource_unserializer=self.unserialize_resource,
|
554
601
|
)
|
555
602
|
for task_data in self.redis.mget(chunk)
|
556
603
|
if task_data is not None
|
@@ -798,12 +845,6 @@ class KartonBackend:
|
|
798
845
|
p.execute()
|
799
846
|
return new_task
|
800
847
|
|
801
|
-
@staticmethod
|
802
|
-
def _log_channel(logger_name: Optional[str], level: Optional[str]) -> str:
|
803
|
-
return ".".join(
|
804
|
-
[KARTON_LOG_CHANNEL, (level or "*").lower(), logger_name or "*"]
|
805
|
-
)
|
806
|
-
|
807
848
|
def produce_log(
|
808
849
|
self,
|
809
850
|
log_record: Dict[str, Any],
|
karton/core/base.py
CHANGED
@@ -9,32 +9,16 @@ from typing import Optional, Union, cast
|
|
9
9
|
from .__version__ import __version__
|
10
10
|
from .backend import KartonBackend, KartonServiceInfo
|
11
11
|
from .config import Config
|
12
|
-
from .logger import KartonLogHandler
|
13
|
-
from .task import Task
|
12
|
+
from .logger import KartonLogHandler, TaskContextFilter
|
13
|
+
from .task import Task, get_current_task, set_current_task
|
14
14
|
from .utils import HardShutdownInterrupt, StrictClassMethod, graceful_killer
|
15
15
|
|
16
16
|
|
17
|
-
class
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
You can set an informative version information by setting the ``version`` class
|
22
|
-
attribute.
|
23
|
-
"""
|
24
|
-
|
25
|
-
#: Karton service identity
|
26
|
-
identity: str = ""
|
27
|
-
#: Karton service version
|
28
|
-
version: Optional[str] = None
|
29
|
-
#: Include extended service information for non-consumer services
|
30
|
-
with_service_info: bool = False
|
17
|
+
class ConfigMixin:
|
18
|
+
identity: Optional[str]
|
19
|
+
version: Optional[str]
|
31
20
|
|
32
|
-
def __init__(
|
33
|
-
self,
|
34
|
-
config: Optional[Config] = None,
|
35
|
-
identity: Optional[str] = None,
|
36
|
-
backend: Optional[KartonBackend] = None,
|
37
|
-
) -> None:
|
21
|
+
def __init__(self, config: Optional[Config] = None, identity: Optional[str] = None):
|
38
22
|
self.config = config or Config()
|
39
23
|
self.enable_publish_log = self.config.getboolean(
|
40
24
|
"logging", "enable_publish", True
|
@@ -50,25 +34,81 @@ class KartonBase(abc.ABC):
|
|
50
34
|
|
51
35
|
self.debug = self.config.getboolean("karton", "debug", False)
|
52
36
|
|
53
|
-
if self.debug:
|
37
|
+
if self.debug and self.identity:
|
54
38
|
self.identity += "-" + os.urandom(4).hex() + "-dev"
|
55
39
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
)
|
40
|
+
@classmethod
|
41
|
+
def args_description(cls) -> str:
|
42
|
+
"""Return short description for argument parser."""
|
43
|
+
if not cls.__doc__:
|
44
|
+
return ""
|
45
|
+
return textwrap.dedent(cls.__doc__).strip().splitlines()[0]
|
63
46
|
|
64
|
-
|
65
|
-
|
47
|
+
@classmethod
|
48
|
+
def args_parser(cls) -> argparse.ArgumentParser:
|
49
|
+
"""
|
50
|
+
Return ArgumentParser for main() class method.
|
51
|
+
|
52
|
+
This method should be overridden and call super methods
|
53
|
+
if you want to add more arguments.
|
54
|
+
"""
|
55
|
+
parser = argparse.ArgumentParser(description=cls.args_description())
|
56
|
+
parser.add_argument(
|
57
|
+
"--version", action="version", version=cast(str, cls.version)
|
58
|
+
)
|
59
|
+
parser.add_argument("--config-file", help="Alternative configuration path")
|
60
|
+
parser.add_argument(
|
61
|
+
"--identity", help="Alternative identity for Karton service"
|
66
62
|
)
|
63
|
+
parser.add_argument("--log-level", help="Logging level of Karton logger")
|
64
|
+
parser.add_argument(
|
65
|
+
"--debug", help="Enable debugging mode", action="store_true", default=None
|
66
|
+
)
|
67
|
+
return parser
|
68
|
+
|
69
|
+
@classmethod
|
70
|
+
def config_from_args(cls, config: Config, args: argparse.Namespace) -> None:
|
71
|
+
"""
|
72
|
+
Updates configuration with settings from arguments
|
67
73
|
|
68
|
-
|
69
|
-
|
74
|
+
This method should be overridden and call super methods
|
75
|
+
if you want to add more arguments.
|
76
|
+
"""
|
77
|
+
config.load_from_dict(
|
78
|
+
{
|
79
|
+
"karton": {
|
80
|
+
"identity": args.identity,
|
81
|
+
"debug": args.debug,
|
82
|
+
},
|
83
|
+
"logging": {"level": args.log_level},
|
84
|
+
}
|
70
85
|
)
|
71
|
-
|
86
|
+
|
87
|
+
@classmethod
|
88
|
+
def karton_from_args(cls, args: Optional[argparse.Namespace] = None):
|
89
|
+
"""
|
90
|
+
Returns Karton instance configured using configuration files
|
91
|
+
and provided arguments
|
92
|
+
|
93
|
+
Used by :py:meth:`KartonServiceBase.main` method
|
94
|
+
"""
|
95
|
+
if args is None:
|
96
|
+
parser = cls.args_parser()
|
97
|
+
args = parser.parse_args()
|
98
|
+
config = Config(path=args.config_file)
|
99
|
+
cls.config_from_args(config, args)
|
100
|
+
return cls(config=config)
|
101
|
+
|
102
|
+
|
103
|
+
class LoggingMixin:
|
104
|
+
config: Config
|
105
|
+
identity: Optional[str]
|
106
|
+
debug: bool
|
107
|
+
enable_publish_log: bool
|
108
|
+
|
109
|
+
def __init__(self, log_handler: logging.Handler, log_format: str) -> None:
|
110
|
+
self._log_handler = log_handler
|
111
|
+
self._log_format = log_format
|
72
112
|
|
73
113
|
def setup_logger(self, level: Optional[Union[str, int]] = None) -> None:
|
74
114
|
"""
|
@@ -94,6 +134,7 @@ class KartonBase(abc.ABC):
|
|
94
134
|
self._log_handler.setFormatter(logging.Formatter())
|
95
135
|
|
96
136
|
logger = logging.getLogger(self.identity)
|
137
|
+
logger.addFilter(TaskContextFilter())
|
97
138
|
|
98
139
|
if logger.handlers:
|
99
140
|
# If logger already have handlers set: clear them
|
@@ -106,16 +147,14 @@ class KartonBase(abc.ABC):
|
|
106
147
|
|
107
148
|
logger.setLevel(log_level)
|
108
149
|
stream_handler = logging.StreamHandler()
|
109
|
-
stream_handler.setFormatter(
|
110
|
-
logging.Formatter("[%(asctime)s][%(levelname)s] %(message)s")
|
111
|
-
)
|
150
|
+
stream_handler.setFormatter(logging.Formatter(self._log_format))
|
112
151
|
logger.addHandler(stream_handler)
|
113
152
|
|
114
153
|
if not self.debug and self.enable_publish_log:
|
115
154
|
logger.addHandler(self._log_handler)
|
116
155
|
|
117
156
|
@property
|
118
|
-
def log_handler(self) ->
|
157
|
+
def log_handler(self) -> logging.Handler:
|
119
158
|
"""
|
120
159
|
Return KartonLogHandler bound to this Karton service.
|
121
160
|
|
@@ -141,67 +180,54 @@ class KartonBase(abc.ABC):
|
|
141
180
|
"""
|
142
181
|
return logging.getLogger(self.identity)
|
143
182
|
|
144
|
-
@classmethod
|
145
|
-
def args_description(cls) -> str:
|
146
|
-
"""Return short description for argument parser."""
|
147
|
-
if not cls.__doc__:
|
148
|
-
return ""
|
149
|
-
return textwrap.dedent(cls.__doc__).strip().splitlines()[0]
|
150
183
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
Return ArgumentParser for main() class method.
|
184
|
+
class KartonBase(abc.ABC, ConfigMixin, LoggingMixin):
|
185
|
+
"""
|
186
|
+
Base class for all Karton services
|
155
187
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
parser = argparse.ArgumentParser(description=cls.args_description())
|
160
|
-
parser.add_argument(
|
161
|
-
"--version", action="version", version=cast(str, cls.version)
|
162
|
-
)
|
163
|
-
parser.add_argument("--config-file", help="Alternative configuration path")
|
164
|
-
parser.add_argument(
|
165
|
-
"--identity", help="Alternative identity for Karton service"
|
166
|
-
)
|
167
|
-
parser.add_argument("--log-level", help="Logging level of Karton logger")
|
168
|
-
parser.add_argument(
|
169
|
-
"--debug", help="Enable debugging mode", action="store_true", default=None
|
170
|
-
)
|
171
|
-
return parser
|
188
|
+
You can set an informative version information by setting the ``version`` class
|
189
|
+
attribute.
|
190
|
+
"""
|
172
191
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
192
|
+
#: Karton service identity
|
193
|
+
identity: str = ""
|
194
|
+
#: Karton service version
|
195
|
+
version: Optional[str] = None
|
196
|
+
#: Include extended service information for non-consumer services
|
197
|
+
with_service_info: bool = False
|
177
198
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
199
|
+
def __init__(
|
200
|
+
self,
|
201
|
+
config: Optional[Config] = None,
|
202
|
+
identity: Optional[str] = None,
|
203
|
+
backend: Optional[KartonBackend] = None,
|
204
|
+
) -> None:
|
205
|
+
ConfigMixin.__init__(self, config, identity)
|
206
|
+
|
207
|
+
self.service_info = None
|
208
|
+
if self.identity is not None and self.with_service_info:
|
209
|
+
self.service_info = KartonServiceInfo(
|
210
|
+
identity=self.identity,
|
211
|
+
karton_version=__version__,
|
212
|
+
service_version=self.version,
|
213
|
+
)
|
214
|
+
|
215
|
+
self.backend = backend or KartonBackend(
|
216
|
+
self.config, identity=self.identity, service_info=self.service_info
|
189
217
|
)
|
190
218
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
and provided arguments
|
219
|
+
log_handler = KartonLogHandler(backend=self.backend, channel=self.identity)
|
220
|
+
LoggingMixin.__init__(
|
221
|
+
self, log_handler, log_format="[%(asctime)s][%(levelname)s] %(message)s"
|
222
|
+
)
|
196
223
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
return cls(config=config)
|
224
|
+
@property
|
225
|
+
def current_task(self) -> Optional[Task]:
|
226
|
+
return get_current_task()
|
227
|
+
|
228
|
+
@current_task.setter
|
229
|
+
def current_task(self, task: Optional[Task]):
|
230
|
+
set_current_task(task)
|
205
231
|
|
206
232
|
|
207
233
|
class KartonServiceBase(KartonBase):
|
karton/core/config.py
CHANGED
@@ -116,6 +116,11 @@ class Config(object):
|
|
116
116
|
@overload
|
117
117
|
def getint(self, section_name: str, option_name: str) -> Optional[int]: ...
|
118
118
|
|
119
|
+
@overload
|
120
|
+
def getint(
|
121
|
+
self, section_name: str, option_name: str, fallback: Optional[int]
|
122
|
+
) -> Optional[int]: ...
|
123
|
+
|
119
124
|
def getint(
|
120
125
|
self, section_name: str, option_name: str, fallback: Optional[int] = None
|
121
126
|
) -> Optional[int]:
|