CryptoDataHub 0.12.6__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 (70) hide show
  1. CryptoDataHub-0.12.6.dist-info/LICENSE.txt +373 -0
  2. CryptoDataHub-0.12.6.dist-info/METADATA +119 -0
  3. CryptoDataHub-0.12.6.dist-info/RECORD +70 -0
  4. CryptoDataHub-0.12.6.dist-info/WHEEL +5 -0
  5. CryptoDataHub-0.12.6.dist-info/top_level.txt +1 -0
  6. cryptodatahub/__init__.py +0 -0
  7. cryptodatahub/__setup__.py +10 -0
  8. cryptodatahub/common/__init__.py +0 -0
  9. cryptodatahub/common/algorithm.py +164 -0
  10. cryptodatahub/common/attack-named.json +74 -0
  11. cryptodatahub/common/attack-type.json +58 -0
  12. cryptodatahub/common/authentication.json +113 -0
  13. cryptodatahub/common/block-cipher-mode.json +75 -0
  14. cryptodatahub/common/block-cipher.json +474 -0
  15. cryptodatahub/common/certificate-transparency-log.json +2394 -0
  16. cryptodatahub/common/client.json +20 -0
  17. cryptodatahub/common/dhparam-well-known.json +1975 -0
  18. cryptodatahub/common/ecparam-well-known.json +1868 -0
  19. cryptodatahub/common/entity.json +269 -0
  20. cryptodatahub/common/entity.py +110 -0
  21. cryptodatahub/common/exception.py +28 -0
  22. cryptodatahub/common/grade.py +200 -0
  23. cryptodatahub/common/hash.json +273 -0
  24. cryptodatahub/common/key-exchange.json +140 -0
  25. cryptodatahub/common/key.py +571 -0
  26. cryptodatahub/common/mac.json +404 -0
  27. cryptodatahub/common/named-group.json +902 -0
  28. cryptodatahub/common/parameter.py +149 -0
  29. cryptodatahub/common/root-certificate.json +19240 -0
  30. cryptodatahub/common/server.json +56 -0
  31. cryptodatahub/common/signature.json +233 -0
  32. cryptodatahub/common/standard.json +57 -0
  33. cryptodatahub/common/stores.py +323 -0
  34. cryptodatahub/common/types.py +524 -0
  35. cryptodatahub/common/utils.py +112 -0
  36. cryptodatahub/common/vulnerability.json +2 -0
  37. cryptodatahub/dnsrec/__init__.py +0 -0
  38. cryptodatahub/dnsrec/algorithm.json +114 -0
  39. cryptodatahub/dnsrec/algorithm.py +87 -0
  40. cryptodatahub/dnsrec/digest-type.json +26 -0
  41. cryptodatahub/dnsrec/rr-type.json +805 -0
  42. cryptodatahub/ssh/__init__.py +0 -0
  43. cryptodatahub/ssh/algorithm.py +194 -0
  44. cryptodatahub/ssh/compression-algorithm.json +24 -0
  45. cryptodatahub/ssh/elliptic-curve-identifier.json +50 -0
  46. cryptodatahub/ssh/encryption-algorithm.json +587 -0
  47. cryptodatahub/ssh/host-key-algorithm.json +482 -0
  48. cryptodatahub/ssh/kex-algorithm.json +709 -0
  49. cryptodatahub/ssh/mac-algorithm.json +566 -0
  50. cryptodatahub/tls/__init__.py +0 -0
  51. cryptodatahub/tls/algorithm.py +324 -0
  52. cryptodatahub/tls/certificate-compression-algorithm.json +14 -0
  53. cryptodatahub/tls/cipher-kind.json +171 -0
  54. cryptodatahub/tls/cipher-suite-extension.json +10 -0
  55. cryptodatahub/tls/cipher-suite.json +5098 -0
  56. cryptodatahub/tls/client.json +4757 -0
  57. cryptodatahub/tls/client.py +220 -0
  58. cryptodatahub/tls/compression-method.json +20 -0
  59. cryptodatahub/tls/ec-point-format.json +20 -0
  60. cryptodatahub/tls/extension-type.json +282 -0
  61. cryptodatahub/tls/grease-one-byte.json +34 -0
  62. cryptodatahub/tls/grease-two-byte.json +66 -0
  63. cryptodatahub/tls/hash-and-signature-algorithm.json +266 -0
  64. cryptodatahub/tls/named-curve.json +292 -0
  65. cryptodatahub/tls/next-protocol-name.json +20 -0
  66. cryptodatahub/tls/protocol-name.json +71 -0
  67. cryptodatahub/tls/psk-key-exchange-mode.json +10 -0
  68. cryptodatahub/tls/token-binding-paramater.json +14 -0
  69. cryptodatahub/tls/version.json +154 -0
  70. cryptodatahub/tls/version.py +17 -0
