1
1
#!/usr/bin/env python3
2
+
3
+ from __future__ import annotations
4
+
2
5
import logging
3
6
from datetime import date , datetime
4
7
from deprecated import deprecated # type: ignore
5
8
from json import JSONEncoder
6
9
from uuid import UUID
7
10
from abc import ABCMeta
8
11
from enum import Enum
9
- from typing import Union , Optional , Any , Dict , List , Set , Mapping
12
+ from typing import Any , Mapping
10
13
from collections .abc import MutableMapping
11
14
from functools import lru_cache
12
15
from pathlib import Path
13
16
14
17
try :
15
18
import orjson # type: ignore
16
- from orjson import loads , dumps # type: ignore
19
+ from orjson import loads , dumps
17
20
HAS_ORJSON = True
18
21
except ImportError :
19
22
from json import loads , dumps
30
33
describe_types = loads (f .read ())['result' ]
31
34
32
35
33
- class MISPFileCache ( object ) :
36
+ class MISPFileCache :
34
37
# cache up to 150 JSON structures in class attribute
35
38
36
39
@staticmethod
37
40
@lru_cache (maxsize = 150 )
38
- def _load_json (path : Path ) -> Optional [ dict ] :
41
+ def _load_json (path : Path ) -> dict | None :
39
42
if not path .exists ():
40
43
return None
41
44
with path .open ('rb' ) as f :
@@ -65,7 +68,7 @@ class Analysis(Enum):
65
68
completed = 2
66
69
67
70
68
- def _int_to_str (d : Dict [str , Any ]) -> Dict [str , Any ]:
71
+ def _int_to_str (d : dict [str , Any ]) -> dict [str , Any ]:
69
72
# transform all integer back to string
70
73
for k , v in d .items ():
71
74
if isinstance (v , dict ):
@@ -94,7 +97,7 @@ class AbstractMISP(MutableMapping, MISPFileCache, metaclass=ABCMeta):
94
97
__misp_objects_path = misp_objects_path
95
98
__describe_types = describe_types
96
99
97
- def __init__ (self , ** kwargs : Dict ):
100
+ def __init__ (self , ** kwargs : dict ):
98
101
"""Abstract class for all the MISP objects.
99
102
NOTE: Every method in every classes inheriting this one are doing
100
103
changes in memory and do not modify data on a remote MISP instance.
@@ -103,9 +106,9 @@ def __init__(self, **kwargs: Dict):
103
106
"""
104
107
super ().__init__ ()
105
108
self .__edited : bool = True # As we create a new object, we assume it is edited
106
- self .__not_jsonable : List [str ] = []
107
- self ._fields_for_feed : Set
108
- self .__self_defined_describe_types : Optional [ Dict ] = None
109
+ self .__not_jsonable : list [str ] = []
110
+ self ._fields_for_feed : set
111
+ self .__self_defined_describe_types : dict | None = None
109
112
self .uuid : str
110
113
111
114
if kwargs .get ('force_timestamps' ) is not None :
@@ -115,13 +118,13 @@ def __init__(self, **kwargs: Dict):
115
118
self .__force_timestamps = False
116
119
117
120
@property
118
- def describe_types (self ) -> Dict :
121
+ def describe_types (self ) -> dict :
119
122
if self .__self_defined_describe_types :
120
123
return self .__self_defined_describe_types
121
124
return self .__describe_types
122
125
123
126
@describe_types .setter
124
- def describe_types (self , describe_types : Dict ):
127
+ def describe_types (self , describe_types : dict ):
125
128
self .__self_defined_describe_types = describe_types
126
129
127
130
@property
@@ -133,7 +136,7 @@ def misp_objects_path(self) -> Path:
133
136
return self .__misp_objects_path
134
137
135
138
@misp_objects_path .setter
136
- def misp_objects_path (self , misp_objects_path : Union [ str , Path ] ):
139
+ def misp_objects_path (self , misp_objects_path : str | Path ):
137
140
if isinstance (misp_objects_path , str ):
138
141
misp_objects_path = Path (misp_objects_path )
139
142
self .__misp_objects_path = misp_objects_path
@@ -155,7 +158,7 @@ def update_not_jsonable(self, *args) -> None:
155
158
"""Add entries to the __not_jsonable list"""
156
159
self .__not_jsonable += args
157
160
158
- def set_not_jsonable (self , args : List [str ]) -> None :
161
+ def set_not_jsonable (self , args : list [str ]) -> None :
159
162
"""Set __not_jsonable to a new list"""
160
163
self .__not_jsonable = args
161
164
@@ -171,7 +174,7 @@ def from_json(self, json_string: str) -> None:
171
174
"""Load a JSON string"""
172
175
self .from_dict (** loads (json_string ))
173
176
174
- def to_dict (self , json_format : bool = False ) -> Dict :
177
+ def to_dict (self , json_format : bool = False ) -> dict :
175
178
"""Dump the class to a dictionary.
176
179
This method automatically removes the timestamp recursively in every object
177
180
that has been edited is order to let MISP update the event accordingly."""
@@ -213,15 +216,15 @@ def to_dict(self, json_format: bool = False) -> Dict:
213
216
to_return = _int_to_str (to_return )
214
217
return to_return
215
218
216
- def jsonable (self ) -> Dict :
219
+ def jsonable (self ) -> dict :
217
220
"""This method is used by the JSON encoder"""
218
221
return self .to_dict ()
219
222
220
- def _to_feed (self ) -> Dict :
223
+ def _to_feed (self ) -> dict :
221
224
if not hasattr (self , '_fields_for_feed' ) or not self ._fields_for_feed :
222
225
raise PyMISPError ('Unable to export in the feed format, _fields_for_feed is missing.' )
223
- if hasattr (self , '_set_default' ) and callable (self ._set_default ): # type: ignore
224
- self ._set_default () # type: ignore
226
+ if hasattr (self , '_set_default' ) and callable (self ._set_default ):
227
+ self ._set_default ()
225
228
to_return = {}
226
229
for field in sorted (self ._fields_for_feed ):
227
230
if getattr (self , field , None ) is not None :
@@ -235,11 +238,11 @@ def _to_feed(self) -> Dict:
235
238
if field in ['data' , 'first_seen' , 'last_seen' , 'deleted' ]:
236
239
# special fields
237
240
continue
238
- raise PyMISPError ('The field {} is required in {} when generating a feed.' . format ( field , self . __class__ . __name__ ) )
241
+ raise PyMISPError (f 'The field { field } is required in { self . __class__ . __name__ } when generating a feed.' )
239
242
to_return = _int_to_str (to_return )
240
243
return to_return
241
244
242
- def to_json (self , sort_keys : bool = False , indent : Optional [ int ] = None ) -> str :
245
+ def to_json (self , sort_keys : bool = False , indent : int | None = None ) -> str :
243
246
"""Dump recursively any class of type MISPAbstract to a json string"""
244
247
if HAS_ORJSON :
245
248
option = 0
@@ -320,14 +323,14 @@ def __setattr__(self, name: str, value: Any):
320
323
self .__edited = True
321
324
super ().__setattr__ (name , value )
322
325
323
- def _datetime_to_timestamp (self , d : Union [ int , float , str , datetime ] ) -> int :
326
+ def _datetime_to_timestamp (self , d : int | float | str | datetime ) -> int :
324
327
"""Convert a datetime object to a timestamp (int)"""
325
328
if isinstance (d , (int , float , str )):
326
329
# Assume we already have a timestamp
327
330
return int (d )
328
331
return int (d .timestamp ())
329
332
330
- def _add_tag (self , tag : Optional [ Union [ str , ' MISPTag' , Mapping ]] = None , ** kwargs ):
333
+ def _add_tag (self , tag : str | MISPTag | Mapping | None = None , ** kwargs ):
331
334
"""Add a tag to the attribute (by name or a MISPTag object)"""
332
335
if isinstance (tag , str ):
333
336
misp_tag = MISPTag ()
@@ -347,7 +350,7 @@ def _add_tag(self, tag: Optional[Union[str, 'MISPTag', Mapping]] = None, **kwarg
347
350
self .edited = True
348
351
return misp_tag
349
352
350
- def _set_tags (self , tags : List [ ' MISPTag' ]):
353
+ def _set_tags (self , tags : list [ MISPTag ]):
351
354
"""Set a list of prepared MISPTag."""
352
355
if all (isinstance (x , MISPTag ) for x in tags ):
353
356
self .Tag = tags
@@ -363,19 +366,19 @@ def __eq__(self, other) -> bool:
363
366
return False
364
367
365
368
def __repr__ (self ) -> str :
366
- return '<{self.__class__.__name__} - please define me>' . format ( self = self )
369
+ return f '<{ self .__class__ .__name__ } - please define me>'
367
370
368
371
369
372
class MISPTag (AbstractMISP ):
370
373
371
374
_fields_for_feed : set = {'name' , 'colour' , 'relationship_type' , 'local' }
372
375
373
- def __init__ (self , ** kwargs : Dict ):
376
+ def __init__ (self , ** kwargs : dict ):
374
377
super ().__init__ (** kwargs )
375
378
self .name : str
376
379
self .exportable : bool
377
380
self .local : bool
378
- self .relationship_type : Optional [ str ]
381
+ self .relationship_type : str | None
379
382
380
383
def from_dict (self , ** kwargs ):
381
384
if kwargs .get ('Tag' ):
@@ -390,7 +393,7 @@ def _set_default(self):
390
393
if not hasattr (self , 'local' ):
391
394
self .local = False
392
395
393
- def _to_feed (self , with_local : bool = True ) -> Dict :
396
+ def _to_feed (self , with_local : bool = True ) -> dict :
394
397
if hasattr (self , 'exportable' ) and not self .exportable :
395
398
return {}
396
399
if with_local is False and hasattr (self , 'local' ) and self .local :
@@ -404,11 +407,11 @@ def delete(self):
404
407
def __repr__ (self ) -> str :
405
408
if hasattr (self , 'name' ):
406
409
return '<{self.__class__.__name__}(name={self.name})>' .format (self = self )
407
- return '<{self.__class__.__name__}(NotInitialized)>' . format ( self = self )
410
+ return f '<{ self .__class__ .__name__ } (NotInitialized)>'
408
411
409
412
410
413
# UUID, datetime, date and Enum is serialized by ORJSON by default
411
- def pymisp_json_default (obj : Union [ AbstractMISP , datetime , date , Enum , UUID ] ) -> Union [ Dict , str ] :
414
+ def pymisp_json_default (obj : AbstractMISP | datetime | date | Enum | UUID ) -> dict | str :
412
415
if isinstance (obj , AbstractMISP ):
413
416
return obj .jsonable ()
414
417
elif isinstance (obj , (datetime , date )):
0 commit comments