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/karton.py
CHANGED
@@ -137,7 +137,7 @@ class Consumer(KartonServiceBase):
|
|
137
137
|
)
|
138
138
|
if self.task_timeout is None:
|
139
139
|
self.task_timeout = self.config.getint("karton", "task_timeout")
|
140
|
-
|
140
|
+
|
141
141
|
self._pre_hooks: List[Tuple[Optional[str], Callable[[Task], None]]] = []
|
142
142
|
self._post_hooks: List[
|
143
143
|
Tuple[
|
@@ -170,19 +170,18 @@ class Consumer(KartonServiceBase):
|
|
170
170
|
"""
|
171
171
|
|
172
172
|
self.current_task = task
|
173
|
-
self.log_handler.set_task(self.current_task)
|
174
173
|
|
175
|
-
if not
|
174
|
+
if not task.matches_filters(self.filters):
|
176
175
|
self.log.info("Task rejected because binds are no longer valid.")
|
177
|
-
self.backend.set_task_status(
|
176
|
+
self.backend.set_task_status(task, TaskState.FINISHED)
|
178
177
|
# Task rejected: end of processing
|
179
178
|
return
|
180
179
|
|
181
180
|
exception_str = None
|
182
181
|
|
183
182
|
try:
|
184
|
-
self.log.info("Received new task - %s",
|
185
|
-
self.backend.set_task_status(
|
183
|
+
self.log.info("Received new task - %s", task.uid)
|
184
|
+
self.backend.set_task_status(task, TaskState.STARTED)
|
186
185
|
|
187
186
|
self._run_pre_hooks()
|
188
187
|
|
@@ -190,22 +189,22 @@ class Consumer(KartonServiceBase):
|
|
190
189
|
try:
|
191
190
|
if self.task_timeout:
|
192
191
|
with timeout(self.task_timeout):
|
193
|
-
self.process(
|
192
|
+
self.process(task)
|
194
193
|
else:
|
195
|
-
self.process(
|
194
|
+
self.process(task)
|
196
195
|
except (Exception, TaskTimeoutError) as exc:
|
197
196
|
saved_exception = exc
|
198
197
|
raise
|
199
198
|
finally:
|
200
199
|
self._run_post_hooks(saved_exception)
|
201
200
|
|
202
|
-
self.log.info("Task done - %s",
|
201
|
+
self.log.info("Task done - %s", task.uid)
|
203
202
|
except (Exception, TaskTimeoutError):
|
204
203
|
exc_info = sys.exc_info()
|
205
204
|
exception_str = traceback.format_exception(*exc_info)
|
206
205
|
|
207
206
|
self.backend.increment_metrics(KartonMetrics.TASK_CRASHED, self.identity)
|
208
|
-
self.log.exception("Failed to process task - %s",
|
207
|
+
self.log.exception("Failed to process task - %s", task.uid)
|
209
208
|
finally:
|
210
209
|
self.backend.increment_metrics(KartonMetrics.TASK_CONSUMED, self.identity)
|
211
210
|
|
@@ -215,9 +214,10 @@ class Consumer(KartonServiceBase):
|
|
215
214
|
# if an exception was caught while processing
|
216
215
|
if exception_str is not None:
|
217
216
|
task_state = TaskState.CRASHED
|
218
|
-
|
217
|
+
task.error = exception_str
|
219
218
|
|
220
|
-
self.backend.set_task_status(
|
219
|
+
self.backend.set_task_status(task, task_state)
|
220
|
+
self.current_task = None
|
221
221
|
|
222
222
|
@property
|
223
223
|
def _bind(self) -> KartonBind:
|
@@ -228,6 +228,7 @@ class Consumer(KartonServiceBase):
|
|
228
228
|
filters=self.filters,
|
229
229
|
persistent=self.persistent,
|
230
230
|
service_version=self.__class__.version,
|
231
|
+
is_async=False,
|
231
232
|
)
|
232
233
|
|
233
234
|
@classmethod
|
karton/core/logger.py
CHANGED
@@ -2,30 +2,32 @@ import logging
|
|
2
2
|
import platform
|
3
3
|
import traceback
|
4
4
|
import warnings
|
5
|
-
from typing import
|
5
|
+
from typing import Any, Callable, Dict
|
6
6
|
|
7
7
|
from .backend import KartonBackend
|
8
|
-
from .task import
|
8
|
+
from .task import get_current_task
|
9
9
|
|
10
10
|
HOSTNAME = platform.node()
|
11
11
|
|
12
12
|
|
13
|
-
class
|
13
|
+
class TaskContextFilter(logging.Filter):
|
14
14
|
"""
|
15
|
-
|
15
|
+
This is a filter which injects information about current task ID to the log.
|
16
16
|
"""
|
17
17
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
19
|
+
current_task = get_current_task()
|
20
|
+
if current_task is not None:
|
21
|
+
record.task_id = current_task.task_uid
|
22
|
+
else:
|
23
|
+
record.task_id = "(no task)"
|
24
|
+
return True
|
24
25
|
|
25
|
-
def set_task(self, task: Task) -> None:
|
26
|
-
self.task = task
|
27
26
|
|
28
|
-
|
27
|
+
class LogLineFormatterMixin:
|
28
|
+
format: Callable[[logging.LogRecord], str]
|
29
|
+
|
30
|
+
def prepare_log_line(self, record: logging.LogRecord) -> Dict[str, Any]:
|
29
31
|
ignore_fields = [
|
30
32
|
"args",
|
31
33
|
"asctime",
|
@@ -54,11 +56,27 @@ class KartonLogHandler(logging.Handler):
|
|
54
56
|
log_line["type"] = "log"
|
55
57
|
log_line["message"] = self.format(record)
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
+
current_task = get_current_task()
|
60
|
+
if current_task is not None:
|
61
|
+
log_line["task"] = current_task.serialize()
|
59
62
|
|
60
63
|
log_line["hostname"] = HOSTNAME
|
64
|
+
return log_line
|
65
|
+
|
66
|
+
|
67
|
+
class KartonLogHandler(logging.Handler, LogLineFormatterMixin):
|
68
|
+
"""
|
69
|
+
logging.Handler that passes logs to the Karton backend.
|
70
|
+
"""
|
71
|
+
|
72
|
+
def __init__(self, backend: KartonBackend, channel: str) -> None:
|
73
|
+
logging.Handler.__init__(self)
|
74
|
+
self.backend = backend
|
75
|
+
self.is_consumer_active: bool = True
|
76
|
+
self.channel: str = channel
|
61
77
|
|
78
|
+
def emit(self, record: logging.LogRecord) -> None:
|
79
|
+
log_line = self.prepare_log_line(record)
|
62
80
|
log_consumed = self.backend.produce_log(
|
63
81
|
log_line, logger_name=self.channel, level=record.levelname
|
64
82
|
)
|
karton/core/resource.py
CHANGED
@@ -150,34 +150,7 @@ class ResourceBase(object):
|
|
150
150
|
}
|
151
151
|
|
152
152
|
|
153
|
-
class
|
154
|
-
"""
|
155
|
-
Represents local resource with arbitrary binary data e.g. file contents.
|
156
|
-
|
157
|
-
Local resources will be uploaded to object hub (S3) during
|
158
|
-
task dispatching.
|
159
|
-
|
160
|
-
.. code-block:: python
|
161
|
-
|
162
|
-
# Creating resource from bytes
|
163
|
-
sample = Resource("original_name.exe", content=b"X5O!P%@AP[4\\
|
164
|
-
PZX54(P^)7CC)7}$EICAR-STANDARD-ANT...")
|
165
|
-
|
166
|
-
# Creating resource from path
|
167
|
-
sample = Resource("original_name.exe", path="sample/original_name.exe")
|
168
|
-
|
169
|
-
:param name: Name of the resource (e.g. name of file)
|
170
|
-
:param content: Resource content
|
171
|
-
:param path: Path of file with resource content
|
172
|
-
:param bucket: Alternative S3 bucket for resource
|
173
|
-
:param metadata: Resource metadata
|
174
|
-
:param uid: Alternative S3 resource id
|
175
|
-
:param sha256: Resource sha256 hash
|
176
|
-
:param fd: Seekable file descriptor
|
177
|
-
:param _flags: Resource flags
|
178
|
-
:param _close_fd: Close file descriptor after upload (default: False)
|
179
|
-
"""
|
180
|
-
|
153
|
+
class LocalResourceBase(ResourceBase):
|
181
154
|
def __init__(
|
182
155
|
self,
|
183
156
|
name: str,
|
@@ -194,7 +167,7 @@ class LocalResource(ResourceBase):
|
|
194
167
|
if len(list(filter(None, [path, content, fd]))) != 1:
|
195
168
|
raise ValueError("You must exclusively provide a path, content or fd")
|
196
169
|
|
197
|
-
super(
|
170
|
+
super().__init__(
|
198
171
|
name,
|
199
172
|
content=content,
|
200
173
|
path=path,
|
@@ -247,7 +220,7 @@ class LocalResource(ResourceBase):
|
|
247
220
|
bucket: Optional[str] = None,
|
248
221
|
metadata: Optional[Dict[str, Any]] = None,
|
249
222
|
uid: Optional[str] = None,
|
250
|
-
) -> "
|
223
|
+
) -> "LocalResourceBase":
|
251
224
|
"""
|
252
225
|
Resource extension, allowing to pass whole directory as a zipped resource.
|
253
226
|
|
@@ -305,6 +278,35 @@ class LocalResource(ResourceBase):
|
|
305
278
|
_close_fd=True,
|
306
279
|
)
|
307
280
|
|
281
|
+
|
282
|
+
class LocalResource(LocalResourceBase):
|
283
|
+
"""
|
284
|
+
Represents local resource with arbitrary binary data e.g. file contents.
|
285
|
+
|
286
|
+
Local resources will be uploaded to object hub (S3) during
|
287
|
+
task dispatching.
|
288
|
+
|
289
|
+
.. code-block:: python
|
290
|
+
|
291
|
+
# Creating resource from bytes
|
292
|
+
sample = Resource("original_name.exe", content=b"X5O!P%@AP[4\\
|
293
|
+
PZX54(P^)7CC)7}$EICAR-STANDARD-ANT...")
|
294
|
+
|
295
|
+
# Creating resource from path
|
296
|
+
sample = Resource("original_name.exe", path="sample/original_name.exe")
|
297
|
+
|
298
|
+
:param name: Name of the resource (e.g. name of file)
|
299
|
+
:param content: Resource content
|
300
|
+
:param path: Path of file with resource content
|
301
|
+
:param bucket: Alternative S3 bucket for resource
|
302
|
+
:param metadata: Resource metadata
|
303
|
+
:param uid: Alternative S3 resource id
|
304
|
+
:param sha256: Resource sha256 hash
|
305
|
+
:param fd: Seekable file descriptor
|
306
|
+
:param _flags: Resource flags
|
307
|
+
:param _close_fd: Close file descriptor after upload (default: False)
|
308
|
+
"""
|
309
|
+
|
308
310
|
def _upload(self, backend: "KartonBackend") -> None:
|
309
311
|
"""Internal function for uploading resources
|
310
312
|
|
karton/core/task.py
CHANGED
@@ -3,6 +3,7 @@ import json
|
|
3
3
|
import time
|
4
4
|
import uuid
|
5
5
|
import warnings
|
6
|
+
from contextvars import ContextVar
|
6
7
|
from typing import (
|
7
8
|
TYPE_CHECKING,
|
8
9
|
Any,
|
@@ -24,6 +25,16 @@ if TYPE_CHECKING:
|
|
24
25
|
|
25
26
|
import orjson
|
26
27
|
|
28
|
+
current_task: ContextVar[Optional["Task"]] = ContextVar("current_task")
|
29
|
+
|
30
|
+
|
31
|
+
def get_current_task() -> Optional["Task"]:
|
32
|
+
return current_task.get(None)
|
33
|
+
|
34
|
+
|
35
|
+
def set_current_task(task: Optional["Task"]):
|
36
|
+
current_task.set(task)
|
37
|
+
|
27
38
|
|
28
39
|
class TaskState(enum.Enum):
|
29
40
|
DECLARED = "Declared" # Task declared in TASKS_QUEUE
|
@@ -375,12 +386,15 @@ class Task(object):
|
|
375
386
|
data: Union[str, bytes],
|
376
387
|
backend: Optional["KartonBackend"] = None,
|
377
388
|
parse_resources: bool = True,
|
389
|
+
resource_unserializer: Optional[Callable[[Dict], Any]] = None,
|
378
390
|
) -> "Task":
|
379
391
|
"""
|
380
392
|
Unserialize Task instance from JSON string
|
381
393
|
|
382
394
|
:param data: JSON-serialized task
|
383
|
-
:param backend:
|
395
|
+
:param backend: |
|
396
|
+
Backend instance to be bound to RemoteResource objects.
|
397
|
+
Deprecated: pass resource_unserializer instead.
|
384
398
|
:param parse_resources: |
|
385
399
|
If set to False (default is True), method doesn't
|
386
400
|
deserialize '__karton_resource__' entries, which speeds up deserialization
|
@@ -388,6 +402,9 @@ class Task(object):
|
|
388
402
|
filtering based on status.
|
389
403
|
When resource deserialization is turned off, Task.unserialize will try
|
390
404
|
to use faster 3rd-party JSON parser (orjson).
|
405
|
+
:param resource_unserializer: |
|
406
|
+
Resource factory used for deserialization of __karton_resource__
|
407
|
+
dictionary values.
|
391
408
|
:return: Unserialized Task object
|
392
409
|
|
393
410
|
:meta private:
|
@@ -399,7 +416,12 @@ class Task(object):
|
|
399
416
|
RemoteResource object instances
|
400
417
|
"""
|
401
418
|
if isinstance(value, dict) and "__karton_resource__" in value:
|
402
|
-
|
419
|
+
if resource_unserializer is None:
|
420
|
+
return RemoteResource.from_dict(
|
421
|
+
value["__karton_resource__"], backend
|
422
|
+
)
|
423
|
+
else:
|
424
|
+
return resource_unserializer(value["__karton_resource__"])
|
403
425
|
return value
|
404
426
|
|
405
427
|
if not isinstance(data, str):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: karton-core
|
3
|
-
Version: 5.
|
3
|
+
Version: 5.8.0
|
4
4
|
Summary: Distributed malware analysis orchestration framework
|
5
5
|
Home-page: https://github.com/CERT-Polska/karton
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
@@ -9,7 +9,8 @@ Classifier: License :: OSI Approved :: BSD License
|
|
9
9
|
Requires-Python: >=3.8
|
10
10
|
Description-Content-Type: text/markdown
|
11
11
|
License-File: LICENSE
|
12
|
-
Requires-Dist:
|
12
|
+
Requires-Dist: aioboto3 ==14.3.0
|
13
|
+
Requires-Dist: boto3 <1.37.4,>=1.37.2
|
13
14
|
Requires-Dist: orjson
|
14
15
|
Requires-Dist: redis
|
15
16
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
karton_core-5.8.0-nspkg.pth,sha256=vHa-jm6pBTeInFrmnsHMg9AOeD88czzQy-6QCFbpRcM,539
|
2
|
+
karton/core/__init__.py,sha256=QuT0BWZyp799eY90tK3H1OD2hwuusqMJq8vQwpB3kG4,337
|
3
|
+
karton/core/__version__.py,sha256=duDTiv1rL8Ee9_jtzzhpq-j4xkkpVPkUh2Daa_Ou-xA,22
|
4
|
+
karton/core/backend.py,sha256=3tmjAM35jaoZr_5QqarcVH81LvKMEB63vyvMKCYXPCM,39934
|
5
|
+
karton/core/base.py,sha256=6PEQiEWHjMtJKRj0dfgEEpNhgSRoKOBYF1WBlrmyBp0,9064
|
6
|
+
karton/core/config.py,sha256=UTd0hLqYNUttfI7FYUDOJPaz-C3uj1Kw7xxataTG_OM,8234
|
7
|
+
karton/core/exceptions.py,sha256=8i9WVzi4PinNlX10Cb-lQQC35Hl-JB5R_UKXa9AUKoQ,153
|
8
|
+
karton/core/inspect.py,sha256=aIJQEOEkD5q2xLlV8nhxY5qL5zqcnprP-2DdP6ecKlE,6150
|
9
|
+
karton/core/karton.py,sha256=4CISOmUTfaEaCJUtbYxJSBMzydT27o3a-R14VBNpmr0,15269
|
10
|
+
karton/core/logger.py,sha256=UhYCoVARXaatvoJ2lO2mfBHeODOS7z8O-vqdeQhNmV4,2654
|
11
|
+
karton/core/main.py,sha256=ir1-dhn3vbwfh2YHiM6ZYfRBbjwLvJSz0d8tuK1mb_4,8310
|
12
|
+
karton/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
+
karton/core/query.py,sha256=sf24DweVlXfJuBbBD_ns2LXhOV-IBwuPG3jBfTJu77s,12063
|
14
|
+
karton/core/resource.py,sha256=GkU3JGSP1kOsAoblon4BWbQp31eZiDsCjaaDKanEokk,20872
|
15
|
+
karton/core/task.py,sha256=QvAsSUIsyYiRmkgxgugrYkHWH2gczrFDfw2V4izdt1E,19566
|
16
|
+
karton/core/test.py,sha256=cj6W4gNt0BpRjsYiiBt0hPE8dmRfUeIc8sSVkxB50cU,9123
|
17
|
+
karton/core/utils.py,sha256=sEVqGdVPyYswWuVn8wYXBQmln8Az826N_2HgC__pmW8,4090
|
18
|
+
karton/core/asyncio/__init__.py,sha256=ZgndeKzS3Yg2o8hebwFYJWlCRdW3ImdCOShK4EVmZ14,457
|
19
|
+
karton/core/asyncio/backend.py,sha256=GF0z9YxWvUKYkvnBatCDthX0M9Kwt9cRfVSWQq5bc9E,12751
|
20
|
+
karton/core/asyncio/base.py,sha256=YDNGyWzgVvt2TnfKvHYbJbcNJaQl95bdBq45YGEo-3Q,4246
|
21
|
+
karton/core/asyncio/karton.py,sha256=LhzMGuJsmXdTEa323gZted8KgVfHH6l0j0_tTqMh4Z4,12932
|
22
|
+
karton/core/asyncio/logger.py,sha256=BjkbuAeWylTmFjWv8-ckmOGf4nL2Tma96W0nIOc2vwk,1752
|
23
|
+
karton/core/asyncio/resource.py,sha256=86AYm7JeVjEYRNw--h02HIS9xFvgddhktmDUp0qvTO4,12517
|
24
|
+
karton/system/__init__.py,sha256=JF51OqRU_Y4c0unOulvmv1KzSHSq4ZpXU8ZsH4nefRM,63
|
25
|
+
karton/system/__main__.py,sha256=QJkwIlSwaPRdzwKlNmCAL41HtDAa73db9MZKWmOfxGM,56
|
26
|
+
karton/system/system.py,sha256=d_5hhLTthJdr_4gZEGQ6Y-kHvxeBqyQxjjx_wRs3xMA,17285
|
27
|
+
karton_core-5.8.0.dist-info/LICENSE,sha256=o8h7hYhn7BJC_-DmrfqWwLjaR_Gbe0TZOOQJuN2ca3I,1519
|
28
|
+
karton_core-5.8.0.dist-info/METADATA,sha256=26n1VrX-0ESRMG7fug5hqKxYT1URIa_NgypF5N1E2gg,6860
|
29
|
+
karton_core-5.8.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
30
|
+
karton_core-5.8.0.dist-info/entry_points.txt,sha256=OgLlsXy61GP6-Yob3oXqeJ2hlRU6LBLj33fr0NufKz0,98
|
31
|
+
karton_core-5.8.0.dist-info/namespace_packages.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
|
32
|
+
karton_core-5.8.0.dist-info/top_level.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
|
33
|
+
karton_core-5.8.0.dist-info/RECORD,,
|
@@ -1,27 +0,0 @@
|
|
1
|
-
karton_core-5.7.0-nspkg.pth,sha256=vHa-jm6pBTeInFrmnsHMg9AOeD88czzQy-6QCFbpRcM,539
|
2
|
-
karton/core/__init__.py,sha256=QuT0BWZyp799eY90tK3H1OD2hwuusqMJq8vQwpB3kG4,337
|
3
|
-
karton/core/__version__.py,sha256=QmHMXVnw5DVPfWzvN7FS1tOhDAesdxpM_aVOh9CMuSk,22
|
4
|
-
karton/core/backend.py,sha256=_IOjN9pWdSBsDnTMYvg-Fpm6Ag-uf2Jb9LWmrtZqVAU,38773
|
5
|
-
karton/core/base.py,sha256=lqVJvCHRMzvIOpS8SaWlOaSSJBEVkNQe0oClZC_GQYM,8225
|
6
|
-
karton/core/config.py,sha256=M3dB0XgnUO5VzUcGyQa7FyKzmdgmDml1MrzG6CxEuvE,8100
|
7
|
-
karton/core/exceptions.py,sha256=8i9WVzi4PinNlX10Cb-lQQC35Hl-JB5R_UKXa9AUKoQ,153
|
8
|
-
karton/core/inspect.py,sha256=aIJQEOEkD5q2xLlV8nhxY5qL5zqcnprP-2DdP6ecKlE,6150
|
9
|
-
karton/core/karton.py,sha256=l3joJWw8m23wlOErkcQmNFYhLFA5x2la6L0WopxJ7mk,15435
|
10
|
-
karton/core/logger.py,sha256=J3XAyG88U0cwYC9zR6E3QD1uJenrQh7zS9-HgxhqeAs,2040
|
11
|
-
karton/core/main.py,sha256=ir1-dhn3vbwfh2YHiM6ZYfRBbjwLvJSz0d8tuK1mb_4,8310
|
12
|
-
karton/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
karton/core/query.py,sha256=sf24DweVlXfJuBbBD_ns2LXhOV-IBwuPG3jBfTJu77s,12063
|
14
|
-
karton/core/resource.py,sha256=9kWXpMBRfudH0_whJfSSI27K3Gwv2u93CVa7p68Q5UM,20842
|
15
|
-
karton/core/task.py,sha256=gW1szMi5PN2Y06X-Ryo7cmEVluZv1r7W5tvmwIJiD94,18808
|
16
|
-
karton/core/test.py,sha256=cj6W4gNt0BpRjsYiiBt0hPE8dmRfUeIc8sSVkxB50cU,9123
|
17
|
-
karton/core/utils.py,sha256=sEVqGdVPyYswWuVn8wYXBQmln8Az826N_2HgC__pmW8,4090
|
18
|
-
karton/system/__init__.py,sha256=JF51OqRU_Y4c0unOulvmv1KzSHSq4ZpXU8ZsH4nefRM,63
|
19
|
-
karton/system/__main__.py,sha256=QJkwIlSwaPRdzwKlNmCAL41HtDAa73db9MZKWmOfxGM,56
|
20
|
-
karton/system/system.py,sha256=d_5hhLTthJdr_4gZEGQ6Y-kHvxeBqyQxjjx_wRs3xMA,17285
|
21
|
-
karton_core-5.7.0.dist-info/LICENSE,sha256=o8h7hYhn7BJC_-DmrfqWwLjaR_Gbe0TZOOQJuN2ca3I,1519
|
22
|
-
karton_core-5.7.0.dist-info/METADATA,sha256=MrmtycTaYsNB8v0LRyuLIHL2bV17n1Lt6e-ak4RfrH8,6818
|
23
|
-
karton_core-5.7.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
24
|
-
karton_core-5.7.0.dist-info/entry_points.txt,sha256=OgLlsXy61GP6-Yob3oXqeJ2hlRU6LBLj33fr0NufKz0,98
|
25
|
-
karton_core-5.7.0.dist-info/namespace_packages.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
|
26
|
-
karton_core-5.7.0.dist-info/top_level.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
|
27
|
-
karton_core-5.7.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|