@@ -0,0 +1,524 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import abc
4
+ import base64
5
+ import codecs
6
+ import collections
7
+ import datetime
8
+ import enum
9
+ import json
10
+ import inspect
11
+ import os
12
+ import re
13
+ import unicodedata
14
+
15
+ import dateutil.parser
16
+ import six
17
+ from six.moves import collections_abc
18
+
19
+ try:
20
+ import pathlib
21
+ except ImportError: # pragma: no cover
22
+ import pathlib2 as pathlib # pragma: no cover
23
+
24
+ import attr
25
+ import urllib3
26
+
27
+ from cryptodatahub.common.exception import InvalidValue
28
+
29
+
30
+ class _ConverterBase(object):
31
+ @abc.abstractmethod
32
+ def __call__(self, value):
33
+ raise NotImplementedError()
34
+
35
+ @abc.abstractmethod
36
+ def __repr__(self):
37
+ raise NotImplementedError()
38
+
39
+
40
+ @attr.s(repr=False, slots=True, hash=True)
41
+ class _DictObjectConverter(_ConverterBase):
42
+ object_type = attr.ib(validator=attr.validators.instance_of(type))
43
+
44
+ def __call__(self, value):
45
+ if value is None:
46
+ return None
47
+
48
+ try:
49
+ return self.object_type(**value)
50
+ except TypeError:
51
+ pass
52
+
53
+ return value
54
+
55
+ def __repr__(self):
56
+ return '<dict to object converter>'
57
+
58
+
59
+ def convert_dict_to_object(object_type):
60
+ return _DictObjectConverter(object_type)
61
+
62
+
63
+ @attr.s(repr=False, slots=True, hash=True)
64
+ class _ValueToObjectConverter(_ConverterBase):
65
+ object_type = attr.ib(validator=attr.validators.instance_of(type))
66
+ value_converter = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(_ConverterBase)))
67
+
68
+ def __call__(self, value):
69
+ if value is None:
70
+ return None
71
+
72
+ if isinstance(value, self.object_type):
73
+ return value
74
+
75
+ if self.value_converter is not None:
76
+ value = self.value_converter(value)
77
+
78
+ return self.object_type(value)
79
+
80
+ def __repr__(self):
81
+ return '<value to object converter>'
82
+
83
+
84
+ def convert_value_to_object(object_type, value_converter=None):
85
+ return _ValueToObjectConverter(object_type, value_converter)
86
+
87
+
88
+ @attr.s(repr=False, slots=True, hash=True)
89
+ class _EnumConverter(_ConverterBase):
90
+ enum_type = attr.ib(validator=attr.validators.instance_of(type))
91
+
92
+ def __call__(self, value):
93
+ if value is None:
94
+ return None
95
+
96
+ if not isinstance(value, six.string_types):
97
+ return value
98
+
99
+ try:
100
+ value = getattr(self.enum_type, value.upper())
101
+ except AttributeError:
102
+ pass
103
+
104
+ return value
105
+
106
+ def __repr__(self):
107
+ return '<enum converter>'
108
+
109
+
110
+ def convert_enum(enum_type):
111
+ return _EnumConverter(enum_type)
112
+
113
+
114
+ @attr.s(frozen=True)
115
+ class Base64Data(object):
116
+ value = attr.ib(validator=attr.validators.instance_of((bytes, bytearray)))
117
+
118
+ def _asdict(self):
119
+ return str(self)
120
+
121
+ def __str__(self):
122
+ return base64.b64encode(self.value).decode('ascii')
123
+
124
+
125
+ @attr.s(repr=False, slots=True, hash=True)
126
+ class _DateTimeConverter(_ConverterBase):
127
+ strptime_format = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(str)))
128
+
129
+ def __call__(self, date_time):
130
+ if date_time is None:
131
+ return None
132
+
133
+ if isinstance(date_time, datetime.datetime):
134
+ return date_time
135
+
136
+ try:
137
+ if self.strptime_format is None:
138
+ date_time = dateutil.parser.isoparse(date_time)
139
+ else:
140
+ date_time = datetime.datetime.strptime(date_time, self.strptime_format)
141
+ except (TypeError, ValueError):
142
+ pass
143
+
144
+ return date_time
145
+
146
+ def __repr__(self):
147
+ return '<datetime converter>'
148
+
149
+
150
+ def convert_datetime(strptime_format=None):
151
+ return _DateTimeConverter(strptime_format)
152
+
153
+
154
+ @attr.s(repr=False, slots=True, hash=True)
155
+ class _Base64DataConverter(_ConverterBase):
156
+ def __call__(self, value):
157
+ if value is None:
158
+ return None
159
+
160
+ if isinstance(value, bytearray) or (six.PY3 and isinstance(value, bytes)):
161
+ return Base64Data(value)
162
+
163
+ if not isinstance(value, six.string_types):
164
+ return value
165
+
166
+ try:
167
+ value = Base64Data(base64.b64decode(value))
168
+ except (ValueError, TypeError):
169
+ pass
170
+
171
+ return value
172
+
173
+ def __repr__(self):
174
+ return '<base64 data converter>'
175
+
176
+
177
+ def convert_base64_data():
178
+ return _Base64DataConverter()
179
+
180
+
181
+ @attr.s(repr=False, slots=True, hash=True)
182
+ class _BigNumberConverter(_ConverterBase):
183
+ def __call__(self, value):
184
+ if value is None:
185
+ return None
186
+
187
+ if not isinstance(value, collections_abc.Iterable):
188
+ return value
189
+
190
+ if not all(map(lambda big_number_part: isinstance(big_number_part, six.string_types), value)):
191
+ return value
192
+
193
+ try:
194
+ value = int(''.join(value).replace('0x', '').replace(' ', '').replace(',', ''), 16)
195
+ except ValueError:
196
+ pass
197
+
198
+ return value
199
+
200
+ def __repr__(self):
201
+ return '<big number converter>'
202
+
203
+
204
+ def convert_big_enum():
205
+ return _BigNumberConverter()
206
+
207
+
208
+ @attr.s(repr=False, slots=True, hash=True)
209
+ class _IterableConverter(_ConverterBase):
210
+ member_converter = attr.ib(validator=attr.validators.instance_of(collections_abc.Callable))
211
+
212
+ def __call__(self, iterable):
213
+ if iterable is None:
214
+ return None
215
+
216
+ try:
217
+ for idx, member in enumerate(iterable):
218
+ iterable[idx] = self.member_converter(member)
219
+ except (TypeError, ValueError):
220
+ pass
221
+
222
+ return iterable
223
+
224
+ def __repr__(self):
225
+ return '<iterable converter>'
226
+
227
+
228
+ def convert_iterable(member_converter):
229
+ return _IterableConverter(member_converter)
230
+
231
+
232
+ @attr.s(repr=False, slots=True, hash=True)
233
+ class _MappingConverter(_ConverterBase):
234
+ key_converter = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(collections_abc.Callable)))
235
+ value_converter = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(collections_abc.Callable)))
236
+
237
+ def __call__(self, mapping):
238
+ if mapping is None:
239
+ return None
240
+
241
+ if not isinstance(mapping, collections_abc.Mapping):
242
+ return mapping
243
+
244
+ try:
245
+ key_value_pairs = [[key, value] for key, value in mapping.items()]
246
+ if self.key_converter is not None:
247
+ for pair in key_value_pairs:
248
+ pair[0] = self.key_converter(pair[0])
249
+ if self.value_converter is not None:
250
+ for pair in key_value_pairs:
251
+ pair[1] = self.value_converter(pair[1])
252
+ mapping = type(mapping)(key_value_pairs)
253
+ except (TypeError, ValueError):
254
+ pass
255
+
256
+ return mapping
257
+
258
+ def __repr__(self):
259
+ return '<mapping converter>'
260
+
261
+
262
+ def convert_mapping(key_converter=None, value_converter=None):
263
+ return _MappingConverter(key_converter, value_converter)
264
+
265
+
266
+ @attr.s(frozen=True)
267
+ class ClientVersion(object):
268
+ parts = attr.ib(validator=attr.validators.deep_iterable(attr.validators.instance_of(int)))
269
+
270
+ @classmethod
271
+ def from_str(cls, version_str):
272
+ try:
273
+ return cls((int(version_str), ))
274
+ except ValueError:
275
+ pass
276
+
277
+ return cls(tuple(map(int, version_str.split('.'))))
278
+
279
+ def __str__(self):
280
+ return '.'.join(map(str, self.parts))
281
+
282
+
283
+ @attr.s(repr=False, slots=True, hash=True)
284
+ class _ClientVersionConverter(_ConverterBase):
285
+ def __call__(self, version):
286
+ if version is None:
287
+ return None
288
+
289
+ if not isinstance(version, six.string_types):
290
+ return version
291
+
292
+ try:
293
+ version = ClientVersion.from_str(version)
294
+ except (TypeError, ValueError):
295
+ pass
296
+
297
+ return version
298
+
299
+ def __repr__(self):
300
+ return '<client version converter>'
301
+
302
+
303
+ def convert_client_version():
304
+ return _ClientVersionConverter()
305
+
306
+
307
+ @attr.s(repr=False, slots=True, hash=True)
308
+ class _UrlConverter(_ConverterBase):
309
+ def __call__(self, value):
310
+ if value is None:
311
+ return None
312
+
313
+ if not isinstance(value, six.string_types):
314
+ return value
315
+
316
+ try:
317
+ value = urllib3.util.parse_url(value)
318
+ except urllib3.exceptions.LocationParseError:
319
+ pass
320
+
321
+ return value
322
+
323
+ def __repr__(self):
324
+ return '<url converter>'
325
+
326
+
327
+ def convert_url():
328
+ return _UrlConverter()
329
+
330
+
331
+ @attr.s(repr=False, slots=True, hash=True)
332
+ class _VariadicConverter(_ConverterBase):
333
+ converters = attr.ib(
334
+ validator=attr.validators.deep_iterable(attr.validators.instance_of(_ConverterBase))
335
+ )
336
+
337
+ def __call__(self, convertable):
338
+ if convertable is None:
339
+ return None
340
+
341
+ for converter in self.converters:
342
+ converted = converter(convertable)
343
+ if id(converted) != id(convertable):
344
+ return converted
345
+
346
+ return convertable
347
+
348
+ def __repr__(self):
349
+ return '<variadic converter>'
350
+
351
+
352
+ def convert_variadic(converters):
353
+ return _VariadicConverter(converters)
354
+
355
+
356
+ class CryptoDataParamsBase(object):
357
+ @classmethod
358
+ def get_init_attribute_names(cls):
359
+ return [
360
+ six.ensure_text(name)
361
+ for name, attribute in attr.fields_dict(cls).items()
362
+ if attribute.init
363
+ ]
364
+
365
+ def _asdict_filter(self, attribute, value):
366
+ if attribute.name.startswith('_'):
367
+ return False
368
+
369
+ if attribute.default != attr.NOTHING and value == attribute.default:
370
+ return False
371
+
372
+ return True
373
+
374
+ def _asdict_serializer(self, _, __, value):
375
+ if hasattr(value, '_asdict'):
376
+ return getattr(value, '_asdict')()
377
+ if isinstance(value, enum.Enum):
378
+ return value.name
379
+
380
+ return value
381
+
382
+ def _asdict(self):
383
+ return attr.asdict(
384
+ self,
385
+ filter=self._asdict_filter,
386
+ dict_factory=collections.OrderedDict,
387
+ value_serializer=self._asdict_serializer
388
+ )
389
+
390
+
391
+ @attr.s(frozen=True)
392
+ class CryptoDataParamsFetchedBase(CryptoDataParamsBase):
393
+ @property
394
+ @abc.abstractmethod
395
+ def identifier(self):
396
+ raise NotImplementedError()
397
+
398
+
399
+ @attr.s(frozen=True)
400
+ class CryptoDataParamsNamed(CryptoDataParamsBase):
401
+ name = attr.ib(validator=attr.validators.instance_of(six.string_types))
402
+ long_name = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(six.string_types)))
403
+
404
+ def __str__(self):
405
+ return self.name
406
+
407
+
408
+ @attr.s(frozen=True)
409
+ class CryptoDataParamsEnumNumeric(CryptoDataParamsBase):
410
+ code = attr.ib()
411
+
412
+ @code.validator
413
+ def _validator_code(self, _, value):
414
+ if not isinstance(value, int):
415
+ raise ValueError(value)
416
+ if value < 0:
417
+ raise ValueError(value)
418
+ if value >= 2 ** (self.get_code_size() * 8):
419
+ raise ValueError(value)
420
+
421
+ @classmethod
422
+ def get_code_size(cls):
423
+ raise NotImplementedError()
424
+
425
+
426
+ @attr.s(frozen=True)
427
+ class CryptoDataParamsEnumString(CryptoDataParamsBase):
428
+ code = attr.ib(validator=attr.validators.instance_of(six.string_types))
429
+
430
+ def __str__(self):
431
+ return self.code
432
+
433
+ def _asdict(self):
434
+ return str(self)
435
+
436
+ def get_code_size(self):
437
+ return len(self.code)
438
+
439
+
440
+ @attr.s(frozen=True)
441
+ class CryptoDataParamsOIDOptional(CryptoDataParamsNamed):
442
+ oid = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(six.string_types)))
443
+
444
+
445
+ class CryptoDataEnumBase(enum.Enum):
446
+ @classmethod
447
+ def get_json_records(cls, param_class):
448
+ return collections.OrderedDict([
449
+ (six.ensure_str(unicodedata.normalize('NFD', name).encode('ascii', 'ignore')), param_class(**{
450
+ init_param_name.replace('-', '_'): value
451
+ for init_param_name, value in params.items()
452
+ if not init_param_name.startswith('_')
453
+ }))
454
+ for name, params in cls.get_json_object(param_class).items()
455
+ ])
456
+
457
+ @staticmethod
458
+ def get_file_name_from_param_class(param_class):
459
+ if not param_class.__name__.endswith('Params'):
460
+ raise TypeError(param_class)
461
+
462
+ enum_class_name = param_class.__name__[:-6]
463
+ enum_class_name_parts = [
464
+ name_part.lower()
465
+ for name_part in re.split("([A-Z]+[^A-Z]*)", enum_class_name)
466
+ if name_part
467
+ ]
468
+
469
+ return '-'.join(enum_class_name_parts) + '.json'
470
+
471
+ @classmethod
472
+ def get_json_path(cls, param_class):
473
+ return (
474
+ pathlib.Path(inspect.getfile(param_class)).parent /
475
+ cls.get_file_name_from_param_class(param_class)
476
+ )
477
+
478
+ @classmethod
479
+ def get_json_encoding(cls):
480
+ return 'ascii'
481
+
482
+ @classmethod
483
+ def is_json_encoding_ascii(cls):
484
+ return cls.get_json_encoding() == 'ascii'
485
+
486
+ @classmethod
487
+ def get_json_object(cls, param_class):
488
+ json_path = cls.get_json_path(param_class)
489
+ with codecs.open(str(json_path), 'r', encoding=cls.get_json_encoding()) as json_file:
490
+ return json.load(json_file, object_pairs_hook=collections.OrderedDict)
491
+
492
+ @classmethod
493
+ def dump_json(cls, json_object):
494
+ return json.dumps(json_object, ensure_ascii=cls.is_json_encoding_ascii(), indent=4) + os.linesep
495
+
496
+ @classmethod
497
+ def set_json(cls, param_class, json_object):
498
+ json_path = cls.get_json_path(param_class)
499
+ with codecs.open(str(json_path), 'w+', encoding=cls.get_json_encoding()) as json_file:
500
+ json_file.write(cls.dump_json(json_object))
501
+
502
+ @classmethod
503
+ def get_json(cls, param_class):
504
+ return cls.get_json_object(param_class)
505
+
506
+ @classmethod
507
+ def _from_attr(cls, attr_name, value):
508
+ for item in cls:
509
+ if value == getattr(item.value, attr_name):
510
+ return item
511
+
512
+ raise InvalidValue(value, cls, attr_name)
513
+
514
+
515
+ class CryptoDataEnumCodedBase(CryptoDataEnumBase):
516
+ @classmethod
517
+ def from_code(cls, code):
518
+ return cls._from_attr('code', code)
519
+
520
+
521
+ class CryptoDataEnumOIDBase(CryptoDataEnumCodedBase):
522
+ @classmethod
523
+ def from_oid(cls, oid):
524
+ return cls._from_attr('oid', oid)
@@ -0,0 +1,112 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import binascii
4
+ import hashlib
5
+
6
+ import attr
7
+ import six
8
+ import urllib3
9
+
10
+ from cryptodatahub.common.algorithm import Hash
11
+
12
+
13
+ def bytes_to_hex_string(byte_array, separator='', lowercase=False):
14
+ if lowercase:
15
+ format_str = '{:02x}'
16
+ else:
17
+ format_str = '{:02X}'
18
+
19
+ return separator.join([format_str.format(x) for x in six.iterbytes(bytes(byte_array))])
20
+
21
+
22
+ def bytes_from_hex_string(hex_string, separator=''):
23
+ if separator:
24
+ hex_string = ''.join(hex_string.split(separator))
25
+
26
+ try:
27
+ binary_data = binascii.a2b_hex(hex_string)
28
+ except (TypeError, ValueError) as e:
29
+ six.raise_from(ValueError(*e.args), e)
30
+
31
+ return binary_data
32
+
33
+
34
+ def name_to_enum_item_name(name):
35
+ converted_name = ''
36
+ for char in name:
37
+ if char.isalnum():
38
+ converted_name += char
39
+ elif char == '\'':
40
+ pass
41
+ elif converted_name and converted_name[-1] != '_':
42
+ converted_name += '_'
43
+
44
+ return converted_name.rstrip('_').upper()
45
+
46
+
47
+ _HASHLIB_FUNCS = {
48
+ Hash.MD5: hashlib.md5,
49
+ Hash.SHA1: hashlib.sha1,
50
+ Hash.SHA2_224: hashlib.sha224,
51
+ Hash.SHA2_256: hashlib.sha256,
52
+ Hash.SHA2_384: hashlib.sha384,
53
+ Hash.SHA2_512: hashlib.sha512,
54
+ }
55
+
56
+
57
+ def hash_bytes(hash_algorithm, hashable_value):
58
+ try:
59
+ hashlib_funcs = _HASHLIB_FUNCS[hash_algorithm]
60
+ except KeyError as e:
61
+ six.raise_from(NotImplementedError(hash_algorithm), e)
62
+
63
+ return hashlib_funcs(hashable_value).digest()
64
+
65
+
66
+ @attr.s
67
+ class HttpFetcher(object):
68
+ connect_timeout = attr.ib(default=2, validator=attr.validators.instance_of((int, float)))
69
+ read_timeout = attr.ib(default=1, validator=attr.validators.instance_of((int, float)))
70
+ retry = attr.ib(default=1, validator=attr.validators.instance_of(int))
71
+ _request_params = attr.ib(default=None, init=False)
72
+ _response = attr.ib(default=None, init=False)
73
+
74
+ def __attrs_post_init__(self):
75
+ request_params = {
76
+ 'preload_content': False,
77
+ 'timeout': urllib3.Timeout(connect=self.connect_timeout, read=self.read_timeout),
78
+ 'retries': urllib3.Retry(
79
+ self.retry, status_forcelist=urllib3.Retry.RETRY_AFTER_STATUS_CODES | frozenset([502])
80
+ ),
81
+ }
82
+
83
+ object.__setattr__(self, '_request_params', request_params)
84
+
85
+ def get_response_header(self, header_name):
86
+ if self._response is None:
87
+ raise AttributeError()
88
+
89
+ return self._response.headers.get(header_name, None)
90
+
91
+ @property
92
+ def response_data(self):
93
+ if self._response is None:
94
+ raise AttributeError()
95
+
96
+ return self._response.data
97
+
98
+ def fetch(self, url):
99
+ pool_manager = urllib3.PoolManager()
100
+
101
+ try:
102
+ self._response = pool_manager.request('GET', str(url), **self._request_params)
103
+ except BaseException as e: # pylint: disable=broad-except
104
+ if e.__class__.__name__ != 'TimeoutError' and not isinstance(e, urllib3.exceptions.HTTPError):
105
+ raise e
106
+
107
+ pool_manager.clear()
108
+
109
+ def __call__(self, url):
110
+ self.fetch(url)
111
+
112
+ return self.response_data
@@ -0,0 +1,2 @@
1
+ {
2
+ }
File without changes