diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/MarkupSafe-2.0.1.dist-info/RECORD b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/MarkupSafe-2.0.1.dist-info/RECORD
index 7f8a3c7..b3d640d 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/MarkupSafe-2.0.1.dist-info/RECORD
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/MarkupSafe-2.0.1.dist-info/RECORD
@@ -2,6 +2,7 @@ MarkupSafe-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7ze
MarkupSafe-2.0.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
MarkupSafe-2.0.1.dist-info/METADATA,sha256=lknelt-VPHWai5EJcvZpATGKVbXkg74h7CQuPwDS71U,3237
MarkupSafe-2.0.1.dist-info/RECORD,,
+MarkupSafe-2.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
MarkupSafe-2.0.1.dist-info/WHEEL,sha256=T7Cp5xu87yB0VfKahSR3N0JT_FVycX4pq6-fNwtW39g,221
MarkupSafe-2.0.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
markupsafe/__init__.py,sha256=9Tez4UIlI7J6_sQcUFK1dKniT_b_8YefpGIyYJ3Sr2Q,8923
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/_pyrsistent_version.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/_pyrsistent_version.py
index 5877c8d..b699138 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/_pyrsistent_version.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/_pyrsistent_version.py
@@ -1 +1 @@
-__version__ = '0.18.1'
+__version__ = '0.19.3'
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.py
index f95c96d..9226258 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.py
@@ -1,13 +1,15 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
-
-import sys
+"""
+Classes Without Boilerplate
+"""
from functools import partial
+from typing import Callable
from . import converters, exceptions, filters, setters, validators
from ._cmp import cmp_using
+from ._compat import Protocol
from ._config import get_run_validators, set_run_validators
from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types
from ._make import (
@@ -21,31 +23,22 @@
make_class,
validate,
)
+from ._next_gen import define, field, frozen, mutable
from ._version_info import VersionInfo
-__version__ = "21.4.0"
-__version_info__ = VersionInfo._from_version_string(__version__)
-
-__title__ = "attrs"
-__description__ = "Classes Without Boilerplate"
-__url__ = "https://www.attrs.org/"
-__uri__ = __url__
-__doc__ = __description__ + " <" + __uri__ + ">"
-
-__author__ = "Hynek Schlawack"
-__email__ = "hs@ox.cx"
-
-__license__ = "MIT"
-__copyright__ = "Copyright (c) 2015 Hynek Schlawack"
-
-
s = attributes = attrs
ib = attr = attrib
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
+
+class AttrsInstance(Protocol):
+ pass
+
+
__all__ = [
"Attribute",
+ "AttrsInstance",
"Factory",
"NOTHING",
"asdict",
@@ -57,15 +50,19 @@
"attrs",
"cmp_using",
"converters",
+ "define",
"evolve",
"exceptions",
+ "field",
"fields",
"fields_dict",
"filters",
+ "frozen",
"get_run_validators",
"has",
"ib",
"make_class",
+ "mutable",
"resolve_types",
"s",
"set_run_validators",
@@ -74,7 +71,64 @@
"validators",
]
-if sys.version_info[:2] >= (3, 6):
- from ._next_gen import define, field, frozen, mutable # noqa: F401
- __all__.extend(("define", "field", "frozen", "mutable"))
+def _make_getattr(mod_name: str) -> Callable:
+ """
+ Create a metadata proxy for packaging information that uses *mod_name* in
+ its warnings and errors.
+ """
+
+ def __getattr__(name: str) -> str:
+ dunder_to_metadata = {
+ "__title__": "Name",
+ "__copyright__": "",
+ "__version__": "version",
+ "__version_info__": "version",
+ "__description__": "summary",
+ "__uri__": "",
+ "__url__": "",
+ "__author__": "",
+ "__email__": "",
+ "__license__": "license",
+ }
+ if name not in dunder_to_metadata:
+ msg = f"module {mod_name} has no attribute {name}"
+ raise AttributeError(msg)
+
+ import sys
+ import warnings
+
+ if sys.version_info < (3, 8):
+ from importlib_metadata import metadata
+ else:
+ from importlib.metadata import metadata
+
+ if name not in ("__version__", "__version_info__"):
+ warnings.warn(
+ f"Accessing {mod_name}.{name} is deprecated and will be "
+ "removed in a future release. Use importlib.metadata directly "
+ "to query for attrs's packaging metadata.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+ meta = metadata("attrs")
+ if name == "__license__":
+ return "MIT"
+ if name == "__copyright__":
+ return "Copyright (c) 2015 Hynek Schlawack"
+ if name in ("__uri__", "__url__"):
+ return meta["Project-URL"].split(" ", 1)[-1]
+ if name == "__version_info__":
+ return VersionInfo._from_version_string(meta["version"])
+ if name == "__author__":
+ return meta["Author-email"].rsplit(" ", 1)[0]
+ if name == "__email__":
+ return meta["Author-email"].rsplit("<", 1)[1][:-1]
+
+ return meta[dunder_to_metadata[name]]
+
+ return __getattr__
+
+
+__getattr__ = _make_getattr(__name__)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.pyi b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.pyi
index c0a2126..37a2087 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.pyi
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/__init__.pyi
@@ -1,3 +1,4 @@
+import enum
import sys
from typing import (
@@ -8,6 +9,7 @@ from typing import (
List,
Mapping,
Optional,
+ Protocol,
Sequence,
Tuple,
Type,
@@ -22,8 +24,20 @@ from . import exceptions as exceptions
from . import filters as filters
from . import setters as setters
from . import validators as validators
+from ._cmp import cmp_using as cmp_using
+from ._typing_compat import AttrsInstance_
from ._version_info import VersionInfo
+if sys.version_info >= (3, 10):
+ from typing import TypeGuard
+else:
+ from typing_extensions import TypeGuard
+
+if sys.version_info >= (3, 11):
+ from typing import dataclass_transform
+else:
+ from typing_extensions import dataclass_transform
+
__version__: str
__version_info__: VersionInfo
__title__: str
@@ -39,27 +53,33 @@ _T = TypeVar("_T")
_C = TypeVar("_C", bound=type)
_EqOrderType = Union[bool, Callable[[Any], Any]]
-_ValidatorType = Callable[[Any, Attribute[_T], _T], Any]
+_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any]
_ConverterType = Callable[[Any], Any]
-_FilterType = Callable[[Attribute[_T], _T], bool]
+_FilterType = Callable[["Attribute[_T]", _T], bool]
_ReprType = Callable[[Any], str]
_ReprArgType = Union[bool, _ReprType]
-_OnSetAttrType = Callable[[Any, Attribute[Any], Any], Any]
+_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any]
_OnSetAttrArgType = Union[
_OnSetAttrType, List[_OnSetAttrType], setters._NoOpType
]
_FieldTransformer = Callable[
- [type, List[Attribute[Any]]], List[Attribute[Any]]
+ [type, List["Attribute[Any]"]], List["Attribute[Any]"]
]
-_CompareWithType = Callable[[Any, Any], bool]
# FIXME: in reality, if multiple validators are passed they must be in a list
# or tuple, but those are invariant and so would prevent subtypes of
# _ValidatorType from working when passed in a list or tuple.
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
-# _make --
+# We subclass this here to keep the protocol's qualified name clean.
+class AttrsInstance(AttrsInstance_, Protocol):
+ pass
-NOTHING: object
+_A = TypeVar("_A", bound=type[AttrsInstance])
+
+class _Nothing(enum.Enum):
+ NOTHING = enum.auto()
+
+NOTHING = _Nothing.NOTHING
# NOTE: Factory lies about its return type to make this possible:
# `x: List[int] # = Factory(list)`
@@ -88,22 +108,6 @@ else:
takes_self: bool = ...,
) -> _T: ...
-# Static type inference support via __dataclass_transform__ implemented as per:
-# https://github.com/microsoft/pyright/blob/1.1.135/specs/dataclass_transforms.md
-# This annotation must be applied to all overloads of "define" and "attrs"
-#
-# NOTE: This is a typing construct and does not exist at runtime. Extensions
-# wrapping attrs decorators should declare a separate __dataclass_transform__
-# signature in the extension module using the specification linked above to
-# provide pyright support.
-def __dataclass_transform__(
- *,
- eq_default: bool = True,
- order_default: bool = False,
- kw_only_default: bool = False,
- field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()),
-) -> Callable[[_T], _T]: ...
-
class Attribute(Generic[_T]):
name: str
default: Optional[_T]
@@ -119,6 +123,8 @@ class Attribute(Generic[_T]):
type: Optional[Type[_T]]
kw_only: bool
on_setattr: _OnSetAttrType
+ alias: Optional[str]
+
def evolve(self, **changes: Any) -> "Attribute[Any]": ...
# NOTE: We had several choices for the annotation to use for type arg:
@@ -161,6 +167,7 @@ def attrib(
eq: Optional[_EqOrderType] = ...,
order: Optional[_EqOrderType] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
) -> Any: ...
# This form catches an explicit None or no default and infers the type from the
@@ -181,6 +188,7 @@ def attrib(
eq: Optional[_EqOrderType] = ...,
order: Optional[_EqOrderType] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
) -> _T: ...
# This form catches an explicit default argument.
@@ -200,6 +208,7 @@ def attrib(
eq: Optional[_EqOrderType] = ...,
order: Optional[_EqOrderType] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
) -> _T: ...
# This form covers type=non-Type: e.g. forward references (str), Any
@@ -219,6 +228,7 @@ def attrib(
eq: Optional[_EqOrderType] = ...,
order: Optional[_EqOrderType] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
) -> Any: ...
@overload
def field(
@@ -235,6 +245,8 @@ def field(
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
+ type: Optional[type] = ...,
) -> Any: ...
# This form catches an explicit None or no default and infers the type from the
@@ -254,6 +266,8 @@ def field(
eq: Optional[_EqOrderType] = ...,
order: Optional[_EqOrderType] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
+ type: Optional[type] = ...,
) -> _T: ...
# This form catches an explicit default argument.
@@ -272,6 +286,8 @@ def field(
eq: Optional[_EqOrderType] = ...,
order: Optional[_EqOrderType] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
+ type: Optional[type] = ...,
) -> _T: ...
# This form covers type=non-Type: e.g. forward references (str), Any
@@ -290,9 +306,11 @@ def field(
eq: Optional[_EqOrderType] = ...,
order: Optional[_EqOrderType] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
+ alias: Optional[str] = ...,
+ type: Optional[type] = ...,
) -> Any: ...
@overload
-@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
+@dataclass_transform(order_default=True, field_specifiers=(attrib, field))
def attrs(
maybe_cls: _C,
these: Optional[Dict[str, Any]] = ...,
@@ -317,9 +335,10 @@ def attrs(
on_setattr: Optional[_OnSetAttrArgType] = ...,
field_transformer: Optional[_FieldTransformer] = ...,
match_args: bool = ...,
+ unsafe_hash: Optional[bool] = ...,
) -> _C: ...
@overload
-@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
+@dataclass_transform(order_default=True, field_specifiers=(attrib, field))
def attrs(
maybe_cls: None = ...,
these: Optional[Dict[str, Any]] = ...,
@@ -344,14 +363,16 @@ def attrs(
on_setattr: Optional[_OnSetAttrArgType] = ...,
field_transformer: Optional[_FieldTransformer] = ...,
match_args: bool = ...,
+ unsafe_hash: Optional[bool] = ...,
) -> Callable[[_C], _C]: ...
@overload
-@__dataclass_transform__(field_descriptors=(attrib, field))
+@dataclass_transform(field_specifiers=(attrib, field))
def define(
maybe_cls: _C,
*,
these: Optional[Dict[str, Any]] = ...,
repr: bool = ...,
+ unsafe_hash: Optional[bool] = ...,
hash: Optional[bool] = ...,
init: bool = ...,
slots: bool = ...,
@@ -371,12 +392,13 @@ def define(
match_args: bool = ...,
) -> _C: ...
@overload
-@__dataclass_transform__(field_descriptors=(attrib, field))
+@dataclass_transform(field_specifiers=(attrib, field))
def define(
maybe_cls: None = ...,
*,
these: Optional[Dict[str, Any]] = ...,
repr: bool = ...,
+ unsafe_hash: Optional[bool] = ...,
hash: Optional[bool] = ...,
init: bool = ...,
slots: bool = ...,
@@ -397,21 +419,69 @@ def define(
) -> Callable[[_C], _C]: ...
mutable = define
-frozen = define # they differ only in their defaults
-
-# TODO: add support for returning NamedTuple from the mypy plugin
-class _Fields(Tuple[Attribute[Any], ...]):
- def __getattr__(self, name: str) -> Attribute[Any]: ...
-def fields(cls: type) -> _Fields: ...
-def fields_dict(cls: type) -> Dict[str, Attribute[Any]]: ...
-def validate(inst: Any) -> None: ...
+@overload
+@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
+def frozen(
+ maybe_cls: _C,
+ *,
+ these: Optional[Dict[str, Any]] = ...,
+ repr: bool = ...,
+ unsafe_hash: Optional[bool] = ...,
+ hash: Optional[bool] = ...,
+ init: bool = ...,
+ slots: bool = ...,
+ frozen: bool = ...,
+ weakref_slot: bool = ...,
+ str: bool = ...,
+ auto_attribs: bool = ...,
+ kw_only: bool = ...,
+ cache_hash: bool = ...,
+ auto_exc: bool = ...,
+ eq: Optional[bool] = ...,
+ order: Optional[bool] = ...,
+ auto_detect: bool = ...,
+ getstate_setstate: Optional[bool] = ...,
+ on_setattr: Optional[_OnSetAttrArgType] = ...,
+ field_transformer: Optional[_FieldTransformer] = ...,
+ match_args: bool = ...,
+) -> _C: ...
+@overload
+@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
+def frozen(
+ maybe_cls: None = ...,
+ *,
+ these: Optional[Dict[str, Any]] = ...,
+ repr: bool = ...,
+ unsafe_hash: Optional[bool] = ...,
+ hash: Optional[bool] = ...,
+ init: bool = ...,
+ slots: bool = ...,
+ frozen: bool = ...,
+ weakref_slot: bool = ...,
+ str: bool = ...,
+ auto_attribs: bool = ...,
+ kw_only: bool = ...,
+ cache_hash: bool = ...,
+ auto_exc: bool = ...,
+ eq: Optional[bool] = ...,
+ order: Optional[bool] = ...,
+ auto_detect: bool = ...,
+ getstate_setstate: Optional[bool] = ...,
+ on_setattr: Optional[_OnSetAttrArgType] = ...,
+ field_transformer: Optional[_FieldTransformer] = ...,
+ match_args: bool = ...,
+) -> Callable[[_C], _C]: ...
+def fields(cls: Type[AttrsInstance]) -> Any: ...
+def fields_dict(cls: Type[AttrsInstance]) -> Dict[str, Attribute[Any]]: ...
+def validate(inst: AttrsInstance) -> None: ...
def resolve_types(
- cls: _C,
+ cls: _A,
globalns: Optional[Dict[str, Any]] = ...,
localns: Optional[Dict[str, Any]] = ...,
attribs: Optional[List[Attribute[Any]]] = ...,
-) -> _C: ...
+ include_extras: bool = ...,
+) -> _A: ...
# TODO: add support for returning a proper attrs class from the mypy plugin
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo',
@@ -420,6 +490,7 @@ def make_class(
name: str,
attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
bases: Tuple[type, ...] = ...,
+ class_body: Optional[Dict[str, Any]] = ...,
repr_ns: Optional[str] = ...,
repr: bool = ...,
cmp: Optional[_EqOrderType] = ...,
@@ -449,7 +520,7 @@ def make_class(
# https://github.com/python/typing/issues/253
# XXX: remember to fix attrs.asdict/astuple too!
def asdict(
- inst: Any,
+ inst: AttrsInstance,
recurse: bool = ...,
filter: Optional[_FilterType[Any]] = ...,
dict_factory: Type[Mapping[Any, Any]] = ...,
@@ -462,13 +533,13 @@ def asdict(
# TODO: add support for returning NamedTuple from the mypy plugin
def astuple(
- inst: Any,
+ inst: AttrsInstance,
recurse: bool = ...,
filter: Optional[_FilterType[Any]] = ...,
tuple_factory: Type[Sequence[Any]] = ...,
retain_collection_types: bool = ...,
) -> Tuple[Any, ...]: ...
-def has(cls: type) -> bool: ...
+def has(cls: type) -> TypeGuard[Type[AttrsInstance]]: ...
def assoc(inst: _T, **changes: Any) -> _T: ...
def evolve(inst: _T, **changes: Any) -> _T: ...
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.py
index 6cffa4d..a4a35e0 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.py
@@ -1,10 +1,9 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
import functools
+import types
-from ._compat import new_class
from ._make import _make_ne
@@ -21,22 +20,22 @@ def cmp_using(
class_name="Comparable",
):
"""
- Create a class that can be passed into `attr.ib`'s ``eq``, ``order``, and
- ``cmp`` arguments to customize field comparison.
-
- The resulting class will have a full set of ordering methods if
- at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
-
- :param Optional[callable] eq: `callable` used to evaluate equality
- of two objects.
- :param Optional[callable] lt: `callable` used to evaluate whether
- one object is less than another object.
- :param Optional[callable] le: `callable` used to evaluate whether
- one object is less than or equal to another object.
- :param Optional[callable] gt: `callable` used to evaluate whether
- one object is greater than another object.
- :param Optional[callable] ge: `callable` used to evaluate whether
- one object is greater than or equal to another object.
+ Create a class that can be passed into `attrs.field`'s ``eq``, ``order``,
+ and ``cmp`` arguments to customize field comparison.
+
+ The resulting class will have a full set of ordering methods if at least
+ one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
+
+ :param Optional[callable] eq: `callable` used to evaluate equality of two
+ objects.
+ :param Optional[callable] lt: `callable` used to evaluate whether one
+ object is less than another object.
+ :param Optional[callable] le: `callable` used to evaluate whether one
+ object is less than or equal to another object.
+ :param Optional[callable] gt: `callable` used to evaluate whether one
+ object is greater than another object.
+ :param Optional[callable] ge: `callable` used to evaluate whether one
+ object is greater than or equal to another object.
:param bool require_same_type: When `True`, equality and ordering methods
will return `NotImplemented` if objects are not of the same type.
@@ -80,7 +79,9 @@ def cmp_using(
num_order_functions += 1
body["__ge__"] = _make_operator("ge", ge)
- type_ = new_class(class_name, (object,), {}, lambda ns: ns.update(body))
+ type_ = types.new_class(
+ class_name, (object,), {}, lambda ns: ns.update(body)
+ )
# Add same type requirement.
if require_same_type:
@@ -91,10 +92,8 @@ def cmp_using(
if not has_eq_function:
# functools.total_ordering requires __eq__ to be defined,
# so raise early error here to keep a nice stack.
- raise ValueError(
- "eq must be define is order to complete ordering from "
- "lt, le, gt, ge."
- )
+ msg = "eq must be define is order to complete ordering from lt, le, gt, ge."
+ raise ValueError(msg)
type_ = functools.total_ordering(type_)
return type_
@@ -129,9 +128,9 @@ def method(self, other):
return result
- method.__name__ = "__%s__" % (name,)
- method.__doc__ = "Return a %s b. Computed by attrs." % (
- _operation_names[name],
+ method.__name__ = f"__{name}__"
+ method.__doc__ = (
+ f"Return a {_operation_names[name]} b. Computed by attrs."
)
return method
@@ -141,10 +140,7 @@ def _is_comparable_to(self, other):
"""
Check whether `other` is comparable to `self`.
"""
- for func in self._requirements:
- if not func(self, other):
- return False
- return True
+ return all(func(self, other) for func in self._requirements)
def _check_same_type(self, other):
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.pyi b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.pyi
index e71aaff..f3dcdc1 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.pyi
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_cmp.pyi
@@ -1,13 +1,13 @@
-from typing import Type
+from typing import Any, Callable, Optional, Type
-from . import _CompareWithType
+_CompareWithType = Callable[[Any, Any], bool]
def cmp_using(
- eq: Optional[_CompareWithType],
- lt: Optional[_CompareWithType],
- le: Optional[_CompareWithType],
- gt: Optional[_CompareWithType],
- ge: Optional[_CompareWithType],
- require_same_type: bool,
- class_name: str,
+ eq: Optional[_CompareWithType] = ...,
+ lt: Optional[_CompareWithType] = ...,
+ le: Optional[_CompareWithType] = ...,
+ gt: Optional[_CompareWithType] = ...,
+ ge: Optional[_CompareWithType] = ...,
+ require_same_type: bool = ...,
+ class_name: str = ...,
) -> Type: ...
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_compat.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_compat.py
index dc0cb02..46b05ca 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_compat.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_compat.py
@@ -1,250 +1,69 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
-
+import inspect
import platform
import sys
import threading
-import types
-import warnings
+
+from collections.abc import Mapping, Sequence # noqa: F401
+from typing import _GenericAlias
-PY2 = sys.version_info[0] == 2
PYPY = platform.python_implementation() == "PyPy"
-PY36 = sys.version_info[:2] >= (3, 6)
-HAS_F_STRINGS = PY36
+PY_3_8_PLUS = sys.version_info[:2] >= (3, 8)
+PY_3_9_PLUS = sys.version_info[:2] >= (3, 9)
PY310 = sys.version_info[:2] >= (3, 10)
+PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
-if PYPY or PY36:
- ordered_dict = dict
+if sys.version_info < (3, 8):
+ try:
+ from typing_extensions import Protocol
+ except ImportError: # pragma: no cover
+ Protocol = object
else:
- from collections import OrderedDict
-
- ordered_dict = OrderedDict
-
-
-if PY2:
- from collections import Mapping, Sequence
+ from typing import Protocol # noqa: F401
- from UserDict import IterableUserDict
- # We 'bundle' isclass instead of using inspect as importing inspect is
- # fairly expensive (order of 10-15 ms for a modern machine in 2016)
- def isclass(klass):
- return isinstance(klass, (type, types.ClassType))
-
- def new_class(name, bases, kwds, exec_body):
- """
- A minimal stub of types.new_class that we need for make_class.
- """
- ns = {}
- exec_body(ns)
-
- return type(name, bases, ns)
+class _AnnotationExtractor:
+ """
+ Extract type annotations from a callable, returning None whenever there
+ is none.
+ """
- # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
- TYPE = "type"
+ __slots__ = ["sig"]
- def iteritems(d):
- return d.iteritems()
+ def __init__(self, callable):
+ try:
+ self.sig = inspect.signature(callable)
+ except (ValueError, TypeError): # inspect failed
+ self.sig = None
- # Python 2 is bereft of a read-only dict proxy, so we make one!
- class ReadOnlyDict(IterableUserDict):
+ def get_first_param_type(self):
"""
- Best-effort read-only dict wrapper.
+ Return the type annotation of the first argument if it's not empty.
"""
+ if not self.sig:
+ return None
- def __setitem__(self, key, val):
- # We gently pretend we're a Python 3 mappingproxy.
- raise TypeError(
- "'mappingproxy' object does not support item assignment"
- )
+ params = list(self.sig.parameters.values())
+ if params and params[0].annotation is not inspect.Parameter.empty:
+ return params[0].annotation
- def update(self, _):
- # We gently pretend we're a Python 3 mappingproxy.
- raise AttributeError(
- "'mappingproxy' object has no attribute 'update'"
- )
+ return None
- def __delitem__(self, _):
- # We gently pretend we're a Python 3 mappingproxy.
- raise TypeError(
- "'mappingproxy' object does not support item deletion"
- )
-
- def clear(self):
- # We gently pretend we're a Python 3 mappingproxy.
- raise AttributeError(
- "'mappingproxy' object has no attribute 'clear'"
- )
-
- def pop(self, key, default=None):
- # We gently pretend we're a Python 3 mappingproxy.
- raise AttributeError(
- "'mappingproxy' object has no attribute 'pop'"
- )
-
- def popitem(self):
- # We gently pretend we're a Python 3 mappingproxy.
- raise AttributeError(
- "'mappingproxy' object has no attribute 'popitem'"
- )
-
- def setdefault(self, key, default=None):
- # We gently pretend we're a Python 3 mappingproxy.
- raise AttributeError(
- "'mappingproxy' object has no attribute 'setdefault'"
- )
-
- def __repr__(self):
- # Override to be identical to the Python 3 version.
- return "mappingproxy(" + repr(self.data) + ")"
-
- def metadata_proxy(d):
- res = ReadOnlyDict()
- res.data.update(d) # We blocked update, so we have to do it like this.
- return res
-
- def just_warn(*args, **kw): # pragma: no cover
- """
- We only warn on Python 3 because we are not aware of any concrete
- consequences of not setting the cell on Python 2.
- """
-
-else: # Python 3 and later.
- from collections.abc import Mapping, Sequence # noqa
-
- def just_warn(*args, **kw):
+ def get_return_type(self):
"""
- We only warn on Python 3 because we are not aware of any concrete
- consequences of not setting the cell on Python 2.
+ Return the return type if it's not empty.
"""
- warnings.warn(
- "Running interpreter doesn't sufficiently support code object "
- "introspection. Some features like bare super() or accessing "
- "__class__ will not work with slotted classes.",
- RuntimeWarning,
- stacklevel=2,
- )
-
- def isclass(klass):
- return isinstance(klass, type)
+ if (
+ self.sig
+ and self.sig.return_annotation is not inspect.Signature.empty
+ ):
+ return self.sig.return_annotation
- TYPE = "class"
+ return None
- def iteritems(d):
- return d.items()
-
- new_class = types.new_class
-
- def metadata_proxy(d):
- return types.MappingProxyType(dict(d))
-
-
-def make_set_closure_cell():
- """Return a function of two arguments (cell, value) which sets
- the value stored in the closure cell `cell` to `value`.
- """
- # pypy makes this easy. (It also supports the logic below, but
- # why not do the easy/fast thing?)
- if PYPY:
-
- def set_closure_cell(cell, value):
- cell.__setstate__((value,))
-
- return set_closure_cell
-
- # Otherwise gotta do it the hard way.
-
- # Create a function that will set its first cellvar to `value`.
- def set_first_cellvar_to(value):
- x = value
- return
-
- # This function will be eliminated as dead code, but
- # not before its reference to `x` forces `x` to be
- # represented as a closure cell rather than a local.
- def force_x_to_be_a_cell(): # pragma: no cover
- return x
-
- try:
- # Extract the code object and make sure our assumptions about
- # the closure behavior are correct.
- if PY2:
- co = set_first_cellvar_to.func_code
- else:
- co = set_first_cellvar_to.__code__
- if co.co_cellvars != ("x",) or co.co_freevars != ():
- raise AssertionError # pragma: no cover
-
- # Convert this code object to a code object that sets the
- # function's first _freevar_ (not cellvar) to the argument.
- if sys.version_info >= (3, 8):
- # CPython 3.8+ has an incompatible CodeType signature
- # (added a posonlyargcount argument) but also added
- # CodeType.replace() to do this without counting parameters.
- set_first_freevar_code = co.replace(
- co_cellvars=co.co_freevars, co_freevars=co.co_cellvars
- )
- else:
- args = [co.co_argcount]
- if not PY2:
- args.append(co.co_kwonlyargcount)
- args.extend(
- [
- co.co_nlocals,
- co.co_stacksize,
- co.co_flags,
- co.co_code,
- co.co_consts,
- co.co_names,
- co.co_varnames,
- co.co_filename,
- co.co_name,
- co.co_firstlineno,
- co.co_lnotab,
- # These two arguments are reversed:
- co.co_cellvars,
- co.co_freevars,
- ]
- )
- set_first_freevar_code = types.CodeType(*args)
-
- def set_closure_cell(cell, value):
- # Create a function using the set_first_freevar_code,
- # whose first closure cell is `cell`. Calling it will
- # change the value of that cell.
- setter = types.FunctionType(
- set_first_freevar_code, {}, "setter", (), (cell,)
- )
- # And call it to set the cell.
- setter(value)
-
- # Make sure it works on this interpreter:
- def make_func_with_cell():
- x = None
-
- def func():
- return x # pragma: no cover
-
- return func
-
- if PY2:
- cell = make_func_with_cell().func_closure[0]
- else:
- cell = make_func_with_cell().__closure__[0]
- set_closure_cell(cell, 100)
- if cell.cell_contents != 100:
- raise AssertionError # pragma: no cover
-
- except Exception:
- return just_warn
- else:
- return set_closure_cell
-
-
-set_closure_cell = make_set_closure_cell()
# Thread-local global to track attrs instances which are already being repr'd.
# This is needed because there is no other (thread-safe) way to pass info
@@ -259,3 +78,10 @@ def func():
# don't have a direct reference to the thread-local in their globals dict.
# If they have such a reference, it breaks cloudpickle.
repr_context = threading.local()
+
+
+def get_generic_base(cl):
+ """If this is a generic class (A[str]), return the generic base for it."""
+ if cl.__class__ is _GenericAlias:
+ return cl.__origin__
+ return None
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_config.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_config.py
index fc9be29..9c245b1 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_config.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_config.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
-
-
__all__ = ["set_run_validators", "get_run_validators"]
_run_validators = True
@@ -17,7 +14,8 @@ def set_run_validators(run):
instead.
"""
if not isinstance(run, bool):
- raise TypeError("'run' must be bool.")
+ msg = "'run' must be bool."
+ raise TypeError(msg)
global _run_validators
_run_validators = run
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_funcs.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_funcs.py
index 4c90085..a888991 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_funcs.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_funcs.py
@@ -1,10 +1,9 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
import copy
-from ._compat import iteritems
+from ._compat import PY_3_9_PLUS, get_generic_base
from ._make import NOTHING, _obj_setattr, fields
from .exceptions import AttrsAttributeNotFoundError
@@ -18,13 +17,13 @@ def asdict(
value_serializer=None,
):
"""
- Return the ``attrs`` attribute values of *inst* as a dict.
+ Return the *attrs* attribute values of *inst* as a dict.
- Optionally recurse into other ``attrs``-decorated classes.
+ Optionally recurse into other *attrs*-decorated classes.
- :param inst: Instance of an ``attrs``-decorated class.
+ :param inst: Instance of an *attrs*-decorated class.
:param bool recurse: Recurse into classes that are also
- ``attrs``-decorated.
+ *attrs*-decorated.
:param callable filter: A callable whose return code determines whether an
attribute or element is included (``True``) or dropped (``False``). Is
called with the `attrs.Attribute` as the first argument and the
@@ -42,7 +41,7 @@ def asdict(
:rtype: return type of *dict_factory*
- :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
class.
.. versionadded:: 16.0.0 *dict_factory*
@@ -73,19 +72,25 @@ def asdict(
)
elif isinstance(v, (tuple, list, set, frozenset)):
cf = v.__class__ if retain_collection_types is True else list
- rv[a.name] = cf(
- [
- _asdict_anything(
- i,
- is_key=False,
- filter=filter,
- dict_factory=dict_factory,
- retain_collection_types=retain_collection_types,
- value_serializer=value_serializer,
- )
- for i in v
- ]
- )
+ items = [
+ _asdict_anything(
+ i,
+ is_key=False,
+ filter=filter,
+ dict_factory=dict_factory,
+ retain_collection_types=retain_collection_types,
+ value_serializer=value_serializer,
+ )
+ for i in v
+ ]
+ try:
+ rv[a.name] = cf(items)
+ except TypeError:
+ if not issubclass(cf, tuple):
+ raise
+ # Workaround for TypeError: cf.__new__() missing 1 required
+ # positional argument (which appears, for a namedturle)
+ rv[a.name] = cf(*items)
elif isinstance(v, dict):
df = dict_factory
rv[a.name] = df(
@@ -107,7 +112,7 @@ def asdict(
value_serializer=value_serializer,
),
)
- for kk, vv in iteritems(v)
+ for kk, vv in v.items()
)
else:
rv[a.name] = v
@@ -179,7 +184,7 @@ def _asdict_anything(
value_serializer=value_serializer,
),
)
- for kk, vv in iteritems(val)
+ for kk, vv in val.items()
)
else:
rv = val
@@ -197,13 +202,13 @@ def astuple(
retain_collection_types=False,
):
"""
- Return the ``attrs`` attribute values of *inst* as a tuple.
+ Return the *attrs* attribute values of *inst* as a tuple.
- Optionally recurse into other ``attrs``-decorated classes.
+ Optionally recurse into other *attrs*-decorated classes.
- :param inst: Instance of an ``attrs``-decorated class.
+ :param inst: Instance of an *attrs*-decorated class.
:param bool recurse: Recurse into classes that are also
- ``attrs``-decorated.
+ *attrs*-decorated.
:param callable filter: A callable whose return code determines whether an
attribute or element is included (``True``) or dropped (``False``). Is
called with the `attrs.Attribute` as the first argument and the
@@ -217,7 +222,7 @@ def astuple(
:rtype: return type of *tuple_factory*
- :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
class.
.. versionadded:: 16.2.0
@@ -242,22 +247,26 @@ def astuple(
)
elif isinstance(v, (tuple, list, set, frozenset)):
cf = v.__class__ if retain is True else list
- rv.append(
- cf(
- [
- astuple(
- j,
- recurse=True,
- filter=filter,
- tuple_factory=tuple_factory,
- retain_collection_types=retain,
- )
- if has(j.__class__)
- else j
- for j in v
- ]
+ items = [
+ astuple(
+ j,
+ recurse=True,
+ filter=filter,
+ tuple_factory=tuple_factory,
+ retain_collection_types=retain,
)
- )
+ if has(j.__class__)
+ else j
+ for j in v
+ ]
+ try:
+ rv.append(cf(items))
+ except TypeError:
+ if not issubclass(cf, tuple):
+ raise
+ # Workaround for TypeError: cf.__new__() missing 1 required
+ # positional argument (which appears, for a namedturle)
+ rv.append(cf(*items))
elif isinstance(v, dict):
df = v.__class__ if retain is True else dict
rv.append(
@@ -278,7 +287,7 @@ def astuple(
if has(vv.__class__)
else vv,
)
- for kk, vv in iteritems(v)
+ for kk, vv in v.items()
)
)
else:
@@ -291,28 +300,48 @@ def astuple(
def has(cls):
"""
- Check whether *cls* is a class with ``attrs`` attributes.
+ Check whether *cls* is a class with *attrs* attributes.
:param type cls: Class to introspect.
:raise TypeError: If *cls* is not a class.
:rtype: bool
"""
- return getattr(cls, "__attrs_attrs__", None) is not None
+ attrs = getattr(cls, "__attrs_attrs__", None)
+ if attrs is not None:
+ return True
+
+ # No attrs, maybe it's a specialized generic (A[str])?
+ generic_base = get_generic_base(cls)
+ if generic_base is not None:
+ generic_attrs = getattr(generic_base, "__attrs_attrs__", None)
+ if generic_attrs is not None:
+ # Stick it on here for speed next time.
+ cls.__attrs_attrs__ = generic_attrs
+ return generic_attrs is not None
+ return False
def assoc(inst, **changes):
"""
Copy *inst* and apply *changes*.
- :param inst: Instance of a class with ``attrs`` attributes.
+ This is different from `evolve` that applies the changes to the arguments
+ that create the new instance.
+
+ `evolve`'s behavior is preferable, but there are `edge cases`_ where it
+ doesn't work. Therefore `assoc` is deprecated, but will not be removed.
+
+ .. _`edge cases`: https://github.com/python-attrs/attrs/issues/251
+
+ :param inst: Instance of a class with *attrs* attributes.
:param changes: Keyword changes in the new copy.
:return: A copy of inst with *changes* incorporated.
- :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
- be found on *cls*.
- :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ :raise attrs.exceptions.AttrsAttributeNotFoundError: If *attr_name*
+ couldn't be found on *cls*.
+ :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
class.
.. deprecated:: 17.1.0
@@ -320,57 +349,79 @@ def assoc(inst, **changes):
This function will not be removed du to the slightly different approach
compared to `attrs.evolve`.
"""
- import warnings
-
- warnings.warn(
- "assoc is deprecated and will be removed after 2018/01.",
- DeprecationWarning,
- stacklevel=2,
- )
new = copy.copy(inst)
attrs = fields(inst.__class__)
- for k, v in iteritems(changes):
+ for k, v in changes.items():
a = getattr(attrs, k, NOTHING)
if a is NOTHING:
- raise AttrsAttributeNotFoundError(
- "{k} is not an attrs attribute on {cl}.".format(
- k=k, cl=new.__class__
- )
- )
+ msg = f"{k} is not an attrs attribute on {new.__class__}."
+ raise AttrsAttributeNotFoundError(msg)
_obj_setattr(new, k, v)
return new
-def evolve(inst, **changes):
+def evolve(*args, **changes):
"""
- Create a new instance, based on *inst* with *changes* applied.
+ Create a new instance, based on the first positional argument with
+ *changes* applied.
- :param inst: Instance of a class with ``attrs`` attributes.
+ :param inst: Instance of a class with *attrs* attributes.
:param changes: Keyword changes in the new copy.
:return: A copy of inst with *changes* incorporated.
:raise TypeError: If *attr_name* couldn't be found in the class
``__init__``.
- :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
class.
- .. versionadded:: 17.1.0
+ .. versionadded:: 17.1.0
+ .. deprecated:: 23.1.0
+ It is now deprecated to pass the instance using the keyword argument
+ *inst*. It will raise a warning until at least April 2024, after which
+ it will become an error. Always pass the instance as a positional
+ argument.
"""
+ # Try to get instance by positional argument first.
+ # Use changes otherwise and warn it'll break.
+ if args:
+ try:
+ (inst,) = args
+ except ValueError:
+ msg = f"evolve() takes 1 positional argument, but {len(args)} were given"
+ raise TypeError(msg) from None
+ else:
+ try:
+ inst = changes.pop("inst")
+ except KeyError:
+ msg = "evolve() missing 1 required positional argument: 'inst'"
+ raise TypeError(msg) from None
+
+ import warnings
+
+ warnings.warn(
+ "Passing the instance per keyword argument is deprecated and "
+ "will stop working in, or after, April 2024.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
cls = inst.__class__
attrs = fields(cls)
for a in attrs:
if not a.init:
continue
attr_name = a.name # To deal with private attributes.
- init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
+ init_name = a.alias
if init_name not in changes:
changes[init_name] = getattr(inst, attr_name)
return cls(**changes)
-def resolve_types(cls, globalns=None, localns=None, attribs=None):
+def resolve_types(
+ cls, globalns=None, localns=None, attribs=None, include_extras=True
+):
"""
Resolve any strings and forward annotations in type annotations.
@@ -389,10 +440,14 @@ def resolve_types(cls, globalns=None, localns=None, attribs=None):
:param Optional[dict] localns: Dictionary containing local variables.
:param Optional[list] attribs: List of attribs for the given class.
This is necessary when calling from inside a ``field_transformer``
- since *cls* is not an ``attrs`` class yet.
+ since *cls* is not an *attrs* class yet.
+ :param bool include_extras: Resolve more accurately, if possible.
+ Pass ``include_extras`` to ``typing.get_hints``, if supported by the
+ typing module. On supported Python versions (3.9+), this resolves the
+ types more accurately.
:raise TypeError: If *cls* is not a class.
- :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
class and you didn't pass any attribs.
:raise NameError: If types cannot be resolved because of missing variables.
@@ -402,6 +457,7 @@ class and you didn't pass any attribs.
.. versionadded:: 20.1.0
.. versionadded:: 21.1.0 *attribs*
+ .. versionadded:: 23.1.0 *include_extras*
"""
# Since calling get_type_hints is expensive we cache whether we've
@@ -409,7 +465,12 @@ class and you didn't pass any attribs.
if getattr(cls, "__attrs_types_resolved__", None) != cls:
import typing
- hints = typing.get_type_hints(cls, globalns=globalns, localns=localns)
+ kwargs = {"globalns": globalns, "localns": localns}
+
+ if PY_3_9_PLUS:
+ kwargs["include_extras"] = include_extras
+
+ hints = typing.get_type_hints(cls, **kwargs)
for field in fields(cls) if attribs is None else attribs:
if field.name in hints:
# Since fields have been frozen we must work around it.
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_make.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_make.py
index d46f8a3..10b4eca 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_make.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_make.py
@@ -1,12 +1,15 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
-
+import contextlib
import copy
+import enum
+import functools
import inspect
+import itertools
import linecache
import sys
-import warnings
+import types
+import typing
from operator import itemgetter
@@ -14,37 +17,23 @@
# having the thread-local in the globals here.
from . import _compat, _config, setters
from ._compat import (
- HAS_F_STRINGS,
- PY2,
PY310,
- PYPY,
- isclass,
- iteritems,
- metadata_proxy,
- new_class,
- ordered_dict,
- set_closure_cell,
+ PY_3_8_PLUS,
+ _AnnotationExtractor,
+ get_generic_base,
)
from .exceptions import (
DefaultAlreadySetError,
FrozenInstanceError,
NotAnAttrsClassError,
- PythonTooOldError,
UnannotatedAttributeError,
)
-if not PY2:
- import typing
-
-
# This is used at least twice, so cache it here.
_obj_setattr = object.__setattr__
_init_converter_pat = "__attr_converter_%s"
-_init_factory_pat = "__attr_factory_{}"
-_tuple_property_pat = (
- " {attr_name} = _attrs_property(_attrs_itemgetter({index}))"
-)
+_init_factory_pat = "__attr_factory_%s"
_classvar_prefixes = (
"typing.ClassVar",
"t.ClassVar",
@@ -56,7 +45,7 @@
# (when slots=True)
_hash_cache_field = "_attrs_cached_hash"
-_empty_metadata_singleton = metadata_proxy({})
+_empty_metadata_singleton = types.MappingProxyType({})
# Unique object for unequivocal getattr() defaults.
_sentinel = object()
@@ -64,21 +53,18 @@
_ng_default_on_setattr = setters.pipe(setters.convert, setters.validate)
-class _Nothing(object):
+class _Nothing(enum.Enum):
"""
- Sentinel class to indicate the lack of a value when ``None`` is ambiguous.
+ Sentinel to indicate the lack of a value when ``None`` is ambiguous.
- ``_Nothing`` is a singleton. There is only ever one of it.
+ If extending attrs, you can use ``typing.Literal[NOTHING]`` to show
+ that a value may be ``NOTHING``.
.. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False.
+ .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant.
"""
- _singleton = None
-
- def __new__(cls):
- if _Nothing._singleton is None:
- _Nothing._singleton = super(_Nothing, cls).__new__(cls)
- return _Nothing._singleton
+ NOTHING = enum.auto()
def __repr__(self):
return "NOTHING"
@@ -86,11 +72,8 @@ def __repr__(self):
def __bool__(self):
return False
- def __len__(self):
- return 0 # __bool__ for Python 2
-
-NOTHING = _Nothing()
+NOTHING = _Nothing.NOTHING
"""
Sentinel to indicate the lack of a value when ``None`` is ambiguous.
"""
@@ -108,17 +91,8 @@ class _CacheHashWrapper(int):
See GH #613 for more details.
"""
- if PY2:
- # For some reason `type(None)` isn't callable in Python 2, but we don't
- # actually need a constructor for None objects, we just need any
- # available function that returns None.
- def __reduce__(self, _none_constructor=getattr, _args=(0, "", None)):
- return _none_constructor, _args
-
- else:
-
- def __reduce__(self, _none_constructor=type(None), _args=()):
- return _none_constructor, _args
+ def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008
+ return _none_constructor, _args
def attrib(
@@ -136,16 +110,20 @@ def attrib(
eq=None,
order=None,
on_setattr=None,
+ alias=None,
):
"""
Create a new attribute on a class.
.. warning::
- Does *not* do anything unless the class is also decorated with
- `attr.s`!
+ Does *not* do anything unless the class is also decorated with `attr.s`
+ / `attrs.define` / and so on!
- :param default: A value that is used if an ``attrs``-generated ``__init__``
+ Please consider using `attrs.field` in new code (``attr.ib`` will *never*
+ go away, though).
+
+ :param default: A value that is used if an *attrs*-generated ``__init__``
is used and no value is passed while instantiating or the attribute is
excluded using ``init=False``.
@@ -154,17 +132,17 @@ def attrib(
or dicts).
If a default is not set (or set manually to `attrs.NOTHING`), a value
- *must* be supplied when instantiating; otherwise a `TypeError`
- will be raised.
+ *must* be supplied when instantiating; otherwise a `TypeError` will be
+ raised.
The default can also be set using decorator notation as shown below.
- :type default: Any value
+ .. seealso:: `defaults`
:param callable factory: Syntactic sugar for
``default=attr.Factory(factory)``.
- :param validator: `callable` that is called by ``attrs``-generated
+ :param validator: `callable` that is called by *attrs*-generated
``__init__`` methods after the instance has been initialized. They
receive the initialized instance, the :func:`~attrs.Attribute`, and the
passed value.
@@ -172,77 +150,90 @@ def attrib(
The return value is *not* inspected so the validator has to throw an
exception itself.
- If a `list` is passed, its items are treated as validators and must
- all pass.
+ If a `list` is passed, its items are treated as validators and must all
+ pass.
Validators can be globally disabled and re-enabled using
- `get_run_validators`.
+ `attrs.validators.get_disabled` / `attrs.validators.set_disabled`.
The validator can also be set using decorator notation as shown below.
+ .. seealso:: :ref:`validators`
+
:type validator: `callable` or a `list` of `callable`\\ s.
- :param repr: Include this attribute in the generated ``__repr__``
- method. If ``True``, include the attribute; if ``False``, omit it. By
- default, the built-in ``repr()`` function is used. To override how the
- attribute value is formatted, pass a ``callable`` that takes a single
- value and returns a string. Note that the resulting string is used
- as-is, i.e. it will be used directly *instead* of calling ``repr()``
- (the default).
+ :param repr: Include this attribute in the generated ``__repr__`` method.
+ If ``True``, include the attribute; if ``False``, omit it. By default,
+ the built-in ``repr()`` function is used. To override how the attribute
+ value is formatted, pass a ``callable`` that takes a single value and
+ returns a string. Note that the resulting string is used as-is, i.e. it
+ will be used directly *instead* of calling ``repr()`` (the default).
:type repr: a `bool` or a `callable` to use a custom function.
- :param eq: If ``True`` (default), include this attribute in the
- generated ``__eq__`` and ``__ne__`` methods that check two instances
- for equality. To override how the attribute value is compared,
- pass a ``callable`` that takes a single value and returns the value
- to be compared.
+ :param eq: If ``True`` (default), include this attribute in the generated
+ ``__eq__`` and ``__ne__`` methods that check two instances for
+ equality. To override how the attribute value is compared, pass a
+ ``callable`` that takes a single value and returns the value to be
+ compared.
+
+ .. seealso:: `comparison`
:type eq: a `bool` or a `callable`.
:param order: If ``True`` (default), include this attributes in the
- generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods.
- To override how the attribute value is ordered,
- pass a ``callable`` that takes a single value and returns the value
- to be ordered.
+ generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To
+ override how the attribute value is ordered, pass a ``callable`` that
+ takes a single value and returns the value to be ordered.
+
+ .. seealso:: `comparison`
:type order: a `bool` or a `callable`.
:param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the
same value. Must not be mixed with *eq* or *order*.
+
+ .. seealso:: `comparison`
:type cmp: a `bool` or a `callable`.
- :param Optional[bool] hash: Include this attribute in the generated
+ :param bool | None hash: Include this attribute in the generated
``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This
is the correct behavior according the Python spec. Setting this value
to anything else than ``None`` is *discouraged*.
+
+ .. seealso:: `hashing`
:param bool init: Include this attribute in the generated ``__init__``
method. It is possible to set this to ``False`` and set a default
value. In that case this attributed is unconditionally initialized
with the specified default value or factory.
- :param callable converter: `callable` that is called by
- ``attrs``-generated ``__init__`` methods to convert attribute's value
- to the desired format. It is given the passed-in value, and the
- returned value will be used as the new value of the attribute. The
- value is converted before being passed to the validator, if any.
- :param metadata: An arbitrary mapping, to be used by third-party
- components. See `extending_metadata`.
- :param type: The type of the attribute. In Python 3.6 or greater, the
- preferred method to specify the type is using a variable annotation
- (see `PEP 526 `_).
- This argument is provided for backward compatibility.
- Regardless of the approach used, the type will be stored on
- ``Attribute.type``.
-
- Please note that ``attrs`` doesn't do anything with this metadata by
- itself. You can use it as part of your own code or for
- `static type checking `.
- :param kw_only: Make this attribute keyword-only (Python 3+)
- in the generated ``__init__`` (if ``init`` is ``False``, this
- parameter is ignored).
+
+ .. seealso:: `init`
+ :param callable converter: `callable` that is called by *attrs*-generated
+ ``__init__`` methods to convert attribute's value to the desired
+ format. It is given the passed-in value, and the returned value will
+ be used as the new value of the attribute. The value is converted
+ before being passed to the validator, if any.
+
+ .. seealso:: :ref:`converters`
+ :param dict | None metadata: An arbitrary mapping, to be used by
+ third-party components. See `extending-metadata`.
+
+ :param type: The type of the attribute. Nowadays, the preferred method to
+ specify the type is using a variable annotation (see :pep:`526`). This
+ argument is provided for backward compatibility. Regardless of the
+ approach used, the type will be stored on ``Attribute.type``.
+
+ Please note that *attrs* doesn't do anything with this metadata by
+ itself. You can use it as part of your own code or for `static type
+ checking `.
+ :param bool kw_only: Make this attribute keyword-only in the generated
+ ``__init__`` (if ``init`` is ``False``, this parameter is ignored).
:param on_setattr: Allows to overwrite the *on_setattr* setting from
`attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used.
Set to `attrs.setters.NO_OP` to run **no** `setattr` hooks for this
attribute -- regardless of the setting in `attr.s`.
:type on_setattr: `callable`, or a list of callables, or `None`, or
`attrs.setters.NO_OP`
+ :param str | None alias: Override this attribute's parameter name in the
+ generated ``__init__`` method. If left `None`, default to ``name``
+ stripped of leading underscores. See `private-attributes`.
.. versionadded:: 15.2.0 *convert*
.. versionadded:: 16.3.0 *metadata*
@@ -265,24 +256,25 @@ def attrib(
.. versionchanged:: 21.1.0
*eq*, *order*, and *cmp* also accept a custom callable
.. versionchanged:: 21.1.0 *cmp* undeprecated
+ .. versionadded:: 22.2.0 *alias*
"""
eq, eq_key, order, order_key = _determine_attrib_eq_order(
cmp, eq, order, True
)
if hash is not None and hash is not True and hash is not False:
- raise TypeError(
- "Invalid value for hash. Must be True, False, or None."
- )
+ msg = "Invalid value for hash. Must be True, False, or None."
+ raise TypeError(msg)
if factory is not None:
if default is not NOTHING:
- raise ValueError(
- "The `default` and `factory` arguments are mutually "
- "exclusive."
+ msg = (
+ "The `default` and `factory` arguments are mutually exclusive."
)
+ raise ValueError(msg)
if not callable(factory):
- raise ValueError("The `factory` argument must be a callable.")
+ msg = "The `factory` argument must be a callable."
+ raise ValueError(msg)
default = Factory(factory)
if metadata is None:
@@ -314,6 +306,7 @@ def attrib(
order=order,
order_key=order_key,
on_setattr=on_setattr,
+ alias=alias,
)
@@ -325,13 +318,11 @@ def _compile_and_eval(script, globs, locs=None, filename=""):
eval(bytecode, globs, locs)
-def _make_method(name, script, filename, globs=None):
+def _make_method(name, script, filename, globs):
"""
Create the method with the script given and return the method object.
"""
locs = {}
- if globs is None:
- globs = {}
# In order of debuggers like PDB being able to step through the code,
# we add a fake linecache entry.
@@ -347,9 +338,9 @@ def _make_method(name, script, filename, globs=None):
old_val = linecache.cache.setdefault(filename, linecache_tuple)
if old_val == linecache_tuple:
break
- else:
- filename = "{}-{}>".format(base_filename[:-1], count)
- count += 1
+
+ filename = f"{base_filename[:-1]}-{count}>"
+ count += 1
_compile_and_eval(script, globs, locs, filename)
@@ -366,15 +357,15 @@ class MyClassAttributes(tuple):
__slots__ = ()
x = property(itemgetter(0))
"""
- attr_class_name = "{}Attributes".format(cls_name)
+ attr_class_name = f"{cls_name}Attributes"
attr_class_template = [
- "class {}(tuple):".format(attr_class_name),
+ f"class {attr_class_name}(tuple):",
" __slots__ = ()",
]
if attr_names:
for i, attr_name in enumerate(attr_names):
attr_class_template.append(
- _tuple_property_pat.format(index=i, attr_name=attr_name)
+ f" {attr_name} = _attrs_property(_attrs_itemgetter({i}))"
)
else:
attr_class_template.append(" pass")
@@ -418,8 +409,6 @@ def _is_class_var(annot):
def _has_own_attribute(cls, attrib_name):
"""
Check whether *cls* defines *attrib_name* (and doesn't just inherit it).
-
- Requires Python 3.
"""
attr = getattr(cls, attrib_name, _sentinel)
if attr is _sentinel:
@@ -443,13 +432,6 @@ def _get_annotations(cls):
return {}
-def _counter_getter(e):
- """
- Key function for sorting to avoid re-creating a lambda for every class.
- """
- return e[1].counter
-
-
def _collect_base_attrs(cls, taken_attr_names):
"""
Collect attr.ibs from base classes of *cls*, except *taken_attr_names*.
@@ -463,7 +445,7 @@ def _collect_base_attrs(cls, taken_attr_names):
if a.inherited or a.name in taken_attr_names:
continue
- a = a.evolve(inherited=True)
+ a = a.evolve(inherited=True) # noqa: PLW2901
base_attrs.append(a)
base_attr_map[a.name] = base_cls
@@ -501,7 +483,7 @@ def _collect_base_attrs_broken(cls, taken_attr_names):
if a.name in taken_attr_names:
continue
- a = a.evolve(inherited=True)
+ a = a.evolve(inherited=True) # noqa: PLW2901
taken_attr_names.add(a.name)
base_attrs.append(a)
base_attr_map[a.name] = base_cls
@@ -526,10 +508,7 @@ def _transform_attrs(
anns = _get_annotations(cls)
if these is not None:
- ca_list = [(name, ca) for name, ca in iteritems(these)]
-
- if not isinstance(these, ordered_dict):
- ca_list.sort(key=_counter_getter)
+ ca_list = list(these.items())
elif auto_attribs is True:
ca_names = {
name
@@ -545,10 +524,7 @@ def _transform_attrs(
a = cd.get(attr_name, NOTHING)
if not isinstance(a, _CountingAttr):
- if a is NOTHING:
- a = attrib()
- else:
- a = attrib(default=a)
+ a = attrib() if a is NOTHING else attrib(default=a)
ca_list.append((attr_name, a))
unannotated = ca_names - annot_names
@@ -599,10 +575,8 @@ def _transform_attrs(
had_default = False
for a in (a for a in attrs if a.init is not False and a.kw_only is False):
if had_default is True and a.default is NOTHING:
- raise ValueError(
- "No mandatory attributes allowed after an attribute with a "
- "default value or factory. Attribute in question: %r" % (a,)
- )
+ msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}"
+ raise ValueError(msg)
if had_default is False and a.default is not NOTHING:
had_default = True
@@ -610,6 +584,14 @@ def _transform_attrs(
if field_transformer is not None:
attrs = field_transformer(cls, attrs)
+ # Resolve default field alias after executing field_transformer.
+ # This allows field_transformer to differentiate between explicit vs
+ # default aliases and supply their own defaults.
+ attrs = [
+ a.evolve(alias=_default_init_alias_for(a.name)) if not a.alias else a
+ for a in attrs
+ ]
+
# Create AttrsClass *after* applying the field_transformer since it may
# add or remove attributes!
attr_names = [a.name for a in attrs]
@@ -618,28 +600,75 @@ def _transform_attrs(
return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map))
-if PYPY:
+def _make_cached_property_getattr(
+ cached_properties,
+ original_getattr,
+ cls,
+):
+ lines = [
+ # Wrapped to get `__class__` into closure cell for super()
+ # (It will be replaced with the newly constructed class after construction).
+ "def wrapper():",
+ " __class__ = _cls",
+ " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):",
+ " func = cached_properties.get(item)",
+ " if func is not None:",
+ " result = func(self)",
+ " _setter = _cached_setattr_get(self)",
+ " _setter(item, result)",
+ " return result",
+ ]
+ if original_getattr is not None:
+ lines.append(
+ " return original_getattr(self, item)",
+ )
+ else:
+ lines.extend(
+ [
+ " if hasattr(super(), '__getattr__'):",
+ " return super().__getattr__(item)",
+ " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"",
+ " raise AttributeError(original_error)",
+ ]
+ )
+
+ lines.extend(
+ [
+ " return __getattr__",
+ "__getattr__ = wrapper()",
+ ]
+ )
+
+ unique_filename = _generate_unique_filename(cls, "getattr")
- def _frozen_setattrs(self, name, value):
- """
- Attached to frozen classes as __setattr__.
- """
- if isinstance(self, BaseException) and name in (
- "__cause__",
- "__context__",
- ):
- BaseException.__setattr__(self, name, value)
- return
+ glob = {
+ "cached_properties": cached_properties,
+ "_cached_setattr_get": _obj_setattr.__get__,
+ "_cls": cls,
+ "original_getattr": original_getattr,
+ }
- raise FrozenInstanceError()
+ return _make_method(
+ "__getattr__",
+ "\n".join(lines),
+ unique_filename,
+ glob,
+ )
-else:
- def _frozen_setattrs(self, name, value):
- """
- Attached to frozen classes as __setattr__.
- """
- raise FrozenInstanceError()
+def _frozen_setattrs(self, name, value):
+ """
+ Attached to frozen classes as __setattr__.
+ """
+ if isinstance(self, BaseException) and name in (
+ "__cause__",
+ "__context__",
+ "__traceback__",
+ ):
+ BaseException.__setattr__(self, name, value)
+ return
+
+ raise FrozenInstanceError()
def _frozen_delattrs(self, name):
@@ -649,7 +678,7 @@ def _frozen_delattrs(self, name):
raise FrozenInstanceError()
-class _ClassBuilder(object):
+class _ClassBuilder:
"""
Iteratively build *one* class.
"""
@@ -665,6 +694,7 @@ class _ClassBuilder(object):
"_delete_attribs",
"_frozen",
"_has_pre_init",
+ "_pre_init_has_args",
"_has_post_init",
"_is_exc",
"_on_setattr",
@@ -703,7 +733,7 @@ def __init__(
self._cls = cls
self._cls_dict = dict(cls.__dict__) if slots else {}
self._attrs = attrs
- self._base_names = set(a.name for a in base_attrs)
+ self._base_names = {a.name for a in base_attrs}
self._base_attr_map = base_map
self._attr_names = tuple(a.name for a in attrs)
self._slots = slots
@@ -711,6 +741,13 @@ def __init__(
self._weakref_slot = weakref_slot
self._cache_hash = cache_hash
self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False))
+ self._pre_init_has_args = False
+ if self._has_pre_init:
+ # Check if the pre init method has more arguments than just `self`
+ # We want to pass arguments if pre init expects arguments
+ pre_init_func = cls.__attrs_pre_init__
+ pre_init_signature = inspect.signature(pre_init_func)
+ self._pre_init_has_args = len(pre_init_signature.parameters) > 1
self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))
self._delete_attribs = not bool(these)
self._is_exc = is_exc
@@ -760,17 +797,35 @@ def __init__(
) = self._make_getstate_setstate()
def __repr__(self):
- return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__)
+ return f"<_ClassBuilder(cls={self._cls.__name__})>"
- def build_class(self):
- """
- Finalize class based on the accumulated configuration.
+ if PY310:
+ import abc
+
+ def build_class(self):
+ """
+ Finalize class based on the accumulated configuration.
+
+ Builder cannot be used after calling this method.
+ """
+ if self._slots is True:
+ return self._create_slots_class()
+
+ return self.abc.update_abstractmethods(
+ self._patch_original_class()
+ )
+
+ else:
+
+ def build_class(self):
+ """
+ Finalize class based on the accumulated configuration.
+
+ Builder cannot be used after calling this method.
+ """
+ if self._slots is True:
+ return self._create_slots_class()
- Builder cannot be used after calling this method.
- """
- if self._slots is True:
- return self._create_slots_class()
- else:
return self._patch_original_class()
def _patch_original_class(self):
@@ -787,13 +842,11 @@ def _patch_original_class(self):
name not in base_names
and getattr(cls, name, _sentinel) is not _sentinel
):
- try:
+ # An AttributeError can happen if a base class defines a
+ # class variable and we want to set an attribute with the
+ # same name by using only a type annotation.
+ with contextlib.suppress(AttributeError):
delattr(cls, name)
- except AttributeError:
- # This can happen if a base class defines a class
- # variable and we want to set an attribute with the
- # same name by using only a type annotation.
- pass
# Attach our dunder methods.
for name, value in self._cls_dict.items():
@@ -807,7 +860,7 @@ def _patch_original_class(self):
cls.__attrs_own_setattr__ = False
if not self._has_custom_setattr:
- cls.__setattr__ = object.__setattr__
+ cls.__setattr__ = _obj_setattr
return cls
@@ -817,8 +870,8 @@ def _create_slots_class(self):
"""
cd = {
k: v
- for k, v in iteritems(self._cls_dict)
- if k not in tuple(self._attr_names) + ("__dict__", "__weakref__")
+ for k, v in self._cls_dict.items()
+ if k not in (*tuple(self._attr_names), "__dict__", "__weakref__")
}
# If our class doesn't have its own implementation of __setattr__
@@ -835,12 +888,12 @@ def _create_slots_class(self):
if not self._has_custom_setattr:
for base_cls in self._cls.__bases__:
if base_cls.__dict__.get("__attrs_own_setattr__", False):
- cd["__setattr__"] = object.__setattr__
+ cd["__setattr__"] = _obj_setattr
break
# Traverse the MRO to collect existing slots
# and check for an existing __weakref__.
- existing_slots = dict()
+ existing_slots = {}
weakref_inherited = False
for base_cls in self._cls.__mro__[1:-1]:
if base_cls.__dict__.get("__weakref__", None) is not None:
@@ -863,38 +916,76 @@ def _create_slots_class(self):
):
names += ("__weakref__",)
+ if PY_3_8_PLUS:
+ cached_properties = {
+ name: cached_property.func
+ for name, cached_property in cd.items()
+ if isinstance(cached_property, functools.cached_property)
+ }
+ else:
+ # `functools.cached_property` was introduced in 3.8.
+ # So can't be used before this.
+ cached_properties = {}
+
+ # Collect methods with a `__class__` reference that are shadowed in the new class.
+ # To know to update them.
+ additional_closure_functions_to_update = []
+ if cached_properties:
+ # Add cached properties to names for slotting.
+ names += tuple(cached_properties.keys())
+
+ for name in cached_properties:
+ # Clear out function from class to avoid clashing.
+ del cd[name]
+
+ class_annotations = _get_annotations(self._cls)
+ for name, func in cached_properties.items():
+ annotation = inspect.signature(func).return_annotation
+ if annotation is not inspect.Parameter.empty:
+ class_annotations[name] = annotation
+
+ original_getattr = cd.get("__getattr__")
+ if original_getattr is not None:
+ additional_closure_functions_to_update.append(original_getattr)
+
+ cd["__getattr__"] = _make_cached_property_getattr(
+ cached_properties, original_getattr, self._cls
+ )
+
# We only add the names of attributes that aren't inherited.
# Setting __slots__ to inherited attributes wastes memory.
slot_names = [name for name in names if name not in base_names]
+
# There are slots for attributes from current class
# that are defined in parent classes.
- # As their descriptors may be overriden by a child class,
+ # As their descriptors may be overridden by a child class,
# we collect them here and update the class dict
reused_slots = {
slot: slot_descriptor
- for slot, slot_descriptor in iteritems(existing_slots)
+ for slot, slot_descriptor in existing_slots.items()
if slot in slot_names
}
slot_names = [name for name in slot_names if name not in reused_slots]
cd.update(reused_slots)
if self._cache_hash:
slot_names.append(_hash_cache_field)
+
cd["__slots__"] = tuple(slot_names)
- qualname = getattr(self._cls, "__qualname__", None)
- if qualname is not None:
- cd["__qualname__"] = qualname
+ cd["__qualname__"] = self._cls.__qualname__
# Create new class based on old class and our methods.
cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd)
# The following is a fix for
- # . On Python 3,
- # if a method mentions `__class__` or uses the no-arg super(), the
+ # .
+ # If a method mentions `__class__` or uses the no-arg super(), the
# compiler will bake a reference to the class in the method itself
# as `method.__closure__`. Since we replace the class with a
# clone, we rewrite these references so it keeps working.
- for item in cls.__dict__.values():
+ for item in itertools.chain(
+ cls.__dict__.values(), additional_closure_functions_to_update
+ ):
if isinstance(item, (classmethod, staticmethod)):
# Class- and staticmethods hide their functions inside.
# These might need to be rewritten as well.
@@ -911,12 +1002,12 @@ def _create_slots_class(self):
for cell in closure_cells:
try:
match = cell.cell_contents is self._cls
- except ValueError: # ValueError: Cell is empty
+ except ValueError: # noqa: PERF203
+ # ValueError: Cell is empty
pass
else:
if match:
- set_closure_cell(cell, cls)
-
+ cell.cell_contents = cls
return cls
def add_repr(self, ns):
@@ -928,9 +1019,8 @@ def add_repr(self, ns):
def add_str(self):
repr = self._cls_dict.get("__repr__")
if repr is None:
- raise ValueError(
- "__str__ can only be generated if a __repr__ exists."
- )
+ msg = "__str__ can only be generated if a __repr__ exists."
+ raise ValueError(msg)
def __str__(self):
return self.__repr__()
@@ -951,7 +1041,7 @@ def slots_getstate(self):
"""
Automatically created by attrs.
"""
- return tuple(getattr(self, name) for name in state_attr_names)
+ return {name: getattr(self, name) for name in state_attr_names}
hash_caching_enabled = self._cache_hash
@@ -959,9 +1049,16 @@ def slots_setstate(self, state):
"""
Automatically created by attrs.
"""
- __bound_setattr = _obj_setattr.__get__(self, Attribute)
- for name, value in zip(state_attr_names, state):
- __bound_setattr(name, value)
+ __bound_setattr = _obj_setattr.__get__(self)
+ if isinstance(state, tuple):
+ # Backward compatibility with attrs instances pickled with
+ # attrs versions before v22.2.0 which stored tuples.
+ for name, value in zip(state_attr_names, state):
+ __bound_setattr(name, value)
+ else:
+ for name in state_attr_names:
+ if name in state:
+ __bound_setattr(name, state[name])
# The hash code cache is not included when the object is
# serialized, but it still needs to be initialized to None to
@@ -994,6 +1091,7 @@ def add_init(self):
self._cls,
self._attrs,
self._has_pre_init,
+ self._pre_init_has_args,
self._has_post_init,
self._frozen,
self._slots,
@@ -1020,6 +1118,7 @@ def add_attrs_init(self):
self._cls,
self._attrs,
self._has_pre_init,
+ self._pre_init_has_args,
self._has_post_init,
self._frozen,
self._slots,
@@ -1068,9 +1167,8 @@ def add_setattr(self):
if self._has_custom_setattr:
# We need to write a __setattr__ but there already is one!
- raise ValueError(
- "Can't combine custom __setattr__ with on_setattr hooks."
- )
+ msg = "Can't combine custom __setattr__ with on_setattr hooks."
+ raise ValueError(msg)
# docstring comes from _add_method_dunders
def __setattr__(self, name, val):
@@ -1093,41 +1191,29 @@ def _add_method_dunders(self, method):
"""
Add __module__ and __qualname__ to a *method* if possible.
"""
- try:
+ with contextlib.suppress(AttributeError):
method.__module__ = self._cls.__module__
- except AttributeError:
- pass
- try:
- method.__qualname__ = ".".join(
- (self._cls.__qualname__, method.__name__)
- )
- except AttributeError:
- pass
+ with contextlib.suppress(AttributeError):
+ method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}"
- try:
- method.__doc__ = "Method generated by attrs for class %s." % (
- self._cls.__qualname__,
+ with contextlib.suppress(AttributeError):
+ method.__doc__ = (
+ "Method generated by attrs for class "
+ f"{self._cls.__qualname__}."
)
- except AttributeError:
- pass
return method
-_CMP_DEPRECATION = (
- "The usage of `cmp` is deprecated and will be removed on or after "
- "2021-06-01. Please use `eq` and `order` instead."
-)
-
-
def _determine_attrs_eq_order(cmp, eq, order, default_eq):
"""
Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
values of eq and order. If *eq* is None, set it to *default_eq*.
"""
if cmp is not None and any((eq is not None, order is not None)):
- raise ValueError("Don't mix `cmp` with `eq' and `order`.")
+ msg = "Don't mix `cmp` with `eq' and `order`."
+ raise ValueError(msg)
# cmp takes precedence due to bw-compatibility.
if cmp is not None:
@@ -1142,7 +1228,8 @@ def _determine_attrs_eq_order(cmp, eq, order, default_eq):
order = eq
if eq is False and order is True:
- raise ValueError("`order` can only be True if `eq` is True too.")
+ msg = "`order` can only be True if `eq` is True too."
+ raise ValueError(msg)
return eq, order
@@ -1153,7 +1240,8 @@ def _determine_attrib_eq_order(cmp, eq, order, default_eq):
values of eq and order. If *eq* is None, set it to *default_eq*.
"""
if cmp is not None and any((eq is not None, order is not None)):
- raise ValueError("Don't mix `cmp` with `eq' and `order`.")
+ msg = "Don't mix `cmp` with `eq' and `order`."
+ raise ValueError(msg)
def decide_callable_or_boolean(value):
"""
@@ -1183,7 +1271,8 @@ def decide_callable_or_boolean(value):
order, order_key = decide_callable_or_boolean(order)
if eq is False and order is True:
- raise ValueError("`order` can only be True if `eq` is True too.")
+ msg = "`order` can only be True if `eq` is True too."
+ raise ValueError(msg)
return eq, eq_key, order, order_key
@@ -1199,8 +1288,6 @@ def _determine_whether_to_implement(
whose presence signal that the user has implemented it themselves.
Return *default* if no reason for either for or against is found.
-
- auto_detect must be False on Python 2.
"""
if flag is True or flag is False:
return flag
@@ -1240,24 +1327,24 @@ def attrs(
on_setattr=None,
field_transformer=None,
match_args=True,
+ unsafe_hash=None,
):
r"""
- A class decorator that adds `dunder
- `_\ -methods according to the
+ A class decorator that adds :term:`dunder methods` according to the
specified attributes using `attr.ib` or the *these* argument.
- :param these: A dictionary of name to `attr.ib` mappings. This is
- useful to avoid the definition of your attributes within the class body
+ Please consider using `attrs.define` / `attrs.frozen` in new code
+ (``attr.s`` will *never* go away, though).
+
+ :param these: A dictionary of name to `attr.ib` mappings. This is useful
+ to avoid the definition of your attributes within the class body
because you can't (e.g. if you want to add ``__repr__`` methods to
Django models) or don't want to.
- If *these* is not ``None``, ``attrs`` will *not* search the class body
+ If *these* is not ``None``, *attrs* will *not* search the class body
for attributes and will *not* remove any attributes from it.
- If *these* is an ordered dict (`dict` on Python 3.6+,
- `collections.OrderedDict` otherwise), the order is deduced from
- the order of the attributes inside *these*. Otherwise the order
- of the definition of the attributes is used.
+ The order is deduced from the order of the attributes inside *these*.
:type these: `dict` of `str` to `attr.ib`
@@ -1270,79 +1357,89 @@ def attrs(
arguments is implemented in the *current* class (i.e. it is *not*
inherited from some base class).
- So for example by implementing ``__eq__`` on a class yourself,
- ``attrs`` will deduce ``eq=False`` and will create *neither*
- ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible
- ``__ne__`` by default, so it *should* be enough to only implement
- ``__eq__`` in most cases).
+ So for example by implementing ``__eq__`` on a class yourself, *attrs*
+ will deduce ``eq=False`` and will create *neither* ``__eq__`` *nor*
+ ``__ne__`` (but Python classes come with a sensible ``__ne__`` by
+ default, so it *should* be enough to only implement ``__eq__`` in most
+ cases).
.. warning::
- If you prevent ``attrs`` from creating the ordering methods for you
+ If you prevent *attrs* from creating the ordering methods for you
(``order=False``, e.g. by implementing ``__le__``), it becomes
*your* responsibility to make sure its ordering is sound. The best
way is to use the `functools.total_ordering` decorator.
- Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*,
- *cmp*, or *hash* overrides whatever *auto_detect* would determine.
-
- *auto_detect* requires Python 3. Setting it ``True`` on Python 2 raises
- an `attrs.exceptions.PythonTooOldError`.
+ Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, *cmp*,
+ or *hash* overrides whatever *auto_detect* would determine.
:param bool repr: Create a ``__repr__`` method with a human readable
- representation of ``attrs`` attributes..
+ representation of *attrs* attributes..
:param bool str: Create a ``__str__`` method that is identical to
- ``__repr__``. This is usually not necessary except for
- `Exception`\ s.
- :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__``
+ ``__repr__``. This is usually not necessary except for `Exception`\ s.
+ :param bool | None eq: If ``True`` or ``None`` (default), add ``__eq__``
and ``__ne__`` methods that check two instances for equality.
- They compare the instances as if they were tuples of their ``attrs``
+ They compare the instances as if they were tuples of their *attrs*
attributes if and only if the types of both classes are *identical*!
- :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``,
+
+ .. seealso:: `comparison`
+ :param bool | None order: If ``True``, add ``__lt__``, ``__le__``,
``__gt__``, and ``__ge__`` methods that behave like *eq* above and
allow instances to be ordered. If ``None`` (default) mirror value of
*eq*.
- :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq*
- and *order* to the same value. Must not be mixed with *eq* or *order*.
- :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method
- is generated according how *eq* and *frozen* are set.
- 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you.
+ .. seealso:: `comparison`
+ :param bool | None cmp: Setting *cmp* is equivalent to setting *eq* and
+ *order* to the same value. Must not be mixed with *eq* or *order*.
+
+ .. seealso:: `comparison`
+ :param bool | None unsafe_hash: If ``None`` (default), the ``__hash__``
+ method is generated according how *eq* and *frozen* are set.
+
+ 1. If *both* are True, *attrs* will generate a ``__hash__`` for you.
2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to
None, marking it unhashable (which it is).
3. If *eq* is False, ``__hash__`` will be left untouched meaning the
``__hash__`` method of the base class will be used (if base class is
``object``, this means it will fall back to id-based hashing.).
- Although not recommended, you can decide for yourself and force
- ``attrs`` to create one (e.g. if the class is immutable even though you
- didn't freeze it programmatically) by passing ``True`` or not. Both of
- these cases are rather special and should be used carefully.
-
- See our documentation on `hashing`, Python's documentation on
- `object.__hash__`, and the `GitHub issue that led to the default \
- behavior `_ for more
- details.
- :param bool init: Create a ``__init__`` method that initializes the
- ``attrs`` attributes. Leading underscores are stripped for the argument
- name. If a ``__attrs_pre_init__`` method exists on the class, it will
- be called before the class is initialized. If a ``__attrs_post_init__``
- method exists on the class, it will be called after the class is fully
+ Although not recommended, you can decide for yourself and force *attrs*
+ to create one (e.g. if the class is immutable even though you didn't
+ freeze it programmatically) by passing ``True`` or not. Both of these
+ cases are rather special and should be used carefully.
+
+ .. seealso::
+
+ - Our documentation on `hashing`,
+ - Python's documentation on `object.__hash__`,
+ - and the `GitHub issue that led to the default \
+ behavior `_ for
+ more details.
+
+ :param bool | None hash: Alias for *unsafe_hash*. *unsafe_hash* takes
+ precedence.
+ :param bool init: Create a ``__init__`` method that initializes the *attrs*
+ attributes. Leading underscores are stripped for the argument name. If
+ a ``__attrs_pre_init__`` method exists on the class, it will be called
+ before the class is initialized. If a ``__attrs_post_init__`` method
+ exists on the class, it will be called after the class is fully
initialized.
- If ``init`` is ``False``, an ``__attrs_init__`` method will be
- injected instead. This allows you to define a custom ``__init__``
- method that can do pre-init work such as ``super().__init__()``,
- and then call ``__attrs_init__()`` and ``__attrs_post_init__()``.
- :param bool slots: Create a `slotted class ` that's more
- memory-efficient. Slotted classes are generally superior to the default
- dict classes, but have some gotchas you should know about, so we
- encourage you to read the `glossary entry `.
+ If ``init`` is ``False``, an ``__attrs_init__`` method will be injected
+ instead. This allows you to define a custom ``__init__`` method that
+ can do pre-init work such as ``super().__init__()``, and then call
+ ``__attrs_init__()`` and ``__attrs_post_init__()``.
+
+ .. seealso:: `init`
+ :param bool slots: Create a :term:`slotted class ` that's
+ more memory-efficient. Slotted classes are generally superior to the
+ default dict classes, but have some gotchas you should know about, so
+ we encourage you to read the :term:`glossary entry `.
:param bool frozen: Make instances immutable after initialization. If
someone attempts to modify a frozen instance,
- `attr.exceptions.FrozenInstanceError` is raised.
+ `attrs.exceptions.FrozenInstanceError` is raised.
.. note::
@@ -1357,21 +1454,21 @@ def attrs(
4. If a class is frozen, you cannot modify ``self`` in
``__attrs_post_init__`` or a self-written ``__init__``. You can
- circumvent that limitation by using
- ``object.__setattr__(self, "attribute_name", value)``.
+ circumvent that limitation by using ``object.__setattr__(self,
+ "attribute_name", value)``.
5. Subclasses of a frozen class are frozen too.
:param bool weakref_slot: Make instances weak-referenceable. This has no
effect unless ``slots`` is also enabled.
- :param bool auto_attribs: If ``True``, collect `PEP 526`_-annotated
- attributes (Python 3.6 and later only) from the class body.
+ :param bool auto_attribs: If ``True``, collect :pep:`526`-annotated
+ attributes from the class body.
- In this case, you **must** annotate every field. If ``attrs``
- encounters a field that is set to an `attr.ib` but lacks a type
- annotation, an `attr.exceptions.UnannotatedAttributeError` is
- raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't
- want to set a type.
+ In this case, you **must** annotate every field. If *attrs* encounters
+ a field that is set to an `attr.ib` but lacks a type annotation, an
+ `attr.exceptions.UnannotatedAttributeError` is raised. Use
+ ``field_name: typing.Any = attr.ib(...)`` if you don't want to set a
+ type.
If you assign a value to those attributes (e.g. ``x: int = 42``), that
value becomes the default value like if it were passed using
@@ -1383,58 +1480,55 @@ def attrs(
.. warning::
For features that use the attribute name to create decorators (e.g.
- `validators `), you still *must* assign `attr.ib` to
- them. Otherwise Python will either not find the name or try to use
- the default value to call e.g. ``validator`` on it.
+ :ref:`validators `), you still *must* assign `attr.ib`
+ to them. Otherwise Python will either not find the name or try to
+ use the default value to call e.g. ``validator`` on it.
These errors can be quite confusing and probably the most common bug
report on our bug tracker.
- .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/
- :param bool kw_only: Make all attributes keyword-only (Python 3+)
- in the generated ``__init__`` (if ``init`` is ``False``, this
- parameter is ignored).
- :param bool cache_hash: Ensure that the object's hash code is computed
- only once and stored on the object. If this is set to ``True``,
- hashing must be either explicitly or implicitly enabled for this
- class. If the hash code is cached, avoid any reassignments of
- fields involved in hash code computation or mutations of the objects
- those fields point to after object creation. If such changes occur,
- the behavior of the object's hash code is undefined.
- :param bool auto_exc: If the class subclasses `BaseException`
- (which implicitly includes any subclass of any exception), the
- following happens to behave like a well-behaved Python exceptions
- class:
+ :param bool kw_only: Make all attributes keyword-only in the generated
+ ``__init__`` (if ``init`` is ``False``, this parameter is ignored).
+ :param bool cache_hash: Ensure that the object's hash code is computed only
+ once and stored on the object. If this is set to ``True``, hashing
+ must be either explicitly or implicitly enabled for this class. If the
+ hash code is cached, avoid any reassignments of fields involved in hash
+ code computation or mutations of the objects those fields point to
+ after object creation. If such changes occur, the behavior of the
+ object's hash code is undefined.
+ :param bool auto_exc: If the class subclasses `BaseException` (which
+ implicitly includes any subclass of any exception), the following
+ happens to behave like a well-behaved Python exceptions class:
- the values for *eq*, *order*, and *hash* are ignored and the
- instances compare and hash by the instance's ids (N.B. ``attrs`` will
+ instances compare and hash by the instance's ids (N.B. *attrs* will
*not* remove existing implementations of ``__hash__`` or the equality
methods. It just won't add own ones.),
- all attributes that are either passed into ``__init__`` or have a
default value are additionally available as a tuple in the ``args``
attribute,
- the value of *str* is ignored leaving ``__str__`` to base classes.
- :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs``
+ :param bool collect_by_mro: Setting this to `True` fixes the way *attrs*
collects attributes from base classes. The default behavior is
incorrect in certain cases of multiple inheritance. It should be on by
default but is kept off for backward-compatibility.
- See issue `#428 `_ for
- more details.
+ .. seealso::
+ Issue `#428 `_
- :param Optional[bool] getstate_setstate:
+ :param bool | None getstate_setstate:
.. note::
This is usually only interesting for slotted classes and you should
probably just set *auto_detect* to `True`.
- If `True`, ``__getstate__`` and
- ``__setstate__`` are generated and attached to the class. This is
- necessary for slotted classes to be pickleable. If left `None`, it's
- `True` by default for slotted classes and ``False`` for dict classes.
+ If `True`, ``__getstate__`` and ``__setstate__`` are generated and
+ attached to the class. This is necessary for slotted classes to be
+ pickleable. If left `None`, it's `True` by default for slotted classes
+ and ``False`` for dict classes.
- If *auto_detect* is `True`, and *getstate_setstate* is left `None`,
- and **either** ``__getstate__`` or ``__setstate__`` is detected directly
- on the class (i.e. not inherited), it is set to `False` (this is usually
+ If *auto_detect* is `True`, and *getstate_setstate* is left `None`, and
+ **either** ``__getstate__`` or ``__setstate__`` is detected directly on
+ the class (i.e. not inherited), it is set to `False` (this is usually
what you want).
:param on_setattr: A callable that is run whenever the user attempts to set
@@ -1448,19 +1542,22 @@ def attrs(
If a list of callables is passed, they're automatically wrapped in an
`attrs.setters.pipe`.
+ :type on_setattr: `callable`, or a list of callables, or `None`, or
+ `attrs.setters.NO_OP`
- :param Optional[callable] field_transformer:
- A function that is called with the original class object and all
- fields right before ``attrs`` finalizes the class. You can use
- this, e.g., to automatically add converters or validators to
- fields based on their types. See `transform-fields` for more details.
+ :param callable | None field_transformer:
+ A function that is called with the original class object and all fields
+ right before *attrs* finalizes the class. You can use this, e.g., to
+ automatically add converters or validators to fields based on their
+ types.
+
+ .. seealso:: `transform-fields`
:param bool match_args:
If `True` (default), set ``__match_args__`` on the class to support
- `PEP 634 `_ (Structural
- Pattern Matching). It is a tuple of all positional-only ``__init__``
- parameter names on Python 3.10 and later. Ignored on older Python
- versions.
+ :pep:`634` (Structural Pattern Matching). It is a tuple of all
+ non-keyword-only ``__init__`` parameter names on Python 3.10 and later.
+ Ignored on older Python versions.
.. versionadded:: 16.0.0 *slots*
.. versionadded:: 16.1.0 *frozen*
@@ -1496,23 +1593,19 @@ def attrs(
.. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__``
.. versionchanged:: 21.1.0 *cmp* undeprecated
.. versionadded:: 21.3.0 *match_args*
+ .. versionadded:: 22.2.0
+ *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
"""
- if auto_detect and PY2:
- raise PythonTooOldError(
- "auto_detect only works on Python 3 and later."
- )
-
eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None)
- hash_ = hash # work around the lack of nonlocal
+
+ # unsafe_hash takes precedence due to PEP 681.
+ if unsafe_hash is not None:
+ hash = unsafe_hash
if isinstance(on_setattr, (list, tuple)):
on_setattr = setters.pipe(*on_setattr)
def wrap(cls):
-
- if getattr(cls, "__class__", None) is None:
- raise TypeError("attrs only works with new-style classes.")
-
is_frozen = frozen or _has_frozen_base_class(cls)
is_exc = auto_exc is True and issubclass(cls, BaseException)
has_own_setattr = auto_detect and _has_own_attribute(
@@ -1520,7 +1613,8 @@ def wrap(cls):
)
if has_own_setattr and is_frozen:
- raise ValueError("Can't freeze a class with a custom __setattr__.")
+ msg = "Can't freeze a class with a custom __setattr__."
+ raise ValueError(msg)
builder = _ClassBuilder(
cls,
@@ -1563,28 +1657,25 @@ def wrap(cls):
builder.add_setattr()
+ nonlocal hash
if (
- hash_ is None
+ hash is None
and auto_detect is True
and _has_own_attribute(cls, "__hash__")
):
hash = False
- else:
- hash = hash_
+
if hash is not True and hash is not False and hash is not None:
# Can't use `hash in` because 1 == True for example.
- raise TypeError(
- "Invalid value for hash. Must be True, False, or None."
- )
- elif hash is False or (hash is None and eq is False) or is_exc:
+ msg = "Invalid value for hash. Must be True, False, or None."
+ raise TypeError(msg)
+
+ if hash is False or (hash is None and eq is False) or is_exc:
# Don't do anything. Should fall back to __object__'s __hash__
# which is by id.
if cache_hash:
- raise TypeError(
- "Invalid value for cache_hash. To use hash caching,"
- " hashing must be either explicitly or implicitly "
- "enabled."
- )
+ msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
+ raise TypeError(msg)
elif hash is True or (
hash is None and eq is True and is_frozen is True
):
@@ -1593,11 +1684,8 @@ def wrap(cls):
else:
# Raise TypeError on attempts to hash.
if cache_hash:
- raise TypeError(
- "Invalid value for cache_hash. To use hash caching,"
- " hashing must be either explicitly or implicitly "
- "enabled."
- )
+ msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
+ raise TypeError(msg)
builder.make_unhashable()
if _determine_whether_to_implement(
@@ -1607,10 +1695,8 @@ def wrap(cls):
else:
builder.add_attrs_init()
if cache_hash:
- raise TypeError(
- "Invalid value for cache_hash. To use hash caching,"
- " init must be True."
- )
+ msg = "Invalid value for cache_hash. To use hash caching, init must be True."
+ raise TypeError(msg)
if (
PY310
@@ -1625,8 +1711,8 @@ def wrap(cls):
# if it's used as `@attrs` but ``None`` if used as `@attrs()`.
if maybe_cls is None:
return wrap
- else:
- return wrap(maybe_cls)
+
+ return wrap(maybe_cls)
_attrs = attrs
@@ -1636,39 +1722,22 @@ def wrap(cls):
"""
-if PY2:
-
- def _has_frozen_base_class(cls):
- """
- Check whether *cls* has a frozen ancestor by looking at its
- __setattr__.
- """
- return (
- getattr(cls.__setattr__, "__module__", None)
- == _frozen_setattrs.__module__
- and cls.__setattr__.__name__ == _frozen_setattrs.__name__
- )
-
-else:
-
- def _has_frozen_base_class(cls):
- """
- Check whether *cls* has a frozen ancestor by looking at its
- __setattr__.
- """
- return cls.__setattr__ == _frozen_setattrs
+def _has_frozen_base_class(cls):
+ """
+ Check whether *cls* has a frozen ancestor by looking at its
+ __setattr__.
+ """
+ return cls.__setattr__ is _frozen_setattrs
def _generate_unique_filename(cls, func_name):
"""
Create a "filename" suitable for a function being generated.
"""
- unique_filename = "".format(
- func_name,
- cls.__module__,
- getattr(cls, "__qualname__", cls.__name__),
+ return (
+ f""
)
- return unique_filename
def _make_hash(cls, attrs, frozen, cache_hash):
@@ -1680,6 +1749,8 @@ def _make_hash(cls, attrs, frozen, cache_hash):
unique_filename = _generate_unique_filename(cls, "hash")
type_hash = hash(unique_filename)
+ # If eq is custom generated, we need to include the functions in globs
+ globs = {}
hash_def = "def __hash__(self"
hash_func = "hash(("
@@ -1687,13 +1758,9 @@ def _make_hash(cls, attrs, frozen, cache_hash):
if not cache_hash:
hash_def += "):"
else:
- if not PY2:
- hash_def += ", *"
+ hash_def += ", *"
- hash_def += (
- ", _cache_wrapper="
- + "__import__('attr._make')._make._CacheHashWrapper):"
- )
+ hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):"
hash_func = "_cache_wrapper(" + hash_func
closing_braces += ")"
@@ -1709,32 +1776,39 @@ def append_hash_computation_lines(prefix, indent):
method_lines.extend(
[
indent + prefix + hash_func,
- indent + " %d," % (type_hash,),
+ indent + f" {type_hash},",
]
)
for a in attrs:
- method_lines.append(indent + " self.%s," % a.name)
+ if a.eq_key:
+ cmp_name = f"_{a.name}_key"
+ globs[cmp_name] = a.eq_key
+ method_lines.append(
+ indent + f" {cmp_name}(self.{a.name}),"
+ )
+ else:
+ method_lines.append(indent + f" self.{a.name},")
method_lines.append(indent + " " + closing_braces)
if cache_hash:
- method_lines.append(tab + "if self.%s is None:" % _hash_cache_field)
+ method_lines.append(tab + f"if self.{_hash_cache_field} is None:")
if frozen:
append_hash_computation_lines(
- "object.__setattr__(self, '%s', " % _hash_cache_field, tab * 2
+ f"object.__setattr__(self, '{_hash_cache_field}', ", tab * 2
)
method_lines.append(tab * 2 + ")") # close __setattr__
else:
append_hash_computation_lines(
- "self.%s = " % _hash_cache_field, tab * 2
+ f"self.{_hash_cache_field} = ", tab * 2
)
- method_lines.append(tab + "return self.%s" % _hash_cache_field)
+ method_lines.append(tab + f"return self.{_hash_cache_field}")
else:
append_hash_computation_lines("return ", tab)
script = "\n".join(method_lines)
- return _make_method("__hash__", script, unique_filename)
+ return _make_method("__hash__", script, unique_filename, globs)
def _add_hash(cls, attrs):
@@ -1785,29 +1859,17 @@ def _make_eq(cls, attrs):
others = [" ) == ("]
for a in attrs:
if a.eq_key:
- cmp_name = "_%s_key" % (a.name,)
+ cmp_name = f"_{a.name}_key"
# Add the key function to the global namespace
# of the evaluated function.
globs[cmp_name] = a.eq_key
- lines.append(
- " %s(self.%s),"
- % (
- cmp_name,
- a.name,
- )
- )
- others.append(
- " %s(other.%s),"
- % (
- cmp_name,
- a.name,
- )
- )
+ lines.append(f" {cmp_name}(self.{a.name}),")
+ others.append(f" {cmp_name}(other.{a.name}),")
else:
- lines.append(" self.%s," % (a.name,))
- others.append(" other.%s," % (a.name,))
+ lines.append(f" self.{a.name},")
+ others.append(f" other.{a.name},")
- lines += others + [" )"]
+ lines += [*others, " )"]
else:
lines.append(" return True")
@@ -1885,134 +1947,61 @@ def _add_eq(cls, attrs=None):
return cls
-if HAS_F_STRINGS:
-
- def _make_repr(attrs, ns, cls):
- unique_filename = _generate_unique_filename(cls, "repr")
- # Figure out which attributes to include, and which function to use to
- # format them. The a.repr value can be either bool or a custom
- # callable.
- attr_names_with_reprs = tuple(
- (a.name, (repr if a.repr is True else a.repr), a.init)
- for a in attrs
- if a.repr is not False
+def _make_repr(attrs, ns, cls):
+ unique_filename = _generate_unique_filename(cls, "repr")
+ # Figure out which attributes to include, and which function to use to
+ # format them. The a.repr value can be either bool or a custom
+ # callable.
+ attr_names_with_reprs = tuple(
+ (a.name, (repr if a.repr is True else a.repr), a.init)
+ for a in attrs
+ if a.repr is not False
+ )
+ globs = {
+ name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr
+ }
+ globs["_compat"] = _compat
+ globs["AttributeError"] = AttributeError
+ globs["NOTHING"] = NOTHING
+ attribute_fragments = []
+ for name, r, i in attr_names_with_reprs:
+ accessor = (
+ "self." + name if i else 'getattr(self, "' + name + '", NOTHING)'
)
- globs = {
- name + "_repr": r
- for name, r, _ in attr_names_with_reprs
- if r != repr
- }
- globs["_compat"] = _compat
- globs["AttributeError"] = AttributeError
- globs["NOTHING"] = NOTHING
- attribute_fragments = []
- for name, r, i in attr_names_with_reprs:
- accessor = (
- "self." + name
- if i
- else 'getattr(self, "' + name + '", NOTHING)'
- )
- fragment = (
- "%s={%s!r}" % (name, accessor)
- if r == repr
- else "%s={%s_repr(%s)}" % (name, name, accessor)
- )
- attribute_fragments.append(fragment)
- repr_fragment = ", ".join(attribute_fragments)
-
- if ns is None:
- cls_name_fragment = (
- '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}'
- )
- else:
- cls_name_fragment = ns + ".{self.__class__.__name__}"
-
- lines = [
- "def __repr__(self):",
- " try:",
- " already_repring = _compat.repr_context.already_repring",
- " except AttributeError:",
- " already_repring = {id(self),}",
- " _compat.repr_context.already_repring = already_repring",
- " else:",
- " if id(self) in already_repring:",
- " return '...'",
- " else:",
- " already_repring.add(id(self))",
- " try:",
- " return f'%s(%s)'" % (cls_name_fragment, repr_fragment),
- " finally:",
- " already_repring.remove(id(self))",
- ]
-
- return _make_method(
- "__repr__", "\n".join(lines), unique_filename, globs=globs
+ fragment = (
+ "%s={%s!r}" % (name, accessor)
+ if r == repr
+ else "%s={%s_repr(%s)}" % (name, name, accessor)
)
+ attribute_fragments.append(fragment)
+ repr_fragment = ", ".join(attribute_fragments)
-else:
-
- def _make_repr(attrs, ns, _):
- """
- Make a repr method that includes relevant *attrs*, adding *ns* to the
- full name.
- """
-
- # Figure out which attributes to include, and which function to use to
- # format them. The a.repr value can be either bool or a custom
- # callable.
- attr_names_with_reprs = tuple(
- (a.name, repr if a.repr is True else a.repr)
- for a in attrs
- if a.repr is not False
- )
-
- def __repr__(self):
- """
- Automatically created by attrs.
- """
- try:
- already_repring = _compat.repr_context.already_repring
- except AttributeError:
- already_repring = set()
- _compat.repr_context.already_repring = already_repring
-
- if id(self) in already_repring:
- return "..."
- real_cls = self.__class__
- if ns is None:
- qualname = getattr(real_cls, "__qualname__", None)
- if qualname is not None: # pragma: no cover
- # This case only happens on Python 3.5 and 3.6. We exclude
- # it from coverage, because we don't want to slow down our
- # test suite by running them under coverage too for this
- # one line.
- class_name = qualname.rsplit(">.", 1)[-1]
- else:
- class_name = real_cls.__name__
- else:
- class_name = ns + "." + real_cls.__name__
+ if ns is None:
+ cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}'
+ else:
+ cls_name_fragment = ns + ".{self.__class__.__name__}"
- # Since 'self' remains on the stack (i.e.: strongly referenced)
- # for the duration of this call, it's safe to depend on id(...)
- # stability, and not need to track the instance and therefore
- # worry about properties like weakref- or hash-ability.
- already_repring.add(id(self))
- try:
- result = [class_name, "("]
- first = True
- for name, attr_repr in attr_names_with_reprs:
- if first:
- first = False
- else:
- result.append(", ")
- result.extend(
- (name, "=", attr_repr(getattr(self, name, NOTHING)))
- )
- return "".join(result) + ")"
- finally:
- already_repring.remove(id(self))
+ lines = [
+ "def __repr__(self):",
+ " try:",
+ " already_repring = _compat.repr_context.already_repring",
+ " except AttributeError:",
+ " already_repring = {id(self),}",
+ " _compat.repr_context.already_repring = already_repring",
+ " else:",
+ " if id(self) in already_repring:",
+ " return '...'",
+ " else:",
+ " already_repring.add(id(self))",
+ " try:",
+ f" return f'{cls_name_fragment}({repr_fragment})'",
+ " finally:",
+ " already_repring.remove(id(self))",
+ ]
- return __repr__
+ return _make_method(
+ "__repr__", "\n".join(lines), unique_filename, globs=globs
+ )
def _add_repr(cls, ns=None, attrs=None):
@@ -2028,7 +2017,7 @@ def _add_repr(cls, ns=None, attrs=None):
def fields(cls):
"""
- Return the tuple of ``attrs`` attributes for a class.
+ Return the tuple of *attrs* attributes for a class.
The tuple also allows accessing the fields by their names (see below for
examples).
@@ -2036,50 +2025,61 @@ def fields(cls):
:param type cls: Class to introspect.
:raise TypeError: If *cls* is not a class.
- :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
class.
:rtype: tuple (with name accessors) of `attrs.Attribute`
- .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
- by name.
+ .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
+ by name.
+ .. versionchanged:: 23.1.0 Add support for generic classes.
"""
- if not isclass(cls):
- raise TypeError("Passed object must be a class.")
+ generic_base = get_generic_base(cls)
+
+ if generic_base is None and not isinstance(cls, type):
+ msg = "Passed object must be a class."
+ raise TypeError(msg)
+
attrs = getattr(cls, "__attrs_attrs__", None)
+
if attrs is None:
- raise NotAnAttrsClassError(
- "{cls!r} is not an attrs-decorated class.".format(cls=cls)
- )
+ if generic_base is not None:
+ attrs = getattr(generic_base, "__attrs_attrs__", None)
+ if attrs is not None:
+ # Even though this is global state, stick it on here to speed
+ # it up. We rely on `cls` being cached for this to be
+ # efficient.
+ cls.__attrs_attrs__ = attrs
+ return attrs
+ msg = f"{cls!r} is not an attrs-decorated class."
+ raise NotAnAttrsClassError(msg)
+
return attrs
def fields_dict(cls):
"""
- Return an ordered dictionary of ``attrs`` attributes for a class, whose
+ Return an ordered dictionary of *attrs* attributes for a class, whose
keys are the attribute names.
:param type cls: Class to introspect.
:raise TypeError: If *cls* is not a class.
- :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
+ :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
class.
- :rtype: an ordered dict where keys are attribute names and values are
- `attrs.Attribute`\\ s. This will be a `dict` if it's
- naturally ordered like on Python 3.6+ or an
- :class:`~collections.OrderedDict` otherwise.
+ :rtype: dict
.. versionadded:: 18.1.0
"""
- if not isclass(cls):
- raise TypeError("Passed object must be a class.")
+ if not isinstance(cls, type):
+ msg = "Passed object must be a class."
+ raise TypeError(msg)
attrs = getattr(cls, "__attrs_attrs__", None)
if attrs is None:
- raise NotAnAttrsClassError(
- "{cls!r} is not an attrs-decorated class.".format(cls=cls)
- )
- return ordered_dict(((a.name, a) for a in attrs))
+ msg = f"{cls!r} is not an attrs-decorated class."
+ raise NotAnAttrsClassError(msg)
+ return {a.name: a for a in attrs}
def validate(inst):
@@ -2088,7 +2088,7 @@ def validate(inst):
Leaves all exceptions through.
- :param inst: Instance of a class with ``attrs`` attributes.
+ :param inst: Instance of a class with *attrs* attributes.
"""
if _config._run_validators is False:
return
@@ -2114,6 +2114,7 @@ def _make_init(
cls,
attrs,
pre_init,
+ pre_init_has_args,
post_init,
frozen,
slots,
@@ -2128,7 +2129,8 @@ def _make_init(
)
if frozen and has_cls_on_setattr:
- raise ValueError("Frozen classes can't use on_setattr.")
+ msg = "Frozen classes can't use on_setattr."
+ raise ValueError(msg)
needs_cached_setattr = cache_hash or frozen
filtered_attrs = []
@@ -2142,7 +2144,8 @@ def _make_init(
if a.on_setattr is not None:
if frozen is True:
- raise ValueError("Frozen classes can't use on_setattr.")
+ msg = "Frozen classes can't use on_setattr."
+ raise ValueError(msg)
needs_cached_setattr = True
elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP:
@@ -2155,6 +2158,7 @@ def _make_init(
frozen,
slots,
pre_init,
+ pre_init_has_args,
post_init,
cache_hash,
base_attr_map,
@@ -2172,7 +2176,7 @@ def _make_init(
if needs_cached_setattr:
# Save the lookup overhead in __init__ if we need to circumvent
# setattr hooks.
- globs["_cached_setattr"] = _obj_setattr
+ globs["_cached_setattr_get"] = _obj_setattr.__get__
init = _make_method(
"__attrs_init__" if attrs_init else "__init__",
@@ -2189,7 +2193,7 @@ def _setattr(attr_name, value_var, has_on_setattr):
"""
Use the cached object.setattr to set *attr_name* to *value_var*.
"""
- return "_setattr('%s', %s)" % (attr_name, value_var)
+ return f"_setattr('{attr_name}', {value_var})"
def _setattr_with_converter(attr_name, value_var, has_on_setattr):
@@ -2212,7 +2216,7 @@ def _assign(attr_name, value, has_on_setattr):
if has_on_setattr:
return _setattr(attr_name, value, True)
- return "self.%s = %s" % (attr_name, value)
+ return f"self.{attr_name} = {value}"
def _assign_with_converter(attr_name, value_var, has_on_setattr):
@@ -2230,68 +2234,12 @@ def _assign_with_converter(attr_name, value_var, has_on_setattr):
)
-if PY2:
-
- def _unpack_kw_only_py2(attr_name, default=None):
- """
- Unpack *attr_name* from _kw_only dict.
- """
- if default is not None:
- arg_default = ", %s" % default
- else:
- arg_default = ""
- return "%s = _kw_only.pop('%s'%s)" % (
- attr_name,
- attr_name,
- arg_default,
- )
-
- def _unpack_kw_only_lines_py2(kw_only_args):
- """
- Unpack all *kw_only_args* from _kw_only dict and handle errors.
-
- Given a list of strings "{attr_name}" and "{attr_name}={default}"
- generates list of lines of code that pop attrs from _kw_only dict and
- raise TypeError similar to builtin if required attr is missing or
- extra key is passed.
-
- >>> print("\n".join(_unpack_kw_only_lines_py2(["a", "b=42"])))
- try:
- a = _kw_only.pop('a')
- b = _kw_only.pop('b', 42)
- except KeyError as _key_error:
- raise TypeError(
- ...
- if _kw_only:
- raise TypeError(
- ...
- """
- lines = ["try:"]
- lines.extend(
- " " + _unpack_kw_only_py2(*arg.split("="))
- for arg in kw_only_args
- )
- lines += """\
-except KeyError as _key_error:
- raise TypeError(
- '__init__() missing required keyword-only argument: %s' % _key_error
- )
-if _kw_only:
- raise TypeError(
- '__init__() got an unexpected keyword argument %r'
- % next(iter(_kw_only))
- )
-""".split(
- "\n"
- )
- return lines
-
-
def _attrs_to_init_script(
attrs,
frozen,
slots,
pre_init,
+ pre_init_has_args,
post_init,
cache_hash,
base_attr_map,
@@ -2317,7 +2265,7 @@ def _attrs_to_init_script(
# Circumvent the __setattr__ descriptor to save one lookup per
# assignment.
# Note _setattr will be used again below if cache_hash is True
- "_setattr = _cached_setattr.__get__(self, self.__class__)"
+ "_setattr = _cached_setattr_get(self)"
)
if frozen is True:
@@ -2335,7 +2283,7 @@ def fmt_setter(attr_name, value_var, has_on_setattr):
if _is_slot_attr(attr_name, base_attr_map):
return _setattr(attr_name, value_var, has_on_setattr)
- return "_inst_dict['%s'] = %s" % (attr_name, value_var)
+ return f"_inst_dict['{attr_name}'] = {value_var}"
def fmt_setter_with_converter(
attr_name, value_var, has_on_setattr
@@ -2373,22 +2321,21 @@ def fmt_setter_with_converter(
has_on_setattr = a.on_setattr is not None or (
a.on_setattr is not setters.NO_OP and has_cls_on_setattr
)
- arg_name = a.name.lstrip("_")
+ # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not
+ # explicitly provided
+ arg_name = a.alias
has_factory = isinstance(a.default, Factory)
- if has_factory and a.default.takes_self:
- maybe_self = "self"
- else:
- maybe_self = ""
+ maybe_self = "self" if has_factory and a.default.takes_self else ""
if a.init is False:
if has_factory:
- init_factory_name = _init_factory_pat.format(a.name)
+ init_factory_name = _init_factory_pat % (a.name,)
if a.converter is not None:
lines.append(
fmt_setter_with_converter(
attr_name,
- init_factory_name + "(%s)" % (maybe_self,),
+ init_factory_name + f"({maybe_self})",
has_on_setattr,
)
)
@@ -2398,32 +2345,31 @@ def fmt_setter_with_converter(
lines.append(
fmt_setter(
attr_name,
- init_factory_name + "(%s)" % (maybe_self,),
+ init_factory_name + f"({maybe_self})",
has_on_setattr,
)
)
names_for_globals[init_factory_name] = a.default.factory
- else:
- if a.converter is not None:
- lines.append(
- fmt_setter_with_converter(
- attr_name,
- "attr_dict['%s'].default" % (attr_name,),
- has_on_setattr,
- )
+ elif a.converter is not None:
+ lines.append(
+ fmt_setter_with_converter(
+ attr_name,
+ f"attr_dict['{attr_name}'].default",
+ has_on_setattr,
)
- conv_name = _init_converter_pat % (a.name,)
- names_for_globals[conv_name] = a.converter
- else:
- lines.append(
- fmt_setter(
- attr_name,
- "attr_dict['%s'].default" % (attr_name,),
- has_on_setattr,
- )
+ )
+ conv_name = _init_converter_pat % (a.name,)
+ names_for_globals[conv_name] = a.converter
+ else:
+ lines.append(
+ fmt_setter(
+ attr_name,
+ f"attr_dict['{attr_name}'].default",
+ has_on_setattr,
)
+ )
elif a.default is not NOTHING and not has_factory:
- arg = "%s=attr_dict['%s'].default" % (arg_name, attr_name)
+ arg = f"{arg_name}=attr_dict['{attr_name}'].default"
if a.kw_only:
kw_only_args.append(arg)
else:
@@ -2442,14 +2388,14 @@ def fmt_setter_with_converter(
lines.append(fmt_setter(attr_name, arg_name, has_on_setattr))
elif has_factory:
- arg = "%s=NOTHING" % (arg_name,)
+ arg = f"{arg_name}=NOTHING"
if a.kw_only:
kw_only_args.append(arg)
else:
args.append(arg)
- lines.append("if %s is not NOTHING:" % (arg_name,))
+ lines.append(f"if {arg_name} is not NOTHING:")
- init_factory_name = _init_factory_pat.format(a.name)
+ init_factory_name = _init_factory_pat % (a.name,)
if a.converter is not None:
lines.append(
" "
@@ -2504,21 +2450,11 @@ def fmt_setter_with_converter(
if a.init is True:
if a.type is not None and a.converter is None:
annotations[arg_name] = a.type
- elif a.converter is not None and not PY2:
+ elif a.converter is not None:
# Try to get the type from the converter.
- sig = None
- try:
- sig = inspect.signature(a.converter)
- except (ValueError, TypeError): # inspect failed
- pass
- if sig:
- sig_params = list(sig.parameters.values())
- if (
- sig_params
- and sig_params[0].annotation
- is not inspect.Parameter.empty
- ):
- annotations[arg_name] = sig_params[0].annotation
+ t = _AnnotationExtractor(a.converter).get_first_param_type()
+ if t:
+ annotations[arg_name] = t
if attrs_to_validate: # we can skip this if there are no validators.
names_for_globals["_config"] = _config
@@ -2526,23 +2462,21 @@ def fmt_setter_with_converter(
for a in attrs_to_validate:
val_name = "__attr_validator_" + a.name
attr_name = "__attr_" + a.name
- lines.append(
- " %s(self, %s, self.%s)" % (val_name, attr_name, a.name)
- )
+ lines.append(f" {val_name}(self, {attr_name}, self.{a.name})")
names_for_globals[val_name] = a.validator
names_for_globals[attr_name] = a
if post_init:
lines.append("self.__attrs_post_init__()")
- # because this is set only after __attrs_post_init is called, a crash
+ # because this is set only after __attrs_post_init__ is called, a crash
# will result if post-init tries to access the hash code. This seemed
# preferable to setting this beforehand, in which case alteration to
# field values during post-init combined with post-init accessing the
# hash code would result in silent bugs.
if cache_hash:
if frozen:
- if slots:
+ if slots: # noqa: SIM108
# if frozen and slots, then _setattr defined above
init_hash_cache = "_setattr('%s', %s)"
else:
@@ -2555,44 +2489,67 @@ def fmt_setter_with_converter(
# For exceptions we rely on BaseException.__init__ for proper
# initialization.
if is_exc:
- vals = ",".join("self." + a.name for a in attrs if a.init)
+ vals = ",".join(f"self.{a.name}" for a in attrs if a.init)
- lines.append("BaseException.__init__(self, %s)" % (vals,))
+ lines.append(f"BaseException.__init__(self, {vals})")
args = ", ".join(args)
+ pre_init_args = args
if kw_only_args:
- if PY2:
- lines = _unpack_kw_only_lines_py2(kw_only_args) + lines
+ args += "%s*, %s" % (
+ ", " if args else "", # leading comma
+ ", ".join(kw_only_args), # kw_only args
+ )
+ pre_init_kw_only_args = ", ".join(
+ ["%s=%s" % (kw_arg, kw_arg) for kw_arg in kw_only_args]
+ )
+ pre_init_args += (
+ ", " if pre_init_args else ""
+ ) # handle only kwargs and no regular args
+ pre_init_args += pre_init_kw_only_args
+
+ if pre_init and pre_init_has_args:
+ # If pre init method has arguments, pass same arguments as `__init__`
+ lines[0] = "self.__attrs_pre_init__(%s)" % pre_init_args
- args += "%s**_kw_only" % (", " if args else "",) # leading comma
- else:
- args += "%s*, %s" % (
- ", " if args else "", # leading comma
- ", ".join(kw_only_args), # kw_only args
- )
return (
- """\
-def {init_name}(self, {args}):
- {lines}
-""".format(
- init_name=("__attrs_init__" if attrs_init else "__init__"),
- args=args,
- lines="\n ".join(lines) if lines else "pass",
+ "def %s(self, %s):\n %s\n"
+ % (
+ ("__attrs_init__" if attrs_init else "__init__"),
+ args,
+ "\n ".join(lines) if lines else "pass",
),
names_for_globals,
annotations,
)
-class Attribute(object):
+def _default_init_alias_for(name: str) -> str:
+ """
+ The default __init__ parameter name for a field.
+
+ This performs private-name adjustment via leading-unscore stripping,
+ and is the default value of Attribute.alias if not provided.
+ """
+
+ return name.lstrip("_")
+
+
+class Attribute:
"""
*Read-only* representation of an attribute.
+ .. warning::
+
+ You should never instantiate this class yourself.
+
The class has *all* arguments of `attr.ib` (except for ``factory``
which is only syntactic sugar for ``default=Factory(...)`` plus the
following:
- ``name`` (`str`): The name of the attribute.
+ - ``alias`` (`str`): The __init__ parameter name of the attribute, after
+ any explicit overrides and default private-attribute-name handling.
- ``inherited`` (`bool`): Whether or not that attribute has been inherited
from a base class.
- ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The callables
@@ -2608,12 +2565,16 @@ class Attribute(object):
- Validators get them passed as the first argument.
- The :ref:`field transformer ` hook receives a list of
them.
+ - The ``alias`` property exposes the __init__ parameter name of the field,
+ with any overrides and default private-attribute handling applied.
+
.. versionadded:: 20.1.0 *inherited*
.. versionadded:: 20.1.0 *on_setattr*
.. versionchanged:: 20.2.0 *inherited* is not taken into account for
equality checks and hashing anymore.
.. versionadded:: 21.1.0 *eq_key* and *order_key*
+ .. versionadded:: 22.2.0 *alias*
For the full version history of the fields, see `attr.ib`.
"""
@@ -2635,6 +2596,7 @@ class Attribute(object):
"kw_only",
"inherited",
"on_setattr",
+ "alias",
)
def __init__(
@@ -2656,13 +2618,14 @@ def __init__(
order=None,
order_key=None,
on_setattr=None,
+ alias=None,
):
eq, eq_key, order, order_key = _determine_attrib_eq_order(
cmp, eq_key or eq, order_key or order, True
)
# Cache this descriptor here to speed things up later.
- bound_setattr = _obj_setattr.__get__(self, Attribute)
+ bound_setattr = _obj_setattr.__get__(self)
# Despite the big red warning, people *do* instantiate `Attribute`
# themselves.
@@ -2680,7 +2643,7 @@ def __init__(
bound_setattr(
"metadata",
(
- metadata_proxy(metadata)
+ types.MappingProxyType(dict(metadata)) # Shallow copy
if metadata
else _empty_metadata_singleton
),
@@ -2689,6 +2652,7 @@ def __init__(
bound_setattr("kw_only", kw_only)
bound_setattr("inherited", inherited)
bound_setattr("on_setattr", on_setattr)
+ bound_setattr("alias", alias)
def __setattr__(self, name, value):
raise FrozenInstanceError()
@@ -2699,9 +2663,8 @@ def from_counting_attr(cls, name, ca, type=None):
if type is None:
type = ca.type
elif ca.type is not None:
- raise ValueError(
- "Type annotation and type argument cannot both be present"
- )
+ msg = "Type annotation and type argument cannot both be present"
+ raise ValueError(msg)
inst_dict = {
k: getattr(ca, k)
for k in Attribute.__slots__
@@ -2721,25 +2684,16 @@ def from_counting_attr(cls, name, ca, type=None):
type=type,
cmp=None,
inherited=False,
- **inst_dict
+ **inst_dict,
)
- @property
- def cmp(self):
- """
- Simulate the presence of a cmp attribute and warn.
- """
- warnings.warn(_CMP_DEPRECATION, DeprecationWarning, stacklevel=2)
-
- return self.eq and self.order
-
- # Don't use attr.evolve since fields(Attribute) doesn't work
+ # Don't use attrs.evolve since fields(Attribute) doesn't work
def evolve(self, **changes):
"""
Copy *self* and apply *changes*.
- This works similarly to `attr.evolve` but that function does not work
- with ``Attribute``.
+ This works similarly to `attrs.evolve` but that function does not work
+ with `Attribute`.
It is mainly meant to be used for `transform-fields`.
@@ -2768,14 +2722,14 @@ def __setstate__(self, state):
self._setattrs(zip(self.__slots__, state))
def _setattrs(self, name_values_pairs):
- bound_setattr = _obj_setattr.__get__(self, Attribute)
+ bound_setattr = _obj_setattr.__get__(self)
for name, value in name_values_pairs:
if name != "metadata":
bound_setattr(name, value)
else:
bound_setattr(
name,
- metadata_proxy(value)
+ types.MappingProxyType(dict(value))
if value
else _empty_metadata_singleton,
)
@@ -2793,6 +2747,7 @@ def _setattrs(self, name_values_pairs):
hash=(name != "metadata"),
init=True,
inherited=False,
+ alias=_default_init_alias_for(name),
)
for name in Attribute.__slots__
]
@@ -2806,7 +2761,7 @@ def _setattrs(self, name_values_pairs):
)
-class _CountingAttr(object):
+class _CountingAttr:
"""
Intermediate representation of attributes that uses a counter to preserve
the order in which the attributes have been defined.
@@ -2831,37 +2786,42 @@ class _CountingAttr(object):
"type",
"kw_only",
"on_setattr",
+ "alias",
)
- __attrs_attrs__ = tuple(
- Attribute(
- name=name,
- default=NOTHING,
- validator=None,
- repr=True,
- cmp=None,
- hash=True,
- init=True,
- kw_only=False,
- eq=True,
- eq_key=None,
- order=False,
- order_key=None,
- inherited=False,
- on_setattr=None,
- )
- for name in (
- "counter",
- "_default",
- "repr",
- "eq",
- "order",
- "hash",
- "init",
- "on_setattr",
- )
- ) + (
+ __attrs_attrs__ = (
+ *tuple(
+ Attribute(
+ name=name,
+ alias=_default_init_alias_for(name),
+ default=NOTHING,
+ validator=None,
+ repr=True,
+ cmp=None,
+ hash=True,
+ init=True,
+ kw_only=False,
+ eq=True,
+ eq_key=None,
+ order=False,
+ order_key=None,
+ inherited=False,
+ on_setattr=None,
+ )
+ for name in (
+ "counter",
+ "_default",
+ "repr",
+ "eq",
+ "order",
+ "hash",
+ "init",
+ "on_setattr",
+ "alias",
+ )
+ ),
Attribute(
name="metadata",
+ alias="metadata",
default=None,
validator=None,
repr=True,
@@ -2896,6 +2856,7 @@ def __init__(
order,
order_key,
on_setattr,
+ alias,
):
_CountingAttr.cls_counter += 1
self.counter = _CountingAttr.cls_counter
@@ -2913,6 +2874,7 @@ def __init__(
self.type = type
self.kw_only = kw_only
self.on_setattr = on_setattr
+ self.alias = alias
def validator(self, meth):
"""
@@ -2949,7 +2911,7 @@ def default(self, meth):
_CountingAttr = _add_eq(_add_repr(_CountingAttr))
-class Factory(object):
+class Factory:
"""
Stores a factory callable.
@@ -2967,10 +2929,6 @@ class Factory(object):
__slots__ = ("factory", "takes_self")
def __init__(self, factory, takes_self=False):
- """
- `Factory` is part of the default machinery so if we want a default
- value here, we have to implement it ourselves.
- """
self.factory = factory
self.takes_self = takes_self
@@ -3007,23 +2965,26 @@ def __setstate__(self, state):
Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)
-def make_class(name, attrs, bases=(object,), **attributes_arguments):
- """
+def make_class(
+ name, attrs, bases=(object,), class_body=None, **attributes_arguments
+):
+ r"""
A quick way to create a new class called *name* with *attrs*.
:param str name: The name for the new class.
:param attrs: A list of names or a dictionary of mappings of names to
- attributes.
+ `attr.ib`\ s / `attrs.field`\ s.
- If *attrs* is a list or an ordered dict (`dict` on Python 3.6+,
- `collections.OrderedDict` otherwise), the order is deduced from
- the order of the names or attributes inside *attrs*. Otherwise the
- order of the definition of the attributes is used.
+ The order is deduced from the order of the names or attributes inside
+ *attrs*. Otherwise the order of the definition of the attributes is
+ used.
:type attrs: `list` or `dict`
:param tuple bases: Classes that the new class will subclass.
+ :param dict class_body: An optional dictionary of class attributes for the new class.
+
:param attributes_arguments: Passed unmodified to `attr.s`.
:return: A new class with *attrs*.
@@ -3031,19 +2992,23 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
.. versionadded:: 17.1.0 *bases*
.. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained.
+ .. versionchanged:: 23.2.0 *class_body*
"""
if isinstance(attrs, dict):
cls_dict = attrs
elif isinstance(attrs, (list, tuple)):
- cls_dict = dict((a, attrib()) for a in attrs)
+ cls_dict = {a: attrib() for a in attrs}
else:
- raise TypeError("attrs argument must be a dict or a list.")
+ msg = "attrs argument must be a dict or a list."
+ raise TypeError(msg)
pre_init = cls_dict.pop("__attrs_pre_init__", None)
post_init = cls_dict.pop("__attrs_post_init__", None)
user_init = cls_dict.pop("__init__", None)
body = {}
+ if class_body is not None:
+ body.update(class_body)
if pre_init is not None:
body["__attrs_pre_init__"] = pre_init
if post_init is not None:
@@ -3051,18 +3016,16 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
if user_init is not None:
body["__init__"] = user_init
- type_ = new_class(name, bases, {}, lambda ns: ns.update(body))
+ type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body))
# For pickling to work, the __module__ variable needs to be set to the
# frame where the class is created. Bypass this step in environments where
# sys._getframe is not defined (Jython for example) or sys._getframe is not
# defined for arguments greater than 0 (IronPython).
- try:
+ with contextlib.suppress(AttributeError, ValueError):
type_.__module__ = sys._getframe(1).f_globals.get(
"__name__", "__main__"
)
- except (AttributeError, ValueError):
- pass
# We do it here for proper warnings with meaningful stacklevel.
cmp = attributes_arguments.pop("cmp", None)
@@ -3084,7 +3047,7 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
@attrs(slots=True, hash=True)
-class _AndValidator(object):
+class _AndValidator:
"""
Compose many validators to a single one.
"""
@@ -3138,36 +3101,19 @@ def pipe_converter(val):
return val
- if not PY2:
- if not converters:
- # If the converter list is empty, pipe_converter is the identity.
- A = typing.TypeVar("A")
- pipe_converter.__annotations__ = {"val": A, "return": A}
- else:
- # Get parameter type.
- sig = None
- try:
- sig = inspect.signature(converters[0])
- except (ValueError, TypeError): # inspect failed
- pass
- if sig:
- params = list(sig.parameters.values())
- if (
- params
- and params[0].annotation is not inspect.Parameter.empty
- ):
- pipe_converter.__annotations__["val"] = params[
- 0
- ].annotation
- # Get return type.
- sig = None
- try:
- sig = inspect.signature(converters[-1])
- except (ValueError, TypeError): # inspect failed
- pass
- if sig and sig.return_annotation is not inspect.Signature().empty:
- pipe_converter.__annotations__[
- "return"
- ] = sig.return_annotation
+ if not converters:
+ # If the converter list is empty, pipe_converter is the identity.
+ A = typing.TypeVar("A")
+ pipe_converter.__annotations__ = {"val": A, "return": A}
+ else:
+ # Get parameter type from first converter.
+ t = _AnnotationExtractor(converters[0]).get_first_param_type()
+ if t:
+ pipe_converter.__annotations__["val"] = t
+
+ # Get return type from last converter.
+ rt = _AnnotationExtractor(converters[-1]).get_return_type()
+ if rt:
+ pipe_converter.__annotations__["return"] = rt
return pipe_converter
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_next_gen.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_next_gen.py
index 0682536..1fb9f25 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_next_gen.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_next_gen.py
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: MIT
"""
-These are Python 3.6+-only and keyword-only APIs that call `attr.s` and
-`attr.ib` with different default values.
+These are keyword-only APIs that call `attr.s` and `attr.ib` with different
+default values.
"""
@@ -26,6 +26,7 @@ def define(
*,
these=None,
repr=None,
+ unsafe_hash=None,
hash=None,
init=None,
slots=True,
@@ -45,20 +46,24 @@ def define(
match_args=True,
):
r"""
- Define an ``attrs`` class.
+ Define an *attrs* class.
Differences to the classic `attr.s` that it uses underneath:
- - Automatically detect whether or not *auto_attribs* should be `True`
- (c.f. *auto_attribs* parameter).
- - If *frozen* is `False`, run converters and validators when setting an
- attribute by default.
- - *slots=True* (see :term:`slotted classes` for potentially surprising
- behaviors)
+ - Automatically detect whether or not *auto_attribs* should be `True` (c.f.
+ *auto_attribs* parameter).
+ - Converters and validators run when attributes are set by default -- if
+ *frozen* is `False`.
+ - *slots=True*
+
+ .. caution::
+
+ Usually this has only upsides and few visible effects in everyday
+ programming. But it *can* lead to some surprising behaviors, so please
+ make sure to read :term:`slotted classes`.
- *auto_exc=True*
- *auto_detect=True*
- *order=False*
- - *match_args=True*
- Some options that were only relevant on Python 2 or were kept around for
backwards-compatibility have been removed.
@@ -77,6 +82,8 @@ def define(
.. versionadded:: 20.1.0
.. versionchanged:: 21.3.0 Converters are also run ``on_setattr``.
+ .. versionadded:: 22.2.0
+ *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
"""
def do_it(cls, auto_attribs):
@@ -85,6 +92,7 @@ def do_it(cls, auto_attribs):
these=these,
repr=repr,
hash=hash,
+ unsafe_hash=unsafe_hash,
init=init,
slots=slots,
frozen=frozen,
@@ -123,10 +131,8 @@ def wrap(cls):
for base_cls in cls.__bases__:
if base_cls.__setattr__ is _frozen_setattrs:
if had_on_setattr:
- raise ValueError(
- "Frozen classes can't use on_setattr "
- "(frozen-ness was inherited)."
- )
+ msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)."
+ raise ValueError(msg)
on_setattr = setters.NO_OP
break
@@ -143,8 +149,8 @@ def wrap(cls):
# if it's used as `@attrs` but ``None`` if used as `@attrs()`.
if maybe_cls is None:
return wrap
- else:
- return wrap(maybe_cls)
+
+ return wrap(maybe_cls)
mutable = define
@@ -159,17 +165,22 @@ def field(
hash=None,
init=True,
metadata=None,
+ type=None,
converter=None,
factory=None,
kw_only=False,
eq=None,
order=None,
on_setattr=None,
+ alias=None,
):
"""
Identical to `attr.ib`, except keyword-only and with some arguments
removed.
+ .. versionadded:: 23.1.0
+ The *type* parameter has been re-added; mostly for `attrs.make_class`.
+ Please note that type checkers ignore this metadata.
.. versionadded:: 20.1.0
"""
return attrib(
@@ -179,12 +190,14 @@ def field(
hash=hash,
init=init,
metadata=metadata,
+ type=type,
converter=converter,
factory=factory,
kw_only=kw_only,
eq=eq,
order=order,
on_setattr=on_setattr,
+ alias=alias,
)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_version_info.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_version_info.py
index cdaeec3..51a1312 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_version_info.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/_version_info.py
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
from functools import total_ordering
@@ -10,7 +9,7 @@
@total_ordering
@attrs(eq=False, order=False, slots=True, frozen=True)
-class VersionInfo(object):
+class VersionInfo:
"""
A version object that can be compared to tuple of length 1--4:
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.py
index 1fb6c05..2bf4c90 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.py
@@ -4,15 +4,11 @@
Commonly useful converters.
"""
-from __future__ import absolute_import, division, print_function
-
-from ._compat import PY2
-from ._make import NOTHING, Factory, pipe
+import typing
-if not PY2:
- import inspect
- import typing
+from ._compat import _AnnotationExtractor
+from ._make import NOTHING, Factory, pipe
__all__ = [
@@ -42,22 +38,15 @@ def optional_converter(val):
return None
return converter(val)
- if not PY2:
- sig = None
- try:
- sig = inspect.signature(converter)
- except (ValueError, TypeError): # inspect failed
- pass
- if sig:
- params = list(sig.parameters.values())
- if params and params[0].annotation is not inspect.Parameter.empty:
- optional_converter.__annotations__["val"] = typing.Optional[
- params[0].annotation
- ]
- if sig.return_annotation is not inspect.Signature.empty:
- optional_converter.__annotations__["return"] = typing.Optional[
- sig.return_annotation
- ]
+ xtr = _AnnotationExtractor(converter)
+
+ t = xtr.get_first_param_type()
+ if t:
+ optional_converter.__annotations__["val"] = typing.Optional[t]
+
+ rt = xtr.get_return_type()
+ if rt:
+ optional_converter.__annotations__["return"] = typing.Optional[rt]
return optional_converter
@@ -81,21 +70,20 @@ def default_if_none(default=NOTHING, factory=None):
.. versionadded:: 18.2.0
"""
if default is NOTHING and factory is None:
- raise TypeError("Must pass either `default` or `factory`.")
+ msg = "Must pass either `default` or `factory`."
+ raise TypeError(msg)
if default is not NOTHING and factory is not None:
- raise TypeError(
- "Must pass either `default` or `factory` but not both."
- )
+ msg = "Must pass either `default` or `factory` but not both."
+ raise TypeError(msg)
if factory is not None:
default = Factory(factory)
if isinstance(default, Factory):
if default.takes_self:
- raise ValueError(
- "`takes_self` is not supported by default_if_none."
- )
+ msg = "`takes_self` is not supported by default_if_none."
+ raise ValueError(msg)
def default_if_none_converter(val):
if val is not None:
@@ -152,4 +140,5 @@ def to_bool(val):
except TypeError:
# Raised when "val" is not hashable (e.g., lists)
pass
- raise ValueError("Cannot convert value to bool: {}".format(val))
+ msg = f"Cannot convert value to bool: {val}"
+ raise ValueError(msg)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.pyi b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.pyi
index 0f58088..5abb49f 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.pyi
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/converters.pyi
@@ -1,4 +1,4 @@
-from typing import Callable, Optional, TypeVar, overload
+from typing import Callable, TypeVar, overload
from . import _ConverterType
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/exceptions.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/exceptions.py
index b2f1edc..3b7abb8 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/exceptions.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/exceptions.py
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: MIT
-from __future__ import absolute_import, division, print_function
+from __future__ import annotations
+
+from typing import ClassVar
class FrozenError(AttributeError):
@@ -15,7 +17,7 @@ class FrozenError(AttributeError):
"""
msg = "can't set attribute"
- args = [msg]
+ args: ClassVar[tuple[str]] = [msg]
class FrozenInstanceError(FrozenError):
@@ -36,7 +38,7 @@ class FrozenAttributeError(FrozenError):
class AttrsAttributeNotFoundError(ValueError):
"""
- An ``attrs`` function couldn't find an attribute that the user asked for.
+ An *attrs* function couldn't find an attribute that the user asked for.
.. versionadded:: 16.2.0
"""
@@ -44,7 +46,7 @@ class AttrsAttributeNotFoundError(ValueError):
class NotAnAttrsClassError(ValueError):
"""
- A non-``attrs`` class has been passed into an ``attrs`` function.
+ A non-*attrs* class has been passed into an *attrs* function.
.. versionadded:: 16.2.0
"""
@@ -52,7 +54,7 @@ class NotAnAttrsClassError(ValueError):
class DefaultAlreadySetError(RuntimeError):
"""
- A default has been set using ``attr.ib()`` and is attempted to be reset
+ A default has been set when defining the field and is attempted to be reset
using the decorator.
.. versionadded:: 17.1.0
@@ -61,8 +63,7 @@ class DefaultAlreadySetError(RuntimeError):
class UnannotatedAttributeError(RuntimeError):
"""
- A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type
- annotation.
+ A class with ``auto_attribs=True`` has a field without a type annotation.
.. versionadded:: 17.3.0
"""
@@ -70,7 +71,7 @@ class UnannotatedAttributeError(RuntimeError):
class PythonTooOldError(RuntimeError):
"""
- It was attempted to use an ``attrs`` feature that requires a newer Python
+ It was attempted to use an *attrs* feature that requires a newer Python
version.
.. versionadded:: 18.2.0
@@ -79,8 +80,8 @@ class PythonTooOldError(RuntimeError):
class NotCallableError(TypeError):
"""
- A ``attr.ib()`` requiring a callable has been set with a value
- that is not callable.
+ A field requiring a callable has been set with a value that is not
+ callable.
.. versionadded:: 19.2.0
"""
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.py
index a1978a8..a1e40c9 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.py
@@ -4,9 +4,6 @@
Commonly useful filters for `attr.asdict`.
"""
-from __future__ import absolute_import, division, print_function
-
-from ._compat import isclass
from ._make import Attribute
@@ -15,7 +12,8 @@ def _split_what(what):
Returns a tuple of `frozenset`s of classes and attributes.
"""
return (
- frozenset(cls for cls in what if isclass(cls)),
+ frozenset(cls for cls in what if isinstance(cls, type)),
+ frozenset(cls for cls in what if isinstance(cls, str)),
frozenset(cls for cls in what if isinstance(cls, Attribute)),
)
@@ -25,14 +23,21 @@ def include(*what):
Include *what*.
:param what: What to include.
- :type what: `list` of `type` or `attrs.Attribute`\\ s
+ :type what: `list` of classes `type`, field names `str` or
+ `attrs.Attribute`\\ s
:rtype: `callable`
+
+ .. versionchanged:: 23.1.0 Accept strings with field names.
"""
- cls, attrs = _split_what(what)
+ cls, names, attrs = _split_what(what)
def include_(attribute, value):
- return value.__class__ in cls or attribute in attrs
+ return (
+ value.__class__ in cls
+ or attribute.name in names
+ or attribute in attrs
+ )
return include_
@@ -42,13 +47,20 @@ def exclude(*what):
Exclude *what*.
:param what: What to exclude.
- :type what: `list` of classes or `attrs.Attribute`\\ s.
+ :type what: `list` of classes `type`, field names `str` or
+ `attrs.Attribute`\\ s.
:rtype: `callable`
+
+ .. versionchanged:: 23.3.0 Accept field name string as input argument
"""
- cls, attrs = _split_what(what)
+ cls, names, attrs = _split_what(what)
def exclude_(attribute, value):
- return value.__class__ not in cls and attribute not in attrs
+ return not (
+ value.__class__ in cls
+ or attribute.name in names
+ or attribute in attrs
+ )
return exclude_
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.pyi b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.pyi
index 9938668..8a02fa0 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.pyi
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/filters.pyi
@@ -2,5 +2,5 @@ from typing import Any, Union
from . import Attribute, _FilterType
-def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
-def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
+def include(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...
+def exclude(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.py
index b1cbb5d..12ed675 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.py
@@ -4,7 +4,6 @@
Commonly used hooks for on_setattr.
"""
-from __future__ import absolute_import, division, print_function
from . import _config
from .exceptions import FrozenAttributeError
@@ -69,11 +68,6 @@ def convert(instance, attrib, new_value):
return new_value
+# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes.
+# autodata stopped working, so the docstring is inlined in the API docs.
NO_OP = object()
-"""
-Sentinel for disabling class-wide *on_setattr* hooks for certain attributes.
-
-Does not work in `pipe` or within lists.
-
-.. versionadded:: 20.1.0
-"""
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.pyi b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.pyi
index 3f5603c..72f7ce4 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.pyi
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/setters.pyi
@@ -1,4 +1,4 @@
-from typing import Any, NewType, NoReturn, TypeVar, cast
+from typing import Any, NewType, NoReturn, TypeVar
from . import Attribute, _OnSetAttrType
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.py
index 0b0c834..34d6b76 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.py
@@ -4,24 +4,19 @@
Commonly useful validators.
"""
-from __future__ import absolute_import, division, print_function
import operator
import re
from contextlib import contextmanager
+from re import Pattern
from ._config import get_run_validators, set_run_validators
from ._make import _AndValidator, and_, attrib, attrs
+from .converters import default_if_none
from .exceptions import NotCallableError
-try:
- Pattern = re.Pattern
-except AttributeError: # Python <3.7 lacks a Pattern type.
- Pattern = type(re.compile(""))
-
-
__all__ = [
"and_",
"deep_iterable",
@@ -37,6 +32,8 @@
"lt",
"matches_re",
"max_len",
+ "min_len",
+ "not_",
"optional",
"provides",
"set_disabled",
@@ -92,7 +89,7 @@ def disabled():
@attrs(repr=False, slots=True, hash=True)
-class _InstanceOfValidator(object):
+class _InstanceOfValidator:
type = attrib()
def __call__(self, inst, attr, value):
@@ -100,23 +97,21 @@ def __call__(self, inst, attr, value):
We use a callable class to be able to change the ``__repr__``.
"""
if not isinstance(value, self.type):
+ msg = "'{name}' must be {type!r} (got {value!r} that is a {actual!r}).".format(
+ name=attr.name,
+ type=self.type,
+ actual=value.__class__,
+ value=value,
+ )
raise TypeError(
- "'{name}' must be {type!r} (got {value!r} that is a "
- "{actual!r}).".format(
- name=attr.name,
- type=self.type,
- actual=value.__class__,
- value=value,
- ),
+ msg,
attr,
self.type,
value,
)
def __repr__(self):
- return "".format(
- type=self.type
- )
+ return f""
def instance_of(type):
@@ -126,7 +121,7 @@ def instance_of(type):
`isinstance` therefore it's also valid to pass a tuple of types).
:param type: The type to check for.
- :type type: type or tuple of types
+ :type type: type or tuple of type
:raises TypeError: With a human readable error message, the attribute
(of type `attrs.Attribute`), the expected type, and the value it
@@ -136,7 +131,7 @@ def instance_of(type):
@attrs(repr=False, frozen=True, slots=True)
-class _MatchesReValidator(object):
+class _MatchesReValidator:
pattern = attrib()
match_func = attrib()
@@ -145,20 +140,18 @@ def __call__(self, inst, attr, value):
We use a callable class to be able to change the ``__repr__``.
"""
if not self.match_func(value):
+ msg = "'{name}' must match regex {pattern!r} ({value!r} doesn't)".format(
+ name=attr.name, pattern=self.pattern.pattern, value=value
+ )
raise ValueError(
- "'{name}' must match regex {pattern!r}"
- " ({value!r} doesn't)".format(
- name=attr.name, pattern=self.pattern.pattern, value=value
- ),
+ msg,
attr,
self.pattern,
value,
)
def __repr__(self):
- return "".format(
- pattern=self.pattern
- )
+ return f""
def matches_re(regex, flags=0, func=None):
@@ -169,34 +162,27 @@ def matches_re(regex, flags=0, func=None):
:param regex: a regex string or precompiled pattern to match against
:param int flags: flags that will be passed to the underlying re function
(default 0)
- :param callable func: which underlying `re` function to call (options
- are `re.fullmatch`, `re.search`, `re.match`, default
- is ``None`` which means either `re.fullmatch` or an emulation of
- it on Python 2). For performance reasons, they won't be used directly
- but on a pre-`re.compile`\ ed pattern.
+ :param callable func: which underlying `re` function to call. Valid options
+ are `re.fullmatch`, `re.search`, and `re.match`; the default ``None``
+ means `re.fullmatch`. For performance reasons, the pattern is always
+ precompiled using `re.compile`.
.. versionadded:: 19.2.0
.. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern.
"""
- fullmatch = getattr(re, "fullmatch", None)
- valid_funcs = (fullmatch, None, re.search, re.match)
+ valid_funcs = (re.fullmatch, None, re.search, re.match)
if func not in valid_funcs:
- raise ValueError(
- "'func' must be one of {}.".format(
- ", ".join(
- sorted(
- e and e.__name__ or "None" for e in set(valid_funcs)
- )
- )
+ msg = "'func' must be one of {}.".format(
+ ", ".join(
+ sorted(e and e.__name__ or "None" for e in set(valid_funcs))
)
)
+ raise ValueError(msg)
if isinstance(regex, Pattern):
if flags:
- raise TypeError(
- "'flags' can only be used with a string pattern; "
- "pass flags to re.compile() instead"
- )
+ msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead"
+ raise TypeError(msg)
pattern = regex
else:
pattern = re.compile(regex, flags)
@@ -205,19 +191,14 @@ def matches_re(regex, flags=0, func=None):
match_func = pattern.match
elif func is re.search:
match_func = pattern.search
- elif fullmatch:
+ else:
match_func = pattern.fullmatch
- else: # Python 2 fullmatch emulation (https://bugs.python.org/issue16203)
- pattern = re.compile(
- r"(?:{})\Z".format(pattern.pattern), pattern.flags
- )
- match_func = pattern.match
return _MatchesReValidator(pattern, match_func)
@attrs(repr=False, slots=True, hash=True)
-class _ProvidesValidator(object):
+class _ProvidesValidator:
interface = attrib()
def __call__(self, inst, attr, value):
@@ -225,20 +206,18 @@ def __call__(self, inst, attr, value):
We use a callable class to be able to change the ``__repr__``.
"""
if not self.interface.providedBy(value):
+ msg = "'{name}' must provide {interface!r} which {value!r} doesn't.".format(
+ name=attr.name, interface=self.interface, value=value
+ )
raise TypeError(
- "'{name}' must provide {interface!r} which {value!r} "
- "doesn't.".format(
- name=attr.name, interface=self.interface, value=value
- ),
+ msg,
attr,
self.interface,
value,
)
def __repr__(self):
- return "".format(
- interface=self.interface
- )
+ return f""
def provides(interface):
@@ -254,12 +233,22 @@ def provides(interface):
:raises TypeError: With a human readable error message, the attribute
(of type `attrs.Attribute`), the expected interface, and the
value it got.
+
+ .. deprecated:: 23.1.0
"""
+ import warnings
+
+ warnings.warn(
+ "attrs's zope-interface support is deprecated and will be removed in, "
+ "or after, April 2024.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
return _ProvidesValidator(interface)
@attrs(repr=False, slots=True, hash=True)
-class _OptionalValidator(object):
+class _OptionalValidator:
validator = attrib()
def __call__(self, inst, attr, value):
@@ -269,9 +258,7 @@ def __call__(self, inst, attr, value):
self.validator(inst, attr, value)
def __repr__(self):
- return "".format(
- what=repr(self.validator)
- )
+ return f""
def optional(validator):
@@ -280,20 +267,21 @@ def optional(validator):
which can be set to ``None`` in addition to satisfying the requirements of
the sub-validator.
- :param validator: A validator (or a list of validators) that is used for
- non-``None`` values.
- :type validator: callable or `list` of callables.
+ :param Callable | tuple[Callable] | list[Callable] validator: A validator
+ (or validators) that is used for non-``None`` values.
.. versionadded:: 15.1.0
.. versionchanged:: 17.1.0 *validator* can be a list of validators.
+ .. versionchanged:: 23.1.0 *validator* can also be a tuple of validators.
"""
- if isinstance(validator, list):
+ if isinstance(validator, (list, tuple)):
return _OptionalValidator(_AndValidator(validator))
+
return _OptionalValidator(validator)
@attrs(repr=False, slots=True, hash=True)
-class _InValidator(object):
+class _InValidator:
options = attrib()
def __call__(self, inst, attr, value):
@@ -303,16 +291,16 @@ def __call__(self, inst, attr, value):
in_options = False
if not in_options:
+ msg = f"'{attr.name}' must be in {self.options!r} (got {value!r})"
raise ValueError(
- "'{name}' must be in {options!r} (got {value!r})".format(
- name=attr.name, options=self.options, value=value
- )
+ msg,
+ attr,
+ self.options,
+ value,
)
def __repr__(self):
- return "".format(
- options=self.options
- )
+ return f""
def in_(options):
@@ -329,12 +317,16 @@ def in_(options):
got.
.. versionadded:: 17.1.0
+ .. versionchanged:: 22.1.0
+ The ValueError was incomplete until now and only contained the human
+ readable error message. Now it contains all the information that has
+ been promised since 17.1.0.
"""
return _InValidator(options)
@attrs(repr=False, slots=False, hash=True)
-class _IsCallableValidator(object):
+class _IsCallableValidator:
def __call__(self, inst, attr, value):
"""
We use a callable class to be able to change the ``__repr__``.
@@ -357,13 +349,13 @@ def __repr__(self):
def is_callable():
"""
- A validator that raises a `attr.exceptions.NotCallableError` if the
+ A validator that raises a `attrs.exceptions.NotCallableError` if the
initializer is called with a value for this particular attribute
that is not callable.
.. versionadded:: 19.1.0
- :raises `attr.exceptions.NotCallableError`: With a human readable error
+ :raises attrs.exceptions.NotCallableError: With a human readable error
message containing the attribute (`attrs.Attribute`) name,
and the value it got.
"""
@@ -371,7 +363,7 @@ def is_callable():
@attrs(repr=False, slots=True, hash=True)
-class _DeepIterable(object):
+class _DeepIterable:
member_validator = attrib(validator=is_callable())
iterable_validator = attrib(
default=None, validator=optional(is_callable())
@@ -391,14 +383,11 @@ def __repr__(self):
iterable_identifier = (
""
if self.iterable_validator is None
- else " {iterable!r}".format(iterable=self.iterable_validator)
+ else f" {self.iterable_validator!r}"
)
return (
- ""
- ).format(
- iterable_identifier=iterable_identifier,
- member=self.member_validator,
+ f""
)
@@ -406,7 +395,7 @@ def deep_iterable(member_validator, iterable_validator=None):
"""
A validator that performs deep validation of an iterable.
- :param member_validator: Validator to apply to iterable members
+ :param member_validator: Validator(s) to apply to iterable members
:param iterable_validator: Validator to apply to iterable itself
(optional)
@@ -414,11 +403,13 @@ def deep_iterable(member_validator, iterable_validator=None):
:raises TypeError: if any sub-validators fail
"""
+ if isinstance(member_validator, (list, tuple)):
+ member_validator = and_(*member_validator)
return _DeepIterable(member_validator, iterable_validator)
@attrs(repr=False, slots=True, hash=True)
-class _DeepMapping(object):
+class _DeepMapping:
key_validator = attrib(validator=is_callable())
value_validator = attrib(validator=is_callable())
mapping_validator = attrib(default=None, validator=optional(is_callable()))
@@ -457,7 +448,7 @@ def deep_mapping(key_validator, value_validator, mapping_validator=None):
@attrs(repr=False, frozen=True, slots=True)
-class _NumberValidator(object):
+class _NumberValidator:
bound = attrib()
compare_op = attrib()
compare_func = attrib()
@@ -467,19 +458,11 @@ def __call__(self, inst, attr, value):
We use a callable class to be able to change the ``__repr__``.
"""
if not self.compare_func(value, self.bound):
- raise ValueError(
- "'{name}' must be {op} {bound}: {value}".format(
- name=attr.name,
- op=self.compare_op,
- bound=self.bound,
- value=value,
- )
- )
+ msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}"
+ raise ValueError(msg)
def __repr__(self):
- return "".format(
- op=self.compare_op, bound=self.bound
- )
+ return f""
def lt(val):
@@ -531,7 +514,7 @@ def gt(val):
@attrs(repr=False, frozen=True, slots=True)
-class _MaxLengthValidator(object):
+class _MaxLengthValidator:
max_length = attrib()
def __call__(self, inst, attr, value):
@@ -539,14 +522,11 @@ def __call__(self, inst, attr, value):
We use a callable class to be able to change the ``__repr__``.
"""
if len(value) > self.max_length:
- raise ValueError(
- "Length of '{name}' must be <= {max}: {len}".format(
- name=attr.name, max=self.max_length, len=len(value)
- )
- )
+ msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}"
+ raise ValueError(msg)
def __repr__(self):
- return "".format(max=self.max_length)
+ return f""
def max_len(length):
@@ -559,3 +539,143 @@ def max_len(length):
.. versionadded:: 21.3.0
"""
return _MaxLengthValidator(length)
+
+
+@attrs(repr=False, frozen=True, slots=True)
+class _MinLengthValidator:
+ min_length = attrib()
+
+ def __call__(self, inst, attr, value):
+ """
+ We use a callable class to be able to change the ``__repr__``.
+ """
+ if len(value) < self.min_length:
+ msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}"
+ raise ValueError(msg)
+
+ def __repr__(self):
+ return f""
+
+
+def min_len(length):
+ """
+ A validator that raises `ValueError` if the initializer is called
+ with a string or iterable that is shorter than *length*.
+
+ :param int length: Minimum length of the string or iterable
+
+ .. versionadded:: 22.1.0
+ """
+ return _MinLengthValidator(length)
+
+
+@attrs(repr=False, slots=True, hash=True)
+class _SubclassOfValidator:
+ type = attrib()
+
+ def __call__(self, inst, attr, value):
+ """
+ We use a callable class to be able to change the ``__repr__``.
+ """
+ if not issubclass(value, self.type):
+ msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})."
+ raise TypeError(
+ msg,
+ attr,
+ self.type,
+ value,
+ )
+
+ def __repr__(self):
+ return f""
+
+
+def _subclass_of(type):
+ """
+ A validator that raises a `TypeError` if the initializer is called
+ with a wrong type for this particular attribute (checks are performed using
+ `issubclass` therefore it's also valid to pass a tuple of types).
+
+ :param type: The type to check for.
+ :type type: type or tuple of types
+
+ :raises TypeError: With a human readable error message, the attribute
+ (of type `attrs.Attribute`), the expected type, and the value it
+ got.
+ """
+ return _SubclassOfValidator(type)
+
+
+@attrs(repr=False, slots=True, hash=True)
+class _NotValidator:
+ validator = attrib()
+ msg = attrib(
+ converter=default_if_none(
+ "not_ validator child '{validator!r}' "
+ "did not raise a captured error"
+ )
+ )
+ exc_types = attrib(
+ validator=deep_iterable(
+ member_validator=_subclass_of(Exception),
+ iterable_validator=instance_of(tuple),
+ ),
+ )
+
+ def __call__(self, inst, attr, value):
+ try:
+ self.validator(inst, attr, value)
+ except self.exc_types:
+ pass # suppress error to invert validity
+ else:
+ raise ValueError(
+ self.msg.format(
+ validator=self.validator,
+ exc_types=self.exc_types,
+ ),
+ attr,
+ self.validator,
+ value,
+ self.exc_types,
+ )
+
+ def __repr__(self):
+ return (
+ ""
+ ).format(
+ what=self.validator,
+ exc_types=self.exc_types,
+ )
+
+
+def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)):
+ """
+ A validator that wraps and logically 'inverts' the validator passed to it.
+ It will raise a `ValueError` if the provided validator *doesn't* raise a
+ `ValueError` or `TypeError` (by default), and will suppress the exception
+ if the provided validator *does*.
+
+ Intended to be used with existing validators to compose logic without
+ needing to create inverted variants, for example, ``not_(in_(...))``.
+
+ :param validator: A validator to be logically inverted.
+ :param msg: Message to raise if validator fails.
+ Formatted with keys ``exc_types`` and ``validator``.
+ :type msg: str
+ :param exc_types: Exception type(s) to capture.
+ Other types raised by child validators will not be intercepted and
+ pass through.
+
+ :raises ValueError: With a human readable error message,
+ the attribute (of type `attrs.Attribute`),
+ the validator that failed to raise an exception,
+ the value it got,
+ and the expected exception types.
+
+ .. versionadded:: 22.2.0
+ """
+ try:
+ exc_types = tuple(exc_types)
+ except TypeError:
+ exc_types = (exc_types,)
+ return _NotValidator(validator, msg, exc_types)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.pyi b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.pyi
index 5e00b85..d194a75 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.pyi
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attr/validators.pyi
@@ -18,6 +18,7 @@ from typing import (
)
from . import _ValidatorType
+from . import _ValidatorArgType
_T = TypeVar("_T")
_T1 = TypeVar("_T1")
@@ -50,7 +51,9 @@ def instance_of(
def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ...
def provides(interface: Any) -> _ValidatorType[Any]: ...
def optional(
- validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
+ validator: Union[
+ _ValidatorType[_T], List[_ValidatorType[_T]], Tuple[_ValidatorType[_T]]
+ ]
) -> _ValidatorType[Optional[_T]]: ...
def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
@@ -62,7 +65,7 @@ def matches_re(
] = ...,
) -> _ValidatorType[AnyStr]: ...
def deep_iterable(
- member_validator: _ValidatorType[_T],
+ member_validator: _ValidatorArgType[_T],
iterable_validator: Optional[_ValidatorType[_I]] = ...,
) -> _ValidatorType[_I]: ...
def deep_mapping(
@@ -76,3 +79,10 @@ def le(val: _T) -> _ValidatorType[_T]: ...
def ge(val: _T) -> _ValidatorType[_T]: ...
def gt(val: _T) -> _ValidatorType[_T]: ...
def max_len(length: int) -> _ValidatorType[_T]: ...
+def min_len(length: int) -> _ValidatorType[_T]: ...
+def not_(
+ validator: _ValidatorType[_T],
+ *,
+ msg: Optional[str] = None,
+ exc_types: Union[Type[Exception], Iterable[Type[Exception]]] = ...,
+) -> _ValidatorType[_T]: ...
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.py
index a704b8b..0c24815 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.py
@@ -3,17 +3,9 @@
from attr import (
NOTHING,
Attribute,
+ AttrsInstance,
Factory,
- __author__,
- __copyright__,
- __description__,
- __doc__,
- __email__,
- __license__,
- __title__,
- __url__,
- __version__,
- __version_info__,
+ _make_getattr,
assoc,
cmp_using,
define,
@@ -48,6 +40,7 @@
"assoc",
"astuple",
"Attribute",
+ "AttrsInstance",
"cmp_using",
"converters",
"define",
@@ -68,3 +61,5 @@
"validate",
"validators",
]
+
+__getattr__ = _make_getattr(__name__)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.pyi b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.pyi
index 7426fa5..9372cfe 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.pyi
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/__init__.pyi
@@ -23,13 +23,17 @@ from attr import __version_info__ as __version_info__
from attr import _FilterType
from attr import assoc as assoc
from attr import Attribute as Attribute
+from attr import AttrsInstance as AttrsInstance
+from attr import cmp_using as cmp_using
+from attr import converters as converters
from attr import define as define
from attr import evolve as evolve
-from attr import Factory as Factory
from attr import exceptions as exceptions
+from attr import Factory as Factory
from attr import field as field
from attr import fields as fields
from attr import fields_dict as fields_dict
+from attr import filters as filters
from attr import frozen as frozen
from attr import has as has
from attr import make_class as make_class
@@ -42,7 +46,7 @@ from attr import validators as validators
# TODO: see definition of attr.asdict/astuple
def asdict(
- inst: Any,
+ inst: AttrsInstance,
recurse: bool = ...,
filter: Optional[_FilterType[Any]] = ...,
dict_factory: Type[Mapping[Any, Any]] = ...,
@@ -55,7 +59,7 @@ def asdict(
# TODO: add support for returning NamedTuple from the mypy plugin
def astuple(
- inst: Any,
+ inst: AttrsInstance,
recurse: bool = ...,
filter: Optional[_FilterType[Any]] = ...,
tuple_factory: Type[Sequence[Any]] = ...,
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/converters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/converters.py
index edfa8d3..7821f6c 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/converters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/converters.py
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: MIT
-from attr.converters import * # noqa
+from attr.converters import * # noqa: F403
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/exceptions.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/exceptions.py
index bd9efed..3323f9d 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/exceptions.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/exceptions.py
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: MIT
-from attr.exceptions import * # noqa
+from attr.exceptions import * # noqa: F403
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/filters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/filters.py
index 5295900..3080f48 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/filters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/filters.py
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: MIT
-from attr.filters import * # noqa
+from attr.filters import * # noqa: F403
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/setters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/setters.py
index 9b50770..f3d73bb 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/setters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/setters.py
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: MIT
-from attr.setters import * # noqa
+from attr.setters import * # noqa: F403
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/validators.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/validators.py
index ab2c9b3..037e124 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/validators.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/attrs/validators.py
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: MIT
-from attr.validators import * # noqa
+from attr.validators import * # noqa: F403
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/__init__.py
index 8db1a0e..1c91f3e 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/__init__.py
@@ -1,3 +1,4 @@
from .core import contents, where
-__version__ = "2021.10.08"
+__all__ = ["contents", "where"]
+__version__ = "2024.02.02"
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/cacert.pem b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/cacert.pem
index 6d0ccc0..fac3c31 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/cacert.pem
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/cacert.pem
@@ -28,36 +28,6 @@ DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----
-# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
-# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
-# Label: "GlobalSign Root CA - R2"
-# Serial: 4835703278459682885658125
-# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30
-# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe
-# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e
------BEGIN CERTIFICATE-----
-MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
-MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
-v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
-eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
-tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
-C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
-zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
-mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
-V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
-bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
-3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
-J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
-291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
-ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
-AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
-TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
------END CERTIFICATE-----
-
# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
# Label: "Entrust.net Premium 2048 Secure Server CA"
@@ -275,34 +245,6 @@ mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----
-# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1
-# Subject: O=SECOM Trust.net OU=Security Communication RootCA1
-# Label: "Security Communication Root CA"
-# Serial: 0
-# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a
-# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7
-# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c
------BEGIN CERTIFICATE-----
-MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
-MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
-dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
-WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
-VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
-9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
-DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
-Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
-QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
-xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
-A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
-AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
-kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
-Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
-Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
-JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
-RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
------END CERTIFICATE-----
-
# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
# Label: "XRamp Global CA Root"
@@ -491,34 +433,6 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----
-# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co.
-# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co.
-# Label: "DST Root CA X3"
-# Serial: 91299735575339953335919266965803778155
-# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5
-# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13
-# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39
------BEGIN CERTIFICATE-----
-MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
-PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
-Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
-rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
-OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
-xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
-7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
-aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
-SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
-ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
-AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
-R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
-JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
-Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
------END CERTIFICATE-----
-
# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG
# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG
# Label: "SwissSign Gold CA - G2"
@@ -694,37 +608,6 @@ BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
ZQ==
-----END CERTIFICATE-----
-# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
-# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
-# Label: "Network Solutions Certificate Authority"
-# Serial: 116697915152937497490437556386812487904
-# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e
-# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce
-# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c
------BEGIN CERTIFICATE-----
-MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
-MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
-MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
-dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
-UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
-ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
-c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
-OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
-mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
-BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
-qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
-gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
-BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
-bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
-dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
-6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
-h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
-/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
-wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
-pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
------END CERTIFICATE-----
-
# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited
# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited
# Label: "COMODO ECC Certification Authority"
@@ -779,36 +662,6 @@ t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----
-# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc
-# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc
-# Label: "Cybertrust Global Root"
-# Serial: 4835703278459682877484360
-# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1
-# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6
-# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3
------BEGIN CERTIFICATE-----
-MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG
-A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh
-bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE
-ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS
-b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5
-7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS
-J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y
-HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP
-t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz
-FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY
-XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw
-hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js
-MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA
-A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj
-Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx
-XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o
-omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc
-A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
-WL1WMRJOEcgh4LMRkWXbtKaIOM5V
------END CERTIFICATE-----
-
# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority
# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority
# Label: "ePKI Root Certification Authority"
@@ -910,34 +763,6 @@ uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
-----END CERTIFICATE-----
-# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post
-# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post
-# Label: "Hongkong Post Root CA 1"
-# Serial: 1000
-# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca
-# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58
-# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2
------BEGIN CERTIFICATE-----
-MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx
-FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg
-Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG
-A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr
-b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ
-jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn
-PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh
-ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9
-nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h
-q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED
-MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC
-mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3
-7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB
-oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs
-EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO
-fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi
-AmvZWg==
------END CERTIFICATE-----
-
# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
# Label: "SecureSign RootCA11"
@@ -1028,49 +853,6 @@ Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
WD9f
-----END CERTIFICATE-----
-# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
-# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
-# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068"
-# Serial: 6047274297262753887
-# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3
-# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa
-# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef
------BEGIN CERTIFICATE-----
-MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
-BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
-cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
-MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
-Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
-MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
-thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
-cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
-L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
-NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
-X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
-m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
-Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
-EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
-KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
-6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
-OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
-VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
-VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
-cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
-ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
-AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
-661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
-am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
-ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
-PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
-3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
-SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
-3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
-ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
-StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
-Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
-jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
------END CERTIFICATE-----
-
# Issuer: CN=Izenpe.com O=IZENPE S.A.
# Subject: CN=Izenpe.com O=IZENPE S.A.
# Label: "Izenpe.com"
@@ -1411,78 +1193,6 @@ t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE-----
-# Issuer: CN=EC-ACC O=Agencia Catalana de Certificacio (NIF Q-0801176-I) OU=Serveis Publics de Certificacio/Vegeu https://www.catcert.net/verarrel (c)03/Jerarquia Entitats de Certificacio Catalanes
-# Subject: CN=EC-ACC O=Agencia Catalana de Certificacio (NIF Q-0801176-I) OU=Serveis Publics de Certificacio/Vegeu https://www.catcert.net/verarrel (c)03/Jerarquia Entitats de Certificacio Catalanes
-# Label: "EC-ACC"
-# Serial: -23701579247955709139626555126524820479
-# MD5 Fingerprint: eb:f5:9d:29:0d:61:f9:42:1f:7c:c2:ba:6d:e3:15:09
-# SHA1 Fingerprint: 28:90:3a:63:5b:52:80:fa:e6:77:4c:0b:6d:a7:d6:ba:a6:4a:f2:e8
-# SHA256 Fingerprint: 88:49:7f:01:60:2f:31:54:24:6a:e2:8c:4d:5a:ef:10:f1:d8:7e:bb:76:62:6f:4a:e0:b7:f9:5b:a7:96:87:99
------BEGIN CERTIFICATE-----
-MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB
-8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy
-dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1
-YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3
-dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh
-IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD
-LUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQG
-EwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8g
-KE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBD
-ZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQu
-bmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMg
-ZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUNDMIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R
-85iKw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm
-4CgPukLjbo73FCeTae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaV
-HMf5NLWUhdWZXqBIoH7nF2W4onW4HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNd
-QlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0aE9jD2z3Il3rucO2n5nzbcc8t
-lGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw0JDnJwIDAQAB
-o4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
-BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4
-opvpXY0wfwYDVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBo
-dHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidW
-ZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAwDQYJKoZIhvcN
-AQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJlF7W2u++AVtd0x7Y
-/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNaAl6k
-SBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhy
-Rp/7SNVel+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOS
-Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl
-nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI=
------END CERTIFICATE-----
-
-# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
-# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
-# Label: "Hellenic Academic and Research Institutions RootCA 2011"
-# Serial: 0
-# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9
-# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d
-# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71
------BEGIN CERTIFICATE-----
-MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
-RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
-dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
-YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
-NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
-EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
-cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
-c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
-dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
-fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
-bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
-75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
-FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
-HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
-5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
-b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
-A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
-6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
-TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
-dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
-Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
-l7WdmplNsDz4SgCbZN2fOUvRJ9e4
------END CERTIFICATE-----
-
# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
# Label: "Actalis Authentication Root CA"
@@ -1867,50 +1577,6 @@ HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx
SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----
-# Issuer: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi
-# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi
-# Label: "E-Tugra Certification Authority"
-# Serial: 7667447206703254355
-# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49
-# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39
-# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c
------BEGIN CERTIFICATE-----
-MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV
-BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC
-aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV
-BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1
-Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz
-MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+
-BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp
-em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
-ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY
-B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH
-D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF
-Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo
-q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D
-k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH
-fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut
-dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM
-ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8
-zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
-rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX
-U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6
-Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5
-XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF
-Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR
-HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY
-GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c
-77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3
-+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK
-vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6
-FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl
-yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P
-AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD
-y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d
-NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==
------END CERTIFICATE-----
-
# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
# Label: "T-TeleSec GlobalRoot Class 2"
@@ -2342,27 +2008,6 @@ zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
-----END CERTIFICATE-----
-# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
-# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
-# Label: "GlobalSign ECC Root CA - R4"
-# Serial: 14367148294922964480859022125800977897474
-# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e
-# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb
-# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c
------BEGIN CERTIFICATE-----
-MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk
-MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH
-bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
-DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
-QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ
-FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw
-DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F
-uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX
-kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs
-ewv4n4Q=
------END CERTIFICATE-----
-
# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
# Label: "GlobalSign ECC Root CA - R5"
@@ -2385,46 +2030,6 @@ KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
xwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE-----
-# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden
-# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden
-# Label: "Staat der Nederlanden EV Root CA"
-# Serial: 10000013
-# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba
-# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb
-# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a
------BEGIN CERTIFICATE-----
-MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO
-TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh
-dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y
-MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg
-TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS
-b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS
-M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC
-UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d
-Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p
-rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l
-pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb
-j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC
-KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS
-/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X
-cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH
-1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP
-px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
-/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7
-MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
-eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u
-2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS
-v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC
-wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy
-CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e
-vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6
-Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa
-Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL
-eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8
-FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc
-7uzXLg==
------END CERTIFICATE-----
-
# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust
# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust
# Label: "IdenTrust Commercial Root CA 1"
@@ -3032,116 +2637,6 @@ T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe
MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
-----END CERTIFICATE-----
-# Issuer: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority
-# Subject: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority
-# Label: "TrustCor RootCert CA-1"
-# Serial: 15752444095811006489
-# MD5 Fingerprint: 6e:85:f1:dc:1a:00:d3:22:d5:b2:b2:ac:6b:37:05:45
-# SHA1 Fingerprint: ff:bd:cd:e7:82:c8:43:5e:3c:6f:26:86:5c:ca:a8:3a:45:5b:c3:0a
-# SHA256 Fingerprint: d4:0e:9c:86:cd:8f:e4:68:c1:77:69:59:f4:9e:a7:74:fa:54:86:84:b6:c4:06:f3:90:92:61:f4:dc:e2:57:5c
------BEGIN CERTIFICATE-----
-MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD
-VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk
-MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
-cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y
-IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB
-pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h
-IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG
-A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU
-cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid
-RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V
-seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme
-9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV
-EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW
-hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/
-DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw
-DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I
-/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
-ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ
-yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts
-L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN
-zl/HHk484IkzlQsPpTLWPFp5LBk=
------END CERTIFICATE-----
-
-# Issuer: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority
-# Subject: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority
-# Label: "TrustCor RootCert CA-2"
-# Serial: 2711694510199101698
-# MD5 Fingerprint: a2:e1:f8:18:0b:ba:45:d5:c7:41:2a:bb:37:52:45:64
-# SHA1 Fingerprint: b8:be:6d:cb:56:f1:55:b9:63:d4:12:ca:4e:06:34:c7:94:b2:1c:c0
-# SHA256 Fingerprint: 07:53:e9:40:37:8c:1b:d5:e3:83:6e:39:5d:ae:a5:cb:83:9e:50:46:f1:bd:0e:ae:19:51:cf:10:fe:c7:c9:65
------BEGIN CERTIFICATE-----
-MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV
-BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw
-IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy
-dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig
-Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk
-MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg
-Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD
-VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy
-dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+
-QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq
-1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp
-2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK
-DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape
-az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF
-3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88
-oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM
-g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3
-mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh
-8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd
-BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U
-nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX
-dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+
-MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL
-/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX
-CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa
-ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW
-2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7
-N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3
-Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB
-As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp
-5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu
-1uwJ
------END CERTIFICATE-----
-
-# Issuer: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority
-# Subject: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority
-# Label: "TrustCor ECA-1"
-# Serial: 9548242946988625984
-# MD5 Fingerprint: 27:92:23:1d:0a:f5:40:7c:e9:e6:6b:9d:d8:f5:e7:6c
-# SHA1 Fingerprint: 58:d1:df:95:95:67:6b:63:c0:f0:5b:1c:17:4d:8b:84:0b:c8:78:bd
-# SHA256 Fingerprint: 5a:88:5d:b1:9c:01:d9:12:c5:75:93:88:93:8c:af:bb:df:03:1a:b2:d4:8e:91:ee:15:58:9b:42:97:1d:03:9c
------BEGIN CERTIFICATE-----
-MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD
-VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk
-MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
-cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y
-IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV
-BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw
-IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy
-dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig
-RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb
-3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA
-BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5
-3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou
-owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/
-wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF
-ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf
-BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/
-MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv
-civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2
-AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F
-hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50
-soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI
-WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi
-tJ/X5g==
------END CERTIFICATE-----
-
# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation
# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation
# Label: "SSL.com Root Certification Authority RSA"
@@ -3337,126 +2832,6 @@ rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV
Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
-----END CERTIFICATE-----
-# Issuer: CN=GTS Root R1 O=Google Trust Services LLC
-# Subject: CN=GTS Root R1 O=Google Trust Services LLC
-# Label: "GTS Root R1"
-# Serial: 146587175971765017618439757810265552097
-# MD5 Fingerprint: 82:1a:ef:d4:d2:4a:f2:9f:e2:3d:97:06:14:70:72:85
-# SHA1 Fingerprint: e1:c9:50:e6:ef:22:f8:4c:56:45:72:8b:92:20:60:d7:d5:a7:a3:e8
-# SHA256 Fingerprint: 2a:57:54:71:e3:13:40:bc:21:58:1c:bd:2c:f1:3e:15:84:63:20:3e:ce:94:bc:f9:d3:cc:19:6b:f0:9a:54:72
------BEGIN CERTIFICATE-----
-MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH
-MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
-QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy
-MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl
-cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB
-AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM
-f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX
-mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7
-zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P
-fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc
-vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4
-Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp
-zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO
-Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW
-k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+
-DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF
-lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
-HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW
-Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1
-d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z
-XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR
-gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3
-d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv
-J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg
-DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM
-+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy
-F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9
-SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws
-E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl
------END CERTIFICATE-----
-
-# Issuer: CN=GTS Root R2 O=Google Trust Services LLC
-# Subject: CN=GTS Root R2 O=Google Trust Services LLC
-# Label: "GTS Root R2"
-# Serial: 146587176055767053814479386953112547951
-# MD5 Fingerprint: 44:ed:9a:0e:a4:09:3b:00:f2:ae:4c:a3:c6:61:b0:8b
-# SHA1 Fingerprint: d2:73:96:2a:2a:5e:39:9f:73:3f:e1:c7:1e:64:3f:03:38:34:fc:4d
-# SHA256 Fingerprint: c4:5d:7b:b0:8e:6d:67:e6:2e:42:35:11:0b:56:4e:5f:78:fd:92:ef:05:8c:84:0a:ea:4e:64:55:d7:58:5c:60
------BEGIN CERTIFICATE-----
-MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH
-MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
-QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy
-MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl
-cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB
-AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv
-CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg
-GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu
-XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd
-re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu
-PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1
-mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K
-8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj
-x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR
-nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0
-kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok
-twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
-HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp
-8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT
-vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT
-z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA
-pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb
-pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB
-R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R
-RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk
-0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC
-5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF
-izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn
-yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC
------END CERTIFICATE-----
-
-# Issuer: CN=GTS Root R3 O=Google Trust Services LLC
-# Subject: CN=GTS Root R3 O=Google Trust Services LLC
-# Label: "GTS Root R3"
-# Serial: 146587176140553309517047991083707763997
-# MD5 Fingerprint: 1a:79:5b:6b:04:52:9c:5d:c7:74:33:1b:25:9a:f9:25
-# SHA1 Fingerprint: 30:d4:24:6f:07:ff:db:91:89:8a:0b:e9:49:66:11:eb:8c:5e:46:e5
-# SHA256 Fingerprint: 15:d5:b8:77:46:19:ea:7d:54:ce:1c:a6:d0:b0:c4:03:e0:37:a9:17:f1:31:e8:a0:4e:1e:6b:7a:71:ba:bc:e5
------BEGIN CERTIFICATE-----
-MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw
-CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
-MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
-MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
-Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA
-IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout
-736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A
-DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk
-fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA
-njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd
------END CERTIFICATE-----
-
-# Issuer: CN=GTS Root R4 O=Google Trust Services LLC
-# Subject: CN=GTS Root R4 O=Google Trust Services LLC
-# Label: "GTS Root R4"
-# Serial: 146587176229350439916519468929765261721
-# MD5 Fingerprint: 5d:b6:6a:c4:60:17:24:6a:1a:99:a8:4b:ee:5e:b4:26
-# SHA1 Fingerprint: 2a:1d:60:27:d9:4a:b1:0a:1c:4d:91:5c:cd:33:a0:cb:3e:2d:54:cb
-# SHA256 Fingerprint: 71:cc:a5:39:1f:9e:79:4b:04:80:25:30:b3:63:e1:21:da:8a:30:43:bb:26:66:2f:ea:4d:ca:7f:c9:51:a4:bd
------BEGIN CERTIFICATE-----
-MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw
-CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
-MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
-MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
-Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA
-IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu
-hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l
-xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0
-CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx
-sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w==
------END CERTIFICATE-----
-
# Issuer: CN=UCA Global G2 Root O=UniTrust
# Subject: CN=UCA Global G2 Root O=UniTrust
# Label: "UCA Global G2 Root"
@@ -4360,3 +3735,1080 @@ AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw
SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN
nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps
-----END CERTIFICATE-----
+
+# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
+# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068"
+# Serial: 1977337328857672817
+# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3
+# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe
+# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE
+BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
+cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1
+MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
+Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
+thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
+cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
+L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
+NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
+X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
+m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
+Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
+EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
+KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
+6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
+OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc
+tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd
+IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j
+b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC
+AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw
+ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m
+iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF
+Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ
+hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P
+Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE
+EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV
+1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t
+CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR
+5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw
+f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9
+ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK
+GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV
+-----END CERTIFICATE-----
+
+# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd.
+# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd.
+# Label: "vTrus ECC Root CA"
+# Serial: 630369271402956006249506845124680065938238527194
+# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85
+# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1
+# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3
+-----BEGIN CERTIFICATE-----
+MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw
+RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY
+BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz
+MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u
+LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF
+K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0
+v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd
+e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw
+V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA
+AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG
+GJTO
+-----END CERTIFICATE-----
+
+# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd.
+# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd.
+# Label: "vTrus Root CA"
+# Serial: 387574501246983434957692974888460947164905180485
+# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc
+# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7
+# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87
+-----BEGIN CERTIFICATE-----
+MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL
+BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x
+FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx
+MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s
+THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc
+IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU
+AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+
+GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9
+8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH
+flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt
+J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim
+0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN
+pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ
+UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW
+OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB
+AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet
+8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd
+nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j
+bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM
+Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv
+TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS
+S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr
+I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9
+b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB
+UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P
+Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven
+sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s=
+-----END CERTIFICATE-----
+
+# Issuer: CN=ISRG Root X2 O=Internet Security Research Group
+# Subject: CN=ISRG Root X2 O=Internet Security Research Group
+# Label: "ISRG Root X2"
+# Serial: 87493402998870891108772069816698636114
+# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5
+# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af
+# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70
+-----BEGIN CERTIFICATE-----
+MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
+CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
+R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
+MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
+ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
+EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
+ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
+zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
+tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
+/q4AaOeMSQ+2b1tbFfLn
+-----END CERTIFICATE-----
+
+# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd.
+# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd.
+# Label: "HiPKI Root CA - G1"
+# Serial: 60966262342023497858655262305426234976
+# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3
+# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60
+# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc
+-----BEGIN CERTIFICATE-----
+MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP
+MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
+ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa
+Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3
+YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw
+qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv
+Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6
+lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz
+Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ
+KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK
+FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj
+HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr
+y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ
+/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM
+a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6
+fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG
+SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi
+7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc
+SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza
+ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc
+XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg
+iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho
+L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF
+Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr
+kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+
+vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU
+YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
+# Label: "GlobalSign ECC Root CA - R4"
+# Serial: 159662223612894884239637590694
+# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc
+# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28
+# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2
+-----BEGIN CERTIFICATE-----
+MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD
+VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh
+bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw
+MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g
+UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT
+BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx
+uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV
+HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/
++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147
+bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
+-----END CERTIFICATE-----
+
+# Issuer: CN=GTS Root R1 O=Google Trust Services LLC
+# Subject: CN=GTS Root R1 O=Google Trust Services LLC
+# Label: "GTS Root R1"
+# Serial: 159662320309726417404178440727
+# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40
+# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a
+# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf
+-----BEGIN CERTIFICATE-----
+MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
+27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
+Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
+TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
+qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
+szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
+Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
+MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
+wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
+aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
+VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
+AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
+C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
+QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
+h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
+7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
+ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
+MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
+Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
+6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
+0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
+2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
+bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
+-----END CERTIFICATE-----
+
+# Issuer: CN=GTS Root R2 O=Google Trust Services LLC
+# Subject: CN=GTS Root R2 O=Google Trust Services LLC
+# Label: "GTS Root R2"
+# Serial: 159662449406622349769042896298
+# MD5 Fingerprint: 1e:39:c0:53:e6:1e:29:82:0b:ca:52:55:36:5d:57:dc
+# SHA1 Fingerprint: 9a:44:49:76:32:db:de:fa:d0:bc:fb:5a:7b:17:bd:9e:56:09:24:94
+# SHA256 Fingerprint: 8d:25:cd:97:22:9d:bf:70:35:6b:da:4e:b3:cc:73:40:31:e2:4c:f0:0f:af:cf:d3:2d:c7:6e:b5:84:1c:7e:a8
+-----BEGIN CERTIFICATE-----
+MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt
+nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY
+6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu
+MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k
+RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg
+f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV
++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo
+dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW
+Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa
+G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq
+gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID
+AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H
+vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8
+0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC
+B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u
+NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg
+yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev
+HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6
+xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR
+TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg
+JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV
+7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl
+6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL
+-----END CERTIFICATE-----
+
+# Issuer: CN=GTS Root R3 O=Google Trust Services LLC
+# Subject: CN=GTS Root R3 O=Google Trust Services LLC
+# Label: "GTS Root R3"
+# Serial: 159662495401136852707857743206
+# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73
+# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46
+# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48
+-----BEGIN CERTIFICATE-----
+MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD
+VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
+A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
+WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
+IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
+AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G
+jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2
+4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7
+VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm
+ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X
+-----END CERTIFICATE-----
+
+# Issuer: CN=GTS Root R4 O=Google Trust Services LLC
+# Subject: CN=GTS Root R4 O=Google Trust Services LLC
+# Label: "GTS Root R4"
+# Serial: 159662532700760215368942768210
+# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8
+# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47
+# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d
+-----BEGIN CERTIFICATE-----
+MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD
+VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
+A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
+WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
+IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
+AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi
+QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR
+HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
+BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D
+9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8
+p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD
+-----END CERTIFICATE-----
+
+# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj
+# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj
+# Label: "Telia Root CA v2"
+# Serial: 7288924052977061235122729490515358
+# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48
+# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd
+# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c
+-----BEGIN CERTIFICATE-----
+MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx
+CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE
+AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1
+NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ
+MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq
+AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9
+vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9
+lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD
+n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT
+7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o
+6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC
+TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6
+WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R
+DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI
+pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj
+YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy
+rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ
+8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi
+0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM
+A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS
+SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K
+TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF
+6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er
+3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt
+Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT
+VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW
+ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA
+rBPuUBQemMc=
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH
+# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH
+# Label: "D-TRUST BR Root CA 1 2020"
+# Serial: 165870826978392376648679885835942448534
+# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed
+# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67
+# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44
+-----BEGIN CERTIFICATE-----
+MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw
+CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS
+VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5
+NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG
+A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS
+zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0
+QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/
+VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g
+PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf
+Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l
+dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1
+c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO
+PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW
+wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV
+dWNbFJWcHwHP2NVypw87
+-----END CERTIFICATE-----
+
+# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH
+# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH
+# Label: "D-TRUST EV Root CA 1 2020"
+# Serial: 126288379621884218666039612629459926992
+# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e
+# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07
+# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db
+-----BEGIN CERTIFICATE-----
+MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw
+CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS
+VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5
+NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG
+A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC
+/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD
+wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3
+OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g
+PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf
+Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l
+dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1
+c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO
+PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA
+y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb
+gfM0agPnIjhQW+0ZT0MW
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc.
+# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc.
+# Label: "DigiCert TLS ECC P384 Root G5"
+# Serial: 13129116028163249804115411775095713523
+# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed
+# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee
+# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05
+-----BEGIN CERTIFICATE-----
+MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
+Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
+MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
+bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
+7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
+0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
+B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
+BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
+LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
+DXZDjC5Ty3zfDBeWUA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc.
+# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc.
+# Label: "DigiCert TLS RSA4096 Root G5"
+# Serial: 11930366277458970227240571539258396554
+# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1
+# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35
+# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75
+-----BEGIN CERTIFICATE-----
+MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT
+HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN
+NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs
+IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+
+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0
+2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp
+wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM
+pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD
+nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po
+sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx
+Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd
+Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX
+KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe
+XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL
+tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv
+TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
+AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw
+GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H
+PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF
+O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ
+REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik
+AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv
+/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+
+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw
+MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF
+qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK
+ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certainly Root R1 O=Certainly
+# Subject: CN=Certainly Root R1 O=Certainly
+# Label: "Certainly Root R1"
+# Serial: 188833316161142517227353805653483829216
+# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12
+# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af
+# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0
+-----BEGIN CERTIFICATE-----
+MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw
+PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy
+dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9
+MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0
+YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2
+1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT
+vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed
+aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0
+1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5
+r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5
+cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ
+wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ
+6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA
+2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH
+Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR
+eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB
+/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u
+d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr
+PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d
+8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi
+1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd
+rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di
+taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7
+lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj
+yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn
+Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy
+yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n
+wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6
+OV+KmalBWQewLK8=
+-----END CERTIFICATE-----
+
+# Issuer: CN=Certainly Root E1 O=Certainly
+# Subject: CN=Certainly Root E1 O=Certainly
+# Label: "Certainly Root E1"
+# Serial: 8168531406727139161245376702891150584
+# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9
+# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b
+# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2
+-----BEGIN CERTIFICATE-----
+MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw
+CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu
+bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ
+BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s
+eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK
++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2
+QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4
+hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm
+ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG
+BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
+-----END CERTIFICATE-----
+
+# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD.
+# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD.
+# Label: "Security Communication RootCA3"
+# Serial: 16247922307909811815
+# MD5 Fingerprint: 1c:9a:16:ff:9e:5c:e0:4d:8a:14:01:f4:35:5d:29:26
+# SHA1 Fingerprint: c3:03:c8:22:74:92:e5:61:a2:9c:5f:79:91:2b:1e:44:13:91:30:3a
+# SHA256 Fingerprint: 24:a5:5c:2a:b0:51:44:2d:06:17:76:65:41:23:9a:4a:d0:32:d7:c5:51:75:aa:34:ff:de:2f:bc:4f:5c:52:94
+-----BEGIN CERTIFICATE-----
+MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNV
+BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw
+JQYDVQQDEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2
+MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UEAxMeU2VjdXJpdHkg
+Q29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4r
+CmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzA
+lrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG
+TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF7
+9+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK7
+8vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4
+g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3we
+GVPKp7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst
++3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M
+0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQ
+T9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEAAaNCMEAw
+HQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS
+YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PA
+FNr0Y/Dq9HHuTofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd
+9XbXv8S2gVj/yP9kaWJ5rW4OH3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQI
+UYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+
+OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZXSEIx2C/pHF7uNke
+gr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml+LLf
+iAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUV
+nuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD
+2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//
+1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8Laad
+TdJ0MN1kURXbg4NR16/9M51NZg==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD.
+# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD.
+# Label: "Security Communication ECC RootCA1"
+# Serial: 15446673492073852651
+# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86
+# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41
+# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11
+-----BEGIN CERTIFICATE-----
+MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT
+AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD
+VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx
+NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT
+HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5
+IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
+AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl
+dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK
+ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu
+9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O
+be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=
+-----END CERTIFICATE-----
+
+# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
+# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
+# Label: "BJCA Global Root CA1"
+# Serial: 113562791157148395269083148143378328608
+# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90
+# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a
+# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae
+-----BEGIN CERTIFICATE-----
+MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU
+MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI
+T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz
+MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF
+SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh
+bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z
+xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ
+spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5
+58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR
+at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll
+5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq
+nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK
+V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/
+pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO
+z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn
+jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+
+WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF
+7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
+AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4
+YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli
+awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u
++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88
+X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN
+SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo
+P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI
++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz
+znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9
+eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2
+YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy
+r/6zcCwupvI=
+-----END CERTIFICATE-----
+
+# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
+# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
+# Label: "BJCA Global Root CA2"
+# Serial: 58605626836079930195615843123109055211
+# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c
+# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6
+# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82
+-----BEGIN CERTIFICATE-----
+MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw
+CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ
+VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy
+MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ
+TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS
+b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B
+IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+
++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK
+sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA
+94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B
+43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited
+# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited
+# Label: "Sectigo Public Server Authentication Root E46"
+# Serial: 88989738453351742415770396670917916916
+# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01
+# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a
+# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83
+-----BEGIN CERTIFICATE-----
+MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw
+CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T
+ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN
+MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG
+A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT
+ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC
+WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+
+6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B
+Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa
+qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q
+4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited
+# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited
+# Label: "Sectigo Public Server Authentication Root R46"
+# Serial: 156256931880233212765902055439220583700
+# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5
+# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38
+# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06
+-----BEGIN CERTIFICATE-----
+MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf
+MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
+Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
+HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY
+MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp
+YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa
+ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz
+SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf
+iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X
+ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3
+IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS
+VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE
+SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu
++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt
+8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L
+HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt
+zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P
+AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c
+mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ
+YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52
+gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA
+Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB
+JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX
+DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui
+TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5
+dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65
+LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp
+0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY
+QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation
+# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation
+# Label: "SSL.com TLS RSA Root CA 2022"
+# Serial: 148535279242832292258835760425842727825
+# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da
+# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca
+# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed
+-----BEGIN CERTIFICATE-----
+MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO
+MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD
+DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX
+DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw
+b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC
+AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP
+L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY
+t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins
+S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3
+PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO
+L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3
+R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w
+dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS
++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS
+d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG
+AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f
+gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j
+BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z
+NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt
+hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM
+QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf
+R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ
+DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW
+P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy
+lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq
+bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w
+AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q
+r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji
+Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU
+98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=
+-----END CERTIFICATE-----
+
+# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation
+# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation
+# Label: "SSL.com TLS ECC Root CA 2022"
+# Serial: 26605119622390491762507526719404364228
+# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5
+# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39
+# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43
+-----BEGIN CERTIFICATE-----
+MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw
+CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT
+U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2
+MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh
+dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm
+acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN
+SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME
+GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW
+uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp
+15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN
+b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g==
+-----END CERTIFICATE-----
+
+# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos
+# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos
+# Label: "Atos TrustedRoot Root CA ECC TLS 2021"
+# Serial: 81873346711060652204712539181482831616
+# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8
+# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd
+# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8
+-----BEGIN CERTIFICATE-----
+MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w
+LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w
+CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0
+MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF
+Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI
+zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X
+tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4
+AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2
+KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD
+aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu
+CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo
+9H1/IISpQuQo
+-----END CERTIFICATE-----
+
+# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos
+# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos
+# Label: "Atos TrustedRoot Root CA RSA TLS 2021"
+# Serial: 111436099570196163832749341232207667876
+# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2
+# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48
+# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f
+-----BEGIN CERTIFICATE-----
+MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM
+MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx
+MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00
+MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD
+QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z
+4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv
+Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ
+kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs
+GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln
+nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh
+3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD
+0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy
+geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8
+ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB
+c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI
+pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
+dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS
+4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs
+o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ
+qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw
+xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM
+rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4
+AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR
+0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY
+o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5
+dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE
+oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ==
+-----END CERTIFICATE-----
+
+# Issuer: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc.
+# Subject: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc.
+# Label: "TrustAsia Global Root CA G3"
+# Serial: 576386314500428537169965010905813481816650257167
+# MD5 Fingerprint: 30:42:1b:b7:bb:81:75:35:e4:16:4f:53:d2:94:de:04
+# SHA1 Fingerprint: 63:cf:b6:c1:27:2b:56:e4:88:8e:1c:23:9a:b6:2e:81:47:24:c3:c7
+# SHA256 Fingerprint: e0:d3:22:6a:eb:11:63:c2:e4:8f:f9:be:3b:50:b4:c6:43:1b:e7:bb:1e:ac:c5:c3:6b:5d:5e:c5:09:03:9a:08
+-----BEGIN CERTIFICATE-----
+MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM
+BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp
+ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe
+Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw
+IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU
+cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS
+T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK
+AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1
+nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep
+qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA
+yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs
+hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX
+zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv
+kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT
+f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA
+uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB
+o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih
+MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E
+BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4
+wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2
+XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1
+JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j
+ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV
+VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx
+xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on
+AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d
+7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj
+gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV
++Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo
+FGWsJwt0ivKH
+-----END CERTIFICATE-----
+
+# Issuer: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc.
+# Subject: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc.
+# Label: "TrustAsia Global Root CA G4"
+# Serial: 451799571007117016466790293371524403291602933463
+# MD5 Fingerprint: 54:dd:b2:d7:5f:d8:3e:ed:7c:e0:0b:2e:cc:ed:eb:eb
+# SHA1 Fingerprint: 57:73:a5:61:5d:80:b2:e6:ac:38:82:fc:68:07:31:ac:9f:b5:92:5a
+# SHA256 Fingerprint: be:4b:56:cb:50:56:c0:13:6a:52:6d:f4:44:50:8d:aa:36:a0:b5:4f:42:e4:ac:38:f7:2a:f4:70:e4:79:65:4c
+-----BEGIN CERTIFICATE-----
+MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw
+WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs
+IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y
+MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD
+VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz
+dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx
+s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw
+LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij
+YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD
+pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE
+AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR
+UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj
+/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA==
+-----END CERTIFICATE-----
+
+# Issuer: CN=CommScope Public Trust ECC Root-01 O=CommScope
+# Subject: CN=CommScope Public Trust ECC Root-01 O=CommScope
+# Label: "CommScope Public Trust ECC Root-01"
+# Serial: 385011430473757362783587124273108818652468453534
+# MD5 Fingerprint: 3a:40:a7:fc:03:8c:9c:38:79:2f:3a:a2:6c:b6:0a:16
+# SHA1 Fingerprint: 07:86:c0:d8:dd:8e:c0:80:98:06:98:d0:58:7a:ef:de:a6:cc:a2:5d
+# SHA256 Fingerprint: 11:43:7c:da:7b:b4:5e:41:36:5f:45:b3:9a:38:98:6b:0d:e0:0d:ef:34:8e:0c:7b:b0:87:36:33:80:0b:c3:8b
+-----BEGIN CERTIFICATE-----
+MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMw
+TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t
+bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNa
+Fw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv
+cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEw
+djAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLxeP0C
+flfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJE
+hRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq
+hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg
+2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uS
+Um9poIyNStDuiw7LR47QjRE=
+-----END CERTIFICATE-----
+
+# Issuer: CN=CommScope Public Trust ECC Root-02 O=CommScope
+# Subject: CN=CommScope Public Trust ECC Root-02 O=CommScope
+# Label: "CommScope Public Trust ECC Root-02"
+# Serial: 234015080301808452132356021271193974922492992893
+# MD5 Fingerprint: 59:b0:44:d5:65:4d:b8:5c:55:19:92:02:b6:d1:94:b2
+# SHA1 Fingerprint: 3c:3f:ef:57:0f:fe:65:93:86:9e:a0:fe:b0:f6:ed:8e:d1:13:c7:e5
+# SHA256 Fingerprint: 2f:fb:7f:81:3b:bb:b3:c8:9a:b4:e8:16:2d:0f:16:d7:15:09:a8:30:cc:9d:73:c2:62:e5:14:08:75:d1:ad:4a
+-----BEGIN CERTIFICATE-----
+MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMw
+TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t
+bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRa
+Fw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv
+cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIw
+djAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/MMDAL
+j2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmU
+v4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq
+hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/n
+ich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AV
+mkzw5l4lIhVtwodZ0LKOag==
+-----END CERTIFICATE-----
+
+# Issuer: CN=CommScope Public Trust RSA Root-01 O=CommScope
+# Subject: CN=CommScope Public Trust RSA Root-01 O=CommScope
+# Label: "CommScope Public Trust RSA Root-01"
+# Serial: 354030733275608256394402989253558293562031411421
+# MD5 Fingerprint: 0e:b4:15:bc:87:63:5d:5d:02:73:d4:26:38:68:73:d8
+# SHA1 Fingerprint: 6d:0a:5f:f7:b4:23:06:b4:85:b3:b7:97:64:fc:ac:75:f5:33:f2:93
+# SHA256 Fingerprint: 02:bd:f9:6e:2a:45:dd:9b:f1:8f:c7:e1:db:df:21:a0:37:9b:a3:c9:c2:61:03:44:cf:d8:d6:06:fe:c1:ed:81
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQEL
+BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi
+Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1
+NTRaFw00NjA0MjgxNjQ1NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t
+U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt
+MDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwSGWjDR1C45FtnYSk
+YZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2bKOx7dAvnQmtVzslh
+suitQDy6uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBWBB0HW0al
+DrJLpA6lfO741GIDuZNqihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3Oj
+WiE260f6GBfZumbCk6SP/F2krfxQapWsvCQz0b2If4b19bJzKo98rwjyGpg/qYFl
+P8GMicWWMJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/cZip8UlF1y5mO6D1cv547
+KI2DAg+pn3LiLCuz3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTifBSeolz7p
+UcZsBSjBAg/pGG3svZwG1KdJ9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/
+kQO9lLvkuI6cMmPNn7togbGEW682v3fuHX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JO
+Hg9O5j9ZpSPcPYeoKFgo0fEbNttPxP/hjFtyjMcmAyejOQoBqsCyMWCDIqFPEgkB
+Ea801M/XrmLTBQe0MXXgDW1XT2mH+VepuhX2yFJtocucH+X8eKg1mp9BFM6ltM6U
+CBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm45P3luG0wDQYJ
+KoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6
+NWPxzIHIxgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQ
+nmhUQo8mUuJM3y+Xpi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+
+QgvfKNmwrZggvkN80V4aCRckjXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2v
+trV0KnahP/t1MJ+UXjulYPPLXAziDslg+MkfFoom3ecnf+slpoq9uC02EJqxWE2a
+aE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0DLwAHb/WNyVntHKLr4W96ioD
+j8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/xBpMu95yo9GA+o/E4
+Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0w
+lREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn
+YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVoc
+icCMb3SgazNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw
+-----END CERTIFICATE-----
+
+# Issuer: CN=CommScope Public Trust RSA Root-02 O=CommScope
+# Subject: CN=CommScope Public Trust RSA Root-02 O=CommScope
+# Label: "CommScope Public Trust RSA Root-02"
+# Serial: 480062499834624527752716769107743131258796508494
+# MD5 Fingerprint: e1:29:f9:62:7b:76:e2:96:6d:f3:d4:d7:0f:ae:1f:aa
+# SHA1 Fingerprint: ea:b0:e2:52:1b:89:93:4c:11:68:f2:d8:9a:ac:22:4c:a3:8a:57:ae
+# SHA256 Fingerprint: ff:e9:43:d7:93:42:4b:4f:7c:44:0c:1c:3d:64:8d:53:63:f3:4b:82:dc:87:aa:7a:9f:11:8f:c5:de:e1:01:f1
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQEL
+BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi
+Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2
+NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t
+U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt
+MDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3VrCLE
+NQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0
+kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1C
+rWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz
+hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2
+LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcs
+n/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tku
+FT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5
+kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3
+wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6v
+wQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs
+5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ
+KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB
+KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3
++VGXu6TwYofF1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbyme
+APnCKfWxkxlSaRosTKCL4BWaMS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3Nyq
+pgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT
+6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2OHG1QAk8mGEPej1WF
+sQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+NmYWvt
+PjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2d
+lklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670
+v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17O
+rg3bhzjlP1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7
+-----END CERTIFICATE-----
+
+# Issuer: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH
+# Subject: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH
+# Label: "Telekom Security TLS ECC Root 2020"
+# Serial: 72082518505882327255703894282316633856
+# MD5 Fingerprint: c1:ab:fe:6a:10:2c:03:8d:bc:1c:22:32:c0:85:a7:fd
+# SHA1 Fingerprint: c0:f8:96:c5:a9:3b:01:06:21:07:da:18:42:48:bc:e9:9d:88:d5:ec
+# SHA256 Fingerprint: 57:8a:f4:de:d0:85:3f:4e:59:98:db:4a:ea:f9:cb:ea:8d:94:5f:60:b6:20:a3:8d:1a:3c:13:b2:bc:7b:a8:e1
+-----BEGIN CERTIFICATE-----
+MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw
+CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH
+bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw
+MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx
+JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE
+AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49
+AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O
+tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP
+f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA
+MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di
+z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn
+27iQ7t0l
+-----END CERTIFICATE-----
+
+# Issuer: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH
+# Subject: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH
+# Label: "Telekom Security TLS RSA Root 2023"
+# Serial: 44676229530606711399881795178081572759
+# MD5 Fingerprint: bf:5b:eb:54:40:cd:48:71:c4:20:8d:7d:de:0a:42:f2
+# SHA1 Fingerprint: 54:d3:ac:b3:bd:57:56:f6:85:9d:ce:e5:c3:21:e2:d4:ad:83:d0:93
+# SHA256 Fingerprint: ef:c6:5c:ad:bb:59:ad:b6:ef:e8:4d:a2:23:11:b3:56:24:b7:1b:3b:1e:a0:da:8b:66:55:17:4e:c8:97:86:46
+-----BEGIN CERTIFICATE-----
+MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj
+MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0
+eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy
+MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC
+REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG
+A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9
+cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV
+cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA
+U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6
+Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug
+BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy
+8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J
+co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg
+8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8
+rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12
+mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg
++y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX
+gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2
+p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ
+pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm
+9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw
+M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd
+GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+
+CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t
+xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+
+w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK
+L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj
+X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q
+ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm
+dTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
+-----END CERTIFICATE-----
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/core.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/core.py
index 5d2b8cd..91f538b 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/core.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/certifi/core.py
@@ -1,20 +1,24 @@
-# -*- coding: utf-8 -*-
-
"""
certifi.py
~~~~~~~~~~
This module returns the installation location of cacert.pem or its contents.
"""
-import os
+import sys
+import atexit
-try:
- from importlib.resources import path as get_path, read_text
+def exit_cacert_ctx() -> None:
+ _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr]
+
+
+if sys.version_info >= (3, 11):
+
+ from importlib.resources import as_file, files
_CACERT_CTX = None
_CACERT_PATH = None
- def where():
+ def where() -> str:
# This is slightly terrible, but we want to delay extracting the file
# in cases where we're inside of a zipimport situation until someone
# actually calls where(), but we don't want to re-extract the file
@@ -33,28 +37,78 @@ def where():
# We also have to hold onto the actual context manager, because
# it will do the cleanup whenever it gets garbage collected, so
# we will also store that at the global level as well.
+ _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem"))
+ _CACERT_PATH = str(_CACERT_CTX.__enter__())
+ atexit.register(exit_cacert_ctx)
+
+ return _CACERT_PATH
+
+ def contents() -> str:
+ return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii")
+
+elif sys.version_info >= (3, 7):
+
+ from importlib.resources import path as get_path, read_text
+
+ _CACERT_CTX = None
+ _CACERT_PATH = None
+
+ def where() -> str:
+ # This is slightly terrible, but we want to delay extracting the
+ # file in cases where we're inside of a zipimport situation until
+ # someone actually calls where(), but we don't want to re-extract
+ # the file on every call of where(), so we'll do it once then store
+ # it in a global variable.
+ global _CACERT_CTX
+ global _CACERT_PATH
+ if _CACERT_PATH is None:
+ # This is slightly janky, the importlib.resources API wants you
+ # to manage the cleanup of this file, so it doesn't actually
+ # return a path, it returns a context manager that will give
+ # you the path when you enter it and will do any cleanup when
+ # you leave it. In the common case of not needing a temporary
+ # file, it will just return the file system location and the
+ # __exit__() is a no-op.
+ #
+ # We also have to hold onto the actual context manager, because
+ # it will do the cleanup whenever it gets garbage collected, so
+ # we will also store that at the global level as well.
_CACERT_CTX = get_path("certifi", "cacert.pem")
_CACERT_PATH = str(_CACERT_CTX.__enter__())
+ atexit.register(exit_cacert_ctx)
return _CACERT_PATH
+ def contents() -> str:
+ return read_text("certifi", "cacert.pem", encoding="ascii")
+
+else:
+ import os
+ import types
+ from typing import Union
+
+ Package = Union[types.ModuleType, str]
+ Resource = Union[str, "os.PathLike"]
-except ImportError:
# This fallback will work for Python versions prior to 3.7 that lack the
# importlib.resources module but relies on the existing `where` function
# so won't address issues with environments like PyOxidizer that don't set
# __file__ on modules.
- def read_text(_module, _path, encoding="ascii"):
- with open(where(), "r", encoding=encoding) as data:
+ def read_text(
+ package: Package,
+ resource: Resource,
+ encoding: str = 'utf-8',
+ errors: str = 'strict'
+ ) -> str:
+ with open(where(), encoding=encoding) as data:
return data.read()
# If we don't have importlib.resources, then we will just do the old logic
# of assuming we're on the filesystem and munge the path directly.
- def where():
+ def where() -> str:
f = os.path.dirname(__file__)
return os.path.join(f, "cacert.pem")
-
-def contents():
- return read_text("certifi", "cacert.pem", encoding="ascii")
+ def contents() -> str:
+ return read_text("certifi", "cacert.pem", encoding="ascii")
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/md.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/md.py
index b55e95c..f3d6505 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/md.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/md.py
@@ -314,7 +314,7 @@ def feed(self, character: str) -> None:
self._buffer = ""
self._buffer_accent_count = 0
elif (
- character not in {"<", ">", "-", "="}
+ character not in {"<", ">", "-", "=", "~", "|", "_"}
and character.isdigit() is False
and is_symbol(character)
):
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/version.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/version.py
index 69bf050..77cfff2 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/version.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/charset_normalizer/version.py
@@ -2,5 +2,5 @@
Expose version
"""
-__version__ = "2.0.11"
+__version__ = "2.0.12"
VERSION = __version__.split(".")
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/__init__.py
index 1ba7634..5aa568b 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/__init__.py
@@ -14,12 +14,7 @@
# limitations under the License.
#
"""
-APP Cloud Connect
+Cloud Connect library
"""
-import os
-from .common.lib_util import register_cacert_locater
-
-register_cacert_locater(os.path.join(os.path.dirname(__file__), 'core', 'cacerts'))
-
-__version__ = '2.0.2'
+__version__ = "3.1.3"
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/client.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/client.py
index 22c93a8..0383214 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/client.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/client.py
@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import object
import copy
import os.path
import traceback
@@ -27,9 +26,8 @@
_logger = get_cc_logger()
-class CloudConnectClient(object):
- """The client of cloud connect used to start a cloud connect engine instance.
- """
+class CloudConnectClient:
+ """The client of cloud connect used to start a cloud connect engine instance."""
def __init__(self, context, config_file, checkpoint_mgr):
"""
@@ -52,19 +50,20 @@ def _load_config(self):
conf = load_json_file(self._config_file)
except:
raise ConfigException(
- 'Unable to load configuration file %s: %s'
+ "Unable to load configuration file %s: %s"
% (self._config_file, traceback.format_exc())
)
- version = conf.get('meta', {'apiVersion', None}).get('apiVersion', None)
+ version = conf.get("meta", {"apiVersion", None}).get("apiVersion", None)
if not version:
raise ConfigException(
- 'Config meta or api version not present in {}'.format(
- self._config_file))
+ f"Config meta or api version not present in {self._config_file}"
+ )
config_loader, schema_file = get_loader_by_version(version)
schema_path = os.path.join(
- os.path.dirname(__file__), 'configuration', schema_file)
+ os.path.dirname(__file__), "configuration", schema_file
+ )
return config_loader.load(conf, schema_path, self._context)
@@ -80,14 +79,13 @@ def start(self):
self._engine.start(
context=copy.deepcopy(self._context),
config=self._config,
- checkpoint_mgr=self._checkpoint_mgr
+ checkpoint_mgr=self._checkpoint_mgr,
)
except Exception as ex:
- _logger.exception('Error while starting client')
+ _logger.exception("Error while starting client")
raise ex
def stop(self):
- """Stop the current cloud connect engine.
- """
+ """Stop the current cloud connect engine."""
if self._engine:
self._engine.stop()
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/lib_util.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/lib_util.py
index c1fee2e..8710e54 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/lib_util.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/lib_util.py
@@ -20,8 +20,6 @@
import __main__
-from ..splunktacollectorlib.common import log as stulog
-
def get_main_file():
"""Return the running mod input file"""
@@ -36,34 +34,25 @@ def get_app_root_dir():
def get_mod_input_script_name():
"""Return the name of running mod input"""
script_name = os.path.basename(get_main_file())
- if script_name.lower().endswith('.py'):
+ if script_name.lower().endswith(".py"):
script_name = script_name[:-3]
return script_name
def register_module(new_path):
- """ register_module(new_path): adds a directory to sys.path.
+ """register_module(new_path): adds a directory to sys.path.
Do nothing if it does not exist or if it's already in sys.path.
"""
if not os.path.exists(new_path):
return
new_path = os.path.abspath(new_path)
- if platform.system() == 'Windows':
+ if platform.system() == "Windows":
new_path = new_path.lower()
for x in sys.path:
x = os.path.abspath(x)
- if platform.system() == 'Windows':
+ if platform.system() == "Windows":
x = x.lower()
if new_path in (x, x + os.sep):
return
sys.path.insert(0, new_path)
-
-
-def register_cacert_locater(cacerts_locater_path):
- for x in sys.modules:
- if (x == "httplib2" or x.endswith(".httplib2")) and sys.modules[x] \
- is not None:
- stulog.logger.warning("Httplib2 module '{}' is already installed. "
- "The ca_certs_locater may not work".format(x))
- register_module(cacerts_locater_path)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/log.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/log.py
index 907d56d..50a2462 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/log.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/log.py
@@ -16,20 +16,20 @@
import logging
from solnlib.pattern import Singleton
+
from ..splunktacollectorlib.common import log as stulog
from ..splunktacollectorlib.data_collection import ta_helper as th
from .lib_util import get_mod_input_script_name
-from future.utils import with_metaclass
-class CloudClientLogAdapter(with_metaclass(Singleton, logging.LoggerAdapter)):
+class CloudClientLogAdapter(logging.LoggerAdapter, metaclass=Singleton):
def __init__(self, logger=None, extra=None, prefix=""):
- super(CloudClientLogAdapter, self).__init__(logger, extra)
+ super().__init__(logger, extra)
self.cc_prefix = prefix if prefix else ""
def process(self, msg, kwargs):
- msg = "{} {}".format(self.cc_prefix, msg)
- return super(CloudClientLogAdapter, self).process(msg, kwargs)
+ msg = f"{self.cc_prefix} {msg}"
+ return super().process(msg, kwargs)
def set_level(self, val):
self.logger.setLevel(val)
@@ -38,20 +38,20 @@ def set_level(self, val):
_adapter = CloudClientLogAdapter(stulog.logger)
-def set_cc_logger(logger, logger_prefix=''):
+def set_cc_logger(logger, logger_prefix=""):
global _adapter
_adapter.logger = logger
- _adapter.cc_prefix = logger_prefix or ''
+ _adapter.cc_prefix = logger_prefix or ""
def get_cc_logger():
return _adapter
-def reset_cc_logger(stanza_name, logging_level, logger_prefix=''):
+def reset_cc_logger(stanza_name, logging_level, logger_prefix=""):
script_name = get_mod_input_script_name()
logger_name = script_name + "_" + th.format_name_for_file(stanza_name)
stulog.reset_logger(logger_name)
stulog.set_log_level(logging_level)
set_cc_logger(stulog.logger, logger_prefix)
- return get_cc_logger()
\ No newline at end of file
+ return get_cc_logger()
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/util.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/util.py
index e072ac7..1e188ae 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/util.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/common/util.py
@@ -14,7 +14,8 @@
# limitations under the License.
#
import json
-from splunktalib.common import util
+
+from solnlib import utils
from solnlib.modular_input.event import XMLEvent
@@ -23,11 +24,11 @@ def is_valid_bool(val):
:param val: value as string.
:return: `True` if value can be convert to bool else `False`.
"""
- return util.is_true(val) or util.is_false(val)
+ return utils.is_true(val) or utils.is_false(val)
def is_true(val):
- return util.is_true(val)
+ return utils.is_true(val)
def is_valid_port(port):
@@ -47,17 +48,32 @@ def load_json_file(file_path):
:param file_path: JSON file path.
:return: A `dict` object.
"""
- with open(file_path, 'r') as file_pointer:
+ with open(file_path) as file_pointer:
return json.load(file_pointer)
-def format_events(raw_events, time=None,
- index=None, host=None, source=None, sourcetype=None,
- stanza=None, unbroken=False, done=False):
- return XMLEvent.format_events(XMLEvent(data, time=time,
- index=index, host=host,
- source=source,
- sourcetype=sourcetype,
- stanza=stanza, unbroken=unbroken,
- done=done) for data in
- raw_events)
+def format_events(
+ raw_events,
+ time=None,
+ index=None,
+ host=None,
+ source=None,
+ sourcetype=None,
+ stanza=None,
+ unbroken=False,
+ done=False,
+):
+ return XMLEvent.format_events(
+ XMLEvent(
+ data,
+ time=time,
+ index=index,
+ host=host,
+ source=source,
+ sourcetype=sourcetype,
+ stanza=stanza,
+ unbroken=unbroken,
+ done=done,
+ )
+ for data in raw_events
+ )
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/configuration/loader.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/configuration/loader.py
index 1eb6470..c9ac7a6 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/configuration/loader.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/configuration/loader.py
@@ -13,62 +13,62 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import str
-from builtins import object
import logging
import re
import traceback
from abc import abstractmethod
-import six
-from jsonschema import validate, ValidationError
+from jsonschema import ValidationError, validate
from munch import munchify
+
from ..common.log import get_cc_logger
-from ..common.util import (
- load_json_file, is_valid_bool, is_valid_port, is_true
-)
+from ..common.util import is_true, is_valid_bool, is_valid_port, load_json_file
from ..core.exceptions import ConfigException
from ..core.ext import lookup_method
from ..core.models import (
- BasicAuthorization, RequestParams, Processor,
- Condition, Task, Checkpoint, IterationMode,
- DictToken
+ BasicAuthorization,
+ Checkpoint,
+ Condition,
+ DictToken,
+ IterationMode,
+ Processor,
+ RequestParams,
+ Task,
)
_logger = get_cc_logger()
-_PROXY_TYPES = ['http', 'socks4', 'socks5',]
-_AUTH_TYPES = {
- 'basic_auth': BasicAuthorization
-}
+_PROXY_TYPES = ["http", "socks4", "socks5", "http_no_tunnel"]
+_AUTH_TYPES = {"basic_auth": BasicAuthorization}
_LOGGING_LEVELS = {
- 'DEBUG': logging.DEBUG,
- 'INFO': logging.INFO,
- 'WARNING': logging.WARNING,
- 'ERROR': logging.ERROR,
- 'FATAL': logging.FATAL,
- 'CRITICAL': logging.CRITICAL
+ "DEBUG": logging.DEBUG,
+ "INFO": logging.INFO,
+ "WARNING": logging.WARNING,
+ "ERROR": logging.ERROR,
+ "FATAL": logging.FATAL,
+ "CRITICAL": logging.CRITICAL,
}
# FIXME Make this configurable
-_DEFAULT_LOG_LEVEL = 'INFO'
+_DEFAULT_LOG_LEVEL = "INFO"
-class CloudConnectConfigLoader(object):
+class CloudConnectConfigLoader:
"""The Base cloud connect configuration loader"""
@staticmethod
def _get_schema_from_file(schema_file):
- """ Load JSON based schema definition from schema file path.
+ """Load JSON based schema definition from schema file path.
:return: A `dict` contains schema.
"""
try:
return load_json_file(schema_file)
except:
raise ConfigException(
- 'Cannot load schema from file {}: {}'.format(
- schema_file, traceback.format_exc())
+ "Cannot load schema from file {}: {}".format(
+ schema_file, traceback.format_exc()
+ )
)
@abstractmethod
@@ -81,8 +81,7 @@ class CloudConnectConfigLoaderV1(CloudConnectConfigLoader):
def _render_from_dict(source, ctx):
rendered = DictToken(source).render(ctx)
- return dict((k, v.strip() if isinstance(v, six.string_types) else v)
- for k, v in rendered.items())
+ return {k: v.strip() if isinstance(v, str) else v for k, v in rendered.items()}
def _load_proxy(self, candidate, variables):
"""
@@ -96,15 +95,13 @@ def _load_proxy(self, candidate, variables):
proxy = self._render_from_dict(candidate, variables)
- enabled = proxy.get('enabled', '0')
+ enabled = proxy.get("enabled", "0")
if not is_valid_bool(enabled):
- raise ValueError(
- 'Proxy "enabled" expect to be bool type: {}'.format(enabled)
- )
+ raise ValueError(f'Proxy "enabled" expect to be bool type: {enabled}')
- proxy['enabled'] = is_true(enabled)
+ proxy["enabled"] = is_true(enabled)
- host, port = proxy.get('host'), proxy.get('port')
+ host, port = proxy.get("host"), proxy.get("port")
if host or port:
if not host:
@@ -116,24 +113,23 @@ def _load_proxy(self, candidate, variables):
)
# proxy type default to 'http'
- proxy_type = proxy.get('type')
- proxy_type = proxy_type.lower() if proxy_type else 'http'
+ proxy_type = proxy.get("type")
+ proxy_type = proxy_type.lower() if proxy_type else "http"
if proxy_type not in _PROXY_TYPES:
raise ValueError(
'Proxy "type" expect to be one of [{}]: {}'.format(
- ','.join(_PROXY_TYPES), proxy_type)
+ ",".join(_PROXY_TYPES), proxy_type
+ )
)
else:
- proxy['type'] = proxy_type
+ proxy["type"] = proxy_type
# proxy rdns default to '0'
- proxy_rdns = proxy.get('rdns', '0')
+ proxy_rdns = proxy.get("rdns", "0")
if not is_valid_bool(proxy_rdns):
- raise ValueError(
- 'Proxy "rdns" expect to be bool type: {}'.format(proxy_rdns)
- )
+ raise ValueError(f'Proxy "rdns" expect to be bool type: {proxy_rdns}')
else:
- proxy['rdns'] = is_true(proxy_rdns)
+ proxy["rdns"] = is_true(proxy_rdns)
return proxy
@@ -148,7 +144,8 @@ def _get_log_level(level_name):
_logger.warning(
'The log level "%s" is invalid, set it to default: "%s"',
- level_name, _DEFAULT_LOG_LEVEL
+ level_name,
+ _DEFAULT_LOG_LEVEL,
)
return _LOGGING_LEVELS[_DEFAULT_LOG_LEVEL]
@@ -156,7 +153,7 @@ def _get_log_level(level_name):
def _load_logging(self, log_setting, variables):
logger = self._render_from_dict(log_setting, variables)
- logger['level'] = self._get_log_level(logger.get('level'))
+ logger["level"] = self._get_log_level(logger.get("level"))
return logger
@@ -168,98 +165,94 @@ def _load_global_setting(self, candidate, variables):
:return: A `Munch` object
"""
candidate = candidate or {}
- proxy_setting = self._load_proxy(candidate.get('proxy'), variables)
- log_setting = self._load_logging(candidate.get('logging'), variables)
+ proxy_setting = self._load_proxy(candidate.get("proxy"), variables)
+ log_setting = self._load_logging(candidate.get("logging"), variables)
- return munchify({'proxy': proxy_setting, 'logging': log_setting})
+ return munchify({"proxy": proxy_setting, "logging": log_setting})
@staticmethod
def _load_authorization(candidate):
if candidate is None:
return None
- auth_type = candidate['type'].lower()
+ auth_type = candidate["type"].lower()
if auth_type not in _AUTH_TYPES:
raise ValueError(
- 'Auth type expect to be one of [{}]: {}'.format(
- ','.join(list(_AUTH_TYPES.keys())), auth_type)
+ "Auth type expect to be one of [{}]: {}".format(
+ ",".join(list(_AUTH_TYPES.keys())), auth_type
+ )
)
- return _AUTH_TYPES[auth_type](candidate['options'])
+ return _AUTH_TYPES[auth_type](candidate["options"])
def _load_options(self, options):
return RequestParams(
- auth=self._load_authorization(options.get('auth')),
- url=options['url'],
- method=options.get('method', 'GET'),
- header=options.get('headers', {}),
- body=options.get('body', {})
+ auth=self._load_authorization(options.get("auth")),
+ url=options["url"],
+ method=options.get("method", "GET"),
+ header=options.get("headers", {}),
+ body=options.get("body", {}),
)
@staticmethod
def _validate_method(method):
if lookup_method(method) is None:
- raise ValueError('Unimplemented method: {}'.format(method))
+ raise ValueError(f"Unimplemented method: {method}")
def _parse_tasks(self, raw_tasks):
tasks = []
for item in raw_tasks:
- self._validate_method(item['method'])
- tasks.append(Task(item['input'], item['method'], item.get('output')))
+ self._validate_method(item["method"])
+ tasks.append(Task(item["input"], item["method"], item.get("output")))
return tasks
def _parse_conditions(self, raw_conditions):
conditions = []
for item in raw_conditions:
- self._validate_method(item['method'])
- conditions.append(Condition(item['input'], item['method']))
+ self._validate_method(item["method"])
+ conditions.append(Condition(item["input"], item["method"]))
return conditions
@staticmethod
def _load_checkpoint(checkpoint):
if not checkpoint:
return None
- return Checkpoint(
- checkpoint.get('namespace', []), checkpoint['content'])
+ return Checkpoint(checkpoint.get("namespace", []), checkpoint["content"])
def _load_iteration_mode(self, iteration_mode):
- count = iteration_mode.get('iteration_count', '0')
+ count = iteration_mode.get("iteration_count", "0")
try:
iteration_count = int(count)
except ValueError:
- raise ValueError(
- '"iteration_count" must be an integer: %s' % count)
+ raise ValueError('"iteration_count" must be an integer: %s' % count)
- stop_conditions = self._parse_conditions(
- iteration_mode['stop_conditions'])
+ stop_conditions = self._parse_conditions(iteration_mode["stop_conditions"])
- return IterationMode(iteration_count=iteration_count,
- conditions=stop_conditions)
+ return IterationMode(
+ iteration_count=iteration_count, conditions=stop_conditions
+ )
def _load_processor(self, processor):
- skip_conditions = self._parse_conditions(
- processor.get('skip_conditions', [])
- )
- pipeline = self._parse_tasks(processor.get('pipeline', []))
- return Processor(
- skip_conditions=skip_conditions,
- pipeline=pipeline
- )
+ skip_conditions = self._parse_conditions(processor.get("skip_conditions", []))
+ pipeline = self._parse_tasks(processor.get("pipeline", []))
+ return Processor(skip_conditions=skip_conditions, pipeline=pipeline)
def _load_request(self, request):
- options = self._load_options(request['request'])
-
- pre_process = self._load_processor(request.get('pre_process', {}))
- post_process = self._load_processor(request['post_process'])
- checkpoint = self._load_checkpoint(request.get('checkpoint'))
- iteration_mode = self._load_iteration_mode(request['iteration_mode'])
-
- return munchify({
- 'request': options,
- 'pre_process': pre_process,
- 'post_process': post_process,
- 'checkpoint': checkpoint,
- 'iteration_mode': iteration_mode,
- })
+ options = self._load_options(request["request"])
+
+ pre_process = self._load_processor(request.get("pre_process", {}))
+ post_process = self._load_processor(request["post_process"])
+ checkpoint = self._load_checkpoint(request.get("checkpoint"))
+ iteration_mode = self._load_iteration_mode(request["iteration_mode"])
+
+ return munchify(
+ {
+ "request": options,
+ "pre_process": pre_process,
+ "post_process": post_process,
+ "checkpoint": checkpoint,
+ "iteration_mode": iteration_mode,
+ }
+ )
def load(self, definition, schema_file, context):
"""Load cloud connect configuration from a `dict` and validate
@@ -273,35 +266,39 @@ def load(self, definition, schema_file, context):
validate(definition, self._get_schema_from_file(schema_file))
except ValidationError:
raise ConfigException(
- 'Failed to validate interface with schema: {}'.format(
- traceback.format_exc()))
+ "Failed to validate interface with schema: {}".format(
+ traceback.format_exc()
+ )
+ )
try:
global_settings = self._load_global_setting(
- definition.get('global_settings'), context
+ definition.get("global_settings"), context
)
- requests = [self._load_request(item) for item in definition['requests']]
+ requests = [self._load_request(item) for item in definition["requests"]]
- return munchify({
- 'meta': munchify(definition['meta']),
- 'tokens': definition['tokens'],
- 'global_settings': global_settings,
- 'requests': requests,
- })
+ return munchify(
+ {
+ "meta": munchify(definition["meta"]),
+ "tokens": definition["tokens"],
+ "global_settings": global_settings,
+ "requests": requests,
+ }
+ )
except Exception as ex:
- error = 'Unable to load configuration: %s' % str(ex)
+ error = "Unable to load configuration: %s" % str(ex)
_logger.exception(error)
raise ConfigException(error)
_loader_and_schema_by_version = {
- r'1\.0\.0': (CloudConnectConfigLoaderV1, 'schema_1_0_0.json'),
+ r"1\.0\.0": (CloudConnectConfigLoaderV1, "schema_1_0_0.json"),
}
def get_loader_by_version(version):
- """ Instantiate a configuration loader on basis of a given version.
+ """Instantiate a configuration loader on basis of a given version.
A `ConfigException` will raised if the version is not supported.
:param version: Version to lookup config loader.
:return: A config loader.
@@ -312,7 +309,8 @@ def get_loader_by_version(version):
return loader_cls(), schema
raise ConfigException(
- 'Unsupported schema version {}, current supported'
- ' versions should match these regex [{}]'.format(version, ','.join(
- _loader_and_schema_by_version))
+ "Unsupported schema version {}, current supported"
+ " versions should match these regex [{}]".format(
+ version, ",".join(_loader_and_schema_by_version)
+ )
)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/cacerts/ca_certs_locater.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/cacerts/ca_certs_locater.py
deleted file mode 100755
index 44040b2..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/cacerts/ca_certs_locater.py
+++ /dev/null
@@ -1,145 +0,0 @@
-#
-# Copyright 2021 Splunk Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""
-`ca_certs_locater` is a lib for extending httplib2 to allow system certificate store to be used when
-verifying SSL certificates, to enable this lib, you should add it to your python import path before
-initializing httplib2. As we're not trying to implement SSL certificate RFCs, parsing and validating
-certificates are not included.
-"""
-
-import atexit
-import os
-import os.path as op
-import ssl
-import sys
-
-TEMP_CERT_FILE_NAME = 'httplib2_merged_certificates_{}.crt'
-LINUX_CERT_PATH_1 = '/etc/pki/tls/certs/ca-bundle.crt' # RedHat
-LINUX_CERT_PATH_2 = '/etc/ssl/certs/ca-certificates.crt' # Debian
-DARWIN_CERT_PATH = '/usr/local/etc/openssl/cert.pem'
-HTTPLIB2_CA_CERT_FILE_NAME = 'cacerts.txt'
-
-TEMP_CERT_FILE_PATH = None
-
-
-def get():
- """
- Returns: a path to generated certificate authority file
-
- """
- try:
- return _get()
- except (IOError, OSError, ssl.SSLError):
- _fallback() # IO and SSL relative errors should be swallowed to protect the HTTP request
-
-
-def _get():
- global TEMP_CERT_FILE_PATH
- # also check file existence as it's possible for the temp file to be deleted
- if TEMP_CERT_FILE_PATH is None or not os.path.exists(TEMP_CERT_FILE_PATH):
- temp_cert_file_path = _generate_temp_cert_file_name()
- ssl_ca_certs = _read_ssl_default_ca_certs()
- if not ssl_ca_certs:
- # it's possible the ca load path is not well configured, try some typical paths
- ssl_ca_certs = _read_platform_pem_cert_file()
-
- if ssl_ca_certs: # only update temp cert file when there's additional PEM certs found
- cert_files = [ssl_ca_certs, _read_httplib2_default_certs()]
- _update_temp_cert_file(temp_cert_file_path, cert_files)
- TEMP_CERT_FILE_PATH = temp_cert_file_path
- else:
- _fallback()
-
- return TEMP_CERT_FILE_PATH
-
-
-def _fallback():
- """
- Give up the loading process by throwing specified exception, httplib2 will then use its
- bundled certificates
- """
- raise ImportError('Unable to load system certificate authority files')
-
-
-def _read_platform_pem_cert_file():
- if sys.platform.startswith('linux'):
- pem_files = [_read_pem_file(LINUX_CERT_PATH_1), _read_pem_file(LINUX_CERT_PATH_2)]
- return '\n'.join([_f for _f in pem_files if _f])
- elif sys.platform.startswith('darwin'):
- return _read_pem_file(DARWIN_CERT_PATH)
- else:
- return ""
-
-
-def _read_ssl_default_ca_certs():
- # it's not guaranteed to return PEM formatted certs when `binary_form` is False
- der_certs = ssl.create_default_context().get_ca_certs(binary_form=True)
- pem_certs = [ssl.DER_cert_to_PEM_cert(der_cert_bytes) for der_cert_bytes in der_certs]
- return '\n'.join(pem_certs)
-
-
-def _read_httplib2_default_certs():
- import httplib2 # import error should not happen here, and will be well handled by outer called
- httplib_dir = os.path.dirname(os.path.abspath(httplib2.__file__))
- ca_certs_path = os.path.join(httplib_dir, HTTPLIB2_CA_CERT_FILE_NAME)
- return _read_pem_file(ca_certs_path)
-
-
-def _read_pem_file(path):
- if os.path.exists(path):
- with open(path, mode='r') as pem_file:
- return pem_file.read()
- else:
- return ""
-
-
-def _update_temp_cert_file(temp_file, pem_texts):
- with open(temp_file, mode='w') as temp_cert_file:
- for pem_text in pem_texts:
- if len(pem_text) > 0:
- temp_cert_file.write(pem_text + '\n')
- temp_cert_file.flush()
- atexit.register(_do_safe_remove, temp_file)
-
-
-def _do_safe_remove(file_path):
- if os.path.exists(file_path):
- try:
- os.remove(file_path)
- except:
- pass
-
-
-def _get_temp_cert_file_dir():
- import __main__
- app_root = op.dirname(op.dirname(op.abspath(__main__.__file__)))
-
- temp_dir = op.join(app_root, 'temp_certs')
- if not op.isdir(temp_dir):
- try:
- os.mkdir(temp_dir)
- except:
- pass
- for candidate in ['temp_certs', 'local', 'default']:
- dir_path = op.join(app_root, candidate)
- if op.isdir(dir_path):
- return dir_path
- return app_root
-
-
-def _generate_temp_cert_file_name():
- file_name = TEMP_CERT_FILE_NAME.format(os.getpid())
- return os.path.join(_get_temp_cert_file_dir(), file_name)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/checkpoint.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/checkpoint.py
index 6aacd2c..6e8b8b9 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/checkpoint.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/checkpoint.py
@@ -15,7 +15,7 @@
#
import cloudconnectlib.splunktacollectorlib.data_collection.ta_checkpoint_manager as tacm
from cloudconnectlib.common.log import get_cc_logger
-from cloudconnectlib.core.models import _Token, DictToken
+from cloudconnectlib.core.models import DictToken, _Token
logger = get_cc_logger()
@@ -24,7 +24,7 @@ class CheckpointManagerAdapter(tacm.TACheckPointMgr):
"""Wrap TACheckPointMgr for custom usage"""
def __init__(self, namespaces, content, meta_config, task_config):
- super(CheckpointManagerAdapter, self).__init__(meta_config, task_config)
+ super().__init__(meta_config, task_config)
if isinstance(namespaces, (list, tuple)):
self.namespaces = (_Token(t) for t in namespaces)
else:
@@ -36,16 +36,15 @@ def _namespaces_for(self, ctx):
def save(self, ctx):
"""Save checkpoint"""
- super(CheckpointManagerAdapter, self).update_ckpt(
- ckpt=self.content.render(ctx),
- namespaces=self._namespaces_for(ctx)
+ super().update_ckpt(
+ ckpt=self.content.render(ctx), namespaces=self._namespaces_for(ctx)
)
def load(self, ctx):
"""Load checkpoint"""
namespaces = self._namespaces_for(ctx)
- checkpoint = super(CheckpointManagerAdapter, self).get_ckpt(namespaces)
+ checkpoint = super().get_ckpt(namespaces)
if checkpoint is None:
- logger.info('No existing checkpoint found')
+ logger.info("No existing checkpoint found")
checkpoint = {}
return checkpoint
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/defaults.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/defaults.py
index 2144e86..52710e5 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/defaults.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/defaults.py
@@ -22,14 +22,29 @@
success_statuses = (200, 201) # statuses be treated as success.
# response status which need to retry.
-retry_statuses = (429, 500, 501, 502, 503, 504, 505, 506, 507,
- 509, 510, 511)
+retry_statuses = (429, 500, 501, 502, 503, 504, 505, 506, 507, 509, 510, 511)
# response status which need print a warning log.
-warning_statuses = (203, 204, 205, 206, 207, 208, 226,
- 300, 301, 302, 303, 304, 305, 306, 307, 308)
+warning_statuses = (
+ 203,
+ 204,
+ 205,
+ 206,
+ 207,
+ 208,
+ 226,
+ 300,
+ 301,
+ 302,
+ 303,
+ 304,
+ 305,
+ 306,
+ 307,
+ 308,
+)
retries = 3 # Default maximum retry times.
max_iteration_count = 100 # maximum iteration loop count
-charset = 'utf-8' # Default response charset if not found in response header
+charset = "utf-8" # Default response charset if not found in response header
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine.py
index 5e1da2d..4197451 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine.py
@@ -13,20 +13,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import object
import threading
+from ..common.log import get_cc_logger
from . import defaults
from .exceptions import HTTPError, StopCCEIteration
from .http import HttpClient
-from ..common.log import get_cc_logger
_logger = get_cc_logger()
-class CloudConnectEngine(object):
+class CloudConnectEngine:
"""The cloud connect engine to process request instantiated
- from user options."""
+ from user options."""
def __init__(self):
self._stopped = False
@@ -38,17 +37,17 @@ def _set_logging(log_setting):
def start(self, context, config, checkpoint_mgr):
"""Start current client instance to execute each request parsed
- from config.
+ from config.
"""
if not config:
- raise ValueError('Config must not be empty')
+ raise ValueError("Config must not be empty")
context = context or {}
global_setting = config.global_settings
CloudConnectEngine._set_logging(global_setting.logging)
- _logger.info('Start to execute requests jobs.')
+ _logger.info("Start to execute requests jobs.")
processed = 0
for request in config.requests:
@@ -62,34 +61,33 @@ def start(self, context, config, checkpoint_mgr):
job.run()
processed += 1
- _logger.info('%s job(s) process finished', processed)
+ _logger.info("%s job(s) process finished", processed)
if self._stopped:
- _logger.info(
- 'Engine has been stopped, stopping to execute jobs.')
+ _logger.info("Engine has been stopped, stopping to execute jobs.")
break
self._stopped = True
- _logger.info('Engine executing finished')
+ _logger.info("Engine executing finished")
def stop(self):
"""Stops engine and running job. Do nothing if engine already
been stopped."""
if self._stopped:
- _logger.info('Engine already stopped, do nothing.')
+ _logger.info("Engine already stopped, do nothing.")
return
- _logger.info('Stopping engine')
+ _logger.info("Stopping engine")
if self._running_job:
- _logger.info('Attempting to stop the running job.')
+ _logger.info("Attempting to stop the running job.")
self._running_job.terminate()
- _logger.info('Stopping job finished.')
+ _logger.info("Stopping job finished.")
self._stopped = True
-class Job(object):
+class Job:
"""Job class represents a single request to send HTTP request until
reached it's stop condition.
"""
@@ -121,27 +119,30 @@ def __init__(self, request, context, checkpoint_mgr, proxy=None):
def _get_max_iteration_count(self):
mode_max_count = self._iteration_mode.iteration_count
default_max_count = defaults.max_iteration_count
- return min(default_max_count, mode_max_count) \
- if mode_max_count > 0 else default_max_count
+ return (
+ min(default_max_count, mode_max_count)
+ if mode_max_count > 0
+ else default_max_count
+ )
def terminate(self, block=True, timeout=30):
"""Terminate this job, the current thread will blocked util
- the job is terminate finished if block is True """
+ the job is terminate finished if block is True"""
if self.is_stopped():
- _logger.info('Job already been stopped.')
+ _logger.info("Job already been stopped.")
return
if self._running_thread == threading.current_thread():
- _logger.warning('Job cannot terminate itself.')
+ _logger.warning("Job cannot terminate itself.")
return
- _logger.info('Stopping job')
+ _logger.info("Stopping job")
self._should_stop = True
if not block:
return
if not self._terminated.wait(timeout):
- _logger.warning('Terminating job timeout.')
+ _logger.warning("Terminating job timeout.")
def _set_context(self, key, value):
self._context[key] = value
@@ -161,12 +162,11 @@ def _on_pre_process(self):
pre_processor = self._request.pre_process
if pre_processor.should_skipped(self._context):
- _logger.info('Skip pre process condition satisfied, do nothing')
+ _logger.info("Skip pre process condition satisfied, do nothing")
return
tasks = pre_processor.pipeline
- _logger.debug(
- 'Got %s tasks need be executed before process', len(tasks))
+ _logger.debug("Got %s tasks need be executed before process", len(tasks))
self._execute_tasks(tasks)
def _on_post_process(self):
@@ -176,21 +176,18 @@ def _on_post_process(self):
post_processor = self._request.post_process
if post_processor.should_skipped(self._context):
- _logger.info('Skip post process condition satisfied, '
- 'do nothing')
+ _logger.info("Skip post process condition satisfied, " "do nothing")
return
tasks = post_processor.pipeline
- _logger.debug(
- 'Got %s tasks need to be executed after process', len(tasks)
- )
+ _logger.debug("Got %s tasks need to be executed after process", len(tasks))
self._execute_tasks(tasks)
def _update_checkpoint(self):
"""Updates checkpoint based on checkpoint namespace and content."""
checkpoint = self._request.checkpoint
if not checkpoint:
- _logger.info('Checkpoint not specified, do not update it.')
+ _logger.info("Checkpoint not specified, do not update it.")
return
self._checkpoint_mgr.update_ckpt(
@@ -201,7 +198,7 @@ def _update_checkpoint(self):
def _get_checkpoint(self):
checkpoint = self._request.checkpoint
if not checkpoint:
- _logger.info('Checkpoint not specified, do not read it.')
+ _logger.info("Checkpoint not specified, do not read it.")
return
namespaces = checkpoint.normalize_namespace(self._context)
@@ -213,14 +210,15 @@ def _is_stoppable(self):
"""Check if repeat mode conditions satisfied."""
if self._request_iterated_count >= self._max_iteration_count:
_logger.info(
- 'Job iteration count is %s, current request count is %s,'
- ' stop condition satisfied.',
- self._max_iteration_count, self._request_iterated_count
+ "Job iteration count is %s, current request count is %s,"
+ " stop condition satisfied.",
+ self._max_iteration_count,
+ self._request_iterated_count,
)
return True
if self._iteration_mode.passed(self._context):
- _logger.info('Job stop condition satisfied.')
+ _logger.info("Job stop condition satisfied.")
return True
return False
@@ -230,25 +228,25 @@ def is_stopped(self):
return self._stopped
def run(self):
- """Start job and exit util meet stop condition. """
- _logger.info('Start to process job')
+ """Start job and exit util meet stop condition."""
+ _logger.info("Start to process job")
self._stopped = False
try:
self._running_thread = threading.current_thread()
self._run()
except Exception:
- _logger.exception('Error encountered while running job.')
+ _logger.exception("Error encountered while running job.")
raise
finally:
self._terminated.set()
self._stopped = True
- _logger.info('Job processing finished')
+ _logger.info("Job processing finished")
def _check_should_stop(self):
if self._should_stop:
- _logger.info('Job should been stopped.')
+ _logger.info("Job should been stopped.")
return self._should_stop
def _run(self):
@@ -263,7 +261,9 @@ def _run(self):
try:
self._on_pre_process()
except StopCCEIteration:
- _logger.info('Stop iteration command in pre process is received, exit job now.')
+ _logger.info(
+ "Stop iteration command in pre process is received, exit job now."
+ )
return
r = request.render(self._context)
@@ -277,18 +277,20 @@ def _run(self):
response, need_terminate = self._send_request(r)
if need_terminate:
- _logger.info('This job need to be terminated.')
+ _logger.info("This job need to be terminated.")
break
self._request_iterated_count += 1
- self._set_context('__response__', response)
+ self._set_context("__response__", response)
if self._check_should_stop():
return
try:
self._on_post_process()
except StopCCEIteration:
- _logger.info('Stop iteration command in post process is received, exit job now.')
+ _logger.info(
+ "Stop iteration command in post process is received, exit job now."
+ )
return
if self._check_should_stop():
@@ -296,7 +298,7 @@ def _run(self):
self._update_checkpoint()
if self._is_stoppable():
- _logger.info('Stop condition reached, exit job now')
+ _logger.info("Stop condition reached, exit job now")
break
def _send_request(self, request):
@@ -306,26 +308,30 @@ def _send_request(self, request):
response = self._client.send(request)
except HTTPError as error:
_logger.exception(
- 'HTTPError reason=%s when sending request to '
- 'url=%s method=%s', error.reason, request.url, request.method)
+ "HTTPError reason=%s when sending request to " "url=%s method=%s",
+ error.reason,
+ request.url,
+ request.method,
+ )
return None, True
status = response.status_code
if status in defaults.success_statuses:
- if not (response.body or '').strip():
+ if not (response.body or "").strip():
_logger.info(
- 'The response body of request which url=%s and'
- ' method=%s is empty, status=%s.',
- request.url, request.method, status
+ "The response body of request which url=%s and"
+ " method=%s is empty, status=%s.",
+ request.url,
+ request.method,
+ status,
)
return None, True
return response, False
- error_log = ('The response status=%s for request which url=%s and'
- ' method=%s.') % (
- status, request.url, request.method
- )
+ error_log = (
+ "The response status=%s for request which url=%s and" " method=%s."
+ ) % (status, request.url, request.method)
if status in defaults.warning_statuses:
_logger.warning(error_log)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine_v2.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine_v2.py
index a777d7a..250ade6 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine_v2.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/engine_v2.py
@@ -13,27 +13,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import object
import concurrent.futures as cf
import threading
from collections import Iterable
-from ..common.log import get_cc_logger
from os import path as op
+
+from ..common.log import get_cc_logger
from .plugin import init_pipeline_plugins
-logger = get_cc_logger()
+logger = get_cc_logger()
-class CloudConnectEngine(object):
- def __init__(self, max_workers=4):
+class CloudConnectEngine:
+ def __init__(self, max_workers=4, plugin_dir=""):
+ """
+ Initialize CloudConnectEngine object
+ :param max_workers: maximum number of Threads to execute the given calls
+ :param plugin_dir: Absolute path of directory containing cce_plugin_*.py
+ """
self._executor = cf.ThreadPoolExecutor(max_workers)
self._pending_job_results = set()
self._shutdown = False
self._pending_jobs = []
self._counter = 0
self._lock = threading.RLock()
- init_pipeline_plugins(
- op.join(op.dirname(op.dirname(__file__)), "plugin"))
+ plugin_dir = plugin_dir or op.join(op.dirname(op.dirname(__file__)), "plugin")
+ init_pipeline_plugins(plugin_dir)
def start(self, jobs=None):
"""
@@ -54,8 +59,9 @@ def start(self, jobs=None):
break
# check the intermediate results to find the done jobs and not
# done jobs
- done_and_not_done_jobs = cf.wait(self._pending_job_results,
- return_when=cf.FIRST_COMPLETED)
+ done_and_not_done_jobs = cf.wait(
+ self._pending_job_results, return_when=cf.FIRST_COMPLETED
+ )
self._pending_job_results = done_and_not_done_jobs.not_done
done_job_results = done_and_not_done_jobs.done
for future in done_job_results:
@@ -68,7 +74,7 @@ def start(self, jobs=None):
self._add_job(temp)
else:
self._add_job(result)
- except:
+ except Exception:
logger.exception("CloudConnectEngine encountered exception")
finally:
self._teardown()
@@ -86,8 +92,7 @@ def _add_job(self, job):
result = self._executor.submit(self._invoke_job, job)
self._pending_job_results.add(result)
self._counter += 1
- logger.debug("%s job(s) have been added to the engine now",
- self._counter)
+ logger.debug("%s job(s) have been added to the engine now", self._counter)
return True
def _invoke_job(self, job):
@@ -102,7 +107,7 @@ def _invoke_job(self, job):
return None
invoke_result = job.run()
return invoke_result
- except:
+ except Exception:
logger.exception("job %s is invoked with exception", job)
return None
finally:
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/exceptions.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/exceptions.py
index 8738682..3b0ccf7 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/exceptions.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/exceptions.py
@@ -22,32 +22,36 @@ class CCEError(Exception):
class ConfigException(CCEError):
"""Config exception"""
+
pass
class FuncException(CCEError):
"""Ext function call exception"""
+
pass
class HTTPError(CCEError):
- """ HTTPError raised when HTTP request returned a error."""
+ """HTTPError raised when HTTP request returned a error."""
def __init__(self, reason=None):
"""
Initialize HTTPError with `response` object and `status`.
"""
self.reason = reason
- super(HTTPError, self).__init__(reason)
+ super().__init__(reason)
class StopCCEIteration(CCEError):
"""Exception to exit from the engine iteration."""
+
pass
class CCESplitError(CCEError):
"""Exception to exit the job in Split Task"""
+
pass
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/ext.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/ext.py
index cc93735..abe0875 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/ext.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/ext.py
@@ -13,34 +13,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import str
-from builtins import range
import calendar
import json
import re
import traceback
from collections import Iterable
from datetime import datetime
-import six
from jsonpath_ng import parse
-from .exceptions import FuncException, StopCCEIteration, QuitJobError
+
+from ..common import log, util
+from .exceptions import FuncException, QuitJobError, StopCCEIteration
from .pipemgr import PipeManager
-from ..common import util, log
_logger = log.get_cc_logger()
def regex_search(pattern, source, flags=0):
"""Search substring in source through regex"""
- if not isinstance(source, six.string_types):
- _logger.warning('Cannot apply regex search on non-string: %s', type(source))
+ if not isinstance(source, str):
+ _logger.warning("Cannot apply regex search on non-string: %s", type(source))
return {}
try:
matches = re.search(pattern=pattern, string=source, flags=flags)
except Exception:
- _logger.warning('Unable to search pattern=%s and flags=%s in string, error=%s',
- pattern, flags, traceback.format_exc())
+ _logger.warning(
+ "Unable to search pattern=%s and flags=%s in string, error=%s",
+ pattern,
+ flags,
+ traceback.format_exc(),
+ )
return {}
else:
return matches.groupdict() if matches else {}
@@ -59,49 +61,36 @@ def regex_match(pattern, source, flags=0):
return re.match(pattern, source, flags) is not None
except Exception:
_logger.warning(
- 'Unable to match source with pattern=%s, cause=%s',
+ "Unable to match source with pattern=%s, cause=%s",
pattern,
- traceback.format_exc()
+ traceback.format_exc(),
)
return False
-def regex_not_match(pattern, source, flags=0):
- """
- Determine whether a string is not match a regex pattern.
-
- :param pattern: regex expression
- :param source: candidate to match regex
- :param flags: flags for regex match
- :return: `True` if candidate not match pattern else `False`
- """
- return not regex_match(pattern, source, flags)
-
-
def json_path(source, json_path_expr):
- """ Extract value from string with JSONPATH expression.
+ """Extract value from string with JSONPATH expression.
:param json_path_expr: JSONPATH expression
:param source: string to extract value
:return: A `list` contains all values extracted
"""
if not source:
- _logger.debug('source to apply JSONPATH is empty, return empty.')
- return ''
+ _logger.debug("source to apply JSONPATH is empty, return empty.")
+ return ""
- if isinstance(source, six.string_types):
+ if isinstance(source, str):
_logger.debug(
- 'source expected is a JSON, not %s. Attempt to'
- ' convert it to JSON',
- type(source)
+ "source expected is a JSON, not %s. Attempt to" " convert it to JSON",
+ type(source),
)
try:
source = json.loads(source)
except Exception as ex:
_logger.warning(
- 'Unable to load JSON from source: %s. '
+ "Unable to load JSON from source: %s. "
'Attempt to apply JSONPATH "%s" on source directly.',
ex,
- json_path_expr
+ json_path_expr,
)
try:
@@ -110,31 +99,29 @@ def json_path(source, json_path_expr):
_logger.debug(
'Got %s elements extracted with JSONPATH expression "%s"',
- len(results), json_path_expr
+ len(results),
+ json_path_expr,
)
if not results:
- return ''
+ return ""
- return results[0] or '' if len(results) == 1 else results
+ return results[0] or "" if len(results) == 1 else results
except Exception as ex:
_logger.warning(
'Unable to apply JSONPATH expression "%s" on source,'
- ' message=%s cause=%s',
+ " message=%s cause=%s",
json_path_expr,
ex,
- traceback.format_exc()
+ traceback.format_exc(),
)
- return ''
+ return ""
-def splunk_xml(candidates,
- time=None,
- index=None,
- host=None,
- source=None,
- sourcetype=None):
- """ Wrap a event with splunk xml format.
+def splunk_xml(
+ candidates, time=None, index=None, host=None, source=None, sourcetype=None
+):
+ """Wrap a event with splunk xml format.
:param candidates: data used to wrap as event
:param time: timestamp which must be empty or a valid float
:param index: index name for event
@@ -152,8 +139,7 @@ def splunk_xml(candidates,
time = float(time)
except ValueError:
_logger.warning(
- '"time" %s is expected to be a float, set "time" to None',
- time
+ '"time" %s is expected to be a float, set "time" to None', time
)
time = None
xml_events = util.format_events(
@@ -162,60 +148,58 @@ def splunk_xml(candidates,
index=index,
host=host,
source=source,
- sourcetype=sourcetype
+ sourcetype=sourcetype,
)
- _logger.info(
- "[%s] events are formated as splunk stream xml",
- len(candidates)
- )
+ _logger.info("[%s] events are formated as splunk stream xml", len(candidates))
return xml_events
def std_output(candidates):
- """ Output a string to stdout.
+ """Output a string to stdout.
:param candidates: List of string to output to stdout or a single string.
"""
- if isinstance(candidates, six.string_types):
+ if isinstance(candidates, str):
candidates = [candidates]
all_str = True
for candidate in candidates:
- if all_str and not isinstance(candidate, six.string_types):
+ if all_str and not isinstance(candidate, str):
all_str = False
_logger.debug(
- 'The type of data needs to print is "%s" rather than %s',
+ 'The type of data needs to print is "%s" rather than str',
type(candidate),
- str(six.string_types)
)
try:
candidate = json.dumps(candidate)
except:
- _logger.exception('The type of data needs to print is "%s"'
- ' rather than %s',
- type(candidate),
- str(six.string_types))
+ _logger.exception(
+ 'The type of data needs to print is "%s"' " rather than str",
+ type(candidate),
+ )
if not PipeManager().write_events(candidate):
- raise FuncException('Fail to output data to stdout. The event'
- ' writer is stopped or encountered exception')
+ raise FuncException(
+ "Fail to output data to stdout. The event"
+ " writer is stopped or encountered exception"
+ )
- _logger.debug('Writing events to stdout finished.')
+ _logger.debug("Writing events to stdout finished.")
return True
def _parse_json(source, json_path_expr=None):
if not source:
- _logger.debug('Unable to parse JSON from empty source, return empty.')
+ _logger.debug("Unable to parse JSON from empty source, return empty.")
return {}
if json_path_expr:
_logger.debug(
- 'Try to extract JSON from source with JSONPATH expression: %s, ',
- json_path_expr
+ "Try to extract JSON from source with JSONPATH expression: %s, ",
+ json_path_expr,
)
source = json_path(source, json_path_expr)
- elif isinstance(source, six.string_types):
+ elif isinstance(source, str):
source = json.loads(source)
return source
@@ -236,8 +220,9 @@ def json_empty(source, json_path_expr=None):
return len(data) == 0
except Exception as ex:
_logger.warning(
- 'Unable to determine whether source is json_empty, treat it as '
- 'not json_empty: %s', ex
+ "Unable to determine whether source is json_empty, treat it as "
+ "not json_empty: %s",
+ ex,
)
return False
@@ -258,9 +243,9 @@ def json_not_empty(source, json_path_expr=None):
return len(data) > 0
except Exception as ex:
_logger.warning(
- 'Unable to determine whether source is json_not_empty, '
- 'treat it as not json_not_empty: %s',
- ex
+ "Unable to determine whether source is json_not_empty, "
+ "treat it as not json_not_empty: %s",
+ ex,
)
return False
@@ -282,24 +267,25 @@ def _fix_microsecond_format(fmt, micros):
def do_replacement(x, micros):
if int(x.group(1)) in range(1, 7) and len(x.group()) % 2:
- return x.group().replace('%' + x.group(1) + 'f',
- micros[:min(int(x.group(1)), len(micros))])
+ return x.group().replace(
+ "%" + x.group(1) + "f", micros[: min(int(x.group(1)), len(micros))]
+ )
return x.group()
- return re.sub(r'%+([1-6])f', lambda x: do_replacement(x, micros), fmt)
+ return re.sub(r"%+([1-6])f", lambda x: do_replacement(x, micros), fmt)
def _fix_timestamp_format(fmt, timestamp):
"""Replace '%s' in time format with timestamp if the number
- of '%' before 's' is odd."""
+ of '%' before 's' is odd."""
return re.sub(
- r'%+s',
+ r"%+s",
(
- lambda x:
- x.group() if len(x.group()) % 2 else x.group().replace('%s',
- timestamp)
+ lambda x: x.group()
+ if len(x.group()) % 2
+ else x.group().replace("%s", timestamp)
),
- fmt
+ fmt,
)
@@ -307,11 +293,11 @@ def time_str2str(date_string, from_format, to_format):
"""Convert a date string with given format to another format. Return
the original date string if it's type is not string or failed to parse or
convert it with format."""
- if not isinstance(date_string, six.string_types):
+ if not isinstance(date_string, str):
_logger.warning(
'"date_string" must be a string type, found %s,'
- ' return the original date_string directly.',
- type(date_string)
+ " return the original date_string directly.",
+ type(date_string),
)
return date_string
@@ -329,18 +315,18 @@ def time_str2str(date_string, from_format, to_format):
except Exception:
_logger.warning(
'Unable to convert date_string "%s" from format "%s" to "%s",'
- ' return the original date_string, cause=%s',
+ " return the original date_string, cause=%s",
date_string,
from_format,
to_format,
- traceback.format_exc()
+ traceback.format_exc(),
)
return date_string
def is_true(value):
"""Determine whether value is True"""
- return str(value).strip().lower() == 'true'
+ return str(value).strip().lower() == "true"
def exit_if_true(value):
@@ -358,9 +344,7 @@ def exit_job_if_true(value):
def assert_true(value, message=None):
"""Assert value is True"""
if not is_true(value):
- raise AssertionError(
- message or '"{value}" is not true'.format(value=value)
- )
+ raise AssertionError(message or f'"{value}" is not true')
def split_by(source, target, separator=None):
@@ -368,43 +352,45 @@ def split_by(source, target, separator=None):
try:
if not source:
return []
- elif isinstance(source, six.string_types) and separator:
+ elif isinstance(source, str) and separator:
values = source.split(separator)
return [{target: value.strip()} for value in values]
- elif isinstance(source, six.string_types):
+ elif isinstance(source, str):
return [{target: source}]
elif isinstance(source, Iterable):
return [{target: value} for value in source]
else:
return [{target: source}]
except Exception as ex:
- _logger.warning("split_by method encountered exception "
- "source=%s message=%s cause=%s", source, ex,
- traceback.format_exc())
+ _logger.warning(
+ "split_by method encountered exception " "source=%s message=%s cause=%s",
+ source,
+ ex,
+ traceback.format_exc(),
+ )
return []
_extension_functions = {
- 'assert_true': assert_true,
- 'exit_if_true': exit_if_true,
- 'exit_job_if_true': exit_job_if_true,
- 'is_true': is_true,
- 'regex_match': regex_match,
- 'regex_not_match': regex_not_match,
- 'regex_search': regex_search,
- 'set_var': set_var,
- 'splunk_xml': splunk_xml,
- 'std_output': std_output,
- 'json_path': json_path,
- 'json_empty': json_empty,
- 'json_not_empty': json_not_empty,
- 'time_str2str': time_str2str,
- 'split_by': split_by
+ "assert_true": assert_true,
+ "exit_if_true": exit_if_true,
+ "exit_job_if_true": exit_job_if_true,
+ "is_true": is_true,
+ "regex_match": regex_match,
+ "regex_search": regex_search,
+ "set_var": set_var,
+ "splunk_xml": splunk_xml,
+ "std_output": std_output,
+ "json_path": json_path,
+ "json_empty": json_empty,
+ "json_not_empty": json_not_empty,
+ "time_str2str": time_str2str,
+ "split_by": split_by,
}
def lookup_method(name):
- """ Find a predefined function with given function name.
+ """Find a predefined function with given function name.
:param name: function name.
:return: A function with given name.
"""
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/http.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/http.py
index 5992d47..e9d4675 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/http.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/http.py
@@ -13,74 +13,61 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import str
-from builtins import range
-from builtins import object
import time
import traceback
-import six
+import requests
+
import munch
+from requests import PreparedRequest, Session, utils
+from solnlib.utils import is_true
from cloudconnectlib.common import util
from cloudconnectlib.common.log import get_cc_logger
from cloudconnectlib.core import defaults
from cloudconnectlib.core.exceptions import HTTPError
-from httplib2 import Http, socks, ProxyInfo
-from requests import PreparedRequest, utils
-from solnlib.utils import is_true
-
-try: # Python2 environment support
- from httplib2 import SSLHandshakeError
-except: # Python3 environment support
- from ssl import SSLError as SSLHandshakeError
_logger = get_cc_logger()
-_PROXY_TYPE_MAP = {
- 'http': socks.PROXY_TYPE_HTTP,
- 'socks4': socks.PROXY_TYPE_SOCKS4,
- 'socks5': socks.PROXY_TYPE_SOCKS5,
-}
-
-class HTTPResponse(object):
+class HTTPResponse:
"""
HTTPResponse class wraps response of HTTP request for later use.
"""
def __init__(self, response, content):
"""Construct a HTTPResponse from response and content returned
- with httplib2 request"""
- self._status_code = response.status
+ with requests.Session() request"""
+ self._status_code = response.status_code
self._header = response
- self._body = self._decode_content(response, content)
+ self._body = self._decode_content(response.headers, content)
@staticmethod
def _decode_content(response, content):
if not content:
- return ''
+ return ""
charset = utils.get_encoding_from_headers(response)
if charset is None:
charset = defaults.charset
_logger.info(
- 'Unable to find charset in response headers,'
- ' set it to default "%s"', charset
+ "Unable to find charset in response headers," ' set it to default "%s"',
+ charset,
)
- _logger.info('Decoding response content with charset=%s', charset)
+ _logger.info("Decoding response content with charset=%s", charset)
try:
- return content.decode(charset, errors='replace')
+ return content.decode(charset, errors="replace")
except Exception as ex:
_logger.warning(
- 'Failure decoding response content with charset=%s,'
- ' decode it with utf-8: %s',
- charset, ex
+ "Failure decoding response content with charset=%s,"
+ " decode it with utf-8: %s",
+ charset,
+ ex,
)
- return content.decode('utf-8', errors='replace')
+ return content.decode("utf-8", errors="replace")
@property
def header(self):
@@ -117,139 +104,160 @@ def prepare_url(url, params=None):
return prepare_url
-def get_proxy_info(proxy_config):
- if not proxy_config or not is_true(proxy_config.get('proxy_enabled')):
- _logger.info('Proxy is not enabled')
- return None
+def get_proxy_info(proxy_config: dict) -> dict:
+ """
+ @proxy_config: dict like object of the format -
+ {
+ "proxy_url": my-proxy.server.com,
+ "proxy_port": 0000,
+ "proxy_username": username,
+ "proxy_password": password,
+ "proxy_type": http or sock5,
+ "proxy_rdns": 0 or 1,
+ }
+ """
+ proxy_info = {}
+
+ if not proxy_config or not is_true(proxy_config.get("proxy_enabled")):
+ _logger.info("Proxy is not enabled")
+ return {}
+
+ proxy_type = proxy_config.get("proxy_type", "").lower()
+ if proxy_type not in ("http", "socks5"):
+ proxy_type = "http"
+ _logger.info('Proxy type not found, set to "HTTP"')
+
+ if is_true(proxy_config.get("proxy_rdns")) and proxy_type == "socks5":
+ proxy_type = "socks5h"
- url = proxy_config.get('proxy_url')
- port = proxy_config.get('proxy_port')
+ url = proxy_config.get("proxy_url")
+ port = proxy_config.get("proxy_port")
if url or port:
if not url:
raise ValueError('Proxy "url" must not be empty')
if not util.is_valid_port(port):
- raise ValueError(
- 'Proxy "port" must be in range [1,65535]: %s' % port
- )
-
- user = proxy_config.get('proxy_username')
- password = proxy_config.get('proxy_password')
+ raise ValueError('Proxy "port" must be in range [1,65535]: %s' % port)
- if not all((user, password)):
- _logger.info('Proxy has no credentials found')
- user, password = None, None
+ proxy_info["http"] = f"{proxy_type}://{url}:{int(port)}"
+ user = proxy_config.get("proxy_username")
+ password = proxy_config.get("proxy_password")
- proxy_type = proxy_config.get('proxy_type')
- proxy_type = proxy_type.lower() if proxy_type else 'http'
-
- if proxy_type in _PROXY_TYPE_MAP:
- ptv = _PROXY_TYPE_MAP[proxy_type]
- elif proxy_type in list(_PROXY_TYPE_MAP.values()):
- ptv = proxy_type
+ if all((user, password)):
+ proxy_info["http"] = f"{proxy_type}://{user}:{password}@{url}:{int(port)}"
else:
- ptv = socks.PROXY_TYPE_HTTP
- _logger.info('Proxy type not found, set to "HTTP"')
+ _logger.info("Proxy has no credentials found")
- rdns = is_true(proxy_config.get('proxy_rdns'))
-
- proxy_info = ProxyInfo(
- proxy_host=url,
- proxy_port=int(port),
- proxy_type=ptv,
- proxy_user=user,
- proxy_pass=password,
- proxy_rdns=rdns
- )
+ proxy_info["https"] = proxy_info["http"]
return proxy_info
+
def standardize_proxy_config(proxy_config):
"""
- This function is used to standardize the proxy information structure to get it evaluated through `get_proxy_info` function
+ This function is used to standardize the proxy information structure to
+ get it evaluated through `get_proxy_info` function
"""
if not isinstance(proxy_config, dict):
- raise ValueError("Received unexpected format of proxy configuration. Expected format: object, Actual format: {}".format(type(proxy_config)))
+ raise ValueError(
+ "Received unexpected format of proxy configuration. "
+ "Expected format: object, Actual format: {}".format(type(proxy_config))
+ )
standard_proxy_config = {
"proxy_enabled": proxy_config.get("enabled", proxy_config.get("proxy_enabled")),
- "proxy_username": proxy_config.get("username", proxy_config.get("proxy_username")),
- "proxy_password": proxy_config.get("password", proxy_config.get("proxy_password")),
+ "proxy_username": proxy_config.get(
+ "username", proxy_config.get("proxy_username")
+ ),
+ "proxy_password": proxy_config.get(
+ "password", proxy_config.get("proxy_password")
+ ),
"proxy_url": proxy_config.get("host", proxy_config.get("proxy_url")),
"proxy_type": proxy_config.get("type", proxy_config.get("proxy_type")),
"proxy_port": proxy_config.get("port", proxy_config.get("proxy_port")),
- "proxy_rdns": proxy_config.get("rdns", proxy_config.get("proxy_rdns"))
+ "proxy_rdns": proxy_config.get("rdns", proxy_config.get("proxy_rdns")),
}
return standard_proxy_config
-class HttpClient(object):
- def __init__(self, proxy_info=None):
- """Constructs a `HTTPRequest` with a optional proxy setting.
+class HttpClient:
+ def __init__(self, proxy_info=None, verify=True):
+ """
+ Constructs a `HTTPRequest` with a optional proxy setting.
+ :param proxy_info: a dictionary of proxy details. It could directly match the input signature
+ of `requests` library, otherwise will be standardized and converted to match the input signature.
+ :param verify: same as the `verify` parameter of requests.request() method
"""
self._connection = None
-
+ self.requests_verify = verify
+
if proxy_info:
if isinstance(proxy_info, munch.Munch):
proxy_info = dict(proxy_info)
- # Updating the proxy_info object to make it compatible for getting evaluated through `get_proxy_info` function
- proxy_info = standardize_proxy_config(proxy_info)
- self._proxy_info = get_proxy_info(proxy_info)
+ if all((len(proxy_info) == 2, "http" in proxy_info, "https" in proxy_info)):
+ # when `proxy_info` already matches the input signature of `requests` library's proxy dict
+ self._proxy_info = proxy_info
+ else:
+ # Updating the proxy_info object to make it compatible for getting evaluated
+ # through `get_proxy_info` function
+ proxy_info = standardize_proxy_config(proxy_info)
+ self._proxy_info = get_proxy_info(proxy_info)
else:
self._proxy_info = proxy_info
self._url_preparer = PreparedRequest()
- def _send_internal(self, uri, method, headers=None, body=None, proxy_info=None):
- """Do send request to target URL and validate SSL cert by default.
- If validation failed, disable it and try again."""
- try:
- return self._connection.request(
- uri, body=body, method=method, headers=headers
- )
- except SSLHandshakeError:
- _logger.warning(
- "[SSL: CERTIFICATE_VERIFY_FAILED] certificate verification failed. "
- "The certificate of the https server [%s] is not trusted, "
- "this add-on will proceed to connect with this certificate. "
- "You may need to check the certificate and "
- "refer to the documentation and add it to the trust list. %s",
- uri,
- traceback.format_exc()
- )
-
- self._connection = self._build_http_connection(
- proxy_info=proxy_info,
- disable_ssl_cert_validation=True
- )
- return self._connection.request(
- uri, body=body, method=method, headers=headers
- )
+ def _send_internal(self, uri, method, headers=None, body=None):
+ """Do send request to target URL, validate SSL cert by default and return the response."""
+ return self._connection.request(
+ url=uri,
+ data=body,
+ method=method,
+ headers=headers,
+ timeout=defaults.timeout,
+ verify=self.requests_verify,
+ )
- def _retry_send_request_if_needed(self, uri, method='GET', headers=None, body=None):
+ def _retry_send_request_if_needed(self, uri, method="GET", headers=None, body=None):
"""Invokes request and auto retry with an exponential backoff
if the response status is configured in defaults.retry_statuses."""
retries = max(defaults.retries, 0)
- _logger.info('Invoking request to [%s] using [%s] method', uri, method)
+ _logger.info("Invoking request to [%s] using [%s] method", uri, method)
for i in range(retries + 1):
try:
- response, content = self._send_internal(
+ resp = self._send_internal(
uri=uri, body=body, method=method, headers=headers
)
+ content = resp.content
+ response = resp
+ except requests.exceptions.SSLError as err:
+ _logger.error(
+ "[SSL: CERTIFICATE_VERIFY_FAILED] certificate verification failed. "
+ "The certificate of the https server [%s] is not trusted, "
+ "You may need to check the certificate and "
+ "refer to the documentation and add it to the trust list. %s",
+ uri,
+ traceback.format_exc(),
+ )
+ raise HTTPError(f"HTTP Error {err}") from err
except Exception as err:
_logger.exception(
- 'Could not send request url=%s method=%s', uri, method)
- raise HTTPError('HTTP Error %s' % str(err))
+ "Could not send request url=%s method=%s", uri, method
+ )
+ raise HTTPError(f"HTTP Error {err}") from err
- status = response.status
+ status = resp.status_code
if self._is_need_retry(status, i, retries):
- delay = 2 ** i
+ delay = 2**i
_logger.warning(
- 'The response status=%s of request which url=%s and'
- ' method=%s. Retry after %s seconds.',
- status, uri, method, delay,
+ "The response status=%s of request which url=%s and"
+ " method=%s. Retry after %s seconds.",
+ status,
+ uri,
+ method,
+ delay,
)
time.sleep(delay)
continue
@@ -262,16 +270,16 @@ def _prepare_url(self, url, params=None):
def _initialize_connection(self):
if self._proxy_info:
- _logger.info('Proxy is enabled for http connection.')
+ _logger.info("Proxy is enabled for http connection.")
else:
- _logger.info('Proxy is not enabled for http connection.')
+ _logger.info("Proxy is not enabled for http connection.")
self._connection = self._build_http_connection(self._proxy_info)
def send(self, request):
if not request:
- raise ValueError('The request is none')
- if request.body and not isinstance(request.body, six.string_types):
- raise TypeError('Invalid request body type: {}'.format(request.body))
+ raise ValueError("The request is none")
+ if request.body and not isinstance(request.body, str):
+ raise TypeError(f"Invalid request body type: {request.body}")
if self._connection is None:
self._initialize_connection()
@@ -280,8 +288,7 @@ def send(self, request):
url = self._prepare_url(request.url)
except Exception:
_logger.warning(
- 'Failed to encode url=%s: %s',
- request.url, traceback.format_exc()
+ "Failed to encode url=%s: %s", request.url, traceback.format_exc()
)
url = request.url
@@ -291,15 +298,18 @@ def send(self, request):
@staticmethod
def _build_http_connection(
- proxy_info=None,
- timeout=defaults.timeout,
- disable_ssl_cert_validation=defaults.disable_ssl_cert_validation):
- return Http(
- proxy_info=proxy_info,
- timeout=timeout,
- disable_ssl_certificate_validation=disable_ssl_cert_validation)
+ proxy_info=None,
+ disable_ssl_cert_validation=defaults.disable_ssl_cert_validation,
+ ):
+ """
+ Creates a `request.Session()` object, sets the verify
+ and proxy_info parameter and returns this object
+ """
+ s = Session()
+ s.verify = not disable_ssl_cert_validation
+ s.proxies = proxy_info or {}
+ return s
@staticmethod
def _is_need_retry(status, retried, maximum_retries):
- return retried < maximum_retries \
- and status in defaults.retry_statuses
+ return retried < maximum_retries and status in defaults.retry_statuses
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/job.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/job.py
index 497c9f6..090eacd 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/job.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/job.py
@@ -13,18 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import object
import threading
+from ..common import log
from .exceptions import QuitJobError
from .task import BaseTask
-from ..common import log
-
logger = log.get_cc_logger()
-class CCEJob(object):
+class CCEJob:
"""
One CCEJob is composed of a list of tasks. The task could be HTTP
task or Split task(currently supported task types).
@@ -62,15 +60,17 @@ def set_proxy(self, proxy_setting):
:type proxy_setting: ``dict``
"""
self._proxy_info = proxy_setting
- logger.debug("CCEJob proxy info: proxy_enabled='%s', proxy_url='%s', "
- "proxy_port='%s', proxy_rdns='%s', proxy_type='%s', "
- "proxy_username='%s'",
- proxy_setting.get("proxy_enabled"),
- proxy_setting.get("proxy_url"),
- proxy_setting.get("proxy_port"),
- proxy_setting.get("proxy_rdns"),
- proxy_setting.get("proxy_type"),
- proxy_setting.get("proxy_username"))
+ logger.debug(
+ "CCEJob proxy info: proxy_enabled='%s', proxy_url='%s', "
+ "proxy_port='%s', proxy_rdns='%s', proxy_type='%s', "
+ "proxy_username='%s'",
+ proxy_setting.get("proxy_enabled"),
+ proxy_setting.get("proxy_url"),
+ proxy_setting.get("proxy_port"),
+ proxy_setting.get("proxy_rdns"),
+ proxy_setting.get("proxy_type"),
+ proxy_setting.get("proxy_username"),
+ )
def add_task(self, task):
"""
@@ -80,14 +80,14 @@ def add_task(self, task):
:type task: TBD
"""
if not isinstance(task, BaseTask):
- raise ValueError('Unsupported task type: {}'.format(type(task)))
+ raise ValueError(f"Unsupported task type: {type(task)}")
if callable(getattr(task, "set_proxy", None)) and self._proxy_info:
task.set_proxy(self._proxy_info)
self._rest_tasks.append(task)
def _check_if_stop_needed(self):
if self._stop_signal_received:
- logger.info('Stop job signal received, stopping job.')
+ logger.info("Stop job signal received, stopping job.")
self._stopped.set()
return True
return False
@@ -96,10 +96,10 @@ def run(self):
"""
Run current job, which executes tasks in it sequentially.
"""
- logger.debug('Start to run job')
+ logger.debug("Start to run job")
if not self._rest_tasks:
- logger.info('No task found in job')
+ logger.info("No task found in job")
return
if self._check_if_stop_needed():
@@ -111,29 +111,35 @@ def run(self):
try:
contexts = list(self._running_task.perform(self._context) or ())
except QuitJobError:
- logger.info('Quit job signal received, exiting job')
+ logger.info("Quit job signal received, exiting job")
return
if self._check_if_stop_needed():
return
if not self._rest_tasks:
- logger.info('No more task need to perform, exiting job')
+ logger.info("No more task need to perform, exiting job")
return
- jobs = [CCEJob(context=ctx, tasks=self._rest_tasks) for ctx in contexts]
+ count = 0
+
+ for ctx in contexts:
+ count += 1
+ yield CCEJob(context=ctx, tasks=self._rest_tasks)
+
+ if self._check_if_stop_needed():
+ break
- logger.debug('Generated %s job in total', len(jobs))
- logger.debug('Job execution finished successfully.')
+ logger.debug("Generated %s job in total", count)
+ logger.debug("Job execution finished successfully.")
self._stopped.set()
- return jobs
def stop(self, block=False, timeout=30):
"""
Stop current job.
"""
if self._stopped.is_set():
- logger.info('Job is not running, cannot stop it.')
+ logger.info("Job is not running, cannot stop it.")
return
self._stop_signal_received = True
@@ -143,12 +149,12 @@ def stop(self, block=False, timeout=30):
return
if not self._stopped.wait(timeout):
- logger.info('Waiting for stop job timeout')
+ logger.info("Waiting for stop job timeout")
def __str__(self):
if self._running_task:
- return 'Job(running task={})'.format(self._running_task)
- return 'Job(no running task)'
+ return f"Job(running task={self._running_task})"
+ return "Job(no running task)"
def __repr__(self):
return self.__str__()
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/models.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/models.py
index 90854c8..603e1d0 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/models.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/models.py
@@ -13,22 +13,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import str
-from builtins import object
import base64
import json
import sys
import traceback
-import six
+from ..common.log import get_cc_logger
from .ext import lookup_method
from .template import compile_template
-from ..common.log import get_cc_logger
_logger = get_cc_logger()
-class _Token(object):
+class _Token:
"""Token class wraps a template expression"""
def __init__(self, source):
@@ -36,8 +33,7 @@ def __init__(self, source):
will be created if source is string type because Jinja
template must be a string."""
self._source = source
- self._value_for = compile_template(source) \
- if isinstance(source, six.string_types) else None
+ self._value_for = compile_template(source) if isinstance(source, str) else None
def render(self, variables):
"""Render value with variables if source is a string.
@@ -49,31 +45,30 @@ def render(self, variables):
except Exception as ex:
_logger.warning(
'Unable to render template "%s". Please make sure template is'
- ' a valid Jinja2 template and token is exist in variables. '
- 'message=%s cause=%s',
+ " a valid Jinja2 template and token is exist in variables. "
+ "message=%s cause=%s",
self._source,
ex,
- traceback.format_exc()
+ traceback.format_exc(),
)
return self._source
-class DictToken(object):
+class DictToken:
"""DictToken wraps a dict which value is template expression"""
def __init__(self, template_expr):
- self._tokens = {k: _Token(v)
- for k, v in (template_expr or {}).items()}
+ self._tokens = {k: _Token(v) for k, v in (template_expr or {}).items()}
def render(self, variables):
return {k: v.render(variables) for k, v in self._tokens.items()}
-class BaseAuth(object):
+class BaseAuth:
"""A base class for all authorization classes"""
def __call__(self, headers, context):
- raise NotImplementedError('Auth must be callable.')
+ raise NotImplementedError("Auth must be callable.")
class BasicAuthorization(BaseAuth):
@@ -81,19 +76,19 @@ class BasicAuthorization(BaseAuth):
def __init__(self, options):
if not options:
- raise ValueError('Options for basic auth unexpected to be empty')
+ raise ValueError("Options for basic auth unexpected to be empty")
- username = options.get('username')
+ username = options.get("username")
if not username:
- raise ValueError('Username is mandatory for basic auth')
- password = options.get('password')
+ raise ValueError("Username is mandatory for basic auth")
+ password = options.get("password")
if not password:
- raise ValueError('Password is mandatory for basic auth')
+ raise ValueError("Password is mandatory for basic auth")
self._username = _Token(username)
self._password = _Token(password)
- def to_native_string(self, string, encoding='ascii'):
+ def to_native_string(self, string, encoding="ascii"):
"""
According to rfc7230:
Historically, HTTP has allowed field content with text in the
@@ -104,8 +99,8 @@ def to_native_string(self, string, encoding='ascii'):
US-ASCII octets. A recipient SHOULD treat other octets in field
content (obs-text) as opaque data.
"""
- is_py2 = (sys.version_info[0] == 2)
- if isinstance(string, six.text_type):
+ is_py2 = sys.version_info[0] == 2
+ if isinstance(string, str):
out = string
else:
if is_py2:
@@ -118,12 +113,15 @@ def to_native_string(self, string, encoding='ascii'):
def __call__(self, headers, context):
username = self._username.render(context)
password = self._password.render(context)
- headers['Authorization'] = 'Basic %s' % self.to_native_string(
- base64.b64encode((username + ':' + password).encode('latin1'))
- ).strip()
+ headers["Authorization"] = (
+ "Basic %s"
+ % self.to_native_string(
+ base64.b64encode((username + ":" + password).encode("latin1"))
+ ).strip()
+ )
-class RequestParams(object):
+class RequestParams:
def __init__(self, url, method, header=None, auth=None, body=None):
self._header = DictToken(header)
self._url = _Token(url)
@@ -156,7 +154,7 @@ def render(self, ctx):
url=self._url.render(ctx),
method=self._method,
headers=self.normalize_headers(ctx),
- body=self.body.render(ctx)
+ body=self.body.render(ctx),
)
def normalize_url(self, context):
@@ -174,19 +172,19 @@ def normalize_body(self, context):
return self.body.render(context)
-class Request(object):
+class Request:
def __init__(self, method, url, headers, body):
self.method = method
self.url = url
self.headers = headers
if not body:
body = None
- elif not isinstance(body, six.string_types):
+ elif not isinstance(body, str):
body = json.dumps(body)
self.body = body
-class _Function(object):
+class _Function:
def __init__(self, inputs, function):
self._inputs = tuple(_Token(expr) for expr in inputs or [])
self._function = function
@@ -211,7 +209,7 @@ class Task(_Function):
"""Task class wraps a task in processor pipeline"""
def __init__(self, inputs, function, output=None):
- super(Task, self).__init__(inputs, function)
+ super().__init__(inputs, function)
self._output = output
@property
@@ -219,14 +217,16 @@ def output(self):
return self._output
def execute(self, context):
- """Execute task with arguments which rendered from context """
+ """Execute task with arguments which rendered from context"""
args = [arg for arg in self.inputs_values(context)]
caller = lookup_method(self.function)
output = self._output
_logger.info(
- 'Executing task method: [%s], input size: [%s], output: [%s]',
- self.function, len(args), output
+ "Executing task method: [%s], input size: [%s], output: [%s]",
+ self.function,
+ len(args),
+ output,
)
if output is None:
@@ -249,8 +249,9 @@ def calculate(self, context):
callable_method = lookup_method(self.function)
_logger.debug(
- 'Calculating condition with method: [%s], input size: [%s]',
- self.function, len(args)
+ "Calculating condition with method: [%s], input size: [%s]",
+ self.function,
+ len(args),
)
result = callable_method(*args)
@@ -260,7 +261,7 @@ def calculate(self, context):
return result
-class _Conditional(object):
+class _Conditional:
"""A base class for all conditional action"""
def __init__(self, conditions):
@@ -275,16 +276,14 @@ def passed(self, context):
:param context: variables to render template
:return: `True` if all passed else `False`
"""
- return any(
- condition.calculate(context) for condition in self._conditions
- )
+ return any(condition.calculate(context) for condition in self._conditions)
class Processor(_Conditional):
"""Processor class contains a conditional data process pipeline"""
def __init__(self, skip_conditions, pipeline):
- super(Processor, self).__init__(skip_conditions)
+ super().__init__(skip_conditions)
self._pipeline = pipeline or []
@property
@@ -298,7 +297,7 @@ def should_skipped(self, context):
class IterationMode(_Conditional):
def __init__(self, iteration_count, conditions):
- super(IterationMode, self).__init__(conditions)
+ super().__init__(conditions)
self._iteration_count = iteration_count
@property
@@ -310,14 +309,14 @@ def conditions(self):
return self._conditions
-class Checkpoint(object):
+class Checkpoint:
"""A checkpoint includes a namespace to determine the checkpoint location
and a content defined the format of content stored in checkpoint."""
def __init__(self, namespace, content):
- """Constructs checkpoint with given namespace and content template. """
+ """Constructs checkpoint with given namespace and content template."""
if not content:
- raise ValueError('Checkpoint content must not be empty')
+ raise ValueError("Checkpoint content must not be empty")
self._namespace = tuple(_Token(expr) for expr in namespace or ())
self._content = DictToken(content)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/pipemgr.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/pipemgr.py
index d56615a..e62ac54 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/pipemgr.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/pipemgr.py
@@ -13,18 +13,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from __future__ import print_function
-from builtins import object
from solnlib.pattern import Singleton
-from future.utils import with_metaclass
-class PipeManager(with_metaclass(Singleton, object)):
+class PipeManager(metaclass=Singleton):
def __init__(self, event_writer=None):
self._event_writer = event_writer
def write_events(self, events):
if not self._event_writer:
- print(events)
+ print(events, flush=True)
return True
return self._event_writer.write_events(events)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/plugin.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/plugin.py
index d92ca2e..c40451f 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/plugin.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/plugin.py
@@ -13,22 +13,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from __future__ import absolute_import
-from builtins import next
-from .ext import _extension_functions
+import importlib
+import sys
+import traceback
from os import path as op
from os import walk
-import sys
+
from ..common import log
-import traceback
-import importlib
+from .ext import _extension_functions
logger = log.get_cc_logger()
def cce_pipeline_plugin(func):
"""
- Decorator for pipepline plugin functions.
+ Decorator for pipeline plugin functions.
This docorator helps to register user defined pipeline function into CCE
engine so that it could be looked up when executing jobs.
@@ -42,19 +41,23 @@ def cce_pipeline_plugin(func):
>>> do_work()
"""
if not callable(func):
- logger.debug("Function %s is not callable, don't add it as a pipeline"
- " function", func.__name__)
+ logger.debug(
+ "Function %s is not callable, don't add it as a pipeline function",
+ func.__name__,
+ )
else:
if func.__name__ in list(_extension_functions.keys()):
- logger.warning("Pipeline function %s already exists, please rename"
- "it!", func.__name__)
+ logger.warning(
+ "Pipeline function %s already exists, please rename it!",
+ func.__name__,
+ )
else:
_extension_functions[func.__name__] = func
- logger.debug("Added function %s to pipeline plugin system",
- func.__name__)
+ logger.debug("Added function %s to pipeline plugin system", func.__name__)
def pipeline_func(*args, **kwargs):
return func(*args, **kwargs)
+
return pipeline_func
@@ -68,21 +71,24 @@ def import_plugin_file(file_name):
if file_name.endswith(".py"):
module_name = file_name[:-3]
else:
- logger.warning("Plugin file %s is with unsupported extenstion, the "
- "supported are py", file_name)
+ logger.warning(
+ "Plugin file %s is with unsupported extension, the supported are py",
+ file_name,
+ )
return
if module_name in list(sys.modules.keys()):
- logger.warning("Module %s aleady exists and it won't be reload, "
- "please rename your plugin module if it is required.",
- module_name)
+ logger.debug(
+ "Module %s already exists and it won't be reload, "
+ "please rename your plugin module if it is required.",
+ module_name,
+ )
return
try:
importlib.import_module(module_name)
except Exception:
- logger.warning("Failed to load module {}, {}".format(
- module_name, traceback.format_exc()))
+ logger.warning(f"Failed to load module {module_name}, {traceback.format_exc()}")
return
logger.info("Module %s is imported", module_name)
@@ -98,8 +104,9 @@ def init_pipeline_plugins(plugin_dir):
with ".py"
"""
if not op.isdir(plugin_dir):
- logger.warning("%s is not a directory! Pipeline plugin files won't be loaded.",
- plugin_dir)
+ logger.warning(
+ "%s is not a directory! Pipeline plugin files won't be loaded.", plugin_dir
+ )
return
if plugin_dir not in sys.path:
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/task.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/task.py
index e134261..e8246eb 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/task.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/task.py
@@ -13,31 +13,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import object
import copy
import threading
from abc import abstractmethod
-import six
-import re
from cloudconnectlib.common.log import get_cc_logger
from cloudconnectlib.core import defaults
from cloudconnectlib.core.checkpoint import CheckpointManagerAdapter
-from cloudconnectlib.core.exceptions import HTTPError
-from cloudconnectlib.core.exceptions import StopCCEIteration, CCESplitError, QuitJobError
-from cloudconnectlib.core.ext import lookup_method, regex_search
-from cloudconnectlib.core.http import get_proxy_info, HttpClient
-from cloudconnectlib.core.models import DictToken, _Token, BasicAuthorization, Request
+from cloudconnectlib.core.exceptions import (
+ CCESplitError,
+ HTTPError,
+ QuitJobError,
+ StopCCEIteration,
+)
+from cloudconnectlib.core.ext import lookup_method
+from cloudconnectlib.core.http import HttpClient, get_proxy_info
+from cloudconnectlib.core.models import BasicAuthorization, DictToken, Request, _Token
logger = get_cc_logger()
-_RESPONSE_KEY = '__response__'
-_AUTH_TYPES = {
- 'basic_auth': BasicAuthorization
-}
+_RESPONSE_KEY = "__response__"
+_AUTH_TYPES = {"basic_auth": BasicAuthorization}
-class ProcessHandler(object):
+class ProcessHandler:
def __init__(self, method, arguments, output):
self.method = method
self.arguments = [_Token(arg) for arg in arguments or ()]
@@ -45,7 +44,7 @@ def __init__(self, method, arguments, output):
def execute(self, context):
args = [arg.render(context) for arg in self.arguments]
- logger.debug('%s arguments found for method %s', len(args), self.method)
+ logger.debug("%s arguments found for method %s", len(args), self.method)
callable_method = lookup_method(self.method)
result = callable_method(*args)
@@ -56,7 +55,7 @@ def execute(self, context):
return data
-class Condition(object):
+class Condition:
def __init__(self, method, arguments):
self.method = method
self.arguments = [_Token(arg) for arg in arguments or ()]
@@ -64,11 +63,11 @@ def __init__(self, method, arguments):
def is_meet(self, context):
args = [arg.render(context) for arg in self.arguments]
callable_method = lookup_method(self.method)
- logger.debug('%s arguments found for method %s', len(args), self.method)
+ logger.debug("%s arguments found for method %s", len(args), self.method)
return callable_method(*args)
-class ConditionGroup(object):
+class ConditionGroup:
def __init__(self):
self._conditions = []
@@ -76,12 +75,10 @@ def add(self, condition):
self._conditions.append(condition)
def is_meet(self, context):
- return any(
- cdn.is_meet(context) for cdn in self._conditions
- )
+ return any(cdn.is_meet(context) for cdn in self._conditions)
-class ProxyTemplate(object):
+class ProxyTemplate:
def __init__(self, proxy_setting):
self._proxy = DictToken(proxy_setting or {})
@@ -90,31 +87,31 @@ def render(self, context):
return get_proxy_info(rendered)
-class RequestTemplate(object):
+class RequestTemplate:
def __init__(self, request):
if not request:
- raise ValueError('The request is none')
- url = request.get('url')
+ raise ValueError("The request is none")
+ url = request.get("url")
if not url:
raise ValueError("The request doesn't contain a url or it's empty")
self.url = _Token(url)
- self.nextpage_url = _Token(request.get('nextpage_url', url))
- self.headers = DictToken(request.get('headers', {}))
+ self.nextpage_url = _Token(request.get("nextpage_url", url))
+ self.headers = DictToken(request.get("headers", {}))
# Request body could be string or dict
- body = request.get('body')
+ body = request.get("body")
if isinstance(body, dict):
self.body = DictToken(body)
- elif isinstance(body, six.string_types):
+ elif isinstance(body, str):
self.body = _Token(body)
else:
if body:
- logger.warning('Invalid request body: %s', body)
+ logger.warning("Invalid request body: %s", body)
self.body = None
- method = request.get('method', 'GET')
- if not method or method.upper() not in ('GET', 'POST'):
- raise ValueError('Unsupported value for request method: {}'.format(method))
+ method = request.get("method", "GET")
+ if not method or method.upper() not in ("GET", "POST"):
+ raise ValueError(f"Unsupported value for request method: {method}")
self.method = _Token(method)
self.count = 0
@@ -133,11 +130,11 @@ def render(self, context):
url=url,
method=self.method.render(context),
headers=self.headers.render(context),
- body=self.body.render(context) if self.body else None
+ body=self.body.render(context) if self.body else None,
)
-class BaseTask(object):
+class BaseTask:
def __init__(self, name):
self._name = name
self._pre_process_handler = []
@@ -158,6 +155,7 @@ def add_preprocess_handler(self, method, input, output=None):
"""
handler = ProcessHandler(method, input, output)
self._pre_process_handler.append(handler)
+
def add_preprocess_handler_batch(self, handlers):
"""
Add multiple preprocess handlers. All handlers will be maintained and
@@ -205,6 +203,7 @@ def add_postprocess_handler_batch(self, handlers):
"""
for method, args, output in handlers:
self.add_postprocess_handler(method, args, output)
+
def add_postprocess_skip_condition(self, method, input):
"""
Add a preprocess skip condition. The skip_conditions for postprocess
@@ -222,10 +221,10 @@ def add_postprocess_skip_condition(self, method, input):
@staticmethod
def _execute_handlers(skip_conditions, handlers, context, phase):
if skip_conditions.is_meet(context):
- logger.debug('%s process skip conditions are met', phase.capitalize())
+ logger.debug("%s process skip conditions are met", phase.capitalize())
return
if not handlers:
- logger.debug('No handler found in %s process', phase)
+ logger.debug("No handler found in %s process", phase)
return
for handler in handlers:
@@ -233,19 +232,23 @@ def _execute_handlers(skip_conditions, handlers, context, phase):
if data:
# FIXME
context.update(data)
- logger.debug('Execute handlers finished successfully.')
+ if context.get("is_token_refreshed"):
+ # In case of OAuth flow after refreshing access token retrying again with the query to collect records
+ logger.info(
+ "The access token is refreshed hence skipping the rest post process handler tasks. Retrying again."
+ )
+ return
+ logger.debug("Execute handlers finished successfully.")
def _pre_process(self, context):
- self._execute_handlers(self._skip_pre_conditions,
- self._pre_process_handler,
- context,
- 'pre')
+ self._execute_handlers(
+ self._skip_pre_conditions, self._pre_process_handler, context, "pre"
+ )
def _post_process(self, context):
- self._execute_handlers(self._skip_post_conditions,
- self._post_process_handler,
- context,
- 'post')
+ self._execute_handlers(
+ self._skip_post_conditions, self._post_process_handler, context, "post"
+ )
@abstractmethod
def perform(self, context):
@@ -265,43 +268,43 @@ class CCESplitTask(BaseTask):
OUTPUT_KEY = "__cce_split_result__"
def __init__(self, name):
- super(CCESplitTask, self).__init__(name)
+ super().__init__(name)
self._process_handler = None
self._source = None
def configure_split(self, method, source, output, separator=None):
arguments = [source, output, separator]
self._source = source
- self._process_handler = ProcessHandler(method, arguments,
- CCESplitTask.OUTPUT_KEY)
+ self._process_handler = ProcessHandler(
+ method, arguments, CCESplitTask.OUTPUT_KEY
+ )
def perform(self, context):
- logger.debug('Task=%s start to run', self)
+ logger.debug("Task=%s start to run", self)
try:
self._pre_process(context)
except StopCCEIteration:
- logger.info('Task=%s exits in pre_process stage', self)
+ logger.info("Task=%s exits in pre_process stage", self)
yield context
return
if not self._process_handler:
- logger.info('Task=%s has no split method', self)
+ logger.info("Task=%s has no split method", self)
raise CCESplitError
try:
invoke_results = self._process_handler.execute(context)
- except:
+ except Exception:
logger.exception("Task=%s encountered exception", self)
raise CCESplitError
- if not invoke_results or not \
- invoke_results.get(CCESplitTask.OUTPUT_KEY):
+ if not invoke_results or not invoke_results.get(CCESplitTask.OUTPUT_KEY):
raise CCESplitError
for invoke_result in invoke_results[CCESplitTask.OUTPUT_KEY]:
new_context = copy.deepcopy(context)
new_context.update(invoke_result)
yield new_context
- logger.debug('Task=%s finished', self)
+ logger.debug("Task=%s finished", self)
class CCEHTTPRequestTask(BaseTask):
@@ -313,8 +316,20 @@ class CCEHTTPRequestTask(BaseTask):
from context when executing.
"""
- def __init__(self, request, name, meta_config=None, task_config=None):
- super(CCEHTTPRequestTask, self).__init__(name)
+ def __init__(self, request, name, meta_config=None, task_config=None, **kwargs):
+ """
+ :param verify: Absolute path to server certificate, otherwise uses
+ requests' default certificate to verify server's TLS certificate.
+ Explicitly set it to False to not verify TLS certificate.
+ :type verify: ``string or bool``
+ :param custom_func: Custom error code handling for HTTP codes:
+ the function should accept `request`, `response` and `logger` parameters
+ To let the library handle the status code, return a non-list object
+ To handle status code using custom logic, return (response, bool).
+ Bool decides whether to break or continue the code flow
+ :type custom_func: ``function``
+ """
+ super().__init__(name)
self._request = RequestTemplate(request)
self._stop_conditions = ConditionGroup()
self._proxy_info = None
@@ -324,16 +339,20 @@ def __init__(self, request, name, meta_config=None, task_config=None):
self._task_config = task_config
self._meta_config = meta_config
+ self._http_client = None
self._authorizer = None
self._stopped = threading.Event()
self._stop_signal_received = False
+ if kwargs.get("custom_func"):
+ self.custom_handle_status_code = kwargs["custom_func"]
+ self.requests_verify = kwargs.get("verify", True)
def stop(self, block=False, timeout=30):
"""
Stop current task.
"""
if self._stopped.is_set():
- logger.info('Task=%s is not running, cannot stop it.', self)
+ logger.info("Task=%s is not running, cannot stop it.", self)
return
self._stop_signal_received = True
@@ -341,11 +360,11 @@ def stop(self, block=False, timeout=30):
return
if not self._stopped.wait(timeout):
- logger.info('Waiting for stop task %s timeout', self)
+ logger.info("Waiting for stop task %s timeout", self)
def _check_if_stop_needed(self):
if self._stop_signal_received:
- logger.info('Stop task signal received, stopping task %s.', self)
+ logger.info("Stop task signal received, stopping task %s.", self)
self._stopped.set()
return True
return False
@@ -378,10 +397,10 @@ def set_auth(self, auth_type, settings):
:type settings: ``dict``
"""
if not auth_type:
- raise ValueError('Invalid auth type={}'.format(auth_type))
+ raise ValueError(f"Invalid auth type={auth_type}")
authorizer_cls = _AUTH_TYPES.get(auth_type.lower())
if not authorizer_cls:
- raise ValueError('Unsupported auth type={}'.format(auth_type))
+ raise ValueError(f"Unsupported auth type={auth_type}")
self._authorizer = authorizer_cls(settings)
def set_iteration_count(self, count):
@@ -398,8 +417,10 @@ def set_iteration_count(self, count):
except ValueError:
self._max_iteration_count = defaults.max_iteration_count
logger.warning(
- 'Invalid iteration count: %s, using default max iteration count: %s',
- count, self._max_iteration_count)
+ "Invalid iteration count: %s, using default max iteration count: %s",
+ count,
+ self._max_iteration_count,
+ )
def add_stop_condition(self, method, input):
"""
@@ -423,53 +444,61 @@ def configure_checkpoint(self, name, content):
:type content: ``dict``
"""
if not name or not name.strip():
- raise ValueError('Invalid checkpoint name: "{}"'.format(name))
+ raise ValueError(f'Invalid checkpoint name: "{name}"')
if not content:
- raise ValueError('Invalid checkpoint content: {}'.format(content))
+ raise ValueError(f"Invalid checkpoint content: {content}")
self._checkpointer = CheckpointManagerAdapter(
namespaces=name,
content=content,
meta_config=self._meta_config,
- task_config=self._task_config
+ task_config=self._task_config,
)
def _should_exit(self, done_count, context):
if 0 < self._max_iteration_count <= done_count:
- logger.info('Iteration count reached %s', self._max_iteration_count)
+ logger.info("Iteration count reached %s", self._max_iteration_count)
return True
if self._stop_conditions.is_meet(context):
- logger.info('Stop conditions are met')
+ logger.info("Stop conditions are met")
return True
return False
- @staticmethod
- def _send_request(client, request):
+ def _send_request(self, request):
try:
- response = client.send(request)
+ response = self._http_client.send(request)
except HTTPError as error:
logger.exception(
- 'Error occurred in request url=%s method=%s reason=%s',
- request.url, request.method, error.reason
+ "Error occurred in request url=%s method=%s reason=%s",
+ request.url,
+ request.method,
+ error.reason,
)
return None, True
status = response.status_code
if status in defaults.success_statuses:
- if not (response.body or '').strip():
+ if not (response.body or "").strip():
logger.info(
- 'The response body of request which url=%s and'
- ' method=%s is empty, status=%s.',
- request.url, request.method, status
+ "The response body of request which url=%s and"
+ " method=%s is empty, status=%s.",
+ request.url,
+ request.method,
+ status,
)
return None, True
return response, False
- error_log = ('The response status=%s for request which url=%s and'
- ' method=%s.') % (
- status, request.url, request.method
- )
+ if "custom_handle_status_code" in dir(self):
+ returned_items = self.custom_handle_status_code(request, response, logger)
+ if isinstance(returned_items, (list, tuple)):
+ return returned_items[0], returned_items[1]
+
+ error_log = (
+ "The response status=%s for request which url=%s and"
+ " method=%s and message=%s "
+ ) % (status, request.url, request.method, response.body)
if status in defaults.warning_statuses:
logger.warning(error_log)
@@ -480,33 +509,38 @@ def _send_request(client, request):
def _persist_checkpoint(self, context):
if not self._checkpointer:
- logger.debug('Checkpoint is not configured. Skip persisting checkpoint.')
+ logger.debug("Checkpoint is not configured. Skip persisting checkpoint.")
return
try:
self._checkpointer.save(context)
except Exception:
- logger.exception('Error while persisting checkpoint')
+ logger.exception("Error while persisting checkpoint")
else:
- logger.debug('Checkpoint has been updated successfully.')
+ logger.debug("Checkpoint has been updated successfully.")
def _load_checkpoint(self, ctx):
if not self._checkpointer:
- logger.debug('Checkpoint is not configured. Skip loading checkpoint.')
+ logger.debug("Checkpoint is not configured. Skip loading checkpoint.")
return {}
return self._checkpointer.load(ctx=ctx)
def _prepare_http_client(self, ctx):
proxy = self._proxy_info.render(ctx) if self._proxy_info else None
- return HttpClient(proxy)
+ self._http_client = HttpClient(proxy, self.requests_verify)
+
+ def _flush_checkpoint(self):
+ if self._checkpointer:
+ # Flush checkpoint cache to disk
+ self._checkpointer.close()
def perform(self, context):
- logger.info('Starting to perform task=%s', self)
+ logger.info("Starting to perform task=%s", self)
- client = self._prepare_http_client(context)
+ self._prepare_http_client(context)
done_count = 0
context.update(self._load_checkpoint(context))
- update_source = False if context.get('source') else True
+ update_source = False if context.get("source") else True
self._request.reset()
while True:
@@ -526,17 +560,19 @@ def perform(self, context):
if self._authorizer:
self._authorizer(r.headers, context)
- response, need_exit = self._send_request(client, r)
+ response, need_exit = self._send_request(r)
context[_RESPONSE_KEY] = response
if need_exit:
- logger.info('Task=%s need been terminated due to request response', self)
+ logger.info(
+ "Task=%s need been terminated due to request response", self
+ )
break
if self._check_if_stop_needed():
break
if update_source:
- context['source'] = r.url.split('?')[0]
+ context["source"] = r.url.split("?")[0]
try:
self._post_process(context)
@@ -555,12 +591,10 @@ def perform(self, context):
done_count += 1
if self._should_exit(done_count, context):
break
- if update_source and context.get('source'):
- del context['source']
+ if update_source and context.get("source"):
+ del context["source"]
yield context
self._stopped.set()
- if self._checkpointer:
- # Flush checkpoint cache to disk
- self._checkpointer.close()
- logger.info('Perform task=%s finished', self)
+ self._flush_checkpoint()
+ logger.info("Perform task=%s finished", self)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/template.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/template.py
index f4e16fc..fe8c1d4 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/template.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/core/template.py
@@ -13,9 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from jinja2 import Template
import re
+from jinja2 import Template
+
# This pattern matches the template with only one token inside like "{{
# token1}}", "{{ token2 }"
PATTERN = re.compile(r"^\{\{\s*(\w+)\s*\}\}$")
@@ -29,7 +30,7 @@ def translate_internal(context):
match = re.match(PATTERN, _origin_template)
if match:
context_var = context.get(match.groups()[0])
- return context_var if context_var else ''
+ return context_var if context_var else ""
return _template.render(context)
return translate_internal
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/cloud_connect_mod_input.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/cloud_connect_mod_input.py
index 77f3ab6..3af567a 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/cloud_connect_mod_input.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/cloud_connect_mod_input.py
@@ -13,21 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from future import standard_library
-standard_library.install_aliases()
import configparser
import os.path as op
+from ..common.lib_util import get_app_root_dir, get_main_file, get_mod_input_script_name
from .data_collection import ta_mod_input as ta_input
from .ta_cloud_connect_client import TACloudConnectClient as CollectorCls
-from ..common.lib_util import (
- get_main_file, get_app_root_dir, get_mod_input_script_name
-)
def _load_options_from_inputs_spec(app_root, stanza_name):
- input_spec_file = 'inputs.conf.spec'
- file_path = op.join(app_root, 'README', input_spec_file)
+ input_spec_file = "inputs.conf.spec"
+ file_path = op.join(app_root, "README", input_spec_file)
if not op.isfile(file_path):
raise RuntimeError("README/%s doesn't exist" % input_spec_file)
@@ -35,7 +31,7 @@ def _load_options_from_inputs_spec(app_root, stanza_name):
parser = configparser.RawConfigParser(allow_no_value=True)
parser.read(file_path)
options = list(parser.defaults().keys())
- stanza_prefix = '%s://' % stanza_name
+ stanza_prefix = "%s://" % stanza_name
stanza_exist = False
for section in parser.sections():
@@ -49,21 +45,24 @@ def _load_options_from_inputs_spec(app_root, stanza_name):
def _find_ucc_global_config_json(app_root, ucc_config_filename):
"""Find UCC config file from all possible directories"""
- candidates = ['local', 'default', 'bin',
- op.join('appserver', 'static', 'js', 'build')]
+ candidates = [
+ "local",
+ "default",
+ "bin",
+ op.join("appserver", "static", "js", "build"),
+ ]
for candidate in candidates:
file_path = op.join(app_root, candidate, ucc_config_filename)
if op.isfile(file_path):
return file_path
raise RuntimeError(
- 'Unable to load %s from [%s]'
- % (ucc_config_filename, ','.join(candidates))
+ "Unable to load {} from [{}]".format(ucc_config_filename, ",".join(candidates))
)
def _get_cloud_connect_config_json(script_name):
- config_file_name = '.'.join([script_name, 'cc.json'])
+ config_file_name = ".".join([script_name, "cc.json"])
return op.join(op.dirname(get_main_file()), config_file_name)
@@ -73,9 +72,7 @@ def run(single_instance=False):
cce_config_file = _get_cloud_connect_config_json(script_name)
app_root = get_app_root_dir()
- ucc_config_path = _find_ucc_global_config_json(
- app_root, 'globalConfig.json'
- )
+ ucc_config_path = _find_ucc_global_config_json(app_root, "globalConfig.json")
schema_params = _load_options_from_inputs_spec(app_root, script_name)
ta_input.main(
@@ -84,5 +81,5 @@ def run(single_instance=False):
log_suffix=script_name,
cc_json_file=cce_config_file,
schema_para_list=schema_params,
- single_instance=single_instance
+ single_instance=single_instance,
)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/__init__.py
index 966cdf2..4c58c45 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/__init__.py
@@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-import json
import hashlib
+import json
def load_schema_file(schema_file):
@@ -64,4 +64,3 @@ class UCCException(Exception):
"""
pass
-
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/log.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/log.py
index 446e1ac..826e5f2 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/log.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/log.py
@@ -14,8 +14,8 @@
# limitations under the License.
#
import logging
-from splunktalib.common import log as stclog
-import six
+
+from solnlib import log
def set_log_level(log_level):
@@ -23,33 +23,39 @@ def set_log_level(log_level):
Set log level.
"""
- if isinstance(log_level, six.string_types):
+ if isinstance(log_level, str):
if log_level.upper() == "DEBUG":
- stclog.Logs().set_level(logging.DEBUG)
+ log.Logs().set_level(logging.DEBUG)
elif log_level.upper() == "INFO":
- stclog.Logs().set_level(logging.INFO)
+ log.Logs().set_level(logging.INFO)
elif log_level.upper() == "WARN":
- stclog.Logs().set_level(logging.WARN)
+ log.Logs().set_level(logging.WARN)
elif log_level.upper() == "ERROR":
- stclog.Logs().set_level(logging.ERROR)
+ log.Logs().set_level(logging.ERROR)
elif log_level.upper() == "WARNING":
- stclog.Logs().set_level(logging.WARNING)
+ log.Logs().set_level(logging.WARNING)
elif log_level.upper() == "CRITICAL":
- stclog.Logs().set_level(logging.CRITICAL)
+ log.Logs().set_level(logging.CRITICAL)
else:
- stclog.Logs().set_level(logging.INFO)
+ log.Logs().set_level(logging.INFO)
elif isinstance(log_level, int):
- if log_level in [logging.DEBUG, logging.INFO, logging.ERROR,
- logging.WARN, logging.WARNING, logging.CRITICAL]:
- stclog.Logs().set_level(log_level)
+ if log_level in [
+ logging.DEBUG,
+ logging.INFO,
+ logging.ERROR,
+ logging.WARN,
+ logging.WARNING,
+ logging.CRITICAL,
+ ]:
+ log.Logs().set_level(log_level)
else:
- stclog.Logs().set_level(logging.INFO)
+ log.Logs().set_level(logging.INFO)
else:
- stclog.Logs().set_level(logging.INFO)
+ log.Logs().set_level(logging.INFO)
# Global logger
-logger = stclog.Logs().get_logger("cloud_connect_engine")
+logger = log.Logs().get_logger("cloud_connect_engine")
def reset_logger(name):
@@ -57,9 +63,5 @@ def reset_logger(name):
Reset logger.
"""
- stclog.reset_logger(name)
-
global logger
- logger = stclog.Logs().get_logger(name)
-
-
+ logger = log.Logs().get_logger(name)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/rwlock.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/rwlock.py
index 1825619..e91ba85 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/rwlock.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/rwlock.py
@@ -17,11 +17,10 @@
This module provides Read-Write lock.
"""
-from builtins import object
import threading
-class _ReadLocker(object):
+class _ReadLocker:
def __init__(self, lock):
self.lock = lock
@@ -33,7 +32,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
return False
-class _WriteLocker(object):
+class _WriteLocker:
def __init__(self, lock):
self.lock = lock
@@ -45,8 +44,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
return False
-class RWLock(object):
- """ Simple Read-Write lock.
+class RWLock:
+ """Simple Read-Write lock.
Allow multiple read but only one writing concurrently.
"""
@@ -84,4 +83,3 @@ def reader_lock(self):
@property
def writer_lock(self):
return _WriteLocker(self)
-
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/schema_meta.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/schema_meta.py
index 7e98191..1a7f1fa 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/schema_meta.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/common/schema_meta.py
@@ -13,9 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-FIELD_PRODUCT = '_product'
-FIELD_REST_NAMESPACE = '_rest_namespace'
-FIELD_REST_PREFIX = '_rest_prefix'
-FIELD_PROTOCOL_VERSION = '_protocol_version'
-FIELD_VERSION = '_version'
-FIELD_ENCRYPTION_FORMATTER = '_encryption_formatter'
+FIELD_PRODUCT = "_product"
+FIELD_REST_NAMESPACE = "_rest_namespace"
+FIELD_REST_PREFIX = "_rest_prefix"
+FIELD_PROTOCOL_VERSION = "_protocol_version"
+FIELD_VERSION = "_version"
+FIELD_ENCRYPTION_FORMATTER = "_encryption_formatter"
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/config.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/config.py
index 5546a55..a88c75c 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/config.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/config.py
@@ -17,26 +17,17 @@
This is for load/save configuration in UCC server or TA.
The load/save action is based on specified schema.
"""
-
-from __future__ import absolute_import
-
-from future import standard_library
-standard_library.install_aliases()
-from builtins import str
-from builtins import range
-from builtins import object
import json
import logging
-import traceback
import time
-import six
+import traceback
+from urllib.parse import quote
-from splunktalib.rest import splunkd_request, code_to_msg
-from splunktalib.common import util as sc_util
+from solnlib import utils
+from splunktalib.rest import code_to_msg, splunkd_request
-from .common import log as stulog
from .common import UCCException
-from urllib.parse import quote
+from .common import log as stulog
LOGGING_STOPPED = False
@@ -51,7 +42,7 @@ def stop_logging():
LOGGING_STOPPED = True
-def log(msg, msgx='', level=logging.INFO, need_tb=False):
+def log(msg, msgx="", level=logging.INFO, need_tb=False):
"""
Logging in UCC Config Module.
:param msg: message content
@@ -64,42 +55,45 @@ def log(msg, msgx='', level=logging.INFO, need_tb=False):
if LOGGING_STOPPED:
return
- msgx = ' - ' + msgx if msgx else ''
- content = 'UCC Config Module: %s%s' % (msg, msgx)
+ msgx = " - " + msgx if msgx else ""
+ content = f"UCC Config Module: {msg}{msgx}"
if need_tb:
- stack = ''.join(traceback.format_stack())
- content = '%s\r\n%s' % (content, stack)
+ stack = "".join(traceback.format_stack())
+ content = f"{content}\r\n{stack}"
stulog.logger.log(level, content, exc_info=1)
class ConfigException(UCCException):
- """Exception for UCC Config Exception
- """
+ """Exception for UCC Config Exception"""
+
pass
-class Config(object):
- """UCC Config Module
- """
+class Config:
+ """UCC Config Module"""
# Placeholder stands for any field
- FIELD_PLACEHOLDER = '*'
+ FIELD_PLACEHOLDER = "*"
# Head of non-processing endpoint
- NON_PROC_ENDPOINT = '#'
+ NON_PROC_ENDPOINT = "#"
# Some meta fields in UCC Config schema
- META_FIELDS = ('_product', '_rest_namespace', '_rest_prefix',
- '_protocol_version', '_version',
- '_encryption_formatter')
+ META_FIELDS = (
+ "_product",
+ "_rest_namespace",
+ "_rest_prefix",
+ "_protocol_version",
+ "_version",
+ "_encryption_formatter",
+ )
# Default Values for Meta fields
META_FIELDS_DEFAULT = {
- '_encryption_formatter': '',
+ "_encryption_formatter": "",
}
- def __init__(self, splunkd_uri, session_key, schema,
- user='nobody', app='-'):
+ def __init__(self, splunkd_uri, session_key, schema, user="nobody", app="-"):
"""
:param splunkd_uri: the root uri of Splunk server,
like https://127.0.0.1:8089
@@ -109,7 +103,7 @@ def __init__(self, splunkd_uri, session_key, schema,
:param app: namespace of the resources requested
:return:
"""
- self.splunkd_uri = splunkd_uri.strip('/')
+ self.splunkd_uri = splunkd_uri.strip("/")
self.session_key = session_key
self.user, self.app = user, app
self._parse_schema(schema)
@@ -122,27 +116,27 @@ def load(self):
"""
log('"load" method in', level=logging.DEBUG)
- ret = {meta_field: getattr(self, meta_field)
- for meta_field in Config.META_FIELDS}
+ ret = {
+ meta_field: getattr(self, meta_field) for meta_field in Config.META_FIELDS
+ }
for ep_id, ep in self._endpoints.items():
- data = {'output_mode': 'json', '--cred--': '1'}
+ data = {"output_mode": "json", "--cred--": "1"}
retries = 4
waiting_time = [1, 2, 2]
for retry in range(retries):
- resp, cont = splunkd_request(
+ response = splunkd_request(
splunkd_uri=self.make_uri(ep_id),
session_key=self.session_key,
data=data,
- retry=3
+ retry=3,
)
-
- if resp is None or resp.status != 200:
- msg = 'Fail to load endpoint "{ep_id}" - {err}' \
- ''.format(ep_id=ep_id,
- err=code_to_msg(resp, cont)
- if resp else cont)
+ cont = response.text
+ if response is None or response.status_code != 200:
+ msg = 'Fail to load endpoint "{ep_id}" - {err}' "".format(
+ ep_id=ep_id, err=code_to_msg(response)
+ )
log(msg, level=logging.ERROR, need_tb=True)
raise ConfigException(msg)
@@ -150,7 +144,7 @@ def load(self):
ret[ep_id] = self._parse_content(ep_id, cont)
except ConfigException as exc:
log(exc, level=logging.WARNING, need_tb=True)
- if retry < retries-1:
+ if retry < retries - 1:
time.sleep(waiting_time[retry])
else:
break
@@ -161,8 +155,9 @@ def load(self):
log('"load" method out', level=logging.DEBUG)
return ret
- def update_items(self, endpoint_id, item_names, field_names, data,
- raise_if_failed=False):
+ def update_items(
+ self, endpoint_id, item_names, field_names, data, raise_if_failed=False
+ ):
"""Update items in specified endpoint with given fields in data
:param endpoint_id: endpoint id in schema, the key name in schema
:param item_names: a list of item name
@@ -183,40 +178,49 @@ def update_items(self, endpoint_id, item_names, field_names, data,
If raise_if_failed is True, it will exist with an exception
on any updating failed.
"""
- log('"update_items" method in',
- msgx='endpoint_id=%s, item_names=%s, field_names=%s'
- % (endpoint_id, item_names, field_names),
- level=logging.DEBUG)
-
- assert endpoint_id in self._endpoints, \
- 'Unexpected endpoint id in given schema - {ep_id}' \
- ''.format(ep_id=endpoint_id)
+ log(
+ '"update_items" method in',
+ msgx="endpoint_id=%s, item_names=%s, field_names=%s"
+ % (endpoint_id, item_names, field_names),
+ level=logging.DEBUG,
+ )
+
+ assert (
+ endpoint_id in self._endpoints
+ ), "Unexpected endpoint id in given schema - {ep_id}" "".format(
+ ep_id=endpoint_id
+ )
item_names_failed = []
for item_name in item_names:
item_data = data.get(item_name, {})
- item_data = {field_name: self.dump_value(endpoint_id,
- item_name,
- field_name,
- item_data[field_name])
- for field_name in field_names
- if field_name in item_data}
+ item_data = {
+ field_name: self.dump_value(
+ endpoint_id, item_name, field_name, item_data[field_name]
+ )
+ for field_name in field_names
+ if field_name in item_data
+ }
if not item_data:
continue
item_uri = self.make_uri(endpoint_id, item_name=item_name)
- resp, cont = splunkd_request(splunkd_uri=item_uri,
- session_key=self.session_key,
- data=item_data,
- method="POST",
- retry=3
- )
- if resp is None or resp.status not in (200, 201):
- msg = 'Fail to update item "{item}" in endpoint "{ep_id}"' \
- ' - {err}'.format(ep_id=endpoint_id,
- item=item_name,
- err=code_to_msg(resp, cont)
- if resp else cont)
+ response = splunkd_request(
+ splunkd_uri=item_uri,
+ session_key=self.session_key,
+ data=item_data,
+ method="POST",
+ retry=3,
+ )
+ if response is None or response.status_code not in (200, 201):
+ msg = (
+ 'Fail to update item "{item}" in endpoint "{ep_id}"'
+ " - {err}".format(
+ ep_id=endpoint_id,
+ item=item_name,
+ err=code_to_msg(response),
+ )
+ )
log(msg, level=logging.ERROR)
if raise_if_failed:
raise ConfigException(msg)
@@ -231,28 +235,39 @@ def make_uri(self, endpoint_id, item_name=None):
:param item_name: item name for given endpoint. None for listing all
:return:
"""
- endpoint = self._endpoints[endpoint_id]['endpoint']
- ep_full = endpoint[1:].strip('/') \
- if endpoint.startswith(Config.NON_PROC_ENDPOINT) else \
- '{admin_match}/{protocol_version}/{endpoint}' \
- ''.format(admin_match=self._rest_namespace,
- protocol_version=self._protocol_version,
- endpoint=(self._rest_prefix +
- self._endpoints[endpoint_id]['endpoint']))
- ep_uri = None if endpoint_id not in self._endpoints else \
- '{splunkd_uri}/servicesNS/{user}/{app}/{endpoint_full}' \
- ''.format(splunkd_uri=self.splunkd_uri,
- user=self.user,
- app=self.app,
- endpoint_full=ep_full
- )
-
- url = ep_uri if item_name is None else "{ep_uri}/{item_name}"\
- .format(ep_uri=ep_uri, item_name=quote(item_name))
+ endpoint = self._endpoints[endpoint_id]["endpoint"]
+ ep_full = (
+ endpoint[1:].strip("/")
+ if endpoint.startswith(Config.NON_PROC_ENDPOINT)
+ else "{admin_match}/{protocol_version}/{endpoint}"
+ "".format(
+ admin_match=self._rest_namespace,
+ protocol_version=self._protocol_version,
+ endpoint=(self._rest_prefix + self._endpoints[endpoint_id]["endpoint"]),
+ )
+ )
+ ep_uri = (
+ None
+ if endpoint_id not in self._endpoints
+ else "{splunkd_uri}/servicesNS/{user}/{app}/{endpoint_full}"
+ "".format(
+ splunkd_uri=self.splunkd_uri,
+ user=self.user,
+ app=self.app,
+ endpoint_full=ep_full,
+ )
+ )
+
+ url = (
+ ep_uri
+ if item_name is None
+ else "{ep_uri}/{item_name}".format(
+ ep_uri=ep_uri, item_name=quote(item_name)
+ )
+ )
if item_name is None:
- url += '?count=-1'
- log('"make_uri" method', msgx='url=%s' % url,
- level=logging.DEBUG)
+ url += "?count=-1"
+ log('"make_uri" method', msgx="url=%s" % url, level=logging.DEBUG)
return url
def _parse_content(self, endpoint_id, content):
@@ -260,49 +275,59 @@ def _parse_content(self, endpoint_id, content):
:param content: a JSON string returned from REST.
"""
try:
- content = json.loads(content)['entry']
- ret = {ent['name']: ent['content'] for ent in content}
+ content = json.loads(content)["entry"]
+ ret = {ent["name"]: ent["content"] for ent in content}
except Exception as exc:
- msg = 'Fail to parse content from endpoint_id=%s' \
- ' - %s' % (endpoint_id, exc)
+ msg = "Fail to parse content from endpoint_id=%s" " - %s" % (
+ endpoint_id,
+ exc,
+ )
log(msg, level=logging.ERROR, need_tb=True)
raise ConfigException(msg)
- ret = {name: {key: self.load_value(endpoint_id, name, key, val)
- for key, val in ent.items()
- if not key.startswith('eai:')}
- for name, ent in ret.items()}
+ ret = {
+ name: {
+ key: self.load_value(endpoint_id, name, key, val)
+ for key, val in ent.items()
+ if not key.startswith("eai:")
+ }
+ for name, ent in ret.items()
+ }
return ret
def _parse_schema(self, ucc_config_schema):
try:
ucc_config_schema = json.loads(ucc_config_schema)
except ValueError:
- msg = 'Invalid JSON content of schema'
+ msg = "Invalid JSON content of schema"
log(msg, level=logging.ERROR, need_tb=True)
raise ConfigException(msg)
except Exception as exc:
log(exc, level=logging.ERROR, need_tb=True)
raise ConfigException(exc)
- ucc_config_schema.update({key: val for key, val in
- Config.META_FIELDS_DEFAULT.items()
- if key not in ucc_config_schema})
+ ucc_config_schema.update(
+ {
+ key: val
+ for key, val in Config.META_FIELDS_DEFAULT.items()
+ if key not in ucc_config_schema
+ }
+ )
for field in Config.META_FIELDS:
- assert field in ucc_config_schema and \
- isinstance(ucc_config_schema[field], six.string_types), \
- 'Missing or invalid field "%s" in given schema' % field
+ assert field in ucc_config_schema and isinstance(
+ ucc_config_schema[field], str
+ ), ('Missing or invalid field "%s" in given schema' % field)
setattr(self, field, ucc_config_schema[field])
self._endpoints = {}
for key, val in ucc_config_schema.items():
- if key.startswith('_'):
+ if key.startswith("_"):
continue
- assert isinstance(val, dict), \
+ assert isinstance(val, dict), (
'The schema of endpoint "%s" should be dict' % key
- assert 'endpoint' in val, \
- 'The endpoint "%s" has no endpoint entry' % key
+ )
+ assert "endpoint" in val, 'The endpoint "%s" has no endpoint entry' % key
self._endpoints[key] = val
@@ -313,58 +338,68 @@ def _check_protocol_version(self):
"""
if not self._protocol_version:
return
- if not self._protocol_version.startswith('1.'):
- raise ConfigException('Unsupported protocol version "%s" '
- 'in given schema' % self._protocol_version)
+ if not self._protocol_version.startswith("1."):
+ raise ConfigException(
+ 'Unsupported protocol version "%s" '
+ "in given schema" % self._protocol_version
+ )
def load_value(self, endpoint_id, item_name, fname, fval):
field_type = self._get_field_type(endpoint_id, item_name, fname)
- if field_type == '':
+ if field_type == "":
return fval
try:
field_type = field_type.lower()
- if field_type == 'bool':
- return True if sc_util.is_true(fval) else False
- elif field_type == 'int':
+ if field_type == "bool":
+ return True if utils.is_true(fval) else False
+ elif field_type == "int":
return int(fval)
- elif field_type == 'json':
+ elif field_type == "json":
return json.loads(fval)
except Exception as exc:
- msg = 'Fail to load value of "{type_name}" - ' \
- 'endpoint={endpoint}, item={item}, field={field}' \
- ''.format(type_name=field_type,
- endpoint=endpoint_id,
- item=item_name,
- field=fname)
+ msg = (
+ 'Fail to load value of "{type_name}" - '
+ "endpoint={endpoint}, item={item}, field={field}"
+ "".format(
+ type_name=field_type,
+ endpoint=endpoint_id,
+ item=item_name,
+ field=fname,
+ )
+ )
log(msg, msgx=str(exc), level=logging.WARNING, need_tb=True)
raise ConfigException(msg)
def dump_value(self, endpoint_id, item_name, fname, fval):
field_type = self._get_field_type(endpoint_id, item_name, fname)
- if field_type == '':
+ if field_type == "":
return fval
try:
field_type = field_type.lower()
- if field_type == 'bool':
+ if field_type == "bool":
return str(fval).lower()
- elif field_type == 'json':
+ elif field_type == "json":
return json.dumps(fval)
else:
return fval
except Exception as exc:
- msg = 'Fail to dump value of "{type_name}" - ' \
- 'endpoint={endpoint}, item={item}, field={field}' \
- ''.format(type_name=field_type,
- endpoint=endpoint_id,
- item=item_name,
- field=fname)
+ msg = (
+ 'Fail to dump value of "{type_name}" - '
+ "endpoint={endpoint}, item={item}, field={field}"
+ "".format(
+ type_name=field_type,
+ endpoint=endpoint_id,
+ item=item_name,
+ field=fname,
+ )
+ )
log(msg, msgx=str(exc), level=logging.ERROR, need_tb=True)
raise ConfigException(msg)
def _get_field_type(self, endpoint_id, item_name, fname):
- field_types = self._endpoints[endpoint_id].get('field_types', {})
+ field_types = self._endpoints[endpoint_id].get("field_types", {})
if item_name in field_types:
fields = field_types[item_name]
elif Config.FIELD_PLACEHOLDER in field_types:
@@ -372,14 +407,18 @@ def _get_field_type(self, endpoint_id, item_name, fname):
else:
fields = {}
- field_type = fields.get(fname, '')
- if field_type not in ('', 'bool', 'int', 'json'):
- msg = 'Unsupported type "{type_name}" for value in schema - ' \
- 'endpoint={endpoint}, item={item}, field={field}' \
- ''.format(type_name=field_type,
- endpoint=endpoint_id,
- item=item_name,
- field=fname)
+ field_type = fields.get(fname, "")
+ if field_type not in ("", "bool", "int", "json"):
+ msg = (
+ 'Unsupported type "{type_name}" for value in schema - '
+ "endpoint={endpoint}, item={item}, field={field}"
+ "".format(
+ type_name=field_type,
+ endpoint=endpoint_id,
+ item=item_name,
+ field=fname,
+ )
+ )
log(msg, level=logging.ERROR, need_tb=True)
raise ConfigException(msg)
return field_type
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/__init__.py
index d40e936..e1dc0ca 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/__init__.py
@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-__version__ = "1.0.2"
\ No newline at end of file
+__version__ = "1.0.2"
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_checkpoint_manager.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_checkpoint_manager.py
index 9c004e7..4e41db9 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_checkpoint_manager.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_checkpoint_manager.py
@@ -13,19 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import object
import json
-import logging
import re
+from solnlib.utils import is_true
+from splunktalib import state_store as ss
+
+from ..common import log as stulog
from . import ta_consts as c
from . import ta_helper as th
-from ..common import log as stulog
-from splunktalib import state_store as ss
-from splunktalib.common.util import is_true
-class TACheckPointMgr(object):
+class TACheckPointMgr:
SEPARATOR = "_" * 3
# FIXME We'd better move all default values together
@@ -37,52 +36,48 @@ def __init__(self, meta_config, task_config):
self._store = self._create_state_store(
meta_config,
task_config.get(c.checkpoint_storage_type),
- task_config[c.appname]
+ task_config[c.appname],
)
def _create_state_store(self, meta_config, storage_type, app_name):
- stulog.logger.debug('Got checkpoint storage type=%s', storage_type)
+ stulog.logger.debug("Got checkpoint storage type=%s", storage_type)
if storage_type == c.checkpoint_kv_storage:
collection_name = self._get_collection_name()
stulog.logger.debug(
- 'Creating KV state store, collection name=%s', collection_name
+ "Creating KV state store, collection name=%s", collection_name
)
return ss.get_state_store(
meta_config,
appname=app_name,
collection_name=collection_name,
- use_kv_store=True
+ use_kv_store=True,
)
use_cache_file = self._use_cache_file()
- max_cache_seconds = \
- self._get_max_cache_seconds() if use_cache_file else None
+ max_cache_seconds = self._get_max_cache_seconds() if use_cache_file else None
stulog.logger.debug(
- 'Creating file state store, use_cache_file=%s, max_cache_seconds=%s',
- use_cache_file, max_cache_seconds
+ "Creating file state store, use_cache_file=%s, max_cache_seconds=%s",
+ use_cache_file,
+ max_cache_seconds,
)
return ss.get_state_store(
- meta_config,
- app_name,
- use_cached_store=use_cache_file,
- max_cache_seconds=max_cache_seconds
+ meta_config, app_name, use_cached_store=use_cache_file
)
def _get_collection_name(self):
collection = self._task_config.get(c.collection_name)
- collection = collection.strip() if collection else ''
+ collection = collection.strip() if collection else ""
if not collection:
input_name = self._task_config[c.mod_input_name]
stulog.logger.info(
- 'Collection name="%s" is empty, set it to "%s"',
- collection, input_name
+ 'Collection name="%s" is empty, set it to "%s"', collection, input_name
)
collection = input_name
- return re.sub(r'[^\w]+', '_', collection)
+ return re.sub(r"[^\w]+", "_", collection)
def _use_cache_file(self):
# TODO Move the default value outside code
@@ -90,22 +85,21 @@ def _use_cache_file(self):
if use_cache_file:
stulog.logger.info(
"Stanza=%s using cached file store to create checkpoint",
- self._task_config[c.stanza_name]
+ self._task_config[c.stanza_name],
)
return use_cache_file
def _get_max_cache_seconds(self):
default = self._DEFAULT_MAX_CACHE_SECONDS
- seconds = self._task_config.get(
- c.max_cache_seconds, default
- )
+ seconds = self._task_config.get(c.max_cache_seconds, default)
try:
seconds = int(seconds)
except ValueError:
stulog.logger.warning(
"The max_cache_seconds '%s' is not a valid integer,"
" so set this variable to default value %s",
- seconds, default
+ seconds,
+ default,
)
seconds = default
else:
@@ -116,7 +110,9 @@ def _get_max_cache_seconds(self):
stulog.logger.warning(
"The max_cache_seconds (%s) is expected in range[1,%s],"
" set it to %s",
- seconds, maximum, adjusted
+ seconds,
+ maximum,
+ adjusted,
)
seconds = adjusted
return seconds
@@ -127,27 +123,31 @@ def get_ckpt_key(self, namespaces=None):
def get_ckpt(self, namespaces=None, show_namespaces=False):
key, namespaces = self.get_ckpt_key(namespaces)
raw_checkpoint = self._store.get_state(key)
- stulog.logger.info("Get checkpoint key='%s' value='%s'",
- key, json.dumps(raw_checkpoint))
+ stulog.logger.debug(
+ "Get checkpoint key='%s' value='%s'", key, json.dumps(raw_checkpoint)
+ )
if not show_namespaces and raw_checkpoint:
return raw_checkpoint.get("data")
return raw_checkpoint
def delete_if_exists(self, namespaces=None):
"""Return true if exist and deleted else False"""
- key, namespaces = self._get_ckpt_key(namespaces)
- if self._store.exists(key):
+ key, _ = self._key_formatter(namespaces)
+ try:
self._store.delete_state(key)
return True
- return False
+ except Exception:
+ return False
+
def update_ckpt(self, ckpt, namespaces=None):
if not ckpt:
stulog.logger.warning("Checkpoint expect to be not empty.")
return
key, namespaces = self.get_ckpt_key(namespaces)
value = {"namespaces": namespaces, "data": ckpt}
- stulog.logger.info("Update checkpoint key='%s' value='%s'",
- key, json.dumps(value))
+ stulog.logger.info(
+ "Update checkpoint key='%s' value='%s'", key, json.dumps(value)
+ )
self._store.update_state(key, value)
def remove_ckpt(self, namespaces=None):
@@ -156,8 +156,11 @@ def remove_ckpt(self, namespaces=None):
def _key_formatter(self, namespaces=None):
if not namespaces:
- stulog.logger.info('Namespaces is empty, using stanza name instead.')
- namespaces = [self._task_config[c.stanza_name]]
+ stanza = self._task_config[c.stanza_name]
+ stulog.logger.info(
+ f"Namespaces is empty, using stanza name {stanza} instead."
+ )
+ namespaces = [stanza]
key_str = TACheckPointMgr.SEPARATOR.join(namespaces)
hashed_file = th.format_name_for_file(key_str)
stulog.logger.info("raw_file='%s' hashed_file='%s'", key_str, hashed_file)
@@ -166,6 +169,6 @@ def _key_formatter(self, namespaces=None):
def close(self, key=None):
try:
self._store.close(key)
- stulog.logger.info('Closed state store successfully. key=%s', key)
+ stulog.logger.info("Closed state store successfully. key=%s", key)
except Exception:
- stulog.logger.exception('Error closing state store. key=%s', key)
+ stulog.logger.exception("Error closing state store. key=%s", key)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_config.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_config.py
index 6af27b5..4083b30 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_config.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_config.py
@@ -13,27 +13,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from __future__ import absolute_import
-from builtins import object
import os.path as op
import socket
-from . import ta_consts as c
-from . import ta_helper as th
-from ..common import log as stulog
+from solnlib import server_info
from splunktalib import modinput as modinput
-from splunktalib import splunk_cluster as sc
from splunktalib.common import util
+from ..common import log as stulog
+from . import ta_consts as c
+from . import ta_helper as th
+
# methods can be overrided by subclass : process_task_configs
-class TaConfig(object):
+class TaConfig:
_current_hostname = socket.gethostname()
_appname = util.get_appname_from_path(op.abspath(__file__))
- def __init__(self, meta_config, client_schema, log_suffix=None,
- stanza_name=None, input_type=None,
- single_instance=True):
+ def __init__(
+ self,
+ meta_config,
+ client_schema,
+ log_suffix=None,
+ stanza_name=None,
+ input_type=None,
+ single_instance=True,
+ ):
self._meta_config = meta_config
self._stanza_name = stanza_name
self._input_type = input_type
@@ -41,8 +46,9 @@ def __init__(self, meta_config, client_schema, log_suffix=None,
self._single_instance = single_instance
self._task_configs = []
self._client_schema = client_schema
- self._server_info = sc.ServerInfo(meta_config[c.server_uri],
- meta_config[c.session_key])
+ self._server_info = server_info.ServerInfo.from_server_uri(
+ meta_config[c.server_uri], meta_config[c.session_key]
+ )
self._all_conf_contents = {}
self._get_division_settings = {}
self.set_logging()
@@ -65,14 +71,18 @@ def get_task_configs(self):
def get_all_conf_contents(self):
if self._all_conf_contents:
- return self._all_conf_contents.get(c.inputs), \
- self._all_conf_contents.get(c.all_configs), \
- self._all_conf_contents.get(c.global_settings)
+ return (
+ self._all_conf_contents.get(c.inputs),
+ self._all_conf_contents.get(c.all_configs),
+ self._all_conf_contents.get(c.global_settings),
+ )
inputs, configs, global_settings = th.get_all_conf_contents(
self._meta_config[c.server_uri],
self._meta_config[c.session_key],
- self._client_schema, self._input_type)
+ self._client_schema,
+ self._input_type,
+ )
self._all_conf_contents[c.inputs] = inputs
self._all_conf_contents[c.all_configs] = configs
self._all_conf_contents[c.global_settings] = global_settings
@@ -81,8 +91,9 @@ def get_all_conf_contents(self):
def set_logging(self):
# The default logger name is "cloud_connect_engine"
if self._stanza_name and self._log_suffix:
- logger_name = self._log_suffix + "_" + th.format_name_for_file(
- self._stanza_name)
+ logger_name = (
+ self._log_suffix + "_" + th.format_name_for_file(self._stanza_name)
+ )
stulog.reset_logger(logger_name)
inputs, configs, global_settings = self.get_all_conf_contents()
log_level = "INFO"
@@ -91,8 +102,8 @@ def set_logging(self):
log_level = item["loglevel"]
break
stulog.set_log_level(log_level)
- stulog.logger.info("Set log_level={}".format(log_level))
- stulog.logger.info("Start {} task".format(self._stanza_name))
+ stulog.logger.info(f"Set log_level={log_level}")
+ stulog.logger.info(f"Start {self._stanza_name} task")
def get_input_type(self):
return self._input_type
@@ -107,7 +118,8 @@ def _get_checkpoint_storage_type(self, config):
if cs_type not in (c.checkpoint_auto, c.checkpoint_file):
stulog.logger.warning(
"Checkpoint storage type='%s' is invalid, change it to '%s'",
- cs_type, c.checkpoint_auto
+ cs_type,
+ c.checkpoint_auto,
)
cs_type = c.checkpoint_auto
@@ -116,7 +128,7 @@ def _get_checkpoint_storage_type(self, config):
"Checkpoint storage type is '%s' and instance is "
"search head, set checkpoint storage type to '%s'.",
c.checkpoint_auto,
- c.checkpoint_kv_storage
+ c.checkpoint_kv_storage,
)
cs_type = c.checkpoint_kv_storage
return cs_type
@@ -126,15 +138,15 @@ def _load_task_configs(self):
if self._input_type:
inputs = inputs.get(self._input_type)
if not self._single_instance:
- inputs = [input for input in inputs if
- input[c.name] == self._stanza_name]
+ inputs = [input for input in inputs if input[c.name] == self._stanza_name]
all_task_configs = []
for input in inputs:
task_config = {}
task_config.update(input)
task_config[c.configs] = configs
- task_config[c.settings] = \
- {item[c.name]: item for item in global_settings["settings"]}
+ task_config[c.settings] = {
+ item[c.name]: item for item in global_settings["settings"]
+ }
if self.is_single_instance():
collection_interval = "collection_interval"
task_config[c.interval] = task_config.get(collection_interval)
@@ -142,11 +154,12 @@ def _load_task_configs(self):
if task_config[c.interval] <= 0:
raise ValueError(
"The interval value {} is invalid."
- " It should be a positive integer".format(
- task_config[c.interval]))
+ " It should be a positive integer".format(task_config[c.interval])
+ )
- task_config[c.checkpoint_storage_type] = \
- self._get_checkpoint_storage_type(task_config)
+ task_config[c.checkpoint_storage_type] = self._get_checkpoint_storage_type(
+ task_config
+ )
task_config[c.appname] = TaConfig._appname
task_config[c.mod_input_name] = self._input_type
@@ -161,12 +174,19 @@ def process_task_configs(self, task_configs):
pass
-def create_ta_config(settings, config_cls=TaConfig, log_suffix=None,
- single_instance=True):
+def create_ta_config(
+ settings, config_cls=TaConfig, log_suffix=None, single_instance=True
+):
meta_config, configs = modinput.get_modinput_configs_from_stdin()
stanza_name = None
input_type = None
if configs and "://" in configs[0].get("name", ""):
input_type, stanza_name = configs[0].get("name").split("://", 1)
- return config_cls(meta_config, settings, log_suffix, stanza_name,
- input_type, single_instance=single_instance)
+ return config_cls(
+ meta_config,
+ settings,
+ log_suffix,
+ stanza_name,
+ input_type,
+ single_instance=single_instance,
+ )
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_consts.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_consts.py
index a1b0508..c0620bd 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_consts.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_consts.py
@@ -37,7 +37,7 @@
input_data = "input_data"
interval = "interval"
data = "data"
-batch_size = 'batch_size'
+batch_size = "batch_size"
time_fmt = "%Y-%m-%dT%H:%M:%S"
utc_time_fmt = "%Y-%m-%dT%H:%M:%S.%fZ"
@@ -45,9 +45,9 @@
checkpoint_storage_type = "builtin_system_checkpoint_storage_type"
# Possible values for checkpoint storage type
-checkpoint_auto = 'auto'
-checkpoint_kv_storage = 'kv_store'
-checkpoint_file = 'file'
+checkpoint_auto = "auto"
+checkpoint_kv_storage = "kv_store"
+checkpoint_file = "file"
# For cache file
use_cache_file = "builtin_system_use_cache_file"
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_client.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_client.py
index 8358166..65fbf13 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_client.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_client.py
@@ -14,32 +14,31 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from builtins import next
-from builtins import object
from . import ta_checkpoint_manager as cp
from . import ta_data_collector as tdc
-def build_event(host=None,
- source=None,
- sourcetype=None,
- time=None,
- index=None,
- raw_data="",
- is_unbroken=False,
- is_done=False):
+def build_event(
+ host=None,
+ source=None,
+ sourcetype=None,
+ time=None,
+ index=None,
+ raw_data="",
+ is_unbroken=False,
+ is_done=False,
+):
if is_unbroken is False and is_done is True:
- raise Exception('is_unbroken=False is_done=True is invalid')
- return tdc.event_tuple._make([host, source, sourcetype, time, index,
- raw_data, is_unbroken, is_done])
+ raise Exception("is_unbroken=False is_done=True is invalid")
+ return tdc.event_tuple._make(
+ [host, source, sourcetype, time, index, raw_data, is_unbroken, is_done]
+ )
-class TaDataClient(object):
- def __init__(self,
- meta_config,
- task_config,
- checkpoint_mgr=None,
- event_writer=None):
+class TaDataClient:
+ def __init__(
+ self, meta_config, task_config, checkpoint_mgr=None, event_writer=None
+ ):
self._meta_config = meta_config
self._task_config = task_config
self._checkpoint_mgr = checkpoint_mgr
@@ -56,24 +55,24 @@ def get(self):
raise StopIteration
-def create_data_collector(dataloader,
- tconfig,
- meta_configs,
- task_config,
- data_client_cls,
- checkpoint_cls=None):
+def create_data_collector(
+ dataloader, tconfig, meta_configs, task_config, data_client_cls, checkpoint_cls=None
+):
checkpoint_manager_cls = checkpoint_cls or cp.TACheckPointMgr
- return tdc.TADataCollector(tconfig, meta_configs, task_config,
- checkpoint_manager_cls, data_client_cls,
- dataloader)
+ return tdc.TADataCollector(
+ tconfig,
+ meta_configs,
+ task_config,
+ checkpoint_manager_cls,
+ data_client_cls,
+ dataloader,
+ )
def client_adatper(job_func):
class TaDataClientAdapter(TaDataClient):
- def __init__(self, all_conf_contents, meta_config, task_config,
- chp_mgr):
- super(TaDataClientAdapter, self).__init__(meta_config, task_config,
- chp_mgr)
+ def __init__(self, all_conf_contents, meta_config, task_config, chp_mgr):
+ super().__init__(meta_config, task_config, chp_mgr)
self._execute_times = 0
self._gen = job_func(self._task_config, chp_mgr)
@@ -83,7 +82,7 @@ def stop(self):
"""
# normaly base class just set self._stop as True
- super(TaDataClientAdapter, self).stop()
+ super().stop()
def get(self):
"""
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_collector.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_collector.py
index d81cfd3..7d323d6 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_collector.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_collector.py
@@ -14,51 +14,70 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from __future__ import absolute_import
-from builtins import object
import threading
import time
from collections import namedtuple
-from . import ta_consts as c
-from ..common import log as stulog
from splunktalib.common import util as scu
-evt_fmt = ("{0}"
- ""
- ""
- ""
- "{4}"
- "")
-
-unbroken_evt_fmt = (""
- ""
- "{0}"
- ""
- ""
- ""
- "{4}"
- ""
- "{6}"
- ""
- "")
-
-event_tuple = namedtuple('Event',
- ['host', 'source', 'sourcetype', 'time', 'index',
- 'raw_data', 'is_unbroken', 'is_done'])
-
-
-class TADataCollector(object):
- def __init__(self, tconfig, meta_config, task_config,
- checkpoint_manager_cls, data_client_cls, data_loader):
+from ..common import log as stulog
+from . import ta_consts as c
+
+evt_fmt = (
+ "{0}"
+ ""
+ ""
+ ""
+ "{4}"
+ ""
+)
+
+unbroken_evt_fmt = (
+ ""
+ ''
+ "{0}"
+ ""
+ ""
+ ""
+ "{4}"
+ ""
+ "{6}"
+ ""
+ ""
+)
+
+event_tuple = namedtuple(
+ "Event",
+ [
+ "host",
+ "source",
+ "sourcetype",
+ "time",
+ "index",
+ "raw_data",
+ "is_unbroken",
+ "is_done",
+ ],
+)
+
+
+class TADataCollector:
+ def __init__(
+ self,
+ tconfig,
+ meta_config,
+ task_config,
+ checkpoint_manager_cls,
+ data_client_cls,
+ data_loader,
+ ):
self._lock = threading.Lock()
self._ta_config = tconfig
self._meta_config = meta_config
self._task_config = task_config
self._stopped = False
self._p = self._get_logger_prefix()
- self._checkpoint_manager = checkpoint_manager_cls(meta_config,
- task_config)
+ self._checkpoint_manager = checkpoint_manager_cls(meta_config, task_config)
self.data_client_cls = data_client_cls
self._data_loader = data_loader
self._client = None
@@ -73,8 +92,7 @@ def get_interval(self):
return self._task_config[c.interval]
def _get_logger_prefix(self):
- pairs = ['{}="{}"'.format(c.stanza_name, self._task_config[
- c.stanza_name])]
+ pairs = [f'{c.stanza_name}="{self._task_config[c.stanza_name]}"']
return "[{}]".format(" ".join(pairs))
def stop(self):
@@ -95,38 +113,49 @@ def _build_event(self, events):
assert event.raw_data, "the raw data of events is empty"
if event.is_unbroken:
evt = unbroken_evt_fmt.format(
- event.host or "", event.source or "", event.sourcetype or
- "", event.time or "", event.index or "",
- scu.escape_cdata(event.raw_data), "" if
- event.is_done else "")
+ event.host or "",
+ event.source or "",
+ event.sourcetype or "",
+ event.time or "",
+ event.index or "",
+ scu.escape_cdata(event.raw_data),
+ "" if event.is_done else "",
+ )
else:
- evt = evt_fmt.format(event.host or "", event.source or "",
- event.sourcetype or "", event.time or "",
- event.index or "",
- scu.escape_cdata(event.raw_data))
+ evt = evt_fmt.format(
+ event.host or "",
+ event.source or "",
+ event.sourcetype or "",
+ event.time or "",
+ event.index or "",
+ scu.escape_cdata(event.raw_data),
+ )
evts.append(evt)
return evts
def _create_data_client(self):
- return self.data_client_cls(self._meta_config,
- self._task_config,
- self._checkpoint_manager,
- self._data_loader.get_event_writer())
+ return self.data_client_cls(
+ self._meta_config,
+ self._task_config,
+ self._checkpoint_manager,
+ self._data_loader.get_event_writer(),
+ )
def index_data(self):
if self._lock.locked():
stulog.logger.debug(
"Last round of stanza={} is not done yet".format(
- self._task_config[c.stanza_name]))
+ self._task_config[c.stanza_name]
+ )
+ )
return
with self._lock:
try:
self._do_safe_index()
self._checkpoint_manager.close()
except Exception:
- stulog.logger.exception("{} Failed to index data"
- .format(self._p))
- stulog.logger.info("{} End of indexing data".format(self._p))
+ stulog.logger.exception(f"{self._p} Failed to index data")
+ stulog.logger.info(f"{self._p} End of indexing data")
if not self._ta_config.is_single_instance():
self._data_loader.tear_down()
@@ -134,9 +163,10 @@ def _write_events(self, events):
evts = self._build_event(events)
if evts:
if not self._data_loader.write_events(evts):
- stulog.logger.info("{} the event queue is closed and the "
- "received data will be discarded".format(
- self._p))
+ stulog.logger.info(
+ "{} the event queue is closed and the "
+ "received data will be discarded".format(self._p)
+ )
return False
return True
@@ -151,10 +181,10 @@ def _do_safe_index(self):
if not self._write_events(events):
break
except StopIteration:
- stulog.logger.info("{} Finished this round".format(self._p))
+ stulog.logger.info(f"{self._p} Finished this round")
return
except Exception:
- stulog.logger.exception("{} Failed to get msg".format(self._p))
+ stulog.logger.exception(f"{self._p} Failed to get msg")
break
# in case encounter exception or fail to write events
if not self._stopped:
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_loader.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_loader.py
index ab4edd7..3ad8d5d 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_loader.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_data_loader.py
@@ -16,22 +16,20 @@
"""
Data Loader main entry point
"""
-
-
-from future import standard_library
-standard_library.install_aliases()
-from builtins import object
-import queue
-import os.path as op
import configparser
+import os.path as op
+import queue
+from solnlib import log
+from solnlib import timer_queue as tq
from splunktalib.concurrent import concurrent_executor as ce
-from splunktalib import timer_queue as tq
from splunktalib.schedule import job as sjob
-from splunktalib.common import log
+
+# Global logger
+logger = log.Logs().get_logger("util")
-class TADataLoader(object):
+class TADataLoader:
"""
Data Loader boots all underlying facilities to handle data collection
"""
@@ -63,7 +61,7 @@ def run(self, jobs):
self._executor.start()
self._timer_queue.start()
self._scheduler.start()
- log.logger.info("TADataLoader started.")
+ logger.info("TADataLoader started.")
def _enqueue_io_job(job):
job_props = job.get_props()
@@ -71,8 +69,7 @@ def _enqueue_io_job(job):
self.run_io_jobs((real_job,))
for job in jobs:
- j = sjob.Job(_enqueue_io_job, {"real_job": job},
- job.get_interval())
+ j = sjob.Job(_enqueue_io_job, {"real_job": job}, job.get_interval())
self._scheduler.add_jobs((j,))
self._wait_for_tear_down()
@@ -81,10 +78,10 @@ def _enqueue_io_job(job):
job.stop()
self._scheduler.tear_down()
- self._timer_queue.tear_down()
+ self._timer_queue.stop()
self._executor.tear_down()
self._event_writer.tear_down()
- log.logger.info("DataLoader stopped.")
+ logger.info("DataLoader stopped.")
def _wait_for_tear_down(self):
wakeup_q = self._wakeup_queue
@@ -95,13 +92,13 @@ def _wait_for_tear_down(self):
pass
else:
if go_exit:
- log.logger.info("DataLoader got stop signal")
+ logger.info("DataLoader got stop signal")
self._stopped = True
break
def tear_down(self):
self._wakeup_queue.put(True)
- log.logger.info("DataLoader is going to stop.")
+ logger.info("DataLoader is going to stop.")
def stopped(self):
return self._stopped
@@ -117,8 +114,7 @@ def run_compute_job_async(self, func, args=(), kwargs={}, callback=None):
@return: AsyncResult
"""
- return self._executor.run_compute_func_async(func, args,
- kwargs, callback)
+ return self._executor.run_compute_func_async(func, args, kwargs, callback)
def add_timer(self, callback, when, interval):
return self._timer_queue.add_timer(callback, when, interval)
@@ -135,12 +131,11 @@ def get_event_writer(self):
@staticmethod
def _read_default_settings():
cur_dir = op.dirname(op.abspath(__file__))
- setting_file = op.join(cur_dir,"../../../","splunktalib", "setting.conf")
+ setting_file = op.join(cur_dir, "../../../", "splunktalib", "setting.conf")
parser = configparser.ConfigParser()
parser.read(setting_file)
settings = {}
- keys = ("process_size", "thread_min_size", "thread_max_size",
- "task_queue_size")
+ keys = ("process_size", "thread_min_size", "thread_max_size", "task_queue_size")
for option in keys:
try:
settings[option] = parser.get("global", option)
@@ -151,20 +146,19 @@ def _read_default_settings():
settings[option] = int(settings[option])
except ValueError:
settings[option] = -1
- log.logger.debug("settings: %s", settings)
+ logger.debug("settings: %s", settings)
return settings
-class GlobalDataLoader(object):
- """ Singleton, inited when started"""
+class GlobalDataLoader:
+ """Singleton, inited when started"""
__instance = None
@staticmethod
def get_data_loader(scheduler, writer):
if GlobalDataLoader.__instance is None:
- GlobalDataLoader.__instance = TADataLoader(
- scheduler, writer)
+ GlobalDataLoader.__instance = TADataLoader(scheduler, writer)
return GlobalDataLoader.__instance
@staticmethod
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_helper.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_helper.py
index d3b8e3c..bb9ddfa 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_helper.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_helper.py
@@ -13,29 +13,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-import six
-from builtins import object
import hashlib
import json
import os.path as op
import re
from calendar import timegm
from datetime import datetime
+from functools import lru_cache
-import sys
-if sys.version_info[0] >= 3:
- from functools import lru_cache
-else:
- from functools32 import lru_cache
-
+from splunktalib.common import util
from splunktaucclib.global_config import GlobalConfig, GlobalConfigSchema
-from . import ta_consts as c
+
from ...splunktacollectorlib import config as sc
-from splunktalib.common import util
+from . import ta_consts as c
def utc2timestamp(human_time):
- regex1 = "\d{4}-\d{2}-\d{2}.\d{2}:\d{2}:\d{2}"
+ regex1 = r"\d{4}-\d{2}-\d{2}.\d{2}:\d{2}:\d{2}"
match = re.search(regex1, human_time)
if match:
formated = match.group()
@@ -45,7 +39,7 @@ def utc2timestamp(human_time):
strped_time = datetime.strptime(formated, c.time_fmt)
timestamp = timegm(strped_time.utctimetuple())
- regex2 = "\d{4}-\d{2}-\d{2}.\d{2}:\d{2}:\d{2}(\.\d+)"
+ regex2 = r"\d{4}-\d{2}-\d{2}.\d{2}:\d{2}:\d{2}(\.\d+)"
match = re.search(regex2, human_time)
if match:
timestamp += float(match.group(1))
@@ -61,17 +55,15 @@ def get_md5(data):
:return:
"""
assert data is not None, "The input cannot be None"
- if isinstance(data, six.string_types):
- return hashlib.sha256(data.encode('utf-8')).hexdigest()
+ if isinstance(data, str):
+ return hashlib.sha256(data.encode("utf-8")).hexdigest()
elif isinstance(data, (list, tuple, dict)):
- return hashlib.sha256(json.dumps(data).encode('utf-8')).hexdigest()
+ return hashlib.sha256(json.dumps(data).encode("utf-8")).hexdigest()
def get_all_conf_contents(server_uri, sessionkey, settings, input_type=None):
schema = GlobalConfigSchema(settings)
- global_config = GlobalConfig(
- server_uri, sessionkey, schema
- )
+ global_config = GlobalConfig(server_uri, sessionkey, schema)
inputs = global_config.inputs.load(input_type=input_type)
configs = global_config.configs.load()
settings = global_config.settings.load()
@@ -80,10 +72,10 @@ def get_all_conf_contents(server_uri, sessionkey, settings, input_type=None):
@lru_cache(maxsize=64)
def format_name_for_file(name):
- return hashlib.sha256(name.encode('utf-8')).hexdigest()
+ return hashlib.sha256(name.encode("utf-8")).hexdigest()
-class ConfigSchemaHandler(object):
+class ConfigSchemaHandler:
_app_name = util.get_appname_from_path(op.abspath(__file__))
# Division schema keys.
TYPE = "type"
@@ -94,12 +86,13 @@ class ConfigSchemaHandler(object):
SEPARATOR = "separator"
def __init__(self, meta_configs, client_schema):
- self._config = sc.Config(splunkd_uri=meta_configs[c.server_uri],
- session_key=meta_configs[c.session_key],
- schema=json.dumps(client_schema[
- c.config]),
- user="nobody",
- app=ConfigSchemaHandler._app_name)
+ self._config = sc.Config(
+ splunkd_uri=meta_configs[c.server_uri],
+ session_key=meta_configs[c.session_key],
+ schema=json.dumps(client_schema[c.config]),
+ user="nobody",
+ app=ConfigSchemaHandler._app_name,
+ )
self._client_schema = client_schema
self._all_conf_contents = {}
self._load_conf_contents()
@@ -122,7 +115,8 @@ def _divide_settings(self):
division_settings = dict()
for division_endpoint, division_contents in division_schema.items():
division_settings[division_endpoint] = self._process_division(
- division_endpoint, division_contents)
+ division_endpoint, division_contents
+ )
return division_settings
def _load_conf_contents(self):
@@ -133,26 +127,32 @@ def _process_division(self, division_endpoint, division_contents):
assert isinstance(division_contents, dict)
for division_key, division_value in division_contents.items():
try:
- assert self.TYPE in division_value and \
- division_value[self.TYPE] in \
- [self.TYPE_SINGLE, self.TYPE_MULTI] and \
- self.SEPARATOR in division_value if \
- division_value[self.TYPE] == self.TYPE_MULTI else True
+ assert (
+ self.TYPE in division_value
+ and division_value[self.TYPE] in [self.TYPE_SINGLE, self.TYPE_MULTI]
+ and self.SEPARATOR in division_value
+ if division_value[self.TYPE] == self.TYPE_MULTI
+ else True
+ )
except Exception:
raise Exception("Invalid division schema")
- division_metrics.append(DivisionRule(division_endpoint,
- division_key,
- division_value[self.TYPE],
- division_value.get(
- self.SEPARATOR,
- ),
- division_value.get(
- self.REFER,
- )))
+ division_metrics.append(
+ DivisionRule(
+ division_endpoint,
+ division_key,
+ division_value[self.TYPE],
+ division_value.get(
+ self.SEPARATOR,
+ ),
+ division_value.get(
+ self.REFER,
+ ),
+ )
+ )
return division_metrics
-class DivisionRule(object):
+class DivisionRule:
def __init__(self, endpoint, metric, type, separator, refer):
self._endpoint = endpoint
self._metric = metric
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_mod_input.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_mod_input.py
index 55382de..654d7b3 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_mod_input.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/data_collection/ta_mod_input.py
@@ -18,25 +18,24 @@
"""
This is the main entry point for My TA
"""
-from __future__ import print_function
-
-from builtins import str
import os.path as op
import platform
import sys
import time
+from solnlib import file_monitor as fm
+from solnlib import orphan_process_monitor as opm
+from solnlib import utils
+from splunktalib import modinput
+from splunktalib.common import util as sc_util
+
+from ...common.lib_util import get_app_root_dir, get_mod_input_script_name
+from ..common import load_schema_file as ld
+from ..common import log as stulog
from . import ta_checkpoint_manager as cpmgr
from . import ta_config as tc
from . import ta_data_client as tdc
from . import ta_data_loader as dl
-from ..common import load_schema_file as ld
-from ..common import log as stulog
-from ...common.lib_util import get_app_root_dir, get_mod_input_script_name
-from splunktalib import file_monitor as fm
-from splunktalib import modinput
-from splunktalib import orphan_process_monitor as opm
-from splunktalib.common import util as utils
utils.remove_http_proxy_env_vars()
@@ -44,17 +43,22 @@
def do_scheme(
- mod_input_name,
- schema_para_list=None,
- single_instance=True,
+ mod_input_name,
+ schema_para_list=None,
+ single_instance=True,
):
"""
Feed splunkd the TA's scheme
"""
builtin_names = {
- "name", "index", "sourcetype", "host", "source",
- "disabled", "interval"
+ "name",
+ "index",
+ "sourcetype",
+ "host",
+ "source",
+ "disabled",
+ "interval",
}
param_string_list = []
@@ -72,13 +76,18 @@ def do_scheme(
0
0
- """.format(param=param)
+ """.format(
+ param=param
+ )
)
- description = ("Go to the add-on's configuration UI and configure"
- " modular inputs under the Inputs menu.")
+ description = (
+ "Go to the add-on's configuration UI and configure"
+ " modular inputs under the Inputs menu."
+ )
- print("""
+ print(
+ """
{data_input_title}
{description}
@@ -95,11 +104,12 @@ def do_scheme(
""".format(
- single_instance=(str(single_instance)).lower(),
- data_input_title=mod_input_name,
- param_str=''.join(param_string_list),
- description=description,
- ))
+ single_instance=(str(single_instance)).lower(),
+ data_input_title=mod_input_name,
+ param_str="".join(param_string_list),
+ description=description,
+ )
+ )
def _setup_signal_handler(data_loader, ta_short_name):
@@ -109,11 +119,11 @@ def _setup_signal_handler(data_loader, ta_short_name):
"""
def _handle_exit(signum, frame):
- stulog.logger.info("{} receives exit signal".format(ta_short_name))
+ stulog.logger.info(f"{ta_short_name} receives exit signal")
if data_loader is not None:
data_loader.tear_down()
- utils.handle_tear_down_signals(_handle_exit)
+ utils.handle_teardown_signals(_handle_exit)
def _handle_file_changes(data_loader):
@@ -122,8 +132,7 @@ def _handle_file_changes(data_loader):
"""
def _handle_refresh(changed_files):
- stulog.logger.info("Detect {} changed, reboot itself".format(
- changed_files))
+ stulog.logger.info(f"Detect {changed_files} changed, reboot itself")
data_loader.tear_down()
return _handle_refresh
@@ -142,18 +151,25 @@ def _get_conf_files(settings):
return [op.join(ta_dir, "local", f) for f in file_list]
-def run(collector_cls, settings, checkpoint_cls=None, config_cls=None,
- log_suffix=None, single_instance=True, cc_json_file=None):
+def run(
+ collector_cls,
+ settings,
+ checkpoint_cls=None,
+ config_cls=None,
+ log_suffix=None,
+ single_instance=True,
+ cc_json_file=None,
+):
"""
Main loop. Run this TA forever
"""
ta_short_name = settings["meta"]["name"].lower()
# This is for stdout flush
- utils.disable_stdout_buffer()
+ sc_util.disable_stdout_buffer()
# http://bugs.python.org/issue7980
- time.strptime('2016-01-01', '%Y-%m-%d')
+ time.strptime("2016-01-01", "%Y-%m-%d")
loader = dl.create_data_loader()
@@ -162,8 +178,9 @@ def run(collector_cls, settings, checkpoint_cls=None, config_cls=None,
# monitor files to reboot
try:
- monitor = fm.FileMonitor(_handle_file_changes(loader),
- _get_conf_files(settings))
+ monitor = fm.FileMonitor(
+ _handle_file_changes(loader), _get_conf_files(settings)
+ )
loader.add_timer(monitor.check_changes, time.time(), 10)
except Exception:
stulog.logger.exception("Fail to add files for monitoring")
@@ -172,8 +189,9 @@ def run(collector_cls, settings, checkpoint_cls=None, config_cls=None,
orphan_checker = opm.OrphanProcessChecker(loader.tear_down)
loader.add_timer(orphan_checker.check_orphan, time.time(), 1)
- tconfig = tc.create_ta_config(settings, config_cls or tc.TaConfig,
- log_suffix, single_instance=single_instance)
+ tconfig = tc.create_ta_config(
+ settings, config_cls or tc.TaConfig, log_suffix, single_instance=single_instance
+ )
task_configs = tconfig.get_task_configs()
if not task_configs:
@@ -184,17 +202,19 @@ def run(collector_cls, settings, checkpoint_cls=None, config_cls=None,
if tconfig.is_shc_member():
# Don't support SHC env
- stulog.logger.error("This host is in search head cluster environment , "
- "will exit.")
+ stulog.logger.error(
+ "This host is in search head cluster environment , " "will exit."
+ )
return
# In this case, use file for checkpoint
- if _is_checkpoint_dir_length_exceed_limit(tconfig,
- meta_config["checkpoint_dir"]):
- stulog.logger.error("The length of the checkpoint directory path: '{}' "
- "is too long. The max length we support is {}",
- meta_config["checkpoint_dir"],
- __CHECKPOINT_DIR_MAX_LEN__)
+ if _is_checkpoint_dir_length_exceed_limit(tconfig, meta_config["checkpoint_dir"]):
+ stulog.logger.error(
+ "The length of the checkpoint directory path: '{}' "
+ "is too long. The max length we support is {}",
+ meta_config["checkpoint_dir"],
+ __CHECKPOINT_DIR_MAX_LEN__,
+ )
return
jobs = [
@@ -204,18 +224,20 @@ def run(collector_cls, settings, checkpoint_cls=None, config_cls=None,
meta_config,
task_config,
collector_cls,
- checkpoint_cls=checkpoint_cls or cpmgr.TACheckPointMgr
+ checkpoint_cls=checkpoint_cls or cpmgr.TACheckPointMgr,
)
for task_config in task_configs
- ]
+ ]
loader.run(jobs)
def _is_checkpoint_dir_length_exceed_limit(config, checkpoint_dir):
- return platform.system() == 'Windows' \
- and not config.is_search_head() \
- and len(checkpoint_dir) >= __CHECKPOINT_DIR_MAX_LEN__
+ return (
+ platform.system() == "Windows"
+ and not config.is_search_head()
+ and len(checkpoint_dir) >= __CHECKPOINT_DIR_MAX_LEN__
+ )
def validate_config():
@@ -238,14 +260,14 @@ def usage():
def main(
- collector_cls,
- schema_file_path,
- log_suffix="modinput",
- checkpoint_cls=None,
- config_cls=None,
- cc_json_file=None,
- schema_para_list=None,
- single_instance=True
+ collector_cls,
+ schema_file_path,
+ log_suffix="modinput",
+ checkpoint_cls=None,
+ config_cls=None,
+ cc_json_file=None,
+ schema_para_list=None,
+ single_instance=True,
):
"""
Main entry point
@@ -263,7 +285,7 @@ def main(
do_scheme(
mod_input_name=mod_input_name,
schema_para_list=schema_para_list,
- single_instance=single_instance
+ single_instance=single_instance,
)
elif args[1] == "--validate-arguments":
sys.exit(validate_config())
@@ -280,10 +302,9 @@ def main(
config_cls=config_cls,
log_suffix=log_suffix,
single_instance=single_instance,
- cc_json_file=cc_json_file
+ cc_json_file=cc_json_file,
)
except Exception:
- stulog.logger.exception(
- "{} task encounter exception".format(mod_input_name))
- stulog.logger.info("End {} task".format(mod_input_name))
+ stulog.logger.exception(f"{mod_input_name} task encounter exception")
+ stulog.logger.info(f"End {mod_input_name} task")
sys.exit(0)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/splunk_ta_import_declare.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/splunk_ta_import_declare.py
index 789afce..9aa813c 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/splunk_ta_import_declare.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/splunk_ta_import_declare.py
@@ -18,15 +18,13 @@
"""
import os
-import sys
import re
+import sys
ta_name = os.path.basename(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-ta_lib_name = re.sub("[^\w]+", "_", ta_name.lower())
+ta_lib_name = re.sub(r"[^\w]+", "_", ta_name.lower())
assert ta_name or ta_name == "package", "TA name is None or package"
pattern = re.compile(r"[\\/]etc[\\/]apps[\\/][^\\/]+[\\/]bin[\\/]?$")
new_paths = [path for path in sys.path if not pattern.search(path) or ta_name in path]
new_paths.insert(0, os.path.sep.join([os.path.dirname(__file__), ta_lib_name]))
sys.path = new_paths
-
-
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/ta_cloud_connect_client.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/ta_cloud_connect_client.py
index 3bd67b9..42d2dcb 100755
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/ta_cloud_connect_client.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/cloudconnectlib/splunktacollectorlib/ta_cloud_connect_client.py
@@ -13,36 +13,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-from .data_collection.ta_data_client import TaDataClient
+from ..common.log import set_cc_logger
from ..splunktacollectorlib.common import log as stulog
from ..splunktacollectorlib.data_collection import ta_consts as c
-from ..common.log import set_cc_logger
+from .data_collection.ta_data_client import TaDataClient
class TACloudConnectClient(TaDataClient):
- def __init__(self,
- meta_config,
- task_config,
- checkpoint_mgr=None,
- event_writer=None
- ):
- super(TACloudConnectClient, self).__init__(meta_config,
- task_config,
- checkpoint_mgr,
- event_writer)
+ def __init__(
+ self, meta_config, task_config, checkpoint_mgr=None, event_writer=None
+ ):
+ super().__init__(meta_config, task_config, checkpoint_mgr, event_writer)
self._set_log()
self._cc_config_file = self._meta_config["cc_json_file"]
- from ..core.pipemgr import PipeManager
from ..client import CloudConnectClient as Client
+ from ..core.pipemgr import PipeManager
+
self._pipe_mgr = PipeManager(event_writer=event_writer)
- self._client = Client(self._task_config, self._cc_config_file,
- checkpoint_mgr)
+ self._client = Client(self._task_config, self._cc_config_file, checkpoint_mgr)
def _set_log(self):
- pairs = ['{}="{}"'.format(c.stanza_name, self._task_config[
- c.stanza_name])]
- set_cc_logger(stulog.logger,
- logger_prefix="[{}]".format(" ".join(pairs)))
+ pairs = [f'{c.stanza_name}="{self._task_config[c.stanza_name]}"']
+ set_cc_logger(stulog.logger, logger_prefix="[{}]".format(" ".join(pairs)))
def is_stopped(self):
return self._stop
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/__init__.py
deleted file mode 100644
index 8b240db..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/__init__.py
+++ /dev/null
@@ -1,1783 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Small, fast HTTP client library for Python."""
-
-__author__ = "Joe Gregorio (joe@bitworking.org)"
-__copyright__ = "Copyright 2006, Joe Gregorio"
-__contributors__ = [
- "Thomas Broyer (t.broyer@ltgt.net)",
- "James Antill",
- "Xavier Verges Farrero",
- "Jonathan Feinberg",
- "Blair Zajac",
- "Sam Ruby",
- "Louis Nyffenegger",
- "Mark Pilgrim",
- "Alex Yu",
-]
-__license__ = "MIT"
-__version__ = "0.19.1"
-
-import base64
-import calendar
-import copy
-import email
-import email.feedparser
-from email import header
-import email.message
-import email.utils
-import errno
-from gettext import gettext as _
-import gzip
-from hashlib import md5 as _md5
-from hashlib import sha1 as _sha
-import hmac
-import http.client
-import io
-import os
-import random
-import re
-import socket
-import ssl
-import sys
-import time
-import urllib.parse
-import zlib
-
-try:
- import socks
-except ImportError:
- # TODO: remove this fallback and copypasted socksipy module upon py2/3 merge,
- # idea is to have soft-dependency on any compatible module called socks
- from . import socks
-from . import auth
-from .error import *
-from .iri2uri import iri2uri
-
-
-def has_timeout(timeout):
- if hasattr(socket, "_GLOBAL_DEFAULT_TIMEOUT"):
- return timeout is not None and timeout is not socket._GLOBAL_DEFAULT_TIMEOUT
- return timeout is not None
-
-
-__all__ = [
- "debuglevel",
- "FailedToDecompressContent",
- "Http",
- "HttpLib2Error",
- "ProxyInfo",
- "RedirectLimit",
- "RedirectMissingLocation",
- "Response",
- "RETRIES",
- "UnimplementedDigestAuthOptionError",
- "UnimplementedHmacDigestAuthOptionError",
-]
-
-# The httplib debug level, set to a non-zero value to get debug output
-debuglevel = 0
-
-# A request will be tried 'RETRIES' times if it fails at the socket/connection level.
-RETRIES = 2
-
-
-# Open Items:
-# -----------
-
-# Are we removing the cached content too soon on PUT (only delete on 200 Maybe?)
-
-# Pluggable cache storage (supports storing the cache in
-# flat files by default. We need a plug-in architecture
-# that can support Berkeley DB and Squid)
-
-# == Known Issues ==
-# Does not handle a resource that uses conneg and Last-Modified but no ETag as a cache validator.
-# Does not handle Cache-Control: max-stale
-# Does not use Age: headers when calculating cache freshness.
-
-# The number of redirections to follow before giving up.
-# Note that only GET redirects are automatically followed.
-# Will also honor 301 requests by saving that info and never
-# requesting that URI again.
-DEFAULT_MAX_REDIRECTS = 5
-
-# Which headers are hop-by-hop headers by default
-HOP_BY_HOP = [
- "connection",
- "keep-alive",
- "proxy-authenticate",
- "proxy-authorization",
- "te",
- "trailers",
- "transfer-encoding",
- "upgrade",
-]
-
-# https://tools.ietf.org/html/rfc7231#section-8.1.3
-SAFE_METHODS = ("GET", "HEAD", "OPTIONS", "TRACE")
-
-# To change, assign to `Http().redirect_codes`
-REDIRECT_CODES = frozenset((300, 301, 302, 303, 307, 308))
-
-
-from httplib2 import certs
-
-CA_CERTS = certs.where()
-
-# PROTOCOL_TLS is python 3.5.3+. PROTOCOL_SSLv23 is deprecated.
-# Both PROTOCOL_TLS and PROTOCOL_SSLv23 are equivalent and means:
-# > Selects the highest protocol version that both the client and server support.
-# > Despite the name, this option can select “TLS” protocols as well as “SSL”.
-# source: https://docs.python.org/3.5/library/ssl.html#ssl.PROTOCOL_TLS
-DEFAULT_TLS_VERSION = getattr(ssl, "PROTOCOL_TLS", None) or getattr(ssl, "PROTOCOL_SSLv23")
-
-
-def _build_ssl_context(
- disable_ssl_certificate_validation,
- ca_certs,
- cert_file=None,
- key_file=None,
- maximum_version=None,
- minimum_version=None,
- key_password=None,
-):
- if not hasattr(ssl, "SSLContext"):
- raise RuntimeError("httplib2 requires Python 3.2+ for ssl.SSLContext")
-
- context = ssl.SSLContext(DEFAULT_TLS_VERSION)
- context.verify_mode = ssl.CERT_NONE if disable_ssl_certificate_validation else ssl.CERT_REQUIRED
-
- # SSLContext.maximum_version and SSLContext.minimum_version are python 3.7+.
- # source: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.maximum_version
- if maximum_version is not None:
- if hasattr(context, "maximum_version"):
- context.maximum_version = getattr(ssl.TLSVersion, maximum_version)
- else:
- raise RuntimeError("setting tls_maximum_version requires Python 3.7 and OpenSSL 1.1 or newer")
- if minimum_version is not None:
- if hasattr(context, "minimum_version"):
- context.minimum_version = getattr(ssl.TLSVersion, minimum_version)
- else:
- raise RuntimeError("setting tls_minimum_version requires Python 3.7 and OpenSSL 1.1 or newer")
-
- # check_hostname requires python 3.4+
- # we will perform the equivalent in HTTPSConnectionWithTimeout.connect() by calling ssl.match_hostname
- # if check_hostname is not supported.
- if hasattr(context, "check_hostname"):
- context.check_hostname = not disable_ssl_certificate_validation
-
- context.load_verify_locations(ca_certs)
-
- if cert_file:
- context.load_cert_chain(cert_file, key_file, key_password)
-
- return context
-
-
-def _get_end2end_headers(response):
- hopbyhop = list(HOP_BY_HOP)
- hopbyhop.extend([x.strip() for x in response.get("connection", "").split(",")])
- return [header for header in list(response.keys()) if header not in hopbyhop]
-
-
-URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?")
-
-
-def parse_uri(uri):
- """Parses a URI using the regex given in Appendix B of RFC 3986.
-
- (scheme, authority, path, query, fragment) = parse_uri(uri)
- """
- groups = URI.match(uri).groups()
- return (groups[1], groups[3], groups[4], groups[6], groups[8])
-
-
-def urlnorm(uri):
- (scheme, authority, path, query, fragment) = parse_uri(uri)
- if not scheme or not authority:
- raise RelativeURIError("Only absolute URIs are allowed. uri = %s" % uri)
- authority = authority.lower()
- scheme = scheme.lower()
- if not path:
- path = "/"
- # Could do syntax based normalization of the URI before
- # computing the digest. See Section 6.2.2 of Std 66.
- request_uri = query and "?".join([path, query]) or path
- scheme = scheme.lower()
- defrag_uri = scheme + "://" + authority + request_uri
- return scheme, authority, request_uri, defrag_uri
-
-
-# Cache filename construction (original borrowed from Venus http://intertwingly.net/code/venus/)
-re_url_scheme = re.compile(r"^\w+://")
-re_unsafe = re.compile(r"[^\w\-_.()=!]+", re.ASCII)
-
-
-def safename(filename):
- """Return a filename suitable for the cache.
- Strips dangerous and common characters to create a filename we
- can use to store the cache in.
- """
- if isinstance(filename, bytes):
- filename_bytes = filename
- filename = filename.decode("utf-8")
- else:
- filename_bytes = filename.encode("utf-8")
- filemd5 = _md5(filename_bytes).hexdigest()
- filename = re_url_scheme.sub("", filename)
- filename = re_unsafe.sub("", filename)
-
- # limit length of filename (vital for Windows)
- # https://github.com/httplib2/httplib2/pull/74
- # C:\Users\ \AppData\Local\Temp\ ,
- # 9 chars + max 104 chars + 20 chars + x + 1 + 32 = max 259 chars
- # Thus max safe filename x = 93 chars. Let it be 90 to make a round sum:
- filename = filename[:90]
-
- return ",".join((filename, filemd5))
-
-
-NORMALIZE_SPACE = re.compile(r"(?:\r\n)?[ \t]+")
-
-
-def _normalize_headers(headers):
- return dict(
- [
- (_convert_byte_str(key).lower(), NORMALIZE_SPACE.sub(_convert_byte_str(value), " ").strip(),)
- for (key, value) in headers.items()
- ]
- )
-
-
-def _convert_byte_str(s):
- if not isinstance(s, str):
- return str(s, "utf-8")
- return s
-
-
-def _parse_cache_control(headers):
- retval = {}
- if "cache-control" in headers:
- parts = headers["cache-control"].split(",")
- parts_with_args = [
- tuple([x.strip().lower() for x in part.split("=", 1)]) for part in parts if -1 != part.find("=")
- ]
- parts_wo_args = [(name.strip().lower(), 1) for name in parts if -1 == name.find("=")]
- retval = dict(parts_with_args + parts_wo_args)
- return retval
-
-
-# Whether to use a strict mode to parse WWW-Authenticate headers
-# Might lead to bad results in case of ill-formed header value,
-# so disabled by default, falling back to relaxed parsing.
-# Set to true to turn on, useful for testing servers.
-USE_WWW_AUTH_STRICT_PARSING = 0
-
-
-def _entry_disposition(response_headers, request_headers):
- """Determine freshness from the Date, Expires and Cache-Control headers.
-
- We don't handle the following:
-
- 1. Cache-Control: max-stale
- 2. Age: headers are not used in the calculations.
-
- Not that this algorithm is simpler than you might think
- because we are operating as a private (non-shared) cache.
- This lets us ignore 's-maxage'. We can also ignore
- 'proxy-invalidate' since we aren't a proxy.
- We will never return a stale document as
- fresh as a design decision, and thus the non-implementation
- of 'max-stale'. This also lets us safely ignore 'must-revalidate'
- since we operate as if every server has sent 'must-revalidate'.
- Since we are private we get to ignore both 'public' and
- 'private' parameters. We also ignore 'no-transform' since
- we don't do any transformations.
- The 'no-store' parameter is handled at a higher level.
- So the only Cache-Control parameters we look at are:
-
- no-cache
- only-if-cached
- max-age
- min-fresh
- """
-
- retval = "STALE"
- cc = _parse_cache_control(request_headers)
- cc_response = _parse_cache_control(response_headers)
-
- if "pragma" in request_headers and request_headers["pragma"].lower().find("no-cache") != -1:
- retval = "TRANSPARENT"
- if "cache-control" not in request_headers:
- request_headers["cache-control"] = "no-cache"
- elif "no-cache" in cc:
- retval = "TRANSPARENT"
- elif "no-cache" in cc_response:
- retval = "STALE"
- elif "only-if-cached" in cc:
- retval = "FRESH"
- elif "date" in response_headers:
- date = calendar.timegm(email.utils.parsedate_tz(response_headers["date"]))
- now = time.time()
- current_age = max(0, now - date)
- if "max-age" in cc_response:
- try:
- freshness_lifetime = int(cc_response["max-age"])
- except ValueError:
- freshness_lifetime = 0
- elif "expires" in response_headers:
- expires = email.utils.parsedate_tz(response_headers["expires"])
- if None == expires:
- freshness_lifetime = 0
- else:
- freshness_lifetime = max(0, calendar.timegm(expires) - date)
- else:
- freshness_lifetime = 0
- if "max-age" in cc:
- try:
- freshness_lifetime = int(cc["max-age"])
- except ValueError:
- freshness_lifetime = 0
- if "min-fresh" in cc:
- try:
- min_fresh = int(cc["min-fresh"])
- except ValueError:
- min_fresh = 0
- current_age += min_fresh
- if freshness_lifetime > current_age:
- retval = "FRESH"
- return retval
-
-
-def _decompressContent(response, new_content):
- content = new_content
- try:
- encoding = response.get("content-encoding", None)
- if encoding in ["gzip", "deflate"]:
- if encoding == "gzip":
- content = gzip.GzipFile(fileobj=io.BytesIO(new_content)).read()
- if encoding == "deflate":
- content = zlib.decompress(content, -zlib.MAX_WBITS)
- response["content-length"] = str(len(content))
- # Record the historical presence of the encoding in a way the won't interfere.
- response["-content-encoding"] = response["content-encoding"]
- del response["content-encoding"]
- except (IOError, zlib.error):
- content = ""
- raise FailedToDecompressContent(
- _("Content purported to be compressed with %s but failed to decompress.") % response.get("content-encoding"),
- response,
- content,
- )
- return content
-
-
-def _bind_write_headers(msg):
- def _write_headers(self):
- # Self refers to the Generator object.
- for h, v in msg.items():
- print("%s:" % h, end=" ", file=self._fp)
- if isinstance(v, header.Header):
- print(v.encode(maxlinelen=self._maxheaderlen), file=self._fp)
- else:
- # email.Header got lots of smarts, so use it.
- headers = header.Header(v, maxlinelen=self._maxheaderlen, charset="utf-8", header_name=h)
- print(headers.encode(), file=self._fp)
- # A blank line always separates headers from body.
- print(file=self._fp)
-
- return _write_headers
-
-
-def _updateCache(request_headers, response_headers, content, cache, cachekey):
- if cachekey:
- cc = _parse_cache_control(request_headers)
- cc_response = _parse_cache_control(response_headers)
- if "no-store" in cc or "no-store" in cc_response:
- cache.delete(cachekey)
- else:
- info = email.message.Message()
- for key, value in response_headers.items():
- if key not in ["status", "content-encoding", "transfer-encoding"]:
- info[key] = value
-
- # Add annotations to the cache to indicate what headers
- # are variant for this request.
- vary = response_headers.get("vary", None)
- if vary:
- vary_headers = vary.lower().replace(" ", "").split(",")
- for header in vary_headers:
- key = "-varied-%s" % header
- try:
- info[key] = request_headers[header]
- except KeyError:
- pass
-
- status = response_headers.status
- if status == 304:
- status = 200
-
- status_header = "status: %d\r\n" % status
-
- try:
- header_str = info.as_string()
- except UnicodeEncodeError:
- setattr(info, "_write_headers", _bind_write_headers(info))
- header_str = info.as_string()
-
- header_str = re.sub("\r(?!\n)|(? 0:
- service = "cl"
- # No point in guessing Base or Spreadsheet
- # elif request_uri.find("spreadsheets") > 0:
- # service = "wise"
-
- auth = dict(Email=credentials[0], Passwd=credentials[1], service=service, source=headers["user-agent"],)
- resp, content = self.http.request(
- "https://www.google.com/accounts/ClientLogin",
- method="POST",
- body=urlencode(auth),
- headers={"Content-Type": "application/x-www-form-urlencoded"},
- )
- lines = content.split("\n")
- d = dict([tuple(line.split("=", 1)) for line in lines if line])
- if resp.status == 403:
- self.Auth = ""
- else:
- self.Auth = d["Auth"]
-
- def request(self, method, request_uri, headers, content):
- """Modify the request headers to add the appropriate
- Authorization header."""
- headers["authorization"] = "GoogleLogin Auth=" + self.Auth
-
-
-AUTH_SCHEME_CLASSES = {
- "basic": BasicAuthentication,
- "wsse": WsseAuthentication,
- "digest": DigestAuthentication,
- "hmacdigest": HmacDigestAuthentication,
- "googlelogin": GoogleLoginAuthentication,
-}
-
-AUTH_SCHEME_ORDER = ["hmacdigest", "googlelogin", "digest", "wsse", "basic"]
-
-
-class FileCache(object):
- """Uses a local directory as a store for cached files.
- Not really safe to use if multiple threads or processes are going to
- be running on the same cache.
- """
-
- def __init__(self, cache, safe=safename): # use safe=lambda x: md5.new(x).hexdigest() for the old behavior
- self.cache = cache
- self.safe = safe
- if not os.path.exists(cache):
- os.makedirs(self.cache)
-
- def get(self, key):
- retval = None
- cacheFullPath = os.path.join(self.cache, self.safe(key))
- try:
- f = open(cacheFullPath, "rb")
- retval = f.read()
- f.close()
- except IOError:
- pass
- return retval
-
- def set(self, key, value):
- cacheFullPath = os.path.join(self.cache, self.safe(key))
- f = open(cacheFullPath, "wb")
- f.write(value)
- f.close()
-
- def delete(self, key):
- cacheFullPath = os.path.join(self.cache, self.safe(key))
- if os.path.exists(cacheFullPath):
- os.remove(cacheFullPath)
-
-
-class Credentials(object):
- def __init__(self):
- self.credentials = []
-
- def add(self, name, password, domain=""):
- self.credentials.append((domain.lower(), name, password))
-
- def clear(self):
- self.credentials = []
-
- def iter(self, domain):
- for (cdomain, name, password) in self.credentials:
- if cdomain == "" or domain == cdomain:
- yield (name, password)
-
-
-class KeyCerts(Credentials):
- """Identical to Credentials except that
- name/password are mapped to key/cert."""
-
- def add(self, key, cert, domain, password):
- self.credentials.append((domain.lower(), key, cert, password))
-
- def iter(self, domain):
- for (cdomain, key, cert, password) in self.credentials:
- if cdomain == "" or domain == cdomain:
- yield (key, cert, password)
-
-
-class AllHosts(object):
- pass
-
-
-class ProxyInfo(object):
- """Collect information required to use a proxy."""
-
- bypass_hosts = ()
-
- def __init__(
- self, proxy_type, proxy_host, proxy_port, proxy_rdns=True, proxy_user=None, proxy_pass=None, proxy_headers=None,
- ):
- """Args:
-
- proxy_type: The type of proxy server. This must be set to one of
- socks.PROXY_TYPE_XXX constants. For example: p =
- ProxyInfo(proxy_type=socks.PROXY_TYPE_HTTP, proxy_host='localhost',
- proxy_port=8000)
- proxy_host: The hostname or IP address of the proxy server.
- proxy_port: The port that the proxy server is running on.
- proxy_rdns: If True (default), DNS queries will not be performed
- locally, and instead, handed to the proxy to resolve. This is useful
- if the network does not allow resolution of non-local names. In
- httplib2 0.9 and earlier, this defaulted to False.
- proxy_user: The username used to authenticate with the proxy server.
- proxy_pass: The password used to authenticate with the proxy server.
- proxy_headers: Additional or modified headers for the proxy connect
- request.
- """
- if isinstance(proxy_user, bytes):
- proxy_user = proxy_user.decode()
- if isinstance(proxy_pass, bytes):
- proxy_pass = proxy_pass.decode()
- (
- self.proxy_type,
- self.proxy_host,
- self.proxy_port,
- self.proxy_rdns,
- self.proxy_user,
- self.proxy_pass,
- self.proxy_headers,
- ) = (
- proxy_type,
- proxy_host,
- proxy_port,
- proxy_rdns,
- proxy_user,
- proxy_pass,
- proxy_headers,
- )
-
- def astuple(self):
- return (
- self.proxy_type,
- self.proxy_host,
- self.proxy_port,
- self.proxy_rdns,
- self.proxy_user,
- self.proxy_pass,
- self.proxy_headers,
- )
-
- def isgood(self):
- return socks and (self.proxy_host != None) and (self.proxy_port != None)
-
- def applies_to(self, hostname):
- return not self.bypass_host(hostname)
-
- def bypass_host(self, hostname):
- """Has this host been excluded from the proxy config"""
- if self.bypass_hosts is AllHosts:
- return True
-
- hostname = "." + hostname.lstrip(".")
- for skip_name in self.bypass_hosts:
- # *.suffix
- if skip_name.startswith(".") and hostname.endswith(skip_name):
- return True
- # exact match
- if hostname == "." + skip_name:
- return True
- return False
-
- def __repr__(self):
- return (
- ""
- ).format(p=self)
-
-
-def proxy_info_from_environment(method="http"):
- """Read proxy info from the environment variables.
- """
- if method not in ("http", "https"):
- return
-
- env_var = method + "_proxy"
- url = os.environ.get(env_var, os.environ.get(env_var.upper()))
- if not url:
- return
- return proxy_info_from_url(url, method, noproxy=None)
-
-
-def proxy_info_from_url(url, method="http", noproxy=None):
- """Construct a ProxyInfo from a URL (such as http_proxy env var)
- """
- url = urllib.parse.urlparse(url)
- username = None
- password = None
- port = None
- if "@" in url[1]:
- ident, host_port = url[1].split("@", 1)
- if ":" in ident:
- username, password = ident.split(":", 1)
- else:
- password = ident
- else:
- host_port = url[1]
- if ":" in host_port:
- host, port = host_port.split(":", 1)
- else:
- host = host_port
-
- if port:
- port = int(port)
- else:
- port = dict(https=443, http=80)[method]
-
- proxy_type = 3 # socks.PROXY_TYPE_HTTP
- pi = ProxyInfo(
- proxy_type=proxy_type,
- proxy_host=host,
- proxy_port=port,
- proxy_user=username or None,
- proxy_pass=password or None,
- proxy_headers=None,
- )
-
- bypass_hosts = []
- # If not given an explicit noproxy value, respect values in env vars.
- if noproxy is None:
- noproxy = os.environ.get("no_proxy", os.environ.get("NO_PROXY", ""))
- # Special case: A single '*' character means all hosts should be bypassed.
- if noproxy == "*":
- bypass_hosts = AllHosts
- elif noproxy.strip():
- bypass_hosts = noproxy.split(",")
- bypass_hosts = tuple(filter(bool, bypass_hosts)) # To exclude empty string.
-
- pi.bypass_hosts = bypass_hosts
- return pi
-
-
-class HTTPConnectionWithTimeout(http.client.HTTPConnection):
- """HTTPConnection subclass that supports timeouts
-
- HTTPConnection subclass that supports timeouts
-
- All timeouts are in seconds. If None is passed for timeout then
- Python's default timeout for sockets will be used. See for example
- the docs of socket.setdefaulttimeout():
- http://docs.python.org/library/socket.html#socket.setdefaulttimeout
- """
-
- def __init__(self, host, port=None, timeout=None, proxy_info=None):
- http.client.HTTPConnection.__init__(self, host, port=port, timeout=timeout)
-
- self.proxy_info = proxy_info
- if proxy_info and not isinstance(proxy_info, ProxyInfo):
- self.proxy_info = proxy_info("http")
-
- def connect(self):
- """Connect to the host and port specified in __init__."""
- if self.proxy_info and socks is None:
- raise ProxiesUnavailableError("Proxy support missing but proxy use was requested!")
- if self.proxy_info and self.proxy_info.isgood() and self.proxy_info.applies_to(self.host):
- use_proxy = True
- (
- proxy_type,
- proxy_host,
- proxy_port,
- proxy_rdns,
- proxy_user,
- proxy_pass,
- proxy_headers,
- ) = self.proxy_info.astuple()
-
- host = proxy_host
- port = proxy_port
- else:
- use_proxy = False
-
- host = self.host
- port = self.port
- proxy_type = None
-
- socket_err = None
-
- for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
- af, socktype, proto, canonname, sa = res
- try:
- if use_proxy:
- self.sock = socks.socksocket(af, socktype, proto)
- self.sock.setproxy(
- proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass,
- )
- else:
- self.sock = socket.socket(af, socktype, proto)
- self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- if has_timeout(self.timeout):
- self.sock.settimeout(self.timeout)
- if self.debuglevel > 0:
- print("connect: ({0}, {1}) ************".format(self.host, self.port))
- if use_proxy:
- print(
- "proxy: {0} ************".format(
- str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,))
- )
- )
-
- self.sock.connect((self.host, self.port) + sa[2:])
- except socket.error as e:
- socket_err = e
- if self.debuglevel > 0:
- print("connect fail: ({0}, {1})".format(self.host, self.port))
- if use_proxy:
- print(
- "proxy: {0}".format(
- str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,))
- )
- )
- if self.sock:
- self.sock.close()
- self.sock = None
- continue
- break
- if not self.sock:
- raise socket_err
-
-
-class HTTPSConnectionWithTimeout(http.client.HTTPSConnection):
- """This class allows communication via SSL.
-
- All timeouts are in seconds. If None is passed for timeout then
- Python's default timeout for sockets will be used. See for example
- the docs of socket.setdefaulttimeout():
- http://docs.python.org/library/socket.html#socket.setdefaulttimeout
- """
-
- def __init__(
- self,
- host,
- port=None,
- key_file=None,
- cert_file=None,
- timeout=None,
- proxy_info=None,
- ca_certs=None,
- disable_ssl_certificate_validation=False,
- tls_maximum_version=None,
- tls_minimum_version=None,
- key_password=None,
- ):
-
- self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
- self.ca_certs = ca_certs if ca_certs else CA_CERTS
-
- self.proxy_info = proxy_info
- if proxy_info and not isinstance(proxy_info, ProxyInfo):
- self.proxy_info = proxy_info("https")
-
- context = _build_ssl_context(
- self.disable_ssl_certificate_validation,
- self.ca_certs,
- cert_file,
- key_file,
- maximum_version=tls_maximum_version,
- minimum_version=tls_minimum_version,
- key_password=key_password,
- )
- super(HTTPSConnectionWithTimeout, self).__init__(
- host, port=port, timeout=timeout, context=context,
- )
- self.key_file = key_file
- self.cert_file = cert_file
- self.key_password = key_password
-
- def connect(self):
- """Connect to a host on a given (SSL) port."""
- if self.proxy_info and self.proxy_info.isgood() and self.proxy_info.applies_to(self.host):
- use_proxy = True
- (
- proxy_type,
- proxy_host,
- proxy_port,
- proxy_rdns,
- proxy_user,
- proxy_pass,
- proxy_headers,
- ) = self.proxy_info.astuple()
-
- host = proxy_host
- port = proxy_port
- else:
- use_proxy = False
-
- host = self.host
- port = self.port
- proxy_type = None
- proxy_headers = None
-
- socket_err = None
-
- address_info = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
- for family, socktype, proto, canonname, sockaddr in address_info:
- try:
- if use_proxy:
- sock = socks.socksocket(family, socktype, proto)
-
- sock.setproxy(
- proxy_type, proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass,
- )
- else:
- sock = socket.socket(family, socktype, proto)
- sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- if has_timeout(self.timeout):
- sock.settimeout(self.timeout)
- sock.connect((self.host, self.port))
-
- self.sock = self._context.wrap_socket(sock, server_hostname=self.host)
-
- # Python 3.3 compatibility: emulate the check_hostname behavior
- if not hasattr(self._context, "check_hostname") and not self.disable_ssl_certificate_validation:
- try:
- ssl.match_hostname(self.sock.getpeercert(), self.host)
- except Exception:
- self.sock.shutdown(socket.SHUT_RDWR)
- self.sock.close()
- raise
-
- if self.debuglevel > 0:
- print("connect: ({0}, {1})".format(self.host, self.port))
- if use_proxy:
- print(
- "proxy: {0}".format(
- str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,))
- )
- )
- except (ssl.SSLError, ssl.CertificateError) as e:
- if sock:
- sock.close()
- if self.sock:
- self.sock.close()
- self.sock = None
- raise
- except (socket.timeout, socket.gaierror):
- raise
- except socket.error as e:
- socket_err = e
- if self.debuglevel > 0:
- print("connect fail: ({0}, {1})".format(self.host, self.port))
- if use_proxy:
- print(
- "proxy: {0}".format(
- str((proxy_host, proxy_port, proxy_rdns, proxy_user, proxy_pass, proxy_headers,))
- )
- )
- if self.sock:
- self.sock.close()
- self.sock = None
- continue
- break
- if not self.sock:
- raise socket_err
-
-
-SCHEME_TO_CONNECTION = {
- "http": HTTPConnectionWithTimeout,
- "https": HTTPSConnectionWithTimeout,
-}
-
-
-class Http(object):
- """An HTTP client that handles:
-
- - all methods
- - caching
- - ETags
- - compression,
- - HTTPS
- - Basic
- - Digest
- - WSSE
-
- and more.
- """
-
- def __init__(
- self,
- cache=None,
- timeout=None,
- proxy_info=proxy_info_from_environment,
- ca_certs=None,
- disable_ssl_certificate_validation=False,
- tls_maximum_version=None,
- tls_minimum_version=None,
- ):
- """If 'cache' is a string then it is used as a directory name for
- a disk cache. Otherwise it must be an object that supports the
- same interface as FileCache.
-
- All timeouts are in seconds. If None is passed for timeout
- then Python's default timeout for sockets will be used. See
- for example the docs of socket.setdefaulttimeout():
- http://docs.python.org/library/socket.html#socket.setdefaulttimeout
-
- `proxy_info` may be:
- - a callable that takes the http scheme ('http' or 'https') and
- returns a ProxyInfo instance per request. By default, uses
- proxy_info_from_environment.
- - a ProxyInfo instance (static proxy config).
- - None (proxy disabled).
-
- ca_certs is the path of a file containing root CA certificates for SSL
- server certificate validation. By default, a CA cert file bundled with
- httplib2 is used.
-
- If disable_ssl_certificate_validation is true, SSL cert validation will
- not be performed.
-
- tls_maximum_version / tls_minimum_version require Python 3.7+ /
- OpenSSL 1.1.0g+. A value of "TLSv1_3" requires OpenSSL 1.1.1+.
-"""
- self.proxy_info = proxy_info
- self.ca_certs = ca_certs
- self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
- self.tls_maximum_version = tls_maximum_version
- self.tls_minimum_version = tls_minimum_version
- # Map domain name to an httplib connection
- self.connections = {}
- # The location of the cache, for now a directory
- # where cached responses are held.
- if cache and isinstance(cache, str):
- self.cache = FileCache(cache)
- else:
- self.cache = cache
-
- # Name/password
- self.credentials = Credentials()
-
- # Key/cert
- self.certificates = KeyCerts()
-
- # authorization objects
- self.authorizations = []
-
- # If set to False then no redirects are followed, even safe ones.
- self.follow_redirects = True
-
- self.redirect_codes = REDIRECT_CODES
-
- # Which HTTP methods do we apply optimistic concurrency to, i.e.
- # which methods get an "if-match:" etag header added to them.
- self.optimistic_concurrency_methods = ["PUT", "PATCH"]
-
- self.safe_methods = list(SAFE_METHODS)
-
- # If 'follow_redirects' is True, and this is set to True then
- # all redirecs are followed, including unsafe ones.
- self.follow_all_redirects = False
-
- self.ignore_etag = False
-
- self.force_exception_to_status_code = False
-
- self.timeout = timeout
-
- # Keep Authorization: headers on a redirect.
- self.forward_authorization_headers = False
-
- def close(self):
- """Close persistent connections, clear sensitive data.
- Not thread-safe, requires external synchronization against concurrent requests.
- """
- existing, self.connections = self.connections, {}
- for _, c in existing.items():
- c.close()
- self.certificates.clear()
- self.clear_credentials()
-
- def __getstate__(self):
- state_dict = copy.copy(self.__dict__)
- # In case request is augmented by some foreign object such as
- # credentials which handle auth
- if "request" in state_dict:
- del state_dict["request"]
- if "connections" in state_dict:
- del state_dict["connections"]
- return state_dict
-
- def __setstate__(self, state):
- self.__dict__.update(state)
- self.connections = {}
-
- def _auth_from_challenge(self, host, request_uri, headers, response, content):
- """A generator that creates Authorization objects
- that can be applied to requests.
- """
- challenges = auth._parse_www_authenticate(response, "www-authenticate")
- for cred in self.credentials.iter(host):
- for scheme in AUTH_SCHEME_ORDER:
- if scheme in challenges:
- yield AUTH_SCHEME_CLASSES[scheme](cred, host, request_uri, headers, response, content, self)
-
- def add_credentials(self, name, password, domain=""):
- """Add a name and password that will be used
- any time a request requires authentication."""
- self.credentials.add(name, password, domain)
-
- def add_certificate(self, key, cert, domain, password=None):
- """Add a key and cert that will be used
- any time a request requires authentication."""
- self.certificates.add(key, cert, domain, password)
-
- def clear_credentials(self):
- """Remove all the names and passwords
- that are used for authentication"""
- self.credentials.clear()
- self.authorizations = []
-
- def _conn_request(self, conn, request_uri, method, body, headers):
- i = 0
- seen_bad_status_line = False
- while i < RETRIES:
- i += 1
- try:
- if conn.sock is None:
- conn.connect()
- conn.request(method, request_uri, body, headers)
- except socket.timeout:
- conn.close()
- raise
- except socket.gaierror:
- conn.close()
- raise ServerNotFoundError("Unable to find the server at %s" % conn.host)
- except socket.error as e:
- errno_ = e.args[0].errno if isinstance(e.args[0], socket.error) else e.errno
- if errno_ in (errno.ENETUNREACH, errno.EADDRNOTAVAIL) and i < RETRIES:
- continue # retry on potentially transient errors
- raise
- except http.client.HTTPException:
- if conn.sock is None:
- if i < RETRIES - 1:
- conn.close()
- conn.connect()
- continue
- else:
- conn.close()
- raise
- if i < RETRIES - 1:
- conn.close()
- conn.connect()
- continue
- # Just because the server closed the connection doesn't apparently mean
- # that the server didn't send a response.
- pass
- try:
- response = conn.getresponse()
- except (http.client.BadStatusLine, http.client.ResponseNotReady):
- # If we get a BadStatusLine on the first try then that means
- # the connection just went stale, so retry regardless of the
- # number of RETRIES set.
- if not seen_bad_status_line and i == 1:
- i = 0
- seen_bad_status_line = True
- conn.close()
- conn.connect()
- continue
- else:
- conn.close()
- raise
- except socket.timeout:
- raise
- except (socket.error, http.client.HTTPException):
- conn.close()
- if i == 0:
- conn.close()
- conn.connect()
- continue
- else:
- raise
- else:
- content = b""
- if method == "HEAD":
- conn.close()
- else:
- content = response.read()
- response = Response(response)
- if method != "HEAD":
- content = _decompressContent(response, content)
-
- break
- return (response, content)
-
- def _request(
- self, conn, host, absolute_uri, request_uri, method, body, headers, redirections, cachekey,
- ):
- """Do the actual request using the connection object
- and also follow one level of redirects if necessary"""
-
- auths = [(auth.depth(request_uri), auth) for auth in self.authorizations if auth.inscope(host, request_uri)]
- auth = auths and sorted(auths)[0][1] or None
- if auth:
- auth.request(method, request_uri, headers, body)
-
- (response, content) = self._conn_request(conn, request_uri, method, body, headers)
-
- if auth:
- if auth.response(response, body):
- auth.request(method, request_uri, headers, body)
- (response, content) = self._conn_request(conn, request_uri, method, body, headers)
- response._stale_digest = 1
-
- if response.status == 401:
- for authorization in self._auth_from_challenge(host, request_uri, headers, response, content):
- authorization.request(method, request_uri, headers, body)
- (response, content) = self._conn_request(conn, request_uri, method, body, headers)
- if response.status != 401:
- self.authorizations.append(authorization)
- authorization.response(response, body)
- break
-
- if self.follow_all_redirects or method in self.safe_methods or response.status in (303, 308):
- if self.follow_redirects and response.status in self.redirect_codes:
- # Pick out the location header and basically start from the beginning
- # remembering first to strip the ETag header and decrement our 'depth'
- if redirections:
- if "location" not in response and response.status != 300:
- raise RedirectMissingLocation(
- _("Redirected but the response is missing a Location: header."), response, content,
- )
- # Fix-up relative redirects (which violate an RFC 2616 MUST)
- if "location" in response:
- location = response["location"]
- (scheme, authority, path, query, fragment) = parse_uri(location)
- if authority == None:
- response["location"] = urllib.parse.urljoin(absolute_uri, location)
- if response.status == 308 or (response.status == 301 and (method in self.safe_methods)):
- response["-x-permanent-redirect-url"] = response["location"]
- if "content-location" not in response:
- response["content-location"] = absolute_uri
- _updateCache(headers, response, content, self.cache, cachekey)
- if "if-none-match" in headers:
- del headers["if-none-match"]
- if "if-modified-since" in headers:
- del headers["if-modified-since"]
- if "authorization" in headers and not self.forward_authorization_headers:
- del headers["authorization"]
- if "location" in response:
- location = response["location"]
- old_response = copy.deepcopy(response)
- if "content-location" not in old_response:
- old_response["content-location"] = absolute_uri
- redirect_method = method
- if response.status in [302, 303]:
- redirect_method = "GET"
- body = None
- (response, content) = self.request(
- location, method=redirect_method, body=body, headers=headers, redirections=redirections - 1,
- )
- response.previous = old_response
- else:
- raise RedirectLimit(
- "Redirected more times than redirection_limit allows.", response, content,
- )
- elif response.status in [200, 203] and method in self.safe_methods:
- # Don't cache 206's since we aren't going to handle byte range requests
- if "content-location" not in response:
- response["content-location"] = absolute_uri
- _updateCache(headers, response, content, self.cache, cachekey)
-
- return (response, content)
-
- def _normalize_headers(self, headers):
- return _normalize_headers(headers)
-
- # Need to catch and rebrand some exceptions
- # Then need to optionally turn all exceptions into status codes
- # including all socket.* and httplib.* exceptions.
-
- def request(
- self, uri, method="GET", body=None, headers=None, redirections=DEFAULT_MAX_REDIRECTS, connection_type=None,
- ):
- """ Performs a single HTTP request.
-The 'uri' is the URI of the HTTP resource and can begin
-with either 'http' or 'https'. The value of 'uri' must be an absolute URI.
-
-The 'method' is the HTTP method to perform, such as GET, POST, DELETE, etc.
-There is no restriction on the methods allowed.
-
-The 'body' is the entity body to be sent with the request. It is a string
-object.
-
-Any extra headers that are to be sent with the request should be provided in the
-'headers' dictionary.
-
-The maximum number of redirect to follow before raising an
-exception is 'redirections. The default is 5.
-
-The return value is a tuple of (response, content), the first
-being and instance of the 'Response' class, the second being
-a string that contains the response entity body.
- """
- conn_key = ""
-
- try:
- if headers is None:
- headers = {}
- else:
- headers = self._normalize_headers(headers)
-
- if "user-agent" not in headers:
- headers["user-agent"] = "Python-httplib2/%s (gzip)" % __version__
-
- uri = iri2uri(uri)
- # Prevent CWE-75 space injection to manipulate request via part of uri.
- # Prevent CWE-93 CRLF injection to modify headers via part of uri.
- uri = uri.replace(" ", "%20").replace("\r", "%0D").replace("\n", "%0A")
-
- (scheme, authority, request_uri, defrag_uri) = urlnorm(uri)
-
- conn_key = scheme + ":" + authority
- conn = self.connections.get(conn_key)
- if conn is None:
- if not connection_type:
- connection_type = SCHEME_TO_CONNECTION[scheme]
- certs = list(self.certificates.iter(authority))
- if issubclass(connection_type, HTTPSConnectionWithTimeout):
- if certs:
- conn = self.connections[conn_key] = connection_type(
- authority,
- key_file=certs[0][0],
- cert_file=certs[0][1],
- timeout=self.timeout,
- proxy_info=self.proxy_info,
- ca_certs=self.ca_certs,
- disable_ssl_certificate_validation=self.disable_ssl_certificate_validation,
- tls_maximum_version=self.tls_maximum_version,
- tls_minimum_version=self.tls_minimum_version,
- key_password=certs[0][2],
- )
- else:
- conn = self.connections[conn_key] = connection_type(
- authority,
- timeout=self.timeout,
- proxy_info=self.proxy_info,
- ca_certs=self.ca_certs,
- disable_ssl_certificate_validation=self.disable_ssl_certificate_validation,
- tls_maximum_version=self.tls_maximum_version,
- tls_minimum_version=self.tls_minimum_version,
- )
- else:
- conn = self.connections[conn_key] = connection_type(
- authority, timeout=self.timeout, proxy_info=self.proxy_info
- )
- conn.set_debuglevel(debuglevel)
-
- if "range" not in headers and "accept-encoding" not in headers:
- headers["accept-encoding"] = "gzip, deflate"
-
- info = email.message.Message()
- cachekey = None
- cached_value = None
- if self.cache:
- cachekey = defrag_uri
- cached_value = self.cache.get(cachekey)
- if cached_value:
- try:
- info, content = cached_value.split(b"\r\n\r\n", 1)
- info = email.message_from_bytes(info)
- for k, v in info.items():
- if v.startswith("=?") and v.endswith("?="):
- info.replace_header(k, str(*email.header.decode_header(v)[0]))
- except (IndexError, ValueError):
- self.cache.delete(cachekey)
- cachekey = None
- cached_value = None
-
- if (
- method in self.optimistic_concurrency_methods
- and self.cache
- and "etag" in info
- and not self.ignore_etag
- and "if-match" not in headers
- ):
- # http://www.w3.org/1999/04/Editing/
- headers["if-match"] = info["etag"]
-
- # https://tools.ietf.org/html/rfc7234
- # A cache MUST invalidate the effective Request URI as well as [...] Location and Content-Location
- # when a non-error status code is received in response to an unsafe request method.
- if self.cache and cachekey and method not in self.safe_methods:
- self.cache.delete(cachekey)
-
- # Check the vary header in the cache to see if this request
- # matches what varies in the cache.
- if method in self.safe_methods and "vary" in info:
- vary = info["vary"]
- vary_headers = vary.lower().replace(" ", "").split(",")
- for header in vary_headers:
- key = "-varied-%s" % header
- value = info[key]
- if headers.get(header, None) != value:
- cached_value = None
- break
-
- if (
- self.cache
- and cached_value
- and (method in self.safe_methods or info["status"] == "308")
- and "range" not in headers
- ):
- redirect_method = method
- if info["status"] not in ("307", "308"):
- redirect_method = "GET"
- if "-x-permanent-redirect-url" in info:
- # Should cached permanent redirects be counted in our redirection count? For now, yes.
- if redirections <= 0:
- raise RedirectLimit(
- "Redirected more times than redirection_limit allows.", {}, "",
- )
- (response, new_content) = self.request(
- info["-x-permanent-redirect-url"],
- method=redirect_method,
- headers=headers,
- redirections=redirections - 1,
- )
- response.previous = Response(info)
- response.previous.fromcache = True
- else:
- # Determine our course of action:
- # Is the cached entry fresh or stale?
- # Has the client requested a non-cached response?
- #
- # There seems to be three possible answers:
- # 1. [FRESH] Return the cache entry w/o doing a GET
- # 2. [STALE] Do the GET (but add in cache validators if available)
- # 3. [TRANSPARENT] Do a GET w/o any cache validators (Cache-Control: no-cache) on the request
- entry_disposition = _entry_disposition(info, headers)
-
- if entry_disposition == "FRESH":
- if not cached_value:
- info["status"] = "504"
- content = b""
- response = Response(info)
- if cached_value:
- response.fromcache = True
- return (response, content)
-
- if entry_disposition == "STALE":
- if "etag" in info and not self.ignore_etag and not "if-none-match" in headers:
- headers["if-none-match"] = info["etag"]
- if "last-modified" in info and not "last-modified" in headers:
- headers["if-modified-since"] = info["last-modified"]
- elif entry_disposition == "TRANSPARENT":
- pass
-
- (response, new_content) = self._request(
- conn, authority, uri, request_uri, method, body, headers, redirections, cachekey,
- )
-
- if response.status == 304 and method == "GET":
- # Rewrite the cache entry with the new end-to-end headers
- # Take all headers that are in response
- # and overwrite their values in info.
- # unless they are hop-by-hop, or are listed in the connection header.
-
- for key in _get_end2end_headers(response):
- info[key] = response[key]
- merged_response = Response(info)
- if hasattr(response, "_stale_digest"):
- merged_response._stale_digest = response._stale_digest
- _updateCache(headers, merged_response, content, self.cache, cachekey)
- response = merged_response
- response.status = 200
- response.fromcache = True
-
- elif response.status == 200:
- content = new_content
- else:
- self.cache.delete(cachekey)
- content = new_content
- else:
- cc = _parse_cache_control(headers)
- if "only-if-cached" in cc:
- info["status"] = "504"
- response = Response(info)
- content = b""
- else:
- (response, content) = self._request(
- conn, authority, uri, request_uri, method, body, headers, redirections, cachekey,
- )
- except Exception as e:
- is_timeout = isinstance(e, socket.timeout)
- if is_timeout:
- conn = self.connections.pop(conn_key, None)
- if conn:
- conn.close()
-
- if self.force_exception_to_status_code:
- if isinstance(e, HttpLib2ErrorWithResponse):
- response = e.response
- content = e.content
- response.status = 500
- response.reason = str(e)
- elif isinstance(e, socket.timeout):
- content = b"Request Timeout"
- response = Response({"content-type": "text/plain", "status": "408", "content-length": len(content),})
- response.reason = "Request Timeout"
- else:
- content = str(e).encode("utf-8")
- response = Response({"content-type": "text/plain", "status": "400", "content-length": len(content),})
- response.reason = "Bad Request"
- else:
- raise
-
- return (response, content)
-
-
-class Response(dict):
- """An object more like email.message than httplib.HTTPResponse."""
-
- """Is this response from our local cache"""
- fromcache = False
- """HTTP protocol version used by server.
-
- 10 for HTTP/1.0, 11 for HTTP/1.1.
- """
- version = 11
-
- "Status code returned by server. "
- status = 200
- """Reason phrase returned by server."""
- reason = "Ok"
-
- previous = None
-
- def __init__(self, info):
- # info is either an email.message or
- # an httplib.HTTPResponse object.
- if isinstance(info, http.client.HTTPResponse):
- for key, value in info.getheaders():
- key = key.lower()
- prev = self.get(key)
- if prev is not None:
- value = ", ".join((prev, value))
- self[key] = value
- self.status = info.status
- self["status"] = str(self.status)
- self.reason = info.reason
- self.version = info.version
- elif isinstance(info, email.message.Message):
- for key, value in list(info.items()):
- self[key.lower()] = value
- self.status = int(self["status"])
- else:
- for key, value in info.items():
- self[key.lower()] = value
- self.status = int(self.get("status", self.status))
-
- def __getattr__(self, name):
- if name == "dict":
- return self
- else:
- raise AttributeError(name)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/auth.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/auth.py
deleted file mode 100644
index 84b5831..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/auth.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import base64
-import re
-
-import pyparsing as pp
-
-from .error import *
-
-UNQUOTE_PAIRS = re.compile(r"\\(.)")
-unquote = lambda s, l, t: UNQUOTE_PAIRS.sub(r"\1", t[0][1:-1])
-
-# https://tools.ietf.org/html/rfc7235#section-1.2
-# https://tools.ietf.org/html/rfc7235#appendix-B
-tchar = "!#$%&'*+-.^_`|~" + pp.nums + pp.alphas
-token = pp.Word(tchar).setName("token")
-token68 = pp.Combine(pp.Word("-._~+/" + pp.nums + pp.alphas) + pp.Optional(pp.Word("=").leaveWhitespace())).setName(
- "token68"
-)
-
-quoted_string = pp.dblQuotedString.copy().setName("quoted-string").setParseAction(unquote)
-auth_param_name = token.copy().setName("auth-param-name").addParseAction(pp.downcaseTokens)
-auth_param = auth_param_name + pp.Suppress("=") + (quoted_string | token)
-params = pp.Dict(pp.delimitedList(pp.Group(auth_param)))
-
-scheme = token("scheme")
-challenge = scheme + (params("params") | token68("token"))
-
-authentication_info = params.copy()
-www_authenticate = pp.delimitedList(pp.Group(challenge))
-
-
-def _parse_authentication_info(headers, headername="authentication-info"):
- """https://tools.ietf.org/html/rfc7615
- """
- header = headers.get(headername, "").strip()
- if not header:
- return {}
- try:
- parsed = authentication_info.parseString(header)
- except pp.ParseException as ex:
- # print(ex.explain(ex))
- raise MalformedHeader(headername)
-
- return parsed.asDict()
-
-
-def _parse_www_authenticate(headers, headername="www-authenticate"):
- """Returns a dictionary of dictionaries, one dict per auth_scheme."""
- header = headers.get(headername, "").strip()
- if not header:
- return {}
- try:
- parsed = www_authenticate.parseString(header)
- except pp.ParseException as ex:
- # print(ex.explain(ex))
- raise MalformedHeader(headername)
-
- retval = {
- challenge["scheme"].lower(): challenge["params"].asDict()
- if "params" in challenge
- else {"token": challenge.get("token")}
- for challenge in parsed
- }
- return retval
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/cacerts.txt b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/cacerts.txt
deleted file mode 100644
index 8020c1b..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/cacerts.txt
+++ /dev/null
@@ -1,2197 +0,0 @@
-# Issuer: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc.
-# Subject: CN=GTE CyberTrust Global Root O=GTE Corporation OU=GTE CyberTrust Solutions, Inc.
-# Label: "GTE CyberTrust Global Root"
-# Serial: 421
-# MD5 Fingerprint: ca:3d:d3:68:f1:03:5c:d0:32:fa:b8:2b:59:e8:5a:db
-# SHA1 Fingerprint: 97:81:79:50:d8:1c:96:70:cc:34:d8:09:cf:79:44:31:36:7e:f4:74
-# SHA256 Fingerprint: a5:31:25:18:8d:21:10:aa:96:4b:02:c7:b7:c6:da:32:03:17:08:94:e5:fb:71:ff:fb:66:67:d5:e6:81:0a:36
------BEGIN CERTIFICATE-----
-MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
-VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
-bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
-b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
-UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
-cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
-b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
-iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
-r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
-04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
-GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
-3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
-lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
------END CERTIFICATE-----
-
-# Issuer: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division
-# Subject: CN=Thawte Server CA O=Thawte Consulting cc OU=Certification Services Division
-# Label: "Thawte Server CA"
-# Serial: 1
-# MD5 Fingerprint: c5:70:c4:a2:ed:53:78:0c:c8:10:53:81:64:cb:d0:1d
-# SHA1 Fingerprint: 23:e5:94:94:51:95:f2:41:48:03:b4:d5:64:d2:a3:a3:f5:d8:8b:8c
-# SHA256 Fingerprint: b4:41:0b:73:e2:e6:ea:ca:47:fb:c4:2f:8f:a4:01:8a:f4:38:1d:c5:4c:fa:a8:44:50:46:1e:ed:09:45:4d:e9
------BEGIN CERTIFICATE-----
-MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
-FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
-VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
-biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
-MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
-MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
-DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
-dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
-cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
-DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
-gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
-yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
-L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
-EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
-7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
-QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
-qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
------END CERTIFICATE-----
-
-# Issuer: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division
-# Subject: CN=Thawte Premium Server CA O=Thawte Consulting cc OU=Certification Services Division
-# Label: "Thawte Premium Server CA"
-# Serial: 1
-# MD5 Fingerprint: 06:9f:69:79:16:66:90:02:1b:8c:8c:a2:c3:07:6f:3a
-# SHA1 Fingerprint: 62:7f:8d:78:27:65:63:99:d2:7d:7f:90:44:c9:fe:b3:f3:3e:fa:9a
-# SHA256 Fingerprint: ab:70:36:36:5c:71:54:aa:29:c2:c2:9f:5d:41:91:16:3b:16:2a:22:25:01:13:57:d5:6d:07:ff:a7:bc:1f:72
------BEGIN CERTIFICATE-----
-MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
-FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
-VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
-biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
-dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
-MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
-MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
-A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
-b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
-cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
-bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
-VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
-ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
-uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
-9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
-hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
-pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
------END CERTIFICATE-----
-
-# Issuer: O=Equifax OU=Equifax Secure Certificate Authority
-# Subject: O=Equifax OU=Equifax Secure Certificate Authority
-# Label: "Equifax Secure CA"
-# Serial: 903804111
-# MD5 Fingerprint: 67:cb:9d:c0:13:24:8a:82:9b:b2:17:1e:d1:1b:ec:d4
-# SHA1 Fingerprint: d2:32:09:ad:23:d3:14:23:21:74:e4:0d:7f:9d:62:13:97:86:63:3a
-# SHA256 Fingerprint: 08:29:7a:40:47:db:a2:36:80:c7:31:db:6e:31:76:53:ca:78:48:e1:be:bd:3a:0b:01:79:a7:07:f9:2c:f1:78
------BEGIN CERTIFICATE-----
-MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
-UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
-dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
-MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
-dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
-AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
-BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
-cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
-AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
-MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
-aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
-ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
-IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
-MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
-A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
-7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
-1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
------END CERTIFICATE-----
-
-# Issuer: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network
-# Subject: O=VeriSign, Inc. OU=Class 3 Public Primary Certification Authority - G2/(c) 1998 VeriSign, Inc. - For authorized use only/VeriSign Trust Network
-# Label: "Verisign Class 3 Public Primary Certification Authority - G2"
-# Serial: 167285380242319648451154478808036881606
-# MD5 Fingerprint: a2:33:9b:4c:74:78:73:d4:6c:e7:c1:f3:8d:cb:5c:e9
-# SHA1 Fingerprint: 85:37:1c:a6:e5:50:14:3d:ce:28:03:47:1b:de:3a:09:e8:f8:77:0f
-# SHA256 Fingerprint: 83:ce:3c:12:29:68:8a:59:3d:48:5f:81:97:3c:0f:91:95:43:1e:da:37:cc:5e:36:43:0e:79:c7:a8:88:63:8b
------BEGIN CERTIFICATE-----
-MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
-BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
-c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
-MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
-emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
-DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
-FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
-UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
-YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
-MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
-pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
-13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
-AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
-U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
-F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
-oJ2daZH9
------END CERTIFICATE-----
-
-# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
-# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
-# Label: "GlobalSign Root CA"
-# Serial: 4835703278459707669005204
-# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a
-# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c
-# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99
------BEGIN CERTIFICATE-----
-MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
-A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
-b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
-MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
-YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
-aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
-jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
-xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
-1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
-snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
-U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
-9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
-AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
-yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
-38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
-AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
-DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
-HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
------END CERTIFICATE-----
-
-# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
-# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
-# Label: "GlobalSign Root CA - R2"
-# Serial: 4835703278459682885658125
-# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30
-# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe
-# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e
------BEGIN CERTIFICATE-----
-MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
-MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
-v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
-eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
-tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
-C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
-zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
-mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
-V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
-bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
-3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
-J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
-291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
-ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
-AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
-TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
------END CERTIFICATE-----
-
-# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority
-# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 1 Policy Validation Authority
-# Label: "ValiCert Class 1 VA"
-# Serial: 1
-# MD5 Fingerprint: 65:58:ab:15:ad:57:6c:1e:a8:a7:b5:69:ac:bf:ff:eb
-# SHA1 Fingerprint: e5:df:74:3c:b6:01:c4:9b:98:43:dc:ab:8c:e8:6a:81:10:9f:e4:8e
-# SHA256 Fingerprint: f4:c1:49:55:1a:30:13:a3:5b:c7:bf:fe:17:a7:f3:44:9b:c1:ab:5b:5a:0a:e7:4b:06:c2:3b:90:00:4c:01:04
------BEGIN CERTIFICATE-----
-MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
-IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
-BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
-aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
-9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy
-NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
-azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
-YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
-Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
-cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y
-LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+
-TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y
-TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0
-LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW
-I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
-nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
------END CERTIFICATE-----
-
-# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority
-# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 2 Policy Validation Authority
-# Label: "ValiCert Class 2 VA"
-# Serial: 1
-# MD5 Fingerprint: a9:23:75:9b:ba:49:36:6e:31:c2:db:f2:e7:66:ba:87
-# SHA1 Fingerprint: 31:7a:2a:d0:7f:2b:33:5e:f5:a1:c3:4e:4b:57:e8:b7:d8:f1:fc:a6
-# SHA256 Fingerprint: 58:d0:17:27:9c:d4:dc:63:ab:dd:b1:96:a6:c9:90:6c:30:c4:e0:87:83:ea:e8:c1:60:99:54:d6:93:55:59:6b
------BEGIN CERTIFICATE-----
-MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
-IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
-BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
-aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
-9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
-NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
-azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
-YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
-Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
-cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
-dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
-WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
-v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
-UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
-IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
-W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
------END CERTIFICATE-----
-
-# Issuer: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority
-# Subject: CN=http://www.valicert.com/ O=ValiCert, Inc. OU=ValiCert Class 3 Policy Validation Authority
-# Label: "RSA Root Certificate 1"
-# Serial: 1
-# MD5 Fingerprint: a2:6f:53:b7:ee:40:db:4a:68:e7:fa:18:d9:10:4b:72
-# SHA1 Fingerprint: 69:bd:8c:f4:9c:d3:00:fb:59:2e:17:93:ca:55:6a:f3:ec:aa:35:fb
-# SHA256 Fingerprint: bc:23:f9:8a:31:3c:b9:2d:e3:bb:fc:3a:5a:9f:44:61:ac:39:49:4c:4a:e1:5a:9e:9d:f1:31:e9:9b:73:01:9a
------BEGIN CERTIFICATE-----
-MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
-IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
-BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
-aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
-9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy
-NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
-azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
-YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
-Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
-cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD
-cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs
-2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY
-JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE
-Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ
-n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
-PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
------END CERTIFICATE-----
-
-# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
-# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
-# Label: "Verisign Class 3 Public Primary Certification Authority - G3"
-# Serial: 206684696279472310254277870180966723415
-# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09
-# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6
-# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44
------BEGIN CERTIFICATE-----
-MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
-CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
-cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
-LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
-aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
-dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
-VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
-aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
-bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
-IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
-LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
-N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
-KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
-kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
-CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
-Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
-imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
-2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
-DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
-/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
-F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
-TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
------END CERTIFICATE-----
-
-# Issuer: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
-# Subject: CN=VeriSign Class 4 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
-# Label: "Verisign Class 4 Public Primary Certification Authority - G3"
-# Serial: 314531972711909413743075096039378935511
-# MD5 Fingerprint: db:c8:f2:27:2e:b1:ea:6a:29:23:5d:fe:56:3e:33:df
-# SHA1 Fingerprint: c8:ec:8c:87:92:69:cb:4b:ab:39:e9:8d:7e:57:67:f3:14:95:73:9d
-# SHA256 Fingerprint: e3:89:36:0d:0f:db:ae:b3:d2:50:58:4b:47:30:31:4e:22:2f:39:c1:56:a0:20:14:4e:8d:96:05:61:79:15:06
------BEGIN CERTIFICATE-----
-MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
-CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
-cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
-LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
-aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
-dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
-VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
-aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
-bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
-IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
-LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1
-GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ
-+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd
-U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm
-NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
-ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/
-ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1
-CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq
-g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
-fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c
-2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/
-bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
------END CERTIFICATE-----
-
-# Issuer: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
-# Subject: CN=Entrust.net Secure Server Certification Authority O=Entrust.net OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
-# Label: "Entrust.net Secure Server CA"
-# Serial: 927650371
-# MD5 Fingerprint: df:f2:80:73:cc:f1:e6:61:73:fc:f5:42:e9:c5:7c:ee
-# SHA1 Fingerprint: 99:a6:9b:e6:1a:fe:88:6b:4d:2b:82:00:7c:b8:54:fc:31:7e:15:39
-# SHA256 Fingerprint: 62:f2:40:27:8c:56:4c:4d:d8:bf:7d:9d:4f:6f:36:6e:a8:94:d2:2f:5f:34:d9:89:a9:83:ac:ec:2f:ff:ed:50
------BEGIN CERTIFICATE-----
-MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
-VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
-ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
-KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
-ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
-MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
-ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
-b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
-bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
-U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
-A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
-I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
-wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
-AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
-oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
-BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
-dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
-MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
-b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
-dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
-MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
-E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
-MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
-hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
-95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
-2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
------END CERTIFICATE-----
-
-# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
-# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
-# Label: "Entrust.net Premium 2048 Secure Server CA"
-# Serial: 946059622
-# MD5 Fingerprint: ba:21:ea:20:d6:dd:db:8f:c1:57:8b:40:ad:a1:fc:fc
-# SHA1 Fingerprint: 80:1d:62:d0:7b:44:9d:5c:5c:03:5c:98:ea:61:fa:44:3c:2a:58:fe
-# SHA256 Fingerprint: d1:c3:39:ea:27:84:eb:87:0f:93:4f:c5:63:4e:4a:a9:ad:55:05:01:64:01:f2:64:65:d3:7a:57:46:63:35:9f
------BEGIN CERTIFICATE-----
-MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
-RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
-bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
-IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
-ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy
-MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
-LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
-YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
-A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
-K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
-sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
-MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
-XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
-HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
-4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA
-vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G
-CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA
-WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
-oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ
-h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18
-f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN
-B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy
-vUxFnmG6v4SBkgPR0ml8xQ==
------END CERTIFICATE-----
-
-# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust
-# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust
-# Label: "Baltimore CyberTrust Root"
-# Serial: 33554617
-# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4
-# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74
-# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb
------BEGIN CERTIFICATE-----
-MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
-RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
-VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
-DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
-ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
-VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
-mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
-IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
-mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
-XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
-dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
-jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
-BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
-DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
-9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
-jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
-Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
-ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
-R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
------END CERTIFICATE-----
-
-# Issuer: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc.
-# Subject: CN=Equifax Secure Global eBusiness CA-1 O=Equifax Secure Inc.
-# Label: "Equifax Secure Global eBusiness CA"
-# Serial: 1
-# MD5 Fingerprint: 8f:5d:77:06:27:c4:98:3c:5b:93:78:e7:d7:7d:9b:cc
-# SHA1 Fingerprint: 7e:78:4a:10:1c:82:65:cc:2d:e1:f1:6d:47:b4:40:ca:d9:0a:19:45
-# SHA256 Fingerprint: 5f:0b:62:ea:b5:e3:53:ea:65:21:65:16:58:fb:b6:53:59:f4:43:28:0a:4a:fb:d1:04:d7:7d:10:f9:f0:4c:07
------BEGIN CERTIFICATE-----
-MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc
-MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT
-ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw
-MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj
-dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l
-c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC
-UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc
-58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/
-o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH
-MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr
-aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA
-A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA
-Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv
-8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
------END CERTIFICATE-----
-
-# Issuer: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc.
-# Subject: CN=Equifax Secure eBusiness CA-1 O=Equifax Secure Inc.
-# Label: "Equifax Secure eBusiness CA 1"
-# Serial: 4
-# MD5 Fingerprint: 64:9c:ef:2e:44:fc:c6:8f:52:07:d0:51:73:8f:cb:3d
-# SHA1 Fingerprint: da:40:18:8b:91:89:a3:ed:ee:ae:da:97:fe:2f:9d:f5:b7:d1:8a:41
-# SHA256 Fingerprint: cf:56:ff:46:a4:a1:86:10:9d:d9:65:84:b5:ee:b5:8a:51:0c:42:75:b0:e5:f9:4f:40:bb:ae:86:5e:19:f6:73
------BEGIN CERTIFICATE-----
-MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc
-MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT
-ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw
-MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j
-LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ
-KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo
-RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu
-WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw
-Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD
-AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK
-eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM
-zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+
-WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN
-/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==
------END CERTIFICATE-----
-
-# Issuer: O=Equifax Secure OU=Equifax Secure eBusiness CA-2
-# Subject: O=Equifax Secure OU=Equifax Secure eBusiness CA-2
-# Label: "Equifax Secure eBusiness CA 2"
-# Serial: 930140085
-# MD5 Fingerprint: aa:bf:bf:64:97:da:98:1d:6f:c6:08:3a:95:70:33:ca
-# SHA1 Fingerprint: 39:4f:f6:85:0b:06:be:52:e5:18:56:cc:10:e1:80:e8:82:b3:85:cc
-# SHA256 Fingerprint: 2f:27:4e:48:ab:a4:ac:7b:76:59:33:10:17:75:50:6d:c3:0e:e3:8e:f6:ac:d5:c0:49:32:cf:e0:41:23:42:20
------BEGIN CERTIFICATE-----
-MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
-UzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2Vj
-dXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0
-NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXggU2VjdXJlMSYwJAYD
-VQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCBnzANBgkqhkiG9w0B
-AQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn2Z0G
-vxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/
-BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0C
-AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEX
-MBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJl
-IGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkw
-NjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBq
-y/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQF
-MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
-A4GBAAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy
-0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1
-E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN
------END CERTIFICATE-----
-
-# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network
-# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network
-# Label: "AddTrust Low-Value Services Root"
-# Serial: 1
-# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc
-# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d
-# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7
------BEGIN CERTIFICATE-----
-MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
-MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
-b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
-MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
-QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
-VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
-A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
-CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
-tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
-dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
-PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
-+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
-BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
-BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
-MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
-ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
-IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
-7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
-43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
-eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
-pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
-WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
------END CERTIFICATE-----
-
-# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
-# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
-# Label: "AddTrust External Root"
-# Serial: 1
-# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f
-# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68
-# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2
------BEGIN CERTIFICATE-----
-MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
-MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
-IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
-MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
-FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
-bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
-dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
-H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
-uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
-mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
-a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
-E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
-WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
-VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
-Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
-cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
-IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
-AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
-YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
-6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
-Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
-c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
-mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
------END CERTIFICATE-----
-
-# Issuer: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network
-# Subject: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network
-# Label: "AddTrust Public Services Root"
-# Serial: 1
-# MD5 Fingerprint: c1:62:3e:23:c5:82:73:9c:03:59:4b:2b:e9:77:49:7f
-# SHA1 Fingerprint: 2a:b6:28:48:5e:78:fb:f3:ad:9e:79:10:dd:6b:df:99:72:2c:96:e5
-# SHA256 Fingerprint: 07:91:ca:07:49:b2:07:82:aa:d3:c7:d7:bd:0c:df:c9:48:58:35:84:3e:b2:d7:99:60:09:ce:43:ab:6c:69:27
------BEGIN CERTIFICATE-----
-MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU
-MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
-b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx
-MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB
-ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV
-BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV
-6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX
-GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP
-dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH
-1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF
-62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW
-BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
-AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL
-MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
-cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv
-b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6
-IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/
-iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
-GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh
-4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm
-XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
------END CERTIFICATE-----
-
-# Issuer: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network
-# Subject: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network
-# Label: "AddTrust Qualified Certificates Root"
-# Serial: 1
-# MD5 Fingerprint: 27:ec:39:47:cd:da:5a:af:e2:9a:01:65:21:a9:4c:bb
-# SHA1 Fingerprint: 4d:23:78:ec:91:95:39:b5:00:7f:75:8f:03:3b:21:1e:c5:4d:8b:cf
-# SHA256 Fingerprint: 80:95:21:08:05:db:4b:bc:35:5e:44:28:d8:fd:6e:c2:cd:e3:ab:5f:b9:7a:99:42:98:8e:b8:f4:dc:d0:60:16
------BEGIN CERTIFICATE-----
-MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
-MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
-b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
-MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
-EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
-BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
-AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
-xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
-87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
-2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
-WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
-0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
-A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
-AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
-pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
-ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
-aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
-hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
-hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
-dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
-P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
-iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
-xqE=
------END CERTIFICATE-----
-
-# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
-# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
-# Label: "Entrust Root Certification Authority"
-# Serial: 1164660820
-# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4
-# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9
-# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c
------BEGIN CERTIFICATE-----
-MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
-VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
-Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
-KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
-cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
-NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
-NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
-ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
-BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
-KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
-Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
-4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
-KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
-rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
-94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
-sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
-gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
-kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
-vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
-A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
-O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
-AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
-9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
-eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
-0vdXcDazv/wor3ElhVsT/h5/WrQ8
------END CERTIFICATE-----
-
-# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc.
-# Subject: CN=GeoTrust Global CA O=GeoTrust Inc.
-# Label: "GeoTrust Global CA"
-# Serial: 144470
-# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5
-# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12
-# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a
------BEGIN CERTIFICATE-----
-MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
-YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
-R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
-9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
-fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
-iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
-1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
-bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
-MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
-ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
-uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
-Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
-tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
-PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
-hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
-5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
------END CERTIFICATE-----
-
-# Issuer: CN=GeoTrust Global CA 2 O=GeoTrust Inc.
-# Subject: CN=GeoTrust Global CA 2 O=GeoTrust Inc.
-# Label: "GeoTrust Global CA 2"
-# Serial: 1
-# MD5 Fingerprint: 0e:40:a7:6c:de:03:5d:8f:d1:0f:e4:d1:8d:f9:6c:a9
-# SHA1 Fingerprint: a9:e9:78:08:14:37:58:88:f2:05:19:b0:6d:2b:0d:2b:60:16:90:7d
-# SHA256 Fingerprint: ca:2d:82:a0:86:77:07:2f:8a:b6:76:4f:f0:35:67:6c:fe:3e:5e:32:5e:01:21:72:df:3f:92:09:6d:b7:9b:85
------BEGIN CERTIFICATE-----
-MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW
-MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs
-IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG
-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
-R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A
-PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8
-Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL
-TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL
-5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7
-S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe
-2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
-FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap
-EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td
-EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv
-/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN
-A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0
-abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF
-I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz
-4iIprn2DQKi6bA==
------END CERTIFICATE-----
-
-# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc.
-# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc.
-# Label: "GeoTrust Universal CA"
-# Serial: 1
-# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48
-# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79
-# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12
------BEGIN CERTIFICATE-----
-MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
-MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
-c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
-BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
-IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
-VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
-cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
-QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
-F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
-c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
-mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
-VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
-teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
-f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
-Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
-nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
-/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
-MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
-9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
-aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
-IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
-ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
-uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
-Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
-QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
-koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
-ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
-DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
-bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
------END CERTIFICATE-----
-
-# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc.
-# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc.
-# Label: "GeoTrust Universal CA 2"
-# Serial: 1
-# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7
-# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79
-# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b
------BEGIN CERTIFICATE-----
-MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
-MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
-c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
-VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
-c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
-AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
-WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
-FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
-XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
-se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
-KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
-IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
-y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
-hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
-QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
-Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
-HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
-KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
-dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
-L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
-Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
-ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
-T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
-GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
-1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
-OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
-6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
-QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
------END CERTIFICATE-----
-
-# Issuer: CN=America Online Root Certification Authority 1 O=America Online Inc.
-# Subject: CN=America Online Root Certification Authority 1 O=America Online Inc.
-# Label: "America Online Root Certification Authority 1"
-# Serial: 1
-# MD5 Fingerprint: 14:f1:08:ad:9d:fa:64:e2:89:e7:1c:cf:a8:ad:7d:5e
-# SHA1 Fingerprint: 39:21:c1:15:c1:5d:0e:ca:5c:cb:5b:c4:f0:7d:21:d8:05:0b:56:6a
-# SHA256 Fingerprint: 77:40:73:12:c6:3a:15:3d:5b:c0:0b:4e:51:75:9c:df:da:c2:37:dc:2a:33:b6:79:46:e9:8e:9b:fa:68:0a:e3
------BEGIN CERTIFICATE-----
-MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
-MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
-bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2
-MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
-ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
-Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk
-hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym
-1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW
-OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb
-2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko
-O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
-AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU
-AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
-BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
-Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb
-LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir
-oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C
-MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
-sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
------END CERTIFICATE-----
-
-# Issuer: CN=America Online Root Certification Authority 2 O=America Online Inc.
-# Subject: CN=America Online Root Certification Authority 2 O=America Online Inc.
-# Label: "America Online Root Certification Authority 2"
-# Serial: 1
-# MD5 Fingerprint: d6:ed:3c:ca:e2:66:0f:af:10:43:0d:77:9b:04:09:bf
-# SHA1 Fingerprint: 85:b5:ff:67:9b:0c:79:96:1f:c8:6e:44:22:00:46:13:db:17:92:84
-# SHA256 Fingerprint: 7d:3b:46:5a:60:14:e5:26:c0:af:fc:ee:21:27:d2:31:17:27:ad:81:1c:26:84:2d:00:6a:f3:73:06:cc:80:bd
------BEGIN CERTIFICATE-----
-MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
-MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
-bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2
-MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
-ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
-Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
-ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC
-206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci
-KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2
-JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9
-BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e
-Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B
-PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67
-Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq
-Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
-o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3
-+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj
-YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj
-FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
-AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn
-xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2
-LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc
-obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8
-CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe
-IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA
-DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F
-AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX
-Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb
-AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl
-Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
-RY8mkaKO/qk=
------END CERTIFICATE-----
-
-# Issuer: CN=AAA Certificate Services O=Comodo CA Limited
-# Subject: CN=AAA Certificate Services O=Comodo CA Limited
-# Label: "Comodo AAA Services root"
-# Serial: 1
-# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0
-# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49
-# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4
------BEGIN CERTIFICATE-----
-MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
-MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
-GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
-YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
-GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
-BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
-3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
-YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
-rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
-ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
-oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
-MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
-QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
-b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
-AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
-GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
-Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
-G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
-l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
-smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
------END CERTIFICATE-----
-
-# Issuer: CN=Secure Certificate Services O=Comodo CA Limited
-# Subject: CN=Secure Certificate Services O=Comodo CA Limited
-# Label: "Comodo Secure Services root"
-# Serial: 1
-# MD5 Fingerprint: d3:d9:bd:ae:9f:ac:67:24:b3:c8:1b:52:e1:b9:a9:bd
-# SHA1 Fingerprint: 4a:65:d5:f4:1d:ef:39:b8:b8:90:4a:4a:d3:64:81:33:cf:c7:a1:d1
-# SHA256 Fingerprint: bd:81:ce:3b:4f:65:91:d1:1a:67:b5:fc:7a:47:fd:ef:25:52:1b:f9:aa:4e:18:b9:e3:df:2e:34:a7:80:3b:e8
------BEGIN CERTIFICATE-----
-MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb
-MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
-GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp
-ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow
-fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV
-BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM
-cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S
-HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996
-CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk
-3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz
-6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV
-HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
-EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv
-Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw
-Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww
-DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0
-5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
-Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI
-gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ
-aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl
-izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk=
------END CERTIFICATE-----
-
-# Issuer: CN=Trusted Certificate Services O=Comodo CA Limited
-# Subject: CN=Trusted Certificate Services O=Comodo CA Limited
-# Label: "Comodo Trusted Services root"
-# Serial: 1
-# MD5 Fingerprint: 91:1b:3f:6e:cd:9e:ab:ee:07:fe:1f:71:d2:b3:61:27
-# SHA1 Fingerprint: e1:9f:e3:0e:8b:84:60:9e:80:9b:17:0d:72:a8:c5:ba:6e:14:09:bd
-# SHA256 Fingerprint: 3f:06:e5:56:81:d4:96:f5:be:16:9e:b5:38:9f:9f:2b:8f:f6:1e:17:08:df:68:81:72:48:49:cd:5d:27:cb:69
------BEGIN CERTIFICATE-----
-MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb
-MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
-GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0
-aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla
-MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
-BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD
-VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B
-AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW
-fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt
-TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL
-fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW
-1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7
-kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G
-A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD
-VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v
-ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo
-dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu
-Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/
-HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
-pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS
-jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+
-xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn
-dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi
------END CERTIFICATE-----
-
-# Issuer: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com
-# Subject: CN=UTN - DATACorp SGC O=The USERTRUST Network OU=http://www.usertrust.com
-# Label: "UTN DATACorp SGC Root CA"
-# Serial: 91374294542884689855167577680241077609
-# MD5 Fingerprint: b3:a5:3e:77:21:6d:ac:4a:c0:c9:fb:d5:41:3d:ca:06
-# SHA1 Fingerprint: 58:11:9f:0e:12:82:87:ea:50:fd:d9:87:45:6f:4f:78:dc:fa:d6:d4
-# SHA256 Fingerprint: 85:fb:2f:91:dd:12:27:5a:01:45:b6:36:53:4f:84:02:4a:d6:8b:69:b8:ee:88:68:4f:f7:11:37:58:05:b3:48
------BEGIN CERTIFICATE-----
-MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB
-kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
-Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
-IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG
-EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
-VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu
-dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6
-E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ
-D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK
-4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq
-lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW
-bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB
-o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
-MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js
-LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr
-BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB
-AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
-Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj
-j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH
-KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv
-2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3
-mfnGV/TJVTl4uix5yaaIK/QI
------END CERTIFICATE-----
-
-# Issuer: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com
-# Subject: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com
-# Label: "UTN USERFirst Hardware Root CA"
-# Serial: 91374294542884704022267039221184531197
-# MD5 Fingerprint: 4c:56:41:e5:0d:bb:2b:e8:ca:a3:ed:18:08:ad:43:39
-# SHA1 Fingerprint: 04:83:ed:33:99:ac:36:08:05:87:22:ed:bc:5e:46:00:e3:be:f9:d7
-# SHA256 Fingerprint: 6e:a5:47:41:d0:04:66:7e:ed:1b:48:16:63:4a:a3:a7:9e:6e:4b:96:95:0f:82:79:da:fc:8d:9b:d8:81:21:37
------BEGIN CERTIFICATE-----
-MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
-lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
-Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
-SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
-A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
-MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
-d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
-cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
-0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
-M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
-MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
-oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
-DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
-oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
-VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
-dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
-bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
-BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
-//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
-CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
-CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
-3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
-KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==
------END CERTIFICATE-----
-
-# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
-# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
-# Label: "XRamp Global CA Root"
-# Serial: 107108908803651509692980124233745014957
-# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1
-# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6
-# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2
------BEGIN CERTIFICATE-----
-MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB
-gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk
-MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY
-UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx
-NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3
-dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy
-dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
-dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6
-38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP
-KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q
-DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4
-qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa
-JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi
-PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P
-BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs
-jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0
-eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD
-ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR
-vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
-qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa
-IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy
-i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ
-O+7ETPTsJ3xCwnR8gooJybQDJbw=
------END CERTIFICATE-----
-
-# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
-# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
-# Label: "Go Daddy Class 2 CA"
-# Serial: 0
-# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67
-# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4
-# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
-MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
-YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
-MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
-ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
-MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
-ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
-PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
-wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
-EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
-avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
-YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
-sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
-/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
-IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
-YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
-ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
-OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
-TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
-HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
-dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
-ReYNnyicsbkqWletNw+vHX/bvZ8=
------END CERTIFICATE-----
-
-# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
-# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
-# Label: "Starfield Class 2 CA"
-# Serial: 0
-# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24
-# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a
-# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58
------BEGIN CERTIFICATE-----
-MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
-MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
-U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
-NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
-ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
-ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
-DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
-8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
-+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
-X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
-K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
-1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
-A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
-zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
-YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
-bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
-DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
-L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
-eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
-xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
-VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
-WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
------END CERTIFICATE-----
-
-# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
-# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
-# Label: "StartCom Certification Authority"
-# Serial: 1
-# MD5 Fingerprint: 22:4d:8f:8a:fc:f7:35:c2:bb:57:34:90:7b:8b:22:16
-# SHA1 Fingerprint: 3e:2b:f7:f2:03:1b:96:f3:8c:e6:c4:d8:a8:5d:3e:2d:58:47:6a:0f
-# SHA256 Fingerprint: c7:66:a9:be:f2:d4:07:1c:86:3a:31:aa:49:20:e8:13:b2:d1:98:60:8c:b7:b7:cf:e2:11:43:b8:36:df:09:ea
------BEGIN CERTIFICATE-----
-MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
-MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
-Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
-MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
-U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
-cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
-A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
-pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
-OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
-Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
-Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
-HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
-Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
-+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
-Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
-Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
-26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
-AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
-FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
-ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
-LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
-BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
-Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
-dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
-cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
-YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
-dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
-bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
-YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
-TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
-9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
-jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
-FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
-ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
-ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
-EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
-L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
-yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
-O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
-um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
-NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
------END CERTIFICATE-----
-
-# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
-# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
-# Label: "DigiCert Assured ID Root CA"
-# Serial: 17154717934120587862167794914071425081
-# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72
-# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43
-# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c
------BEGIN CERTIFICATE-----
-MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
-b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
-EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
-cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
-JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
-mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
-wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
-VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
-AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
-AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
-BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
-pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
-dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
-fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
-NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
-H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
-+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
------END CERTIFICATE-----
-
-# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
-# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
-# Label: "DigiCert Global Root CA"
-# Serial: 10944719598952040374951832963794454346
-# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e
-# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36
-# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61
------BEGIN CERTIFICATE-----
-MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
-b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
-CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
-nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
-43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
-T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
-gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
-TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
-DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
-hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
-06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
-PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
-YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
-CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
------END CERTIFICATE-----
-
-# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
-# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
-# Label: "DigiCert High Assurance EV Root CA"
-# Serial: 3553400076410547919724730734378100087
-# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a
-# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25
-# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf
------BEGIN CERTIFICATE-----
-MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
-ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
-LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
-RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
-+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
-PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
-xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
-Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
-hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
-EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
-FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
-nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
-eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
-hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
-Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
-vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
-+OkuE6N36B9K
------END CERTIFICATE-----
-
-# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc.
-# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc.
-# Label: "GeoTrust Primary Certification Authority"
-# Serial: 32798226551256963324313806436981982369
-# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf
-# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96
-# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c
------BEGIN CERTIFICATE-----
-MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
-MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
-R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
-MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
-Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
-ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
-AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
-ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
-7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
-kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
-mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
-A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
-KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
-6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
-4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
-oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
-UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
-AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
------END CERTIFICATE-----
-
-# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only
-# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only
-# Label: "thawte Primary Root CA"
-# Serial: 69529181992039203566298953787712940909
-# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12
-# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81
-# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f
------BEGIN CERTIFICATE-----
-MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
-qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
-Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
-MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
-BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
-NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
-LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
-A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
-IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
-W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
-3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
-6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
-Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
-NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
-MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
-r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
-DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
-YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
-xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
-/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
-LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
-jVaMaA==
------END CERTIFICATE-----
-
-# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only
-# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only
-# Label: "VeriSign Class 3 Public Primary Certification Authority - G5"
-# Serial: 33037644167568058970164719475676101450
-# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c
-# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5
-# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df
------BEGIN CERTIFICATE-----
-MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
-yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
-ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
-U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
-ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
-aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
-MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
-ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
-biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
-U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
-aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
-nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
-t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
-SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
-BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
-rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
-NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
-BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
-BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
-aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
-MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
-p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
-5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
-WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
-4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
-hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
------END CERTIFICATE-----
-
-# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
-# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
-# Label: "COMODO Certification Authority"
-# Serial: 104350513648249232941998508985834464573
-# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75
-# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b
-# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66
------BEGIN CERTIFICATE-----
-MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
-gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
-A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
-BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
-MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
-YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
-RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
-aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
-UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
-2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
-Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
-+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
-DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
-nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
-/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
-PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
-QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
-SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
-IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
-RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
-zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
-BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
-ZQ==
------END CERTIFICATE-----
-
-# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
-# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
-# Label: "Network Solutions Certificate Authority"
-# Serial: 116697915152937497490437556386812487904
-# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e
-# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce
-# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c
------BEGIN CERTIFICATE-----
-MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
-MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
-MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
-dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
-UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
-ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
-c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
-OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
-mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
-BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
-qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
-gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
-BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
-bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
-dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
-6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
-h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
-/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
-wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
-pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
------END CERTIFICATE-----
-
-# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited
-# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited
-# Label: "COMODO ECC Certification Authority"
-# Serial: 41578283867086692638256921589707938090
-# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23
-# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11
-# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7
------BEGIN CERTIFICATE-----
-MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
-MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
-BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
-IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
-MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
-ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
-T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
-biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
-FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
-cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
-BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
-fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
-GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
------END CERTIFICATE-----
-
-# Issuer: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA
-# Subject: CN=TC TrustCenter Class 2 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 2 CA
-# Label: "TC TrustCenter Class 2 CA II"
-# Serial: 941389028203453866782103406992443
-# MD5 Fingerprint: ce:78:33:5c:59:78:01:6e:18:ea:b9:36:a0:b9:2e:23
-# SHA1 Fingerprint: ae:50:83:ed:7c:f4:5c:bc:8f:61:c6:21:fe:68:5d:79:42:21:15:6e
-# SHA256 Fingerprint: e6:b8:f8:76:64:85:f8:07:ae:7f:8d:ac:16:70:46:1f:07:c0:a1:3e:ef:3a:1f:f7:17:53:8d:7a:ba:d3:91:b4
------BEGIN CERTIFICATE-----
-MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL
-MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV
-BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0
-Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1
-OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i
-SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc
-VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf
-tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg
-uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J
-XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK
-8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99
-5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud
-EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3
-kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy
-dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6
-Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz
-JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290
-Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
-TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS
-GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt
-ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8
-au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV
-hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI
-dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ==
------END CERTIFICATE-----
-
-# Issuer: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA
-# Subject: CN=TC TrustCenter Class 3 CA II O=TC TrustCenter GmbH OU=TC TrustCenter Class 3 CA
-# Label: "TC TrustCenter Class 3 CA II"
-# Serial: 1506523511417715638772220530020799
-# MD5 Fingerprint: 56:5f:aa:80:61:12:17:f6:67:21:e6:2b:6d:61:56:8e
-# SHA1 Fingerprint: 80:25:ef:f4:6e:70:c8:d4:72:24:65:84:fe:40:3b:8a:8d:6a:db:f5
-# SHA256 Fingerprint: 8d:a0:84:fc:f9:9c:e0:77:22:f8:9b:32:05:93:98:06:fa:5c:b8:11:e1:c8:13:f6:a1:08:c7:d3:36:b3:40:8e
------BEGIN CERTIFICATE-----
-MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL
-MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV
-BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0
-Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1
-OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i
-SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc
-VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW
-Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q
-Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2
-1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq
-ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1
-Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud
-EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX
-XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy
-dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6
-Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz
-JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290
-Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
-TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN
-irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8
-TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6
-g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB
-95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj
-S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A==
------END CERTIFICATE-----
-
-# Issuer: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA
-# Subject: CN=TC TrustCenter Universal CA I O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA
-# Label: "TC TrustCenter Universal CA I"
-# Serial: 601024842042189035295619584734726
-# MD5 Fingerprint: 45:e1:a5:72:c5:a9:36:64:40:9e:f5:e4:58:84:67:8c
-# SHA1 Fingerprint: 6b:2f:34:ad:89:58:be:62:fd:b0:6b:5c:ce:bb:9d:d9:4f:4e:39:f3
-# SHA256 Fingerprint: eb:f3:c0:2a:87:89:b1:fb:7d:51:19:95:d6:63:b7:29:06:d9:13:ce:0d:5e:10:56:8a:8a:77:e2:58:61:67:e7
------BEGIN CERTIFICATE-----
-MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL
-MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV
-BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1
-c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx
-MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg
-R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD
-VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN
-AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR
-JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T
-fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu
-jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z
-wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ
-fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD
-VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO
-BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G
-CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1
-7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn
-8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs
-ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT
-ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/
-2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY
------END CERTIFICATE-----
-
-# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc
-# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc
-# Label: "Cybertrust Global Root"
-# Serial: 4835703278459682877484360
-# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1
-# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6
-# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3
------BEGIN CERTIFICATE-----
-MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG
-A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh
-bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE
-ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS
-b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5
-7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS
-J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y
-HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP
-t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz
-FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY
-XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw
-hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js
-MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA
-A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj
-Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx
-XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o
-omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc
-A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
-WL1WMRJOEcgh4LMRkWXbtKaIOM5V
------END CERTIFICATE-----
-
-# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only
-# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only
-# Label: "GeoTrust Primary Certification Authority - G3"
-# Serial: 28809105769928564313984085209975885599
-# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05
-# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd
-# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4
------BEGIN CERTIFICATE-----
-MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
-MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
-eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
-cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ
-BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
-MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0
-BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
-LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz
-+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm
-hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn
-5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W
-JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL
-DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC
-huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
-HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB
-AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB
-zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN
-kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
-AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH
-SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G
-spki4cErx5z481+oghLrGREt
------END CERTIFICATE-----
-
-# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only
-# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only
-# Label: "thawte Primary Root CA - G2"
-# Serial: 71758320672825410020661621085256472406
-# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f
-# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12
-# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57
------BEGIN CERTIFICATE-----
-MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
-IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
-BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
-MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
-d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
-YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
-dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
-BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
-papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
-DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
-KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
-XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
------END CERTIFICATE-----
-
-# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only
-# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only
-# Label: "thawte Primary Root CA - G3"
-# Serial: 127614157056681299805556476275995414779
-# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31
-# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2
-# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c
------BEGIN CERTIFICATE-----
-MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB
-rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
-Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
-MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
-BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa
-Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl
-LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u
-MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl
-ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm
-gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8
-YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf
-b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9
-9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S
-zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk
-OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
-HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA
-2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW
-oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
-t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c
-KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM
-m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu
-MdRAGmI0Nj81Aa6sY6A=
------END CERTIFICATE-----
-
-# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only
-# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only
-# Label: "GeoTrust Primary Certification Authority - G2"
-# Serial: 80682863203381065782177908751794619243
-# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a
-# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0
-# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66
------BEGIN CERTIFICATE-----
-MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL
-MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
-KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
-MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
-eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV
-BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw
-NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV
-BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
-MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL
-So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal
-tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
-BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG
-CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT
-qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz
-rD6ogRLQy7rQkgu2npaqBA+K
------END CERTIFICATE-----
-
-# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only
-# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only
-# Label: "VeriSign Universal Root Certification Authority"
-# Serial: 85209574734084581917763752644031726877
-# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19
-# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54
-# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c
------BEGIN CERTIFICATE-----
-MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
-vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
-ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
-U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
-ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
-Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
-MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
-IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
-IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
-bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
-9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
-H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
-LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
-/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
-rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
-EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
-WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
-exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
-DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
-sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
-seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
-4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
-BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
-lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
-7M2CYfE45k+XmCpajQ==
------END CERTIFICATE-----
-
-# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only
-# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only
-# Label: "VeriSign Class 3 Public Primary Certification Authority - G4"
-# Serial: 63143484348153506665311985501458640051
-# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41
-# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a
-# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79
------BEGIN CERTIFICATE-----
-MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL
-MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
-ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln
-biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
-U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
-aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG
-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp
-U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg
-SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln
-biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
-IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm
-GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve
-fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw
-AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
-aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj
-aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW
-kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC
-4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga
-FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
------END CERTIFICATE-----
-
-# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
-# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
-# Label: "GlobalSign Root CA - R3"
-# Serial: 4835703278459759426209954
-# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28
-# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad
-# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b
------BEGIN CERTIFICATE-----
-MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
-MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
-RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
-gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
-KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
-QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
-XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
-DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
-LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
-RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
-jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
-6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
-mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
-Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
-WD9f
------END CERTIFICATE-----
-
-# Issuer: CN=TC TrustCenter Universal CA III O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA
-# Subject: CN=TC TrustCenter Universal CA III O=TC TrustCenter GmbH OU=TC TrustCenter Universal CA
-# Label: "TC TrustCenter Universal CA III"
-# Serial: 2010889993983507346460533407902964
-# MD5 Fingerprint: 9f:dd:db:ab:ff:8e:ff:45:21:5f:f0:6c:9d:8f:fe:2b
-# SHA1 Fingerprint: 96:56:cd:7b:57:96:98:95:d0:e1:41:46:68:06:fb:b8:c6:11:06:87
-# SHA256 Fingerprint: 30:9b:4a:87:f6:ca:56:c9:31:69:aa:a9:9c:6d:98:88:54:d7:89:2b:d5:43:7e:2d:07:b2:9c:be:da:55:d3:5d
------BEGIN CERTIFICATE-----
-MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezEL
-MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV
-BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1
-c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAeFw0wOTA5MDkwODE1MjdaFw0yOTEy
-MzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNUQyBUcnVzdENlbnRl
-ciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0ExKDAm
-BgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF
-5+cvAqBNLaT6hdqbJYUtQCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYv
-DIRlzg9uwliT6CwLOunBjvvya8o84pxOjuT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8v
-zArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+EutCHnNaYlAJ/Uqwa1D7KRT
-yGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1M4BDj5yj
-dipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBh
-MB8GA1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMB
-Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI
-4jANBgkqhkiG9w0BAQUFAAOCAQEAg8ev6n9NCjw5sWi+e22JLumzCecYV42Fmhfz
-dkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+KGwWaODIl0YgoGhnYIg5IFHY
-aAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhKBgePxLcHsU0G
-DeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV
-CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPH
-LQNjO9Po5KIqwoIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg==
------END CERTIFICATE-----
-
-# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
-# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
-# Label: "Go Daddy Root Certificate Authority - G2"
-# Serial: 0
-# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01
-# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b
-# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da
------BEGIN CERTIFICATE-----
-MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
-EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
-EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
-ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
-NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
-EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
-AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
-DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
-E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
-/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
-DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
-GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
-tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
-AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
-FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
-WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
-9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
-gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
-2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
-LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
-4uJEvlz36hz1
------END CERTIFICATE-----
-
-# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
-# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
-# Label: "Starfield Root Certificate Authority - G2"
-# Serial: 0
-# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96
-# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e
-# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5
------BEGIN CERTIFICATE-----
-MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
-EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
-HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
-ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
-MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
-b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
-aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
-Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
-nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
-HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
-Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
-dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
-HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
-BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
-CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
-sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
-4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
-8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
-pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
-mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
------END CERTIFICATE-----
-
-# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
-# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
-# Label: "Starfield Services Root Certificate Authority - G2"
-# Serial: 0
-# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2
-# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f
-# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5
------BEGIN CERTIFICATE-----
-MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
-EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
-HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
-ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
-MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
-VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
-ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
-dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
-OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
-8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
-Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
-hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
-6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
-DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
-AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
-bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
-ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
-qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
-iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
-0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
-sSi6
------END CERTIFICATE-----
-
-# Issuer: CN=AffirmTrust Commercial O=AffirmTrust
-# Subject: CN=AffirmTrust Commercial O=AffirmTrust
-# Label: "AffirmTrust Commercial"
-# Serial: 8608355977964138876
-# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7
-# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7
-# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7
------BEGIN CERTIFICATE-----
-MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
-BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
-dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
-MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
-cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
-Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
-ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
-MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
-yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
-VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
-nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
-KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
-XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
-vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
-Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
-N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
-nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
------END CERTIFICATE-----
-
-# Issuer: CN=AffirmTrust Networking O=AffirmTrust
-# Subject: CN=AffirmTrust Networking O=AffirmTrust
-# Label: "AffirmTrust Networking"
-# Serial: 8957382827206547757
-# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f
-# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f
-# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b
------BEGIN CERTIFICATE-----
-MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
-BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
-dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
-MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
-cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
-YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
-kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
-QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
-6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
-yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
-QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
-KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
-tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
-QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
-Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
-olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
-x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
------END CERTIFICATE-----
-
-# Issuer: CN=AffirmTrust Premium O=AffirmTrust
-# Subject: CN=AffirmTrust Premium O=AffirmTrust
-# Label: "AffirmTrust Premium"
-# Serial: 7893706540734352110
-# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57
-# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27
-# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a
------BEGIN CERTIFICATE-----
-MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
-BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
-dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
-A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
-cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
-qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
-JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
-+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
-s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
-HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
-70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
-V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
-qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
-5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
-C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
-OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
-FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
-BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
-KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
-Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
-8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
-MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
-0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
-u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
-u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
-YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
-GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
-RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
-KeC2uAloGRwYQw==
------END CERTIFICATE-----
-
-# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust
-# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust
-# Label: "AffirmTrust Premium ECC"
-# Serial: 8401224907861490260
-# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d
-# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb
-# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23
------BEGIN CERTIFICATE-----
-MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
-VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
-cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
-BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
-VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
-0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
-ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
-A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
-A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
-aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
-flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
------END CERTIFICATE-----
-
-# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
-# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
-# Label: "StartCom Certification Authority"
-# Serial: 45
-# MD5 Fingerprint: c9:3b:0d:84:41:fc:a4:76:79:23:08:57:de:10:19:16
-# SHA1 Fingerprint: a3:f1:33:3f:e2:42:bf:cf:c5:d1:4e:8f:39:42:98:40:68:10:d1:a0
-# SHA256 Fingerprint: e1:78:90:ee:09:a3:fb:f4:f4:8b:9c:41:4a:17:d6:37:b7:a5:06:47:e9:bc:75:23:22:72:7f:cc:17:42:a9:11
------BEGIN CERTIFICATE-----
-MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
-MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
-Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
-dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
-MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
-U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
-cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
-A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
-pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
-OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
-Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
-Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
-HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
-Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
-+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
-Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
-Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
-26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
-AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
-VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
-F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
-ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
-ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
-aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
-YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
-c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
-aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
-d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
-CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
-dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
-wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
-Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
-0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
-pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
-CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
-P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
-1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
-KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
-JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
-8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
-fyWl8kgAwKQB2j8=
------END CERTIFICATE-----
-
-# Issuer: CN=StartCom Certification Authority G2 O=StartCom Ltd.
-# Subject: CN=StartCom Certification Authority G2 O=StartCom Ltd.
-# Label: "StartCom Certification Authority G2"
-# Serial: 59
-# MD5 Fingerprint: 78:4b:fb:9e:64:82:0a:d3:b8:4c:62:f3:64:f2:90:64
-# SHA1 Fingerprint: 31:f1:fd:68:22:63:20:ee:c6:3b:3f:9d:ea:4a:3e:53:7c:7c:39:17
-# SHA256 Fingerprint: c7:ba:65:67:de:93:a7:98:ae:1f:aa:79:1e:71:2d:37:8f:ae:1f:93:c4:39:7f:ea:44:1b:b7:cb:e6:fd:59:95
------BEGIN CERTIFICATE-----
-MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW
-MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm
-aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1
-OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG
-A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G
-CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ
-JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD
-vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo
-D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/
-Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW
-RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK
-HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN
-nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM
-0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i
-UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9
-Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg
-TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
-AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL
-BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
-2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX
-UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl
-6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK
-9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ
-HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI
-wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY
-XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l
-IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo
-hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr
-so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI
------END CERTIFICATE-----
-
-# Issuer: O=Digital Signature Trust Co., CN=DST Root CA X3
-# Subject: O=Digital Signature Trust Co., CN=DST Root CA X3
-# Label: "IdenTrust DST Root CA X3"
-# Serial: 44AFB080D6A327BA893039862EF8406B
-# MD5 Fingerprint: 41:03:52:DC:0F:F7:50:1B:16:F0:02:8E:BA:6F:45:C5
-# SHA1 Fingerprint: DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13
-# SHA256 Fingerprint: 06:87:26:03:31:A7:24:03:D9:09:F1:05:E6:9B:CF:0D:32:E1:BD:24:93:FF:C6:D9:20:6D:11:BC:D6:77:07:39
------BEGIN CERTIFICATE-----
-MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
-PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
-Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
-rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
-OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
-xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
-7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
-aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
-SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
-ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
-AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
-R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
-JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
-Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
------END CERTIFICATE-----
-
-# Issuer: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
-# Subject: CN=DigiCert Global Root G2, OU=www.digicert.com, O=DigiCert Inc, C=US
-# Serial: 33af1e6a711a9a0bb2864b11d09fae5
-# MD5 Fingerprint: E4:A6:8A:C8:54:AC:52:42:46:0A:FD:72:48:1B:2A:44
-# SHA1 Fingerprint: DF:3C:24:F9:BF:D6:66:76:1B:26:80:73:FE:06:D1:CC:8D:4F:82:A4
-# SHA256 Fingerprint: CB:3C:CB:B7:60:31:E5:E0:13:8F:8D:D3:9A:23:F9:DE:47:FF:C3:5E:43:C1:14:4C:EA:27:D4:6A:5A:B1:CB:5F
------BEGIN CERTIFICATE-----
-MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
-MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
-b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
-2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
-1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
-q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
-tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
-vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
-BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
-5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
-1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
-NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
-Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
-8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
-pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
-MrY=
------END CERTIFICATE-----
-
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/certs.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/certs.py
deleted file mode 100644
index 59d1ffc..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/certs.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""Utilities for certificate management."""
-
-import os
-
-certifi_available = False
-certifi_where = None
-try:
- from certifi import where as certifi_where
- certifi_available = True
-except ImportError:
- pass
-
-custom_ca_locater_available = False
-custom_ca_locater_where = None
-try:
- from ca_certs_locater import get as custom_ca_locater_where
- custom_ca_locater_available = True
-except ImportError:
- pass
-
-
-BUILTIN_CA_CERTS = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), "cacerts.txt"
-)
-
-
-def where():
- env = os.environ.get("HTTPLIB2_CA_CERTS")
- if env is not None:
- if os.path.isfile(env):
- return env
- else:
- raise RuntimeError("Environment variable HTTPLIB2_CA_CERTS not a valid file")
- if custom_ca_locater_available:
- return custom_ca_locater_where()
- if certifi_available:
- return certifi_where()
- return BUILTIN_CA_CERTS
-
-
-if __name__ == "__main__":
- print(where())
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/error.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/error.py
deleted file mode 100644
index 0e68c12..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/error.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# All exceptions raised here derive from HttpLib2Error
-class HttpLib2Error(Exception):
- pass
-
-
-# Some exceptions can be caught and optionally
-# be turned back into responses.
-class HttpLib2ErrorWithResponse(HttpLib2Error):
- def __init__(self, desc, response, content):
- self.response = response
- self.content = content
- HttpLib2Error.__init__(self, desc)
-
-
-class RedirectMissingLocation(HttpLib2ErrorWithResponse):
- pass
-
-
-class RedirectLimit(HttpLib2ErrorWithResponse):
- pass
-
-
-class FailedToDecompressContent(HttpLib2ErrorWithResponse):
- pass
-
-
-class UnimplementedDigestAuthOptionError(HttpLib2ErrorWithResponse):
- pass
-
-
-class UnimplementedHmacDigestAuthOptionError(HttpLib2ErrorWithResponse):
- pass
-
-
-class MalformedHeader(HttpLib2Error):
- pass
-
-
-class RelativeURIError(HttpLib2Error):
- pass
-
-
-class ServerNotFoundError(HttpLib2Error):
- pass
-
-
-class ProxiesUnavailableError(HttpLib2Error):
- pass
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/iri2uri.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/iri2uri.py
deleted file mode 100644
index 86e361e..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/iri2uri.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Converts an IRI to a URI."""
-
-__author__ = "Joe Gregorio (joe@bitworking.org)"
-__copyright__ = "Copyright 2006, Joe Gregorio"
-__contributors__ = []
-__version__ = "1.0.0"
-__license__ = "MIT"
-
-import urllib.parse
-
-# Convert an IRI to a URI following the rules in RFC 3987
-#
-# The characters we need to enocde and escape are defined in the spec:
-#
-# iprivate = %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD
-# ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
-# / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
-# / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
-# / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
-# / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
-# / %xD0000-DFFFD / %xE1000-EFFFD
-
-escape_range = [
- (0xA0, 0xD7FF),
- (0xE000, 0xF8FF),
- (0xF900, 0xFDCF),
- (0xFDF0, 0xFFEF),
- (0x10000, 0x1FFFD),
- (0x20000, 0x2FFFD),
- (0x30000, 0x3FFFD),
- (0x40000, 0x4FFFD),
- (0x50000, 0x5FFFD),
- (0x60000, 0x6FFFD),
- (0x70000, 0x7FFFD),
- (0x80000, 0x8FFFD),
- (0x90000, 0x9FFFD),
- (0xA0000, 0xAFFFD),
- (0xB0000, 0xBFFFD),
- (0xC0000, 0xCFFFD),
- (0xD0000, 0xDFFFD),
- (0xE1000, 0xEFFFD),
- (0xF0000, 0xFFFFD),
- (0x100000, 0x10FFFD),
-]
-
-
-def encode(c):
- retval = c
- i = ord(c)
- for low, high in escape_range:
- if i < low:
- break
- if i >= low and i <= high:
- retval = "".join(["%%%2X" % o for o in c.encode("utf-8")])
- break
- return retval
-
-
-def iri2uri(uri):
- """Convert an IRI to a URI. Note that IRIs must be
- passed in a unicode strings. That is, do not utf-8 encode
- the IRI before passing it into the function."""
- if isinstance(uri, str):
- (scheme, authority, path, query, fragment) = urllib.parse.urlsplit(uri)
- authority = authority.encode("idna").decode("utf-8")
- # For each character in 'ucschar' or 'iprivate'
- # 1. encode as utf-8
- # 2. then %-encode each octet of that utf-8
- uri = urllib.parse.urlunsplit((scheme, authority, path, query, fragment))
- uri = "".join([encode(c) for c in uri])
- return uri
-
-
-if __name__ == "__main__":
- import unittest
-
- class Test(unittest.TestCase):
- def test_uris(self):
- """Test that URIs are invariant under the transformation."""
- invariant = [
- "ftp://ftp.is.co.za/rfc/rfc1808.txt",
- "http://www.ietf.org/rfc/rfc2396.txt",
- "ldap://[2001:db8::7]/c=GB?objectClass?one",
- "mailto:John.Doe@example.com",
- "news:comp.infosystems.www.servers.unix",
- "tel:+1-816-555-1212",
- "telnet://192.0.2.16:80/",
- "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
- ]
- for uri in invariant:
- self.assertEqual(uri, iri2uri(uri))
-
- def test_iri(self):
- """Test that the right type of escaping is done for each part of the URI."""
- self.assertEqual(
- "http://xn--o3h.com/%E2%98%84",
- iri2uri("http://\N{COMET}.com/\N{COMET}"),
- )
- self.assertEqual(
- "http://bitworking.org/?fred=%E2%98%84",
- iri2uri("http://bitworking.org/?fred=\N{COMET}"),
- )
- self.assertEqual(
- "http://bitworking.org/#%E2%98%84",
- iri2uri("http://bitworking.org/#\N{COMET}"),
- )
- self.assertEqual("#%E2%98%84", iri2uri("#\N{COMET}"))
- self.assertEqual(
- "/fred?bar=%E2%98%9A#%E2%98%84",
- iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}"),
- )
- self.assertEqual(
- "/fred?bar=%E2%98%9A#%E2%98%84",
- iri2uri(iri2uri("/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}")),
- )
- self.assertNotEqual(
- "/fred?bar=%E2%98%9A#%E2%98%84",
- iri2uri(
- "/fred?bar=\N{BLACK LEFT POINTING INDEX}#\N{COMET}".encode("utf-8")
- ),
- )
-
- unittest.main()
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/socks.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/socks.py
deleted file mode 100644
index cc68e63..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/httplib2/socks.py
+++ /dev/null
@@ -1,518 +0,0 @@
-"""SocksiPy - Python SOCKS module.
-
-Version 1.00
-
-Copyright 2006 Dan-Haim. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-3. Neither the name of Dan Haim nor the names of his contributors may be used
- to endorse or promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
-
-This module provides a standard socket-like interface for Python
-for tunneling connections through SOCKS proxies.
-
-Minor modifications made by Christopher Gilbert (http://motomastyle.com/) for
-use in PyLoris (http://pyloris.sourceforge.net/).
-
-Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
-mainly to merge bug fixes found in Sourceforge.
-"""
-
-import base64
-import socket
-import struct
-import sys
-
-if getattr(socket, "socket", None) is None:
- raise ImportError("socket.socket missing, proxy support unusable")
-
-PROXY_TYPE_SOCKS4 = 1
-PROXY_TYPE_SOCKS5 = 2
-PROXY_TYPE_HTTP = 3
-PROXY_TYPE_HTTP_NO_TUNNEL = 4
-
-_defaultproxy = None
-_orgsocket = socket.socket
-
-
-class ProxyError(Exception):
- pass
-
-
-class GeneralProxyError(ProxyError):
- pass
-
-
-class Socks5AuthError(ProxyError):
- pass
-
-
-class Socks5Error(ProxyError):
- pass
-
-
-class Socks4Error(ProxyError):
- pass
-
-
-class HTTPError(ProxyError):
- pass
-
-
-_generalerrors = (
- "success",
- "invalid data",
- "not connected",
- "not available",
- "bad proxy type",
- "bad input",
-)
-
-_socks5errors = (
- "succeeded",
- "general SOCKS server failure",
- "connection not allowed by ruleset",
- "Network unreachable",
- "Host unreachable",
- "Connection refused",
- "TTL expired",
- "Command not supported",
- "Address type not supported",
- "Unknown error",
-)
-
-_socks5autherrors = (
- "succeeded",
- "authentication is required",
- "all offered authentication methods were rejected",
- "unknown username or invalid password",
- "unknown error",
-)
-
-_socks4errors = (
- "request granted",
- "request rejected or failed",
- "request rejected because SOCKS server cannot connect to identd on the client",
- "request rejected because the client program and identd report different "
- "user-ids",
- "unknown error",
-)
-
-
-def setdefaultproxy(
- proxytype=None, addr=None, port=None, rdns=True, username=None, password=None
-):
- """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
- Sets a default proxy which all further socksocket objects will use,
- unless explicitly changed.
- """
- global _defaultproxy
- _defaultproxy = (proxytype, addr, port, rdns, username, password)
-
-
-def wrapmodule(module):
- """wrapmodule(module)
-
- Attempts to replace a module's socket library with a SOCKS socket. Must set
- a default proxy using setdefaultproxy(...) first.
- This will only work on modules that import socket directly into the
- namespace;
- most of the Python Standard Library falls into this category.
- """
- if _defaultproxy != None:
- module.socket.socket = socksocket
- else:
- raise GeneralProxyError((4, "no proxy specified"))
-
-
-class socksocket(socket.socket):
- """socksocket([family[, type[, proto]]]) -> socket object
- Open a SOCKS enabled socket. The parameters are the same as
- those of the standard socket init. In order for SOCKS to work,
- you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
- """
-
- def __init__(
- self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None
- ):
- _orgsocket.__init__(self, family, type, proto, _sock)
- if _defaultproxy != None:
- self.__proxy = _defaultproxy
- else:
- self.__proxy = (None, None, None, None, None, None)
- self.__proxysockname = None
- self.__proxypeername = None
- self.__httptunnel = True
-
- def __recvall(self, count):
- """__recvall(count) -> data
- Receive EXACTLY the number of bytes requested from the socket.
- Blocks until the required number of bytes have been received.
- """
- data = self.recv(count)
- while len(data) < count:
- d = self.recv(count - len(data))
- if not d:
- raise GeneralProxyError((0, "connection closed unexpectedly"))
- data = data + d
- return data
-
- def sendall(self, content, *args):
- """ override socket.socket.sendall method to rewrite the header
- for non-tunneling proxies if needed
- """
- if not self.__httptunnel:
- content = self.__rewriteproxy(content)
- return super(socksocket, self).sendall(content, *args)
-
- def __rewriteproxy(self, header):
- """ rewrite HTTP request headers to support non-tunneling proxies
- (i.e. those which do not support the CONNECT method).
- This only works for HTTP (not HTTPS) since HTTPS requires tunneling.
- """
- host, endpt = None, None
- hdrs = header.split("\r\n")
- for hdr in hdrs:
- if hdr.lower().startswith("host:"):
- host = hdr
- elif hdr.lower().startswith("get") or hdr.lower().startswith("post"):
- endpt = hdr
- if host and endpt:
- hdrs.remove(host)
- hdrs.remove(endpt)
- host = host.split(" ")[1]
- endpt = endpt.split(" ")
- if self.__proxy[4] != None and self.__proxy[5] != None:
- hdrs.insert(0, self.__getauthheader())
- hdrs.insert(0, "Host: %s" % host)
- hdrs.insert(0, "%s http://%s%s %s" % (endpt[0], host, endpt[1], endpt[2]))
- return "\r\n".join(hdrs)
-
- def __getauthheader(self):
- auth = self.__proxy[4] + b":" + self.__proxy[5]
- return "Proxy-Authorization: Basic " + base64.b64encode(auth).decode()
-
- def setproxy(
- self,
- proxytype=None,
- addr=None,
- port=None,
- rdns=True,
- username=None,
- password=None,
- headers=None,
- ):
- """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
-
- Sets the proxy to be used.
- proxytype - The type of the proxy to be used. Three types
- are supported: PROXY_TYPE_SOCKS4 (including socks4a),
- PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
- addr - The address of the server (IP or DNS).
- port - The port of the server. Defaults to 1080 for SOCKS
- servers and 8080 for HTTP proxy servers.
- rdns - Should DNS queries be preformed on the remote side
- (rather than the local side). The default is True.
- Note: This has no effect with SOCKS4 servers.
- username - Username to authenticate with to the server.
- The default is no authentication.
- password - Password to authenticate with to the server.
- Only relevant when username is also provided.
- headers - Additional or modified headers for the proxy connect
- request.
- """
- self.__proxy = (
- proxytype,
- addr,
- port,
- rdns,
- username.encode() if username else None,
- password.encode() if password else None,
- headers,
- )
-
- def __negotiatesocks5(self, destaddr, destport):
- """__negotiatesocks5(self,destaddr,destport)
- Negotiates a connection through a SOCKS5 server.
- """
- # First we'll send the authentication packages we support.
- if (self.__proxy[4] != None) and (self.__proxy[5] != None):
- # The username/password details were supplied to the
- # setproxy method so we support the USERNAME/PASSWORD
- # authentication (in addition to the standard none).
- self.sendall(struct.pack("BBBB", 0x05, 0x02, 0x00, 0x02))
- else:
- # No username/password were entered, therefore we
- # only support connections with no authentication.
- self.sendall(struct.pack("BBB", 0x05, 0x01, 0x00))
- # We'll receive the server's response to determine which
- # method was selected
- chosenauth = self.__recvall(2)
- if chosenauth[0:1] != chr(0x05).encode():
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- # Check the chosen authentication method
- if chosenauth[1:2] == chr(0x00).encode():
- # No authentication is required
- pass
- elif chosenauth[1:2] == chr(0x02).encode():
- # Okay, we need to perform a basic username/password
- # authentication.
- packet = bytearray()
- packet.append(0x01)
- packet.append(len(self.__proxy[4]))
- packet.extend(self.__proxy[4])
- packet.append(len(self.__proxy[5]))
- packet.extend(self.__proxy[5])
- self.sendall(packet)
- authstat = self.__recvall(2)
- if authstat[0:1] != chr(0x01).encode():
- # Bad response
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- if authstat[1:2] != chr(0x00).encode():
- # Authentication failed
- self.close()
- raise Socks5AuthError((3, _socks5autherrors[3]))
- # Authentication succeeded
- else:
- # Reaching here is always bad
- self.close()
- if chosenauth[1] == chr(0xFF).encode():
- raise Socks5AuthError((2, _socks5autherrors[2]))
- else:
- raise GeneralProxyError((1, _generalerrors[1]))
- # Now we can request the actual connection
- req = struct.pack("BBB", 0x05, 0x01, 0x00)
- # If the given destination address is an IP address, we'll
- # use the IPv4 address request even if remote resolving was specified.
- try:
- ipaddr = socket.inet_aton(destaddr)
- req = req + chr(0x01).encode() + ipaddr
- except socket.error:
- # Well it's not an IP number, so it's probably a DNS name.
- if self.__proxy[3]:
- # Resolve remotely
- ipaddr = None
- req = (
- req
- + chr(0x03).encode()
- + chr(len(destaddr)).encode()
- + destaddr.encode()
- )
- else:
- # Resolve locally
- ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
- req = req + chr(0x01).encode() + ipaddr
- req = req + struct.pack(">H", destport)
- self.sendall(req)
- # Get the response
- resp = self.__recvall(4)
- if resp[0:1] != chr(0x05).encode():
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- elif resp[1:2] != chr(0x00).encode():
- # Connection failed
- self.close()
- if ord(resp[1:2]) <= 8:
- raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
- else:
- raise Socks5Error((9, _socks5errors[9]))
- # Get the bound address/port
- elif resp[3:4] == chr(0x01).encode():
- boundaddr = self.__recvall(4)
- elif resp[3:4] == chr(0x03).encode():
- resp = resp + self.recv(1)
- boundaddr = self.__recvall(ord(resp[4:5]))
- else:
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- boundport = struct.unpack(">H", self.__recvall(2))[0]
- self.__proxysockname = (boundaddr, boundport)
- if ipaddr != None:
- self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
- else:
- self.__proxypeername = (destaddr, destport)
-
- def getproxysockname(self):
- """getsockname() -> address info
- Returns the bound IP address and port number at the proxy.
- """
- return self.__proxysockname
-
- def getproxypeername(self):
- """getproxypeername() -> address info
- Returns the IP and port number of the proxy.
- """
- return _orgsocket.getpeername(self)
-
- def getpeername(self):
- """getpeername() -> address info
- Returns the IP address and port number of the destination
- machine (note: getproxypeername returns the proxy)
- """
- return self.__proxypeername
-
- def __negotiatesocks4(self, destaddr, destport):
- """__negotiatesocks4(self,destaddr,destport)
- Negotiates a connection through a SOCKS4 server.
- """
- # Check if the destination address provided is an IP address
- rmtrslv = False
- try:
- ipaddr = socket.inet_aton(destaddr)
- except socket.error:
- # It's a DNS name. Check where it should be resolved.
- if self.__proxy[3]:
- ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
- rmtrslv = True
- else:
- ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
- # Construct the request packet
- req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
- # The username parameter is considered userid for SOCKS4
- if self.__proxy[4] != None:
- req = req + self.__proxy[4]
- req = req + chr(0x00).encode()
- # DNS name if remote resolving is required
- # NOTE: This is actually an extension to the SOCKS4 protocol
- # called SOCKS4A and may not be supported in all cases.
- if rmtrslv:
- req = req + destaddr + chr(0x00).encode()
- self.sendall(req)
- # Get the response from the server
- resp = self.__recvall(8)
- if resp[0:1] != chr(0x00).encode():
- # Bad data
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- if resp[1:2] != chr(0x5A).encode():
- # Server returned an error
- self.close()
- if ord(resp[1:2]) in (91, 92, 93):
- self.close()
- raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
- else:
- raise Socks4Error((94, _socks4errors[4]))
- # Get the bound address/port
- self.__proxysockname = (
- socket.inet_ntoa(resp[4:]),
- struct.unpack(">H", resp[2:4])[0],
- )
- if rmtrslv != None:
- self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
- else:
- self.__proxypeername = (destaddr, destport)
-
- def __negotiatehttp(self, destaddr, destport):
- """__negotiatehttp(self,destaddr,destport)
- Negotiates a connection through an HTTP server.
- """
- # If we need to resolve locally, we do this now
- if not self.__proxy[3]:
- addr = socket.gethostbyname(destaddr)
- else:
- addr = destaddr
- headers = ["CONNECT ", addr, ":", str(destport), " HTTP/1.1\r\n"]
- wrote_host_header = False
- wrote_auth_header = False
- if self.__proxy[6] != None:
- for key, val in self.__proxy[6].iteritems():
- headers += [key, ": ", val, "\r\n"]
- wrote_host_header = key.lower() == "host"
- wrote_auth_header = key.lower() == "proxy-authorization"
- if not wrote_host_header:
- headers += ["Host: ", destaddr, "\r\n"]
- if not wrote_auth_header:
- if self.__proxy[4] != None and self.__proxy[5] != None:
- headers += [self.__getauthheader(), "\r\n"]
- headers.append("\r\n")
- self.sendall("".join(headers).encode())
- # We read the response until we get the string "\r\n\r\n"
- resp = self.recv(1)
- while resp.find("\r\n\r\n".encode()) == -1:
- resp = resp + self.recv(1)
- # We just need the first line to check if the connection
- # was successful
- statusline = resp.splitlines()[0].split(" ".encode(), 2)
- if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- try:
- statuscode = int(statusline[1])
- except ValueError:
- self.close()
- raise GeneralProxyError((1, _generalerrors[1]))
- if statuscode != 200:
- self.close()
- raise HTTPError((statuscode, statusline[2]))
- self.__proxysockname = ("0.0.0.0", 0)
- self.__proxypeername = (addr, destport)
-
- def connect(self, destpair):
- """connect(self, despair)
- Connects to the specified destination through a proxy.
- destpar - A tuple of the IP/DNS address and the port number.
- (identical to socket's connect).
- To select the proxy server use setproxy().
- """
- # Do a minimal input check first
- if (
- (not type(destpair) in (list, tuple))
- or (len(destpair) < 2)
- or (not isinstance(destpair[0], (str, bytes)))
- or (type(destpair[1]) != int)
- ):
- raise GeneralProxyError((5, _generalerrors[5]))
- if self.__proxy[0] == PROXY_TYPE_SOCKS5:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 1080
- _orgsocket.connect(self, (self.__proxy[1], portnum))
- self.__negotiatesocks5(destpair[0], destpair[1])
- elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 1080
- _orgsocket.connect(self, (self.__proxy[1], portnum))
- self.__negotiatesocks4(destpair[0], destpair[1])
- elif self.__proxy[0] == PROXY_TYPE_HTTP:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 8080
- _orgsocket.connect(self, (self.__proxy[1], portnum))
- self.__negotiatehttp(destpair[0], destpair[1])
- elif self.__proxy[0] == PROXY_TYPE_HTTP_NO_TUNNEL:
- if self.__proxy[2] != None:
- portnum = self.__proxy[2]
- else:
- portnum = 8080
- _orgsocket.connect(self, (self.__proxy[1], portnum))
- if destpair[1] == 443:
- self.__negotiatehttp(destpair[0], destpair[1])
- else:
- self.__httptunnel = False
- elif self.__proxy[0] == None:
- _orgsocket.connect(self, (destpair[0], destpair[1]))
- else:
- raise GeneralProxyError((4, _generalerrors[4]))
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/codec.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/codec.py
index 1ca9ba6..c855a4d 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/codec.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/codec.py
@@ -1,7 +1,7 @@
from .core import encode, decode, alabel, ulabel, IDNAError
import codecs
import re
-from typing import Tuple, Optional
+from typing import Any, Tuple, Optional
_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]')
@@ -26,24 +26,24 @@ def decode(self, data: bytes, errors: str = 'strict') -> Tuple[str, int]:
return decode(data), len(data)
class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
- def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[str, int]: # type: ignore
+ def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]:
if errors != 'strict':
raise IDNAError('Unsupported error handling \"{}\"'.format(errors))
if not data:
- return "", 0
+ return b'', 0
labels = _unicode_dots_re.split(data)
- trailing_dot = ''
+ trailing_dot = b''
if labels:
if not labels[-1]:
- trailing_dot = '.'
+ trailing_dot = b'.'
del labels[-1]
elif not final:
# Keep potentially unfinished label until the next call
del labels[-1]
if labels:
- trailing_dot = '.'
+ trailing_dot = b'.'
result = []
size = 0
@@ -54,18 +54,21 @@ def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[str, int]
size += len(label)
# Join with U+002E
- result_str = '.'.join(result) + trailing_dot # type: ignore
+ result_bytes = b'.'.join(result) + trailing_dot
size += len(trailing_dot)
- return result_str, size
+ return result_bytes, size
class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
- def _buffer_decode(self, data: str, errors: str, final: bool) -> Tuple[str, int]: # type: ignore
+ def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]:
if errors != 'strict':
raise IDNAError('Unsupported error handling \"{}\"'.format(errors))
if not data:
return ('', 0)
+ if not isinstance(data, str):
+ data = str(data, 'ascii')
+
labels = _unicode_dots_re.split(data)
trailing_dot = ''
if labels:
@@ -99,14 +102,17 @@ class StreamReader(Codec, codecs.StreamReader):
pass
-def getregentry() -> codecs.CodecInfo:
- # Compatibility as a search_function for codecs.register()
+def search_function(name: str) -> Optional[codecs.CodecInfo]:
+ if name != 'idna2008':
+ return None
return codecs.CodecInfo(
- name='idna',
- encode=Codec().encode, # type: ignore
- decode=Codec().decode, # type: ignore
+ name=name,
+ encode=Codec().encode,
+ decode=Codec().decode,
incrementalencoder=IncrementalEncoder,
incrementaldecoder=IncrementalDecoder,
streamwriter=StreamWriter,
streamreader=StreamReader,
)
+
+codecs.register(search_function)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/core.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/core.py
index 55ab967..aaf7d65 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/core.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/core.py
@@ -318,7 +318,7 @@ def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False
status = uts46row[1]
replacement = None # type: Optional[str]
if len(uts46row) == 3:
- replacement = uts46row[2] # type: ignore
+ replacement = uts46row[2]
if (status == 'V' or
(status == 'D' and not transitional) or
(status == '3' and not std3_rules and replacement is None)):
@@ -338,8 +338,11 @@ def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False
def encode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False, transitional: bool = False) -> bytes:
- if isinstance(s, (bytes, bytearray)):
- s = s.decode('ascii')
+ if not isinstance(s, str):
+ try:
+ s = str(s, 'ascii')
+ except UnicodeDecodeError:
+ raise IDNAError('should pass a unicode string to the function rather than a byte string.')
if uts46:
s = uts46_remap(s, std3_rules, transitional)
trailing_dot = False
@@ -369,8 +372,8 @@ def encode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool =
def decode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False) -> str:
try:
- if isinstance(s, (bytes, bytearray)):
- s = s.decode('ascii')
+ if not isinstance(s, str):
+ s = str(s, 'ascii')
except UnicodeDecodeError:
raise IDNAError('Invalid ASCII in A-label')
if uts46:
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/idnadata.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/idnadata.py
index 1b5805d..5cd05d9 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/idnadata.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/idnadata.py
@@ -1,6 +1,6 @@
# This file is automatically generated by tools/idna-data
-__version__ = '14.0.0'
+__version__ = '15.1.0'
scripts = {
'Greek': (
0x37000000374,
@@ -55,12 +55,14 @@
0x16fe200016fe4,
0x16ff000016ff2,
0x200000002a6e0,
- 0x2a7000002b739,
+ 0x2a7000002b73a,
0x2b7400002b81e,
0x2b8200002cea2,
0x2ceb00002ebe1,
+ 0x2ebf00002ee5e,
0x2f8000002fa1e,
0x300000003134b,
+ 0x31350000323b0,
),
'Hebrew': (
0x591000005c8,
@@ -77,6 +79,7 @@
0x304100003097,
0x309d000030a0,
0x1b0010001b120,
+ 0x1b1320001b133,
0x1b1500001b153,
0x1f2000001f201,
),
@@ -93,6 +96,7 @@
0x1affd0001afff,
0x1b0000001b001,
0x1b1200001b123,
+ 0x1b1550001b156,
0x1b1640001b168,
),
}
@@ -1331,7 +1335,7 @@
0xcdd00000cdf,
0xce000000ce4,
0xce600000cf0,
- 0xcf100000cf3,
+ 0xcf100000cf4,
0xd0000000d0d,
0xd0e00000d11,
0xd1200000d45,
@@ -1366,7 +1370,7 @@
0xeb400000ebe,
0xec000000ec5,
0xec600000ec7,
- 0xec800000ece,
+ 0xec800000ecf,
0xed000000eda,
0xede00000ee0,
0xf0000000f01,
@@ -1831,7 +1835,6 @@
0xa7d50000a7d6,
0xa7d70000a7d8,
0xa7d90000a7da,
- 0xa7f20000a7f5,
0xa7f60000a7f8,
0xa7fa0000a828,
0xa82c0000a82d,
@@ -1859,7 +1862,7 @@
0xab200000ab27,
0xab280000ab2f,
0xab300000ab5b,
- 0xab600000ab6a,
+ 0xab600000ab69,
0xabc00000abeb,
0xabec0000abee,
0xabf00000abfa,
@@ -1904,9 +1907,7 @@
0x1060000010737,
0x1074000010756,
0x1076000010768,
- 0x1078000010786,
- 0x10787000107b1,
- 0x107b2000107bb,
+ 0x1078000010781,
0x1080000010806,
0x1080800010809,
0x1080a00010836,
@@ -1943,7 +1944,7 @@
0x10e8000010eaa,
0x10eab00010ead,
0x10eb000010eb2,
- 0x10f0000010f1d,
+ 0x10efd00010f1d,
0x10f2700010f28,
0x10f3000010f51,
0x10f7000010f86,
@@ -1966,7 +1967,7 @@
0x111dc000111dd,
0x1120000011212,
0x1121300011238,
- 0x1123e0001123f,
+ 0x1123e00011242,
0x1128000011287,
0x1128800011289,
0x1128a0001128e,
@@ -2047,11 +2048,16 @@
0x11d9300011d99,
0x11da000011daa,
0x11ee000011ef7,
+ 0x11f0000011f11,
+ 0x11f1200011f3b,
+ 0x11f3e00011f43,
+ 0x11f5000011f5a,
0x11fb000011fb1,
0x120000001239a,
0x1248000012544,
0x12f9000012ff1,
- 0x130000001342f,
+ 0x1300000013430,
+ 0x1344000013456,
0x1440000014647,
0x1680000016a39,
0x16a4000016a5f,
@@ -2079,7 +2085,9 @@
0x1aff50001affc,
0x1affd0001afff,
0x1b0000001b123,
+ 0x1b1320001b133,
0x1b1500001b153,
+ 0x1b1550001b156,
0x1b1640001b168,
0x1b1700001b2fc,
0x1bc000001bc6b,
@@ -2096,17 +2104,21 @@
0x1da9b0001daa0,
0x1daa10001dab0,
0x1df000001df1f,
+ 0x1df250001df2b,
0x1e0000001e007,
0x1e0080001e019,
0x1e01b0001e022,
0x1e0230001e025,
0x1e0260001e02b,
+ 0x1e0300001e06e,
+ 0x1e08f0001e090,
0x1e1000001e12d,
0x1e1300001e13e,
0x1e1400001e14a,
0x1e14e0001e14f,
0x1e2900001e2af,
0x1e2c00001e2fa,
+ 0x1e4d00001e4fa,
0x1e7e00001e7e7,
0x1e7e80001e7ec,
0x1e7ed0001e7ef,
@@ -2115,13 +2127,14 @@
0x1e8d00001e8d7,
0x1e9220001e94c,
0x1e9500001e95a,
- 0x1fbf00001fbfa,
0x200000002a6e0,
- 0x2a7000002b739,
+ 0x2a7000002b73a,
0x2b7400002b81e,
0x2b8200002cea2,
0x2ceb00002ebe1,
+ 0x2ebf00002ee5e,
0x300000003134b,
+ 0x31350000323b0,
),
'CONTEXTJ': (
0x200c0000200e,
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/package_data.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/package_data.py
index f5ea87c..c5b7220 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/package_data.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/package_data.py
@@ -1,2 +1,2 @@
-__version__ = '3.3'
+__version__ = '3.6'
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/uts46data.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/uts46data.py
index 8f65705..6a1eddb 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/uts46data.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/idna/uts46data.py
@@ -7,7 +7,7 @@
"""IDNA Mapping Table from UTS46."""
-__version__ = '14.0.0'
+__version__ = '15.1.0'
def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
return [
(0x0, '3'),
@@ -1300,7 +1300,7 @@ def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xCE6, 'V'),
(0xCF0, 'X'),
(0xCF1, 'V'),
- (0xCF3, 'X'),
+ (0xCF4, 'X'),
(0xD00, 'V'),
(0xD0D, 'X'),
(0xD0E, 'V'),
@@ -1368,7 +1368,7 @@ def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xEC6, 'V'),
(0xEC7, 'X'),
(0xEC8, 'V'),
- (0xECE, 'X'),
+ (0xECF, 'X'),
(0xED0, 'V'),
(0xEDA, 'X'),
(0xEDC, 'M', 'ຫນ'),
@@ -1899,7 +1899,7 @@ def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1E9A, 'M', 'aʾ'),
(0x1E9B, 'M', 'ṡ'),
(0x1E9C, 'V'),
- (0x1E9E, 'M', 'ss'),
+ (0x1E9E, 'M', 'ß'),
(0x1E9F, 'V'),
(0x1EA0, 'M', 'ạ'),
(0x1EA1, 'V'),
@@ -2418,10 +2418,6 @@ def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x222F, 'M', '∮∮'),
(0x2230, 'M', '∮∮∮'),
(0x2231, 'V'),
- (0x2260, '3'),
- (0x2261, 'V'),
- (0x226E, '3'),
- (0x2270, 'V'),
(0x2329, 'M', '〈'),
(0x232A, 'M', '〉'),
(0x232B, 'V'),
@@ -2502,14 +2498,14 @@ def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x24BA, 'M', 'e'),
(0x24BB, 'M', 'f'),
(0x24BC, 'M', 'g'),
- ]
-
-def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x24BD, 'M', 'h'),
(0x24BE, 'M', 'i'),
(0x24BF, 'M', 'j'),
(0x24C0, 'M', 'k'),
+ ]
+
+def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x24C1, 'M', 'l'),
(0x24C2, 'M', 'm'),
(0x24C3, 'M', 'n'),
@@ -2606,14 +2602,14 @@ def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2C26, 'M', 'ⱖ'),
(0x2C27, 'M', 'ⱗ'),
(0x2C28, 'M', 'ⱘ'),
- ]
-
-def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2C29, 'M', 'ⱙ'),
(0x2C2A, 'M', 'ⱚ'),
(0x2C2B, 'M', 'ⱛ'),
(0x2C2C, 'M', 'ⱜ'),
+ ]
+
+def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2C2D, 'M', 'ⱝ'),
(0x2C2E, 'M', 'ⱞ'),
(0x2C2F, 'M', 'ⱟ'),
@@ -2710,14 +2706,14 @@ def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2CC0, 'M', 'ⳁ'),
(0x2CC1, 'V'),
(0x2CC2, 'M', 'ⳃ'),
- ]
-
-def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2CC3, 'V'),
(0x2CC4, 'M', 'ⳅ'),
(0x2CC5, 'V'),
(0x2CC6, 'M', 'ⳇ'),
+ ]
+
+def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2CC7, 'V'),
(0x2CC8, 'M', 'ⳉ'),
(0x2CC9, 'V'),
@@ -2814,14 +2810,14 @@ def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F13, 'M', '勹'),
(0x2F14, 'M', '匕'),
(0x2F15, 'M', '匚'),
- ]
-
-def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2F16, 'M', '匸'),
(0x2F17, 'M', '十'),
(0x2F18, 'M', '卜'),
(0x2F19, 'M', '卩'),
+ ]
+
+def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2F1A, 'M', '厂'),
(0x2F1B, 'M', '厶'),
(0x2F1C, 'M', '又'),
@@ -2918,14 +2914,14 @@ def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F77, 'M', '糸'),
(0x2F78, 'M', '缶'),
(0x2F79, 'M', '网'),
- ]
-
-def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2F7A, 'M', '羊'),
(0x2F7B, 'M', '羽'),
(0x2F7C, 'M', '老'),
(0x2F7D, 'M', '而'),
+ ]
+
+def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2F7E, 'M', '耒'),
(0x2F7F, 'M', '耳'),
(0x2F80, 'M', '聿'),
@@ -3022,14 +3018,14 @@ def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x3036, 'M', '〒'),
(0x3037, 'V'),
(0x3038, 'M', '十'),
- ]
-
-def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x3039, 'M', '卄'),
(0x303A, 'M', '卅'),
(0x303B, 'V'),
(0x3040, 'X'),
+ ]
+
+def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x3041, 'V'),
(0x3097, 'X'),
(0x3099, 'V'),
@@ -3126,14 +3122,14 @@ def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x3182, 'M', 'ᇱ'),
(0x3183, 'M', 'ᇲ'),
(0x3184, 'M', 'ᅗ'),
- ]
-
-def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x3185, 'M', 'ᅘ'),
(0x3186, 'M', 'ᅙ'),
(0x3187, 'M', 'ᆄ'),
(0x3188, 'M', 'ᆅ'),
+ ]
+
+def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x3189, 'M', 'ᆈ'),
(0x318A, 'M', 'ᆑ'),
(0x318B, 'M', 'ᆒ'),
@@ -3230,14 +3226,14 @@ def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x3244, 'M', '問'),
(0x3245, 'M', '幼'),
(0x3246, 'M', '文'),
- ]
-
-def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x3247, 'M', '箏'),
(0x3248, 'V'),
(0x3250, 'M', 'pte'),
(0x3251, 'M', '21'),
+ ]
+
+def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x3252, 'M', '22'),
(0x3253, 'M', '23'),
(0x3254, 'M', '24'),
@@ -3334,14 +3330,14 @@ def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x32AF, 'M', '協'),
(0x32B0, 'M', '夜'),
(0x32B1, 'M', '36'),
- ]
-
-def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x32B2, 'M', '37'),
(0x32B3, 'M', '38'),
(0x32B4, 'M', '39'),
(0x32B5, 'M', '40'),
+ ]
+
+def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x32B6, 'M', '41'),
(0x32B7, 'M', '42'),
(0x32B8, 'M', '43'),
@@ -3438,14 +3434,14 @@ def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x3313, 'M', 'ギルダー'),
(0x3314, 'M', 'キロ'),
(0x3315, 'M', 'キログラム'),
- ]
-
-def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x3316, 'M', 'キロメートル'),
(0x3317, 'M', 'キロワット'),
(0x3318, 'M', 'グラム'),
(0x3319, 'M', 'グラムトン'),
+ ]
+
+def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x331A, 'M', 'クルゼイロ'),
(0x331B, 'M', 'クローネ'),
(0x331C, 'M', 'ケース'),
@@ -3542,14 +3538,14 @@ def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x3377, 'M', 'dm'),
(0x3378, 'M', 'dm2'),
(0x3379, 'M', 'dm3'),
- ]
-
-def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x337A, 'M', 'iu'),
(0x337B, 'M', '平成'),
(0x337C, 'M', '昭和'),
(0x337D, 'M', '大正'),
+ ]
+
+def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x337E, 'M', '明治'),
(0x337F, 'M', '株式会社'),
(0x3380, 'M', 'pa'),
@@ -3646,14 +3642,14 @@ def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x33DB, 'M', 'sr'),
(0x33DC, 'M', 'sv'),
(0x33DD, 'M', 'wb'),
- ]
-
-def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x33DE, 'M', 'v∕m'),
(0x33DF, 'M', 'a∕m'),
(0x33E0, 'M', '1日'),
(0x33E1, 'M', '2日'),
+ ]
+
+def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x33E2, 'M', '3日'),
(0x33E3, 'M', '4日'),
(0x33E4, 'M', '5日'),
@@ -3750,14 +3746,14 @@ def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xA68B, 'V'),
(0xA68C, 'M', 'ꚍ'),
(0xA68D, 'V'),
- ]
-
-def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xA68E, 'M', 'ꚏ'),
(0xA68F, 'V'),
(0xA690, 'M', 'ꚑ'),
(0xA691, 'V'),
+ ]
+
+def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xA692, 'M', 'ꚓ'),
(0xA693, 'V'),
(0xA694, 'M', 'ꚕ'),
@@ -3854,14 +3850,14 @@ def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xA779, 'M', 'ꝺ'),
(0xA77A, 'V'),
(0xA77B, 'M', 'ꝼ'),
- ]
-
-def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xA77C, 'V'),
(0xA77D, 'M', 'ᵹ'),
(0xA77E, 'M', 'ꝿ'),
(0xA77F, 'V'),
+ ]
+
+def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xA780, 'M', 'ꞁ'),
(0xA781, 'V'),
(0xA782, 'M', 'ꞃ'),
@@ -3958,14 +3954,14 @@ def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xA878, 'X'),
(0xA880, 'V'),
(0xA8C6, 'X'),
- ]
-
-def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xA8CE, 'V'),
(0xA8DA, 'X'),
(0xA8E0, 'V'),
(0xA954, 'X'),
+ ]
+
+def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xA95F, 'V'),
(0xA97D, 'X'),
(0xA980, 'V'),
@@ -4062,14 +4058,14 @@ def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xABA8, 'M', 'Ꮨ'),
(0xABA9, 'M', 'Ꮩ'),
(0xABAA, 'M', 'Ꮪ'),
- ]
-
-def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xABAB, 'M', 'Ꮫ'),
(0xABAC, 'M', 'Ꮬ'),
(0xABAD, 'M', 'Ꮭ'),
(0xABAE, 'M', 'Ꮮ'),
+ ]
+
+def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xABAF, 'M', 'Ꮯ'),
(0xABB0, 'M', 'Ꮰ'),
(0xABB1, 'M', 'Ꮱ'),
@@ -4166,14 +4162,14 @@ def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xF943, 'M', '弄'),
(0xF944, 'M', '籠'),
(0xF945, 'M', '聾'),
- ]
-
-def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xF946, 'M', '牢'),
(0xF947, 'M', '磊'),
(0xF948, 'M', '賂'),
(0xF949, 'M', '雷'),
+ ]
+
+def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xF94A, 'M', '壘'),
(0xF94B, 'M', '屢'),
(0xF94C, 'M', '樓'),
@@ -4270,14 +4266,14 @@ def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xF9A7, 'M', '獵'),
(0xF9A8, 'M', '令'),
(0xF9A9, 'M', '囹'),
- ]
-
-def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xF9AA, 'M', '寧'),
(0xF9AB, 'M', '嶺'),
(0xF9AC, 'M', '怜'),
(0xF9AD, 'M', '玲'),
+ ]
+
+def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xF9AE, 'M', '瑩'),
(0xF9AF, 'M', '羚'),
(0xF9B0, 'M', '聆'),
@@ -4374,14 +4370,14 @@ def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFA0B, 'M', '廓'),
(0xFA0C, 'M', '兀'),
(0xFA0D, 'M', '嗀'),
- ]
-
-def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFA0E, 'V'),
(0xFA10, 'M', '塚'),
(0xFA11, 'V'),
(0xFA12, 'M', '晴'),
+ ]
+
+def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFA13, 'V'),
(0xFA15, 'M', '凞'),
(0xFA16, 'M', '猪'),
@@ -4478,14 +4474,14 @@ def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFA76, 'M', '勇'),
(0xFA77, 'M', '勺'),
(0xFA78, 'M', '喝'),
- ]
-
-def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFA79, 'M', '啕'),
(0xFA7A, 'M', '喙'),
(0xFA7B, 'M', '嗢'),
(0xFA7C, 'M', '塚'),
+ ]
+
+def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFA7D, 'M', '墳'),
(0xFA7E, 'M', '奄'),
(0xFA7F, 'M', '奔'),
@@ -4582,14 +4578,14 @@ def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFADA, 'X'),
(0xFB00, 'M', 'ff'),
(0xFB01, 'M', 'fi'),
- ]
-
-def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFB02, 'M', 'fl'),
(0xFB03, 'M', 'ffi'),
(0xFB04, 'M', 'ffl'),
(0xFB05, 'M', 'st'),
+ ]
+
+def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFB07, 'X'),
(0xFB13, 'M', 'մն'),
(0xFB14, 'M', 'մե'),
@@ -4686,14 +4682,14 @@ def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFBDB, 'M', 'ۈ'),
(0xFBDD, 'M', 'ۇٴ'),
(0xFBDE, 'M', 'ۋ'),
- ]
-
-def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFBE0, 'M', 'ۅ'),
(0xFBE2, 'M', 'ۉ'),
(0xFBE4, 'M', 'ې'),
(0xFBE8, 'M', 'ى'),
+ ]
+
+def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFBEA, 'M', 'ئا'),
(0xFBEC, 'M', 'ئە'),
(0xFBEE, 'M', 'ئو'),
@@ -4790,14 +4786,14 @@ def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFC54, 'M', 'هي'),
(0xFC55, 'M', 'يج'),
(0xFC56, 'M', 'يح'),
- ]
-
-def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFC57, 'M', 'يخ'),
(0xFC58, 'M', 'يم'),
(0xFC59, 'M', 'يى'),
(0xFC5A, 'M', 'يي'),
+ ]
+
+def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFC5B, 'M', 'ذٰ'),
(0xFC5C, 'M', 'رٰ'),
(0xFC5D, 'M', 'ىٰ'),
@@ -4894,14 +4890,14 @@ def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFCB8, 'M', 'طح'),
(0xFCB9, 'M', 'ظم'),
(0xFCBA, 'M', 'عج'),
- ]
-
-def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFCBB, 'M', 'عم'),
(0xFCBC, 'M', 'غج'),
(0xFCBD, 'M', 'غم'),
(0xFCBE, 'M', 'فج'),
+ ]
+
+def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFCBF, 'M', 'فح'),
(0xFCC0, 'M', 'فخ'),
(0xFCC1, 'M', 'فم'),
@@ -4998,14 +4994,14 @@ def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFD1C, 'M', 'حي'),
(0xFD1D, 'M', 'جى'),
(0xFD1E, 'M', 'جي'),
- ]
-
-def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFD1F, 'M', 'خى'),
(0xFD20, 'M', 'خي'),
(0xFD21, 'M', 'صى'),
(0xFD22, 'M', 'صي'),
+ ]
+
+def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFD23, 'M', 'ضى'),
(0xFD24, 'M', 'ضي'),
(0xFD25, 'M', 'شج'),
@@ -5102,14 +5098,14 @@ def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFDA4, 'M', 'تمى'),
(0xFDA5, 'M', 'جمي'),
(0xFDA6, 'M', 'جحى'),
- ]
-
-def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFDA7, 'M', 'جمى'),
(0xFDA8, 'M', 'سخى'),
(0xFDA9, 'M', 'صحي'),
(0xFDAA, 'M', 'شحي'),
+ ]
+
+def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFDAB, 'M', 'ضحي'),
(0xFDAC, 'M', 'لجي'),
(0xFDAD, 'M', 'لمي'),
@@ -5206,14 +5202,14 @@ def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFE5B, '3', '{'),
(0xFE5C, '3', '}'),
(0xFE5D, 'M', '〔'),
- ]
-
-def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFE5E, 'M', '〕'),
(0xFE5F, '3', '#'),
(0xFE60, '3', '&'),
(0xFE61, '3', '*'),
+ ]
+
+def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFE62, '3', '+'),
(0xFE63, 'M', '-'),
(0xFE64, '3', '<'),
@@ -5310,14 +5306,14 @@ def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFF18, 'M', '8'),
(0xFF19, 'M', '9'),
(0xFF1A, '3', ':'),
- ]
-
-def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFF1B, '3', ';'),
(0xFF1C, '3', '<'),
(0xFF1D, '3', '='),
(0xFF1E, '3', '>'),
+ ]
+
+def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFF1F, '3', '?'),
(0xFF20, '3', '@'),
(0xFF21, 'M', 'a'),
@@ -5414,14 +5410,14 @@ def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFF7C, 'M', 'シ'),
(0xFF7D, 'M', 'ス'),
(0xFF7E, 'M', 'セ'),
- ]
-
-def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFF7F, 'M', 'ソ'),
(0xFF80, 'M', 'タ'),
(0xFF81, 'M', 'チ'),
(0xFF82, 'M', 'ツ'),
+ ]
+
+def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFF83, 'M', 'テ'),
(0xFF84, 'M', 'ト'),
(0xFF85, 'M', 'ナ'),
@@ -5518,14 +5514,14 @@ def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0xFFE7, 'X'),
(0xFFE8, 'M', '│'),
(0xFFE9, 'M', '←'),
- ]
-
-def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0xFFEA, 'M', '↑'),
(0xFFEB, 'M', '→'),
(0xFFEC, 'M', '↓'),
(0xFFED, 'M', '■'),
+ ]
+
+def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0xFFEE, 'M', '○'),
(0xFFEF, 'X'),
(0x10000, 'V'),
@@ -5622,14 +5618,14 @@ def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x104B3, 'M', '𐓛'),
(0x104B4, 'M', '𐓜'),
(0x104B5, 'M', '𐓝'),
- ]
-
-def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x104B6, 'M', '𐓞'),
(0x104B7, 'M', '𐓟'),
(0x104B8, 'M', '𐓠'),
(0x104B9, 'M', '𐓡'),
+ ]
+
+def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x104BA, 'M', '𐓢'),
(0x104BB, 'M', '𐓣'),
(0x104BC, 'M', '𐓤'),
@@ -5726,14 +5722,14 @@ def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x10786, 'X'),
(0x10787, 'M', 'ʣ'),
(0x10788, 'M', 'ꭦ'),
- ]
-
-def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x10789, 'M', 'ʥ'),
(0x1078A, 'M', 'ʤ'),
(0x1078B, 'M', 'ɖ'),
(0x1078C, 'M', 'ɗ'),
+ ]
+
+def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1078D, 'M', 'ᶑ'),
(0x1078E, 'M', 'ɘ'),
(0x1078F, 'M', 'ɞ'),
@@ -5830,14 +5826,14 @@ def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x10A60, 'V'),
(0x10AA0, 'X'),
(0x10AC0, 'V'),
- ]
-
-def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x10AE7, 'X'),
(0x10AEB, 'V'),
(0x10AF7, 'X'),
(0x10B00, 'V'),
+ ]
+
+def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x10B36, 'X'),
(0x10B39, 'V'),
(0x10B56, 'X'),
@@ -5917,7 +5913,7 @@ def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x10EAE, 'X'),
(0x10EB0, 'V'),
(0x10EB2, 'X'),
- (0x10F00, 'V'),
+ (0x10EFD, 'V'),
(0x10F28, 'X'),
(0x10F30, 'V'),
(0x10F5A, 'X'),
@@ -5934,14 +5930,14 @@ def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1107F, 'V'),
(0x110BD, 'X'),
(0x110BE, 'V'),
- ]
-
-def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x110C3, 'X'),
(0x110D0, 'V'),
(0x110E9, 'X'),
(0x110F0, 'V'),
+ ]
+
+def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x110FA, 'X'),
(0x11100, 'V'),
(0x11135, 'X'),
@@ -5956,7 +5952,7 @@ def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x11200, 'V'),
(0x11212, 'X'),
(0x11213, 'V'),
- (0x1123F, 'X'),
+ (0x11242, 'X'),
(0x11280, 'V'),
(0x11287, 'X'),
(0x11288, 'V'),
@@ -6038,14 +6034,14 @@ def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x118A4, 'M', '𑣄'),
(0x118A5, 'M', '𑣅'),
(0x118A6, 'M', '𑣆'),
- ]
-
-def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x118A7, 'M', '𑣇'),
(0x118A8, 'M', '𑣈'),
(0x118A9, 'M', '𑣉'),
(0x118AA, 'M', '𑣊'),
+ ]
+
+def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x118AB, 'M', '𑣋'),
(0x118AC, 'M', '𑣌'),
(0x118AD, 'M', '𑣍'),
@@ -6097,6 +6093,8 @@ def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x11AA3, 'X'),
(0x11AB0, 'V'),
(0x11AF9, 'X'),
+ (0x11B00, 'V'),
+ (0x11B0A, 'X'),
(0x11C00, 'V'),
(0x11C09, 'X'),
(0x11C0A, 'V'),
@@ -6139,13 +6137,19 @@ def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x11DAA, 'X'),
(0x11EE0, 'V'),
(0x11EF9, 'X'),
- (0x11FB0, 'V'),
- (0x11FB1, 'X'),
- (0x11FC0, 'V'),
+ (0x11F00, 'V'),
+ (0x11F11, 'X'),
+ (0x11F12, 'V'),
+ (0x11F3B, 'X'),
+ (0x11F3E, 'V'),
]
def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
return [
+ (0x11F5A, 'X'),
+ (0x11FB0, 'V'),
+ (0x11FB1, 'X'),
+ (0x11FC0, 'V'),
(0x11FF2, 'X'),
(0x11FFF, 'V'),
(0x1239A, 'X'),
@@ -6158,7 +6162,9 @@ def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x12F90, 'V'),
(0x12FF3, 'X'),
(0x13000, 'V'),
- (0x1342F, 'X'),
+ (0x13430, 'X'),
+ (0x13440, 'V'),
+ (0x13456, 'X'),
(0x14400, 'V'),
(0x14647, 'X'),
(0x16800, 'V'),
@@ -6240,16 +6246,20 @@ def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1AFF5, 'V'),
(0x1AFFC, 'X'),
(0x1AFFD, 'V'),
+ ]
+
+def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1AFFF, 'X'),
(0x1B000, 'V'),
(0x1B123, 'X'),
+ (0x1B132, 'V'),
+ (0x1B133, 'X'),
(0x1B150, 'V'),
(0x1B153, 'X'),
+ (0x1B155, 'V'),
+ (0x1B156, 'X'),
(0x1B164, 'V'),
- ]
-
-def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1B168, 'X'),
(0x1B170, 'V'),
(0x1B2FC, 'X'),
@@ -6295,6 +6305,8 @@ def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D1EB, 'X'),
(0x1D200, 'V'),
(0x1D246, 'X'),
+ (0x1D2C0, 'V'),
+ (0x1D2D4, 'X'),
(0x1D2E0, 'V'),
(0x1D2F4, 'X'),
(0x1D300, 'V'),
@@ -6338,6 +6350,10 @@ def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D422, 'M', 'i'),
(0x1D423, 'M', 'j'),
(0x1D424, 'M', 'k'),
+ ]
+
+def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D425, 'M', 'l'),
(0x1D426, 'M', 'm'),
(0x1D427, 'M', 'n'),
@@ -6350,10 +6366,6 @@ def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D42E, 'M', 'u'),
(0x1D42F, 'M', 'v'),
(0x1D430, 'M', 'w'),
- ]
-
-def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D431, 'M', 'x'),
(0x1D432, 'M', 'y'),
(0x1D433, 'M', 'z'),
@@ -6442,6 +6454,10 @@ def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D486, 'M', 'e'),
(0x1D487, 'M', 'f'),
(0x1D488, 'M', 'g'),
+ ]
+
+def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D489, 'M', 'h'),
(0x1D48A, 'M', 'i'),
(0x1D48B, 'M', 'j'),
@@ -6454,10 +6470,6 @@ def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D492, 'M', 'q'),
(0x1D493, 'M', 'r'),
(0x1D494, 'M', 's'),
- ]
-
-def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D495, 'M', 't'),
(0x1D496, 'M', 'u'),
(0x1D497, 'M', 'v'),
@@ -6546,6 +6558,10 @@ def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D4ED, 'M', 'd'),
(0x1D4EE, 'M', 'e'),
(0x1D4EF, 'M', 'f'),
+ ]
+
+def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D4F0, 'M', 'g'),
(0x1D4F1, 'M', 'h'),
(0x1D4F2, 'M', 'i'),
@@ -6558,10 +6574,6 @@ def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D4F9, 'M', 'p'),
(0x1D4FA, 'M', 'q'),
(0x1D4FB, 'M', 'r'),
- ]
-
-def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D4FC, 'M', 's'),
(0x1D4FD, 'M', 't'),
(0x1D4FE, 'M', 'u'),
@@ -6650,6 +6662,10 @@ def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D554, 'M', 'c'),
(0x1D555, 'M', 'd'),
(0x1D556, 'M', 'e'),
+ ]
+
+def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D557, 'M', 'f'),
(0x1D558, 'M', 'g'),
(0x1D559, 'M', 'h'),
@@ -6662,10 +6678,6 @@ def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D560, 'M', 'o'),
(0x1D561, 'M', 'p'),
(0x1D562, 'M', 'q'),
- ]
-
-def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D563, 'M', 'r'),
(0x1D564, 'M', 's'),
(0x1D565, 'M', 't'),
@@ -6754,6 +6766,10 @@ def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D5B8, 'M', 'y'),
(0x1D5B9, 'M', 'z'),
(0x1D5BA, 'M', 'a'),
+ ]
+
+def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D5BB, 'M', 'b'),
(0x1D5BC, 'M', 'c'),
(0x1D5BD, 'M', 'd'),
@@ -6766,10 +6782,6 @@ def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D5C4, 'M', 'k'),
(0x1D5C5, 'M', 'l'),
(0x1D5C6, 'M', 'm'),
- ]
-
-def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D5C7, 'M', 'n'),
(0x1D5C8, 'M', 'o'),
(0x1D5C9, 'M', 'p'),
@@ -6858,6 +6870,10 @@ def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D61C, 'M', 'u'),
(0x1D61D, 'M', 'v'),
(0x1D61E, 'M', 'w'),
+ ]
+
+def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D61F, 'M', 'x'),
(0x1D620, 'M', 'y'),
(0x1D621, 'M', 'z'),
@@ -6870,10 +6886,6 @@ def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D628, 'M', 'g'),
(0x1D629, 'M', 'h'),
(0x1D62A, 'M', 'i'),
- ]
-
-def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D62B, 'M', 'j'),
(0x1D62C, 'M', 'k'),
(0x1D62D, 'M', 'l'),
@@ -6962,6 +6974,10 @@ def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D680, 'M', 'q'),
(0x1D681, 'M', 'r'),
(0x1D682, 'M', 's'),
+ ]
+
+def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D683, 'M', 't'),
(0x1D684, 'M', 'u'),
(0x1D685, 'M', 'v'),
@@ -6974,10 +6990,6 @@ def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D68C, 'M', 'c'),
(0x1D68D, 'M', 'd'),
(0x1D68E, 'M', 'e'),
- ]
-
-def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D68F, 'M', 'f'),
(0x1D690, 'M', 'g'),
(0x1D691, 'M', 'h'),
@@ -7066,6 +7078,10 @@ def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D6E6, 'M', 'ε'),
(0x1D6E7, 'M', 'ζ'),
(0x1D6E8, 'M', 'η'),
+ ]
+
+def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D6E9, 'M', 'θ'),
(0x1D6EA, 'M', 'ι'),
(0x1D6EB, 'M', 'κ'),
@@ -7078,10 +7094,6 @@ def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D6F2, 'M', 'ρ'),
(0x1D6F3, 'M', 'θ'),
(0x1D6F4, 'M', 'σ'),
- ]
-
-def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D6F5, 'M', 'τ'),
(0x1D6F6, 'M', 'υ'),
(0x1D6F7, 'M', 'φ'),
@@ -7170,6 +7182,10 @@ def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D74C, 'M', 'χ'),
(0x1D74D, 'M', 'ψ'),
(0x1D74E, 'M', 'ω'),
+ ]
+
+def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D74F, 'M', '∂'),
(0x1D750, 'M', 'ε'),
(0x1D751, 'M', 'θ'),
@@ -7182,10 +7198,6 @@ def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D758, 'M', 'γ'),
(0x1D759, 'M', 'δ'),
(0x1D75A, 'M', 'ε'),
- ]
-
-def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D75B, 'M', 'ζ'),
(0x1D75C, 'M', 'η'),
(0x1D75D, 'M', 'θ'),
@@ -7274,6 +7286,10 @@ def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D7B1, 'M', 'θ'),
(0x1D7B2, 'M', 'ι'),
(0x1D7B3, 'M', 'κ'),
+ ]
+
+def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1D7B4, 'M', 'λ'),
(0x1D7B5, 'M', 'μ'),
(0x1D7B6, 'M', 'ν'),
@@ -7286,10 +7302,6 @@ def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1D7BE, 'M', 'υ'),
(0x1D7BF, 'M', 'φ'),
(0x1D7C0, 'M', 'χ'),
- ]
-
-def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1D7C1, 'M', 'ψ'),
(0x1D7C2, 'M', 'ω'),
(0x1D7C3, 'M', '∂'),
@@ -7359,6 +7371,8 @@ def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1DAB0, 'X'),
(0x1DF00, 'V'),
(0x1DF1F, 'X'),
+ (0x1DF25, 'V'),
+ (0x1DF2B, 'X'),
(0x1E000, 'V'),
(0x1E007, 'X'),
(0x1E008, 'V'),
@@ -7369,6 +7383,75 @@ def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1E025, 'X'),
(0x1E026, 'V'),
(0x1E02B, 'X'),
+ (0x1E030, 'M', 'а'),
+ (0x1E031, 'M', 'б'),
+ (0x1E032, 'M', 'в'),
+ (0x1E033, 'M', 'г'),
+ (0x1E034, 'M', 'д'),
+ (0x1E035, 'M', 'е'),
+ (0x1E036, 'M', 'ж'),
+ ]
+
+def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
+ (0x1E037, 'M', 'з'),
+ (0x1E038, 'M', 'и'),
+ (0x1E039, 'M', 'к'),
+ (0x1E03A, 'M', 'л'),
+ (0x1E03B, 'M', 'м'),
+ (0x1E03C, 'M', 'о'),
+ (0x1E03D, 'M', 'п'),
+ (0x1E03E, 'M', 'р'),
+ (0x1E03F, 'M', 'с'),
+ (0x1E040, 'M', 'т'),
+ (0x1E041, 'M', 'у'),
+ (0x1E042, 'M', 'ф'),
+ (0x1E043, 'M', 'х'),
+ (0x1E044, 'M', 'ц'),
+ (0x1E045, 'M', 'ч'),
+ (0x1E046, 'M', 'ш'),
+ (0x1E047, 'M', 'ы'),
+ (0x1E048, 'M', 'э'),
+ (0x1E049, 'M', 'ю'),
+ (0x1E04A, 'M', 'ꚉ'),
+ (0x1E04B, 'M', 'ә'),
+ (0x1E04C, 'M', 'і'),
+ (0x1E04D, 'M', 'ј'),
+ (0x1E04E, 'M', 'ө'),
+ (0x1E04F, 'M', 'ү'),
+ (0x1E050, 'M', 'ӏ'),
+ (0x1E051, 'M', 'а'),
+ (0x1E052, 'M', 'б'),
+ (0x1E053, 'M', 'в'),
+ (0x1E054, 'M', 'г'),
+ (0x1E055, 'M', 'д'),
+ (0x1E056, 'M', 'е'),
+ (0x1E057, 'M', 'ж'),
+ (0x1E058, 'M', 'з'),
+ (0x1E059, 'M', 'и'),
+ (0x1E05A, 'M', 'к'),
+ (0x1E05B, 'M', 'л'),
+ (0x1E05C, 'M', 'о'),
+ (0x1E05D, 'M', 'п'),
+ (0x1E05E, 'M', 'с'),
+ (0x1E05F, 'M', 'у'),
+ (0x1E060, 'M', 'ф'),
+ (0x1E061, 'M', 'х'),
+ (0x1E062, 'M', 'ц'),
+ (0x1E063, 'M', 'ч'),
+ (0x1E064, 'M', 'ш'),
+ (0x1E065, 'M', 'ъ'),
+ (0x1E066, 'M', 'ы'),
+ (0x1E067, 'M', 'ґ'),
+ (0x1E068, 'M', 'і'),
+ (0x1E069, 'M', 'ѕ'),
+ (0x1E06A, 'M', 'џ'),
+ (0x1E06B, 'M', 'ҫ'),
+ (0x1E06C, 'M', 'ꙑ'),
+ (0x1E06D, 'M', 'ұ'),
+ (0x1E06E, 'X'),
+ (0x1E08F, 'V'),
+ (0x1E090, 'X'),
(0x1E100, 'V'),
(0x1E12D, 'X'),
(0x1E130, 'V'),
@@ -7383,6 +7466,8 @@ def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1E2FA, 'X'),
(0x1E2FF, 'V'),
(0x1E300, 'X'),
+ (0x1E4D0, 'V'),
+ (0x1E4FA, 'X'),
(0x1E7E0, 'V'),
(0x1E7E7, 'X'),
(0x1E7E8, 'V'),
@@ -7390,10 +7475,6 @@ def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1E7ED, 'V'),
(0x1E7EF, 'X'),
(0x1E7F0, 'V'),
- ]
-
-def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1E7FF, 'X'),
(0x1E800, 'V'),
(0x1E8C5, 'X'),
@@ -7413,6 +7494,10 @@ def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1E90B, 'M', '𞤭'),
(0x1E90C, 'M', '𞤮'),
(0x1E90D, 'M', '𞤯'),
+ ]
+
+def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1E90E, 'M', '𞤰'),
(0x1E90F, 'M', '𞤱'),
(0x1E910, 'M', '𞤲'),
@@ -7494,10 +7579,6 @@ def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1EE31, 'M', 'ص'),
(0x1EE32, 'M', 'ق'),
(0x1EE33, 'X'),
- ]
-
-def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1EE34, 'M', 'ش'),
(0x1EE35, 'M', 'ت'),
(0x1EE36, 'M', 'ث'),
@@ -7517,6 +7598,10 @@ def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1EE4C, 'X'),
(0x1EE4D, 'M', 'ن'),
(0x1EE4E, 'M', 'س'),
+ ]
+
+def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1EE4F, 'M', 'ع'),
(0x1EE50, 'X'),
(0x1EE51, 'M', 'ص'),
@@ -7598,10 +7683,6 @@ def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1EEA3, 'M', 'د'),
(0x1EEA4, 'X'),
(0x1EEA5, 'M', 'و'),
- ]
-
-def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1EEA6, 'M', 'ز'),
(0x1EEA7, 'M', 'ح'),
(0x1EEA8, 'M', 'ط'),
@@ -7621,6 +7702,10 @@ def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1EEB6, 'M', 'ث'),
(0x1EEB7, 'M', 'خ'),
(0x1EEB8, 'M', 'ذ'),
+ ]
+
+def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1EEB9, 'M', 'ض'),
(0x1EEBA, 'M', 'ظ'),
(0x1EEBB, 'M', 'غ'),
@@ -7702,10 +7787,6 @@ def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1F141, 'M', 'r'),
(0x1F142, 'M', 's'),
(0x1F143, 'M', 't'),
- ]
-
-def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1F144, 'M', 'u'),
(0x1F145, 'M', 'v'),
(0x1F146, 'M', 'w'),
@@ -7725,6 +7806,10 @@ def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1F16D, 'V'),
(0x1F190, 'M', 'dj'),
(0x1F191, 'V'),
+ ]
+
+def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1F1AE, 'X'),
(0x1F1E6, 'V'),
(0x1F200, 'M', 'ほか'),
@@ -7793,23 +7878,19 @@ def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1F266, 'X'),
(0x1F300, 'V'),
(0x1F6D8, 'X'),
- (0x1F6DD, 'V'),
+ (0x1F6DC, 'V'),
(0x1F6ED, 'X'),
(0x1F6F0, 'V'),
(0x1F6FD, 'X'),
(0x1F700, 'V'),
- (0x1F774, 'X'),
- (0x1F780, 'V'),
- (0x1F7D9, 'X'),
+ (0x1F777, 'X'),
+ (0x1F77B, 'V'),
+ (0x1F7DA, 'X'),
(0x1F7E0, 'V'),
(0x1F7EC, 'X'),
(0x1F7F0, 'V'),
(0x1F7F1, 'X'),
(0x1F800, 'V'),
- ]
-
-def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x1F80C, 'X'),
(0x1F810, 'V'),
(0x1F848, 'X'),
@@ -7826,23 +7907,23 @@ def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x1FA60, 'V'),
(0x1FA6E, 'X'),
(0x1FA70, 'V'),
- (0x1FA75, 'X'),
- (0x1FA78, 'V'),
(0x1FA7D, 'X'),
(0x1FA80, 'V'),
- (0x1FA87, 'X'),
+ (0x1FA89, 'X'),
+ ]
+
+def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x1FA90, 'V'),
- (0x1FAAD, 'X'),
- (0x1FAB0, 'V'),
- (0x1FABB, 'X'),
- (0x1FAC0, 'V'),
+ (0x1FABE, 'X'),
+ (0x1FABF, 'V'),
(0x1FAC6, 'X'),
- (0x1FAD0, 'V'),
- (0x1FADA, 'X'),
+ (0x1FACE, 'V'),
+ (0x1FADC, 'X'),
(0x1FAE0, 'V'),
- (0x1FAE8, 'X'),
+ (0x1FAE9, 'X'),
(0x1FAF0, 'V'),
- (0x1FAF7, 'X'),
+ (0x1FAF9, 'X'),
(0x1FB00, 'V'),
(0x1FB93, 'X'),
(0x1FB94, 'V'),
@@ -7861,13 +7942,15 @@ def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x20000, 'V'),
(0x2A6E0, 'X'),
(0x2A700, 'V'),
- (0x2B739, 'X'),
+ (0x2B73A, 'X'),
(0x2B740, 'V'),
(0x2B81E, 'X'),
(0x2B820, 'V'),
(0x2CEA2, 'X'),
(0x2CEB0, 'V'),
(0x2EBE1, 'X'),
+ (0x2EBF0, 'V'),
+ (0x2EE5E, 'X'),
(0x2F800, 'M', '丽'),
(0x2F801, 'M', '丸'),
(0x2F802, 'M', '乁'),
@@ -7910,10 +7993,6 @@ def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F827, 'M', '勤'),
(0x2F828, 'M', '勺'),
(0x2F829, 'M', '包'),
- ]
-
-def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2F82A, 'M', '匆'),
(0x2F82B, 'M', '北'),
(0x2F82C, 'M', '卉'),
@@ -7935,6 +8014,10 @@ def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F83E, 'M', '呈'),
(0x2F83F, 'M', '周'),
(0x2F840, 'M', '咢'),
+ ]
+
+def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2F841, 'M', '哶'),
(0x2F842, 'M', '唐'),
(0x2F843, 'M', '啓'),
@@ -8014,10 +8097,6 @@ def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F88F, 'M', '𪎒'),
(0x2F890, 'M', '廾'),
(0x2F891, 'M', '𢌱'),
- ]
-
-def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2F893, 'M', '舁'),
(0x2F894, 'M', '弢'),
(0x2F896, 'M', '㣇'),
@@ -8039,6 +8118,10 @@ def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F8A6, 'M', '慈'),
(0x2F8A7, 'M', '慌'),
(0x2F8A8, 'M', '慎'),
+ ]
+
+def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2F8A9, 'M', '慌'),
(0x2F8AA, 'M', '慺'),
(0x2F8AB, 'M', '憎'),
@@ -8118,10 +8201,6 @@ def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F8F5, 'M', '殺'),
(0x2F8F6, 'M', '殻'),
(0x2F8F7, 'M', '𣪍'),
- ]
-
-def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2F8F8, 'M', '𡴋'),
(0x2F8F9, 'M', '𣫺'),
(0x2F8FA, 'M', '汎'),
@@ -8143,6 +8222,10 @@ def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F90A, 'M', '㴳'),
(0x2F90B, 'M', '滋'),
(0x2F90C, 'M', '滇'),
+ ]
+
+def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2F90D, 'M', '𣻑'),
(0x2F90E, 'M', '淹'),
(0x2F90F, 'M', '潮'),
@@ -8222,10 +8305,6 @@ def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F95B, 'M', '穏'),
(0x2F95C, 'M', '𥥼'),
(0x2F95D, 'M', '𥪧'),
- ]
-
-def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2F95F, 'X'),
(0x2F960, 'M', '䈂'),
(0x2F961, 'M', '𥮫'),
@@ -8247,6 +8326,10 @@ def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F971, 'M', '䌴'),
(0x2F972, 'M', '𦈨'),
(0x2F973, 'M', '𦉇'),
+ ]
+
+def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2F974, 'M', '䍙'),
(0x2F975, 'M', '𦋙'),
(0x2F976, 'M', '罺'),
@@ -8326,10 +8409,6 @@ def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F9C0, 'M', '蟡'),
(0x2F9C1, 'M', '蠁'),
(0x2F9C2, 'M', '䗹'),
- ]
-
-def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
- return [
(0x2F9C3, 'M', '衠'),
(0x2F9C4, 'M', '衣'),
(0x2F9C5, 'M', '𧙧'),
@@ -8351,6 +8430,10 @@ def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2F9D5, 'M', '賁'),
(0x2F9D6, 'M', '贛'),
(0x2F9D7, 'M', '起'),
+ ]
+
+def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ return [
(0x2F9D8, 'M', '𧼯'),
(0x2F9D9, 'M', '𠠄'),
(0x2F9DA, 'M', '跋'),
@@ -8423,6 +8506,8 @@ def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
(0x2FA1E, 'X'),
(0x30000, 'V'),
(0x3134B, 'X'),
+ (0x31350, 'V'),
+ (0x323B0, 'X'),
(0xE0100, 'I'),
(0xE01F0, 'X'),
]
@@ -8509,4 +8594,5 @@ def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]:
+ _seg_78()
+ _seg_79()
+ _seg_80()
+ + _seg_81()
) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...]
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/__init__.py
index 7713e1e..8147d2f 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/__init__.py
@@ -5,6 +5,7 @@
import sys
import zipp
import email
+import inspect
import pathlib
import operator
import textwrap
@@ -14,10 +15,11 @@
import posixpath
import collections
-from . import _adapters, _meta
+from . import _adapters, _meta, _py39compat
from ._collections import FreezableDefaultDict, Pair
from ._compat import (
NullFinder,
+ StrPath,
install,
pypy_partial,
)
@@ -29,8 +31,7 @@
from importlib import import_module
from importlib.abc import MetaPathFinder
from itertools import starmap
-from typing import List, Mapping, Optional, Union
-
+from typing import Iterable, List, Mapping, Optional, Set, cast
__all__ = [
'Distribution',
@@ -51,11 +52,11 @@
class PackageNotFoundError(ModuleNotFoundError):
"""The package was not found."""
- def __str__(self):
+ def __str__(self) -> str:
return f"No package metadata was found for {self.name}"
@property
- def name(self):
+ def name(self) -> str: # type: ignore[override]
(name,) = self.args
return name
@@ -121,7 +122,7 @@ def read(text, filter_=None):
yield Pair(name, value)
@staticmethod
- def valid(line):
+ def valid(line: str):
return line and not line.startswith('#')
@@ -139,6 +140,7 @@ class DeprecatedTuple:
1
"""
+ # Do not remove prior to 2023-05-01 or Python 3.13
_warn = functools.partial(
warnings.warn,
"EntryPoint tuple interface is deprecated. Access members by name.",
@@ -157,6 +159,15 @@ class EntryPoint(DeprecatedTuple):
See `the packaging docs on entry points
`_
for more information.
+
+ >>> ep = EntryPoint(
+ ... name=None, group=None, value='package.module:attr [extra1, extra2]')
+ >>> ep.module
+ 'package.module'
+ >>> ep.attr
+ 'attr'
+ >>> ep.extras
+ ['extra1', 'extra2']
"""
pattern = re.compile(
@@ -180,9 +191,13 @@ class EntryPoint(DeprecatedTuple):
following the attr, and following any extras.
"""
+ name: str
+ value: str
+ group: str
+
dist: Optional['Distribution'] = None
- def __init__(self, name, value, group):
+ def __init__(self, name: str, value: str, group: str) -> None:
vars(self).update(name=name, value=value, group=group)
def load(self):
@@ -196,36 +211,47 @@ def load(self):
return functools.reduce(getattr, attrs, module)
@property
- def module(self):
+ def module(self) -> str:
match = self.pattern.match(self.value)
+ assert match is not None
return match.group('module')
@property
- def attr(self):
+ def attr(self) -> str:
match = self.pattern.match(self.value)
+ assert match is not None
return match.group('attr')
@property
- def extras(self):
+ def extras(self) -> List[str]:
match = self.pattern.match(self.value)
- return list(re.finditer(r'\w+', match.group('extras') or ''))
+ assert match is not None
+ return re.findall(r'\w+', match.group('extras') or '')
def _for(self, dist):
vars(self).update(dist=dist)
return self
- def __iter__(self):
+ def matches(self, **params):
"""
- Supply iter so one may construct dicts of EntryPoints by name.
+ EntryPoint matches the given parameters.
+
+ >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]')
+ >>> ep.matches(group='foo')
+ True
+ >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]')
+ True
+ >>> ep.matches(group='foo', name='other')
+ False
+ >>> ep.matches()
+ True
+ >>> ep.matches(extras=['extra1', 'extra2'])
+ True
+ >>> ep.matches(module='bing')
+ True
+ >>> ep.matches(attr='bong')
+ True
"""
- msg = (
- "Construction of dict of EntryPoints is deprecated in "
- "favor of EntryPoints."
- )
- warnings.warn(msg, DeprecationWarning)
- return iter((self.name, self))
-
- def matches(self, **params):
attrs = (getattr(self, param) for param in params)
return all(map(operator.eq, params.values(), attrs))
@@ -247,103 +273,21 @@ def __repr__(self):
f'group={self.group!r})'
)
- def __hash__(self):
+ def __hash__(self) -> int:
return hash(self._key())
-class DeprecatedList(list):
- """
- Allow an otherwise immutable object to implement mutability
- for compatibility.
-
- >>> recwarn = getfixture('recwarn')
- >>> dl = DeprecatedList(range(3))
- >>> dl[0] = 1
- >>> dl.append(3)
- >>> del dl[3]
- >>> dl.reverse()
- >>> dl.sort()
- >>> dl.extend([4])
- >>> dl.pop(-1)
- 4
- >>> dl.remove(1)
- >>> dl += [5]
- >>> dl + [6]
- [1, 2, 5, 6]
- >>> dl + (6,)
- [1, 2, 5, 6]
- >>> dl.insert(0, 0)
- >>> dl
- [0, 1, 2, 5]
- >>> dl == [0, 1, 2, 5]
- True
- >>> dl == (0, 1, 2, 5)
- True
- >>> len(recwarn)
- 1
- """
-
- _warn = functools.partial(
- warnings.warn,
- "EntryPoints list interface is deprecated. Cast to list if needed.",
- DeprecationWarning,
- stacklevel=pypy_partial(2),
- )
-
- def _wrap_deprecated_method(method_name: str): # type: ignore
- def wrapped(self, *args, **kwargs):
- self._warn()
- return getattr(super(), method_name)(*args, **kwargs)
-
- return wrapped
-
- for method_name in [
- '__setitem__',
- '__delitem__',
- 'append',
- 'reverse',
- 'extend',
- 'pop',
- 'remove',
- '__iadd__',
- 'insert',
- 'sort',
- ]:
- locals()[method_name] = _wrap_deprecated_method(method_name)
-
- def __add__(self, other):
- if not isinstance(other, tuple):
- self._warn()
- other = tuple(other)
- return self.__class__(tuple(self) + other)
-
- def __eq__(self, other):
- if not isinstance(other, tuple):
- self._warn()
- other = tuple(other)
-
- return tuple(self).__eq__(other)
-
-
-class EntryPoints(DeprecatedList):
+class EntryPoints(tuple):
"""
An immutable collection of selectable EntryPoint objects.
"""
__slots__ = ()
- def __getitem__(self, name): # -> EntryPoint:
+ def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override]
"""
Get the EntryPoint in self matching name.
"""
- if isinstance(name, int):
- warnings.warn(
- "Accessing entry points by index is deprecated. "
- "Cast to tuple if needed.",
- DeprecationWarning,
- stacklevel=2,
- )
- return super().__getitem__(name)
try:
return next(iter(self.select(name=name)))
except StopIteration:
@@ -354,23 +298,19 @@ def select(self, **params):
Select entry points from self that match the
given parameters (typically group and/or name).
"""
- return EntryPoints(ep for ep in self if ep.matches(**params))
+ return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params))
@property
- def names(self):
+ def names(self) -> Set[str]:
"""
Return the set of all names of all entry points.
"""
return {ep.name for ep in self}
@property
- def groups(self):
+ def groups(self) -> Set[str]:
"""
Return the set of all groups of all entry points.
-
- For coverage while SelectableGroups is present.
- >>> EntryPoints().groups
- set()
"""
return {ep.group for ep in self}
@@ -386,130 +326,58 @@ def _from_text(text):
)
-class Deprecated:
- """
- Compatibility add-in for mapping to indicate that
- mapping behavior is deprecated.
-
- >>> recwarn = getfixture('recwarn')
- >>> class DeprecatedDict(Deprecated, dict): pass
- >>> dd = DeprecatedDict(foo='bar')
- >>> dd.get('baz', None)
- >>> dd['foo']
- 'bar'
- >>> list(dd)
- ['foo']
- >>> list(dd.keys())
- ['foo']
- >>> 'foo' in dd
- True
- >>> list(dd.values())
- ['bar']
- >>> len(recwarn)
- 1
- """
-
- _warn = functools.partial(
- warnings.warn,
- "SelectableGroups dict interface is deprecated. Use select.",
- DeprecationWarning,
- stacklevel=pypy_partial(2),
- )
-
- def __getitem__(self, name):
- self._warn()
- return super().__getitem__(name)
-
- def get(self, name, default=None):
- self._warn()
- return super().get(name, default)
-
- def __iter__(self):
- self._warn()
- return super().__iter__()
-
- def __contains__(self, *args):
- self._warn()
- return super().__contains__(*args)
-
- def keys(self):
- self._warn()
- return super().keys()
-
- def values(self):
- self._warn()
- return super().values()
-
-
-class SelectableGroups(Deprecated, dict):
- """
- A backward- and forward-compatible result from
- entry_points that fully implements the dict interface.
- """
-
- @classmethod
- def load(cls, eps):
- by_group = operator.attrgetter('group')
- ordered = sorted(eps, key=by_group)
- grouped = itertools.groupby(ordered, by_group)
- return cls((group, EntryPoints(eps)) for group, eps in grouped)
-
- @property
- def _all(self):
- """
- Reconstruct a list of all entrypoints from the groups.
- """
- groups = super(Deprecated, self).values()
- return EntryPoints(itertools.chain.from_iterable(groups))
-
- @property
- def groups(self):
- return self._all.groups
-
- @property
- def names(self):
- """
- for coverage:
- >>> SelectableGroups().names
- set()
- """
- return self._all.names
-
- def select(self, **params):
- if not params:
- return self
- return self._all.select(**params)
-
-
class PackagePath(pathlib.PurePosixPath):
"""A reference to a path in a package"""
- def read_text(self, encoding='utf-8'):
+ hash: Optional["FileHash"]
+ size: int
+ dist: "Distribution"
+
+ def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override]
with self.locate().open(encoding=encoding) as stream:
return stream.read()
- def read_binary(self):
+ def read_binary(self) -> bytes:
with self.locate().open('rb') as stream:
return stream.read()
- def locate(self):
+ def locate(self) -> pathlib.Path:
"""Return a path-like object for this path"""
return self.dist.locate_file(self)
class FileHash:
- def __init__(self, spec):
+ def __init__(self, spec: str) -> None:
self.mode, _, self.value = spec.partition('=')
- def __repr__(self):
+ def __repr__(self) -> str:
return f''
-class Distribution:
+class DeprecatedNonAbstract:
+ def __new__(cls, *args, **kwargs):
+ all_names = {
+ name for subclass in inspect.getmro(cls) for name in vars(subclass)
+ }
+ abstract = {
+ name
+ for name in all_names
+ if getattr(getattr(cls, name), '__isabstractmethod__', False)
+ }
+ if abstract:
+ warnings.warn(
+ f"Unimplemented abstract methods {abstract}",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return super().__new__(cls)
+
+
+class Distribution(DeprecatedNonAbstract):
"""A Python distribution package."""
@abc.abstractmethod
- def read_text(self, filename):
+ def read_text(self, filename) -> Optional[str]:
"""Attempt to load metadata file given by the name.
:param filename: The name of the file in the distribution info.
@@ -517,14 +385,14 @@ def read_text(self, filename):
"""
@abc.abstractmethod
- def locate_file(self, path):
+ def locate_file(self, path: StrPath) -> pathlib.Path:
"""
Given a path to a file in this distribution, return a path
to it.
"""
@classmethod
- def from_name(cls, name):
+ def from_name(cls, name: str) -> "Distribution":
"""Return the Distribution for the given package name.
:param name: The name of the distribution package to search for.
@@ -532,17 +400,17 @@ def from_name(cls, name):
package, if found.
:raises PackageNotFoundError: When the named package's distribution
metadata cannot be found.
+ :raises ValueError: When an invalid value is supplied for name.
"""
- for resolver in cls._discover_resolvers():
- dists = resolver(DistributionFinder.Context(name=name))
- dist = next(iter(dists), None)
- if dist is not None:
- return dist
- else:
+ if not name:
+ raise ValueError("A distribution name is required.")
+ try:
+ return next(iter(cls.discover(name=name)))
+ except StopIteration:
raise PackageNotFoundError(name)
@classmethod
- def discover(cls, **kwargs):
+ def discover(cls, **kwargs) -> Iterable["Distribution"]:
"""Return an iterable of Distribution objects for all packages.
Pass a ``context`` or pass keyword arguments for constructing
@@ -560,7 +428,7 @@ def discover(cls, **kwargs):
)
@staticmethod
- def at(path):
+ def at(path: StrPath) -> "Distribution":
"""Return a Distribution for the indicated metadata path
:param path: a string or path-like object
@@ -583,7 +451,7 @@ def metadata(self) -> _meta.PackageMetadata:
The returned object will have keys that name the various bits of
metadata. See PEP 566 for details.
"""
- text = (
+ opt_text = (
self.read_text('METADATA')
or self.read_text('PKG-INFO')
# This last clause is here to support old egg-info files. Its
@@ -591,10 +459,11 @@ def metadata(self) -> _meta.PackageMetadata:
# (which points to the egg-info file) attribute unchanged.
or self.read_text('')
)
+ text = cast(str, opt_text)
return _adapters.Message(email.message_from_string(text))
@property
- def name(self):
+ def name(self) -> str:
"""Return the 'Name' metadata for the distribution package."""
return self.metadata['Name']
@@ -604,23 +473,23 @@ def _normalized_name(self):
return Prepared.normalize(self.name)
@property
- def version(self):
+ def version(self) -> str:
"""Return the 'Version' metadata for the distribution package."""
return self.metadata['Version']
@property
- def entry_points(self):
+ def entry_points(self) -> EntryPoints:
return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
@property
- def files(self):
+ def files(self) -> Optional[List[PackagePath]]:
"""Files in this distribution.
:return: List of PackagePath for this distribution or None
Result is `None` if the metadata file that enumerates files
- (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is
- missing.
+ (i.e. RECORD for dist-info, or installed-files.txt or
+ SOURCES.txt for egg-info) is missing.
Result may be empty if the metadata exists but is empty.
"""
@@ -633,9 +502,19 @@ def make_file(name, hash=None, size_str=None):
@pass_none
def make_files(lines):
- return list(starmap(make_file, csv.reader(lines)))
+ return starmap(make_file, csv.reader(lines))
- return make_files(self._read_files_distinfo() or self._read_files_egginfo())
+ @pass_none
+ def skip_missing_files(package_paths):
+ return list(filter(lambda path: path.locate().exists(), package_paths))
+
+ return skip_missing_files(
+ make_files(
+ self._read_files_distinfo()
+ or self._read_files_egginfo_installed()
+ or self._read_files_egginfo_sources()
+ )
+ )
def _read_files_distinfo(self):
"""
@@ -644,16 +523,51 @@ def _read_files_distinfo(self):
text = self.read_text('RECORD')
return text and text.splitlines()
- def _read_files_egginfo(self):
+ def _read_files_egginfo_installed(self):
"""
- SOURCES.txt might contain literal commas, so wrap each line
- in quotes.
+ Read installed-files.txt and return lines in a similar
+ CSV-parsable format as RECORD: each file must be placed
+ relative to the site-packages directory and must also be
+ quoted (since file names can contain literal commas).
+
+ This file is written when the package is installed by pip,
+ but it might not be written for other installation methods.
+ Assume the file is accurate if it exists.
+ """
+ text = self.read_text('installed-files.txt')
+ # Prepend the .egg-info/ subdir to the lines in this file.
+ # But this subdir is only available from PathDistribution's
+ # self._path.
+ subdir = getattr(self, '_path', None)
+ if not text or not subdir:
+ return
+
+ paths = (
+ (subdir / name)
+ .resolve()
+ .relative_to(self.locate_file('').resolve())
+ .as_posix()
+ for name in text.splitlines()
+ )
+ return map('"{}"'.format, paths)
+
+ def _read_files_egginfo_sources(self):
+ """
+ Read SOURCES.txt and return lines in a similar CSV-parsable
+ format as RECORD: each file name must be quoted (since it
+ might contain literal commas).
+
+ Note that SOURCES.txt is not a reliable source for what
+ files are installed by a package. This file is generated
+ for a source archive, and the files that are present
+ there (e.g. setup.py) may not correctly reflect the files
+ that are present after the package has been installed.
"""
text = self.read_text('SOURCES.txt')
return text and map('"{}"'.format, text.splitlines())
@property
- def requires(self):
+ def requires(self) -> Optional[List[str]]:
"""Generated requirements specified for this Distribution"""
reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
return reqs and list(reqs)
@@ -663,7 +577,7 @@ def _read_dist_info_reqs(self):
def _read_egg_info_reqs(self):
source = self.read_text('requires.txt')
- return source and self._deps_from_requires_text(source)
+ return pass_none(self._deps_from_requires_text)(source)
@classmethod
def _deps_from_requires_text(cls, source):
@@ -732,7 +646,7 @@ def __init__(self, **kwargs):
vars(self).update(kwargs)
@property
- def path(self):
+ def path(self) -> List[str]:
"""
The sequence of directory path that a distribution finder
should search.
@@ -743,7 +657,7 @@ def path(self):
return vars(self).get('path', sys.path)
@abc.abstractmethod
- def find_distributions(self, context=Context()):
+ def find_distributions(self, context=Context()) -> Iterable[Distribution]:
"""
Find distributions.
@@ -767,7 +681,7 @@ def __new__(cls, root):
return super().__new__(cls)
def __init__(self, root):
- self.root = str(root)
+ self.root = root
def joinpath(self, child):
return pathlib.Path(self.root, child)
@@ -878,7 +792,9 @@ class MetadataPathFinder(NullFinder, DistributionFinder):
of Python that do not have a PathFinder find_distributions().
"""
- def find_distributions(self, context=DistributionFinder.Context()):
+ def find_distributions(
+ self, context=DistributionFinder.Context()
+ ) -> Iterable["PathDistribution"]:
"""
Find distributions.
@@ -898,19 +814,19 @@ def _search_paths(cls, name, paths):
path.search(prepared) for path in map(FastPath, paths)
)
- def invalidate_caches(cls):
+ def invalidate_caches(cls) -> None:
FastPath.__new__.cache_clear()
class PathDistribution(Distribution):
- def __init__(self, path: SimplePath):
+ def __init__(self, path: SimplePath) -> None:
"""Construct a distribution.
:param path: SimplePath indicating the metadata directory.
"""
self._path = path
- def read_text(self, filename):
+ def read_text(self, filename: StrPath) -> Optional[str]:
with suppress(
FileNotFoundError,
IsADirectoryError,
@@ -920,9 +836,11 @@ def read_text(self, filename):
):
return self._path.joinpath(filename).read_text(encoding='utf-8')
+ return None
+
read_text.__doc__ = Distribution.read_text.__doc__
- def locate_file(self, path):
+ def locate_file(self, path: StrPath) -> pathlib.Path:
return self._path.parent / path
@property
@@ -932,17 +850,30 @@ def _normalized_name(self):
normalized name from the file system path.
"""
stem = os.path.basename(str(self._path))
- return self._name_from_stem(stem) or super()._normalized_name
+ return (
+ pass_none(Prepared.normalize)(self._name_from_stem(stem))
+ or super()._normalized_name
+ )
- def _name_from_stem(self, stem):
- name, ext = os.path.splitext(stem)
+ @staticmethod
+ def _name_from_stem(stem):
+ """
+ >>> PathDistribution._name_from_stem('foo-3.0.egg-info')
+ 'foo'
+ >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info')
+ 'CherryPy'
+ >>> PathDistribution._name_from_stem('face.egg-info')
+ 'face'
+ >>> PathDistribution._name_from_stem('foo.bar')
+ """
+ filename, ext = os.path.splitext(stem)
if ext not in ('.dist-info', '.egg-info'):
return
- name, sep, rest = stem.partition('-')
+ name, sep, rest = filename.partition('-')
return name
-def distribution(distribution_name):
+def distribution(distribution_name) -> Distribution:
"""Get the ``Distribution`` instance for the named package.
:param distribution_name: The name of the distribution package as a string.
@@ -951,7 +882,7 @@ def distribution(distribution_name):
return Distribution.from_name(distribution_name)
-def distributions(**kwargs):
+def distributions(**kwargs) -> Iterable[Distribution]:
"""Get all ``Distribution`` instances in the current environment.
:return: An iterable of ``Distribution`` instances.
@@ -968,7 +899,7 @@ def metadata(distribution_name) -> _meta.PackageMetadata:
return Distribution.from_name(distribution_name).metadata
-def version(distribution_name):
+def version(distribution_name) -> str:
"""Get the version string for the named package.
:param distribution_name: The name of the distribution package to query.
@@ -978,32 +909,31 @@ def version(distribution_name):
return distribution(distribution_name).version
-def entry_points(**params) -> Union[EntryPoints, SelectableGroups]:
+_unique = functools.partial(
+ unique_everseen,
+ key=_py39compat.normalized_name,
+)
+"""
+Wrapper for ``distributions`` to return unique distributions by name.
+"""
+
+
+def entry_points(**params) -> EntryPoints:
"""Return EntryPoint objects for all installed packages.
Pass selection parameters (group or name) to filter the
result to entry points matching those properties (see
EntryPoints.select()).
- For compatibility, returns ``SelectableGroups`` object unless
- selection parameters are supplied. In the future, this function
- will return ``EntryPoints`` instead of ``SelectableGroups``
- even when no selection parameters are supplied.
-
- For maximum future compatibility, pass selection parameters
- or invoke ``.select`` with parameters on the result.
-
- :return: EntryPoints or SelectableGroups for all installed packages.
+ :return: EntryPoints for all installed packages.
"""
- norm_name = operator.attrgetter('_normalized_name')
- unique = functools.partial(unique_everseen, key=norm_name)
eps = itertools.chain.from_iterable(
- dist.entry_points for dist in unique(distributions())
+ dist.entry_points for dist in _unique(distributions())
)
- return SelectableGroups.load(eps).select(**params)
+ return EntryPoints(eps).select(**params)
-def files(distribution_name):
+def files(distribution_name) -> Optional[List[PackagePath]]:
"""Return a list of files for the named package.
:param distribution_name: The name of the distribution package to query.
@@ -1012,11 +942,11 @@ def files(distribution_name):
return distribution(distribution_name).files
-def requires(distribution_name):
+def requires(distribution_name) -> Optional[List[str]]:
"""
Return a list of requirements for the named package.
- :return: An iterator of requirements, suitable for
+ :return: An iterable of requirements, suitable for
packaging.requirement.Requirement.
"""
return distribution(distribution_name).requires
@@ -1043,9 +973,43 @@ def _top_level_declared(dist):
return (dist.read_text('top_level.txt') or '').split()
+def _topmost(name: PackagePath) -> Optional[str]:
+ """
+ Return the top-most parent as long as there is a parent.
+ """
+ top, *rest = name.parts
+ return top if rest else None
+
+
+def _get_toplevel_name(name: PackagePath) -> str:
+ """
+ Infer a possibly importable module name from a name presumed on
+ sys.path.
+
+ >>> _get_toplevel_name(PackagePath('foo.py'))
+ 'foo'
+ >>> _get_toplevel_name(PackagePath('foo'))
+ 'foo'
+ >>> _get_toplevel_name(PackagePath('foo.pyc'))
+ 'foo'
+ >>> _get_toplevel_name(PackagePath('foo/__init__.py'))
+ 'foo'
+ >>> _get_toplevel_name(PackagePath('foo.pth'))
+ 'foo.pth'
+ >>> _get_toplevel_name(PackagePath('foo.dist-info'))
+ 'foo.dist-info'
+ """
+ return _topmost(name) or (
+ # python/typeshed#10328
+ inspect.getmodulename(name) # type: ignore
+ or str(name)
+ )
+
+
def _top_level_inferred(dist):
- return {
- f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name
- for f in always_iterable(dist.files)
- if f.suffix == ".py"
- }
+ opt_names = set(map(_get_toplevel_name, always_iterable(dist.files)))
+
+ def importable_name(name):
+ return '.' not in name
+
+ return filter(importable_name, opt_names)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_adapters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_adapters.py
index aa460d3..e33cba5 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_adapters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_adapters.py
@@ -1,8 +1,20 @@
+import functools
+import warnings
import re
import textwrap
import email.message
from ._text import FoldedCase
+from ._compat import pypy_partial
+
+
+# Do not remove prior to 2024-01-01 or Python 3.14
+_warn = functools.partial(
+ warnings.warn,
+ "Implicit None on return values is deprecated and will raise KeyErrors.",
+ DeprecationWarning,
+ stacklevel=pypy_partial(2),
+)
class Message(email.message.Message):
@@ -39,6 +51,16 @@ def __init__(self, *args, **kwargs):
def __iter__(self):
return super().__iter__()
+ def __getitem__(self, item):
+ """
+ Warn users that a ``KeyError`` can be expected when a
+ mising key is supplied. Ref python/importlib_metadata#371.
+ """
+ res = super().__getitem__(item)
+ if res is None:
+ _warn()
+ return res
+
def _repair_headers(self):
def redent(value):
"Correct for RFC822 indentation"
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_compat.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_compat.py
index 8fe4e4e..b7abd09 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_compat.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_compat.py
@@ -1,6 +1,9 @@
+import os
import sys
import platform
+from typing import Union
+
__all__ = ['install', 'NullFinder', 'Protocol']
@@ -8,6 +11,7 @@
try:
from typing import Protocol
except ImportError: # pragma: no cover
+ # Python 3.7 compatibility
from typing_extensions import Protocol # type: ignore
@@ -52,14 +56,6 @@ class NullFinder:
def find_spec(*args, **kwargs):
return None
- # In Python 2, the import system requires finders
- # to have a find_module() method, but this usage
- # is deprecated in Python 3 in favor of find_spec().
- # For the purposes of this finder (i.e. being present
- # on sys.meta_path but having no other import
- # system functionality), the two methods are identical.
- find_module = find_spec
-
def pypy_partial(val):
"""
@@ -69,3 +65,10 @@ def pypy_partial(val):
"""
is_pypy = platform.python_implementation() == 'PyPy'
return val + is_pypy
+
+
+if sys.version_info >= (3, 9):
+ StrPath = Union[str, os.PathLike[str]]
+else:
+ # PathLike is only subscriptable at runtime in 3.9+
+ StrPath = Union[str, "os.PathLike[str]"] # pragma: no cover
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_meta.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_meta.py
index 37ee43e..0c7e879 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_meta.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/importlib_metadata/_meta.py
@@ -1,5 +1,5 @@
from ._compat import Protocol
-from typing import Any, Dict, Iterator, List, TypeVar, Union
+from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload
_T = TypeVar("_T")
@@ -18,7 +18,21 @@ def __getitem__(self, key: str) -> str:
def __iter__(self) -> Iterator[str]:
... # pragma: no cover
- def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]:
+ @overload
+ def get(self, name: str, failobj: None = None) -> Optional[str]:
+ ... # pragma: no cover
+
+ @overload
+ def get(self, name: str, failobj: _T) -> Union[str, _T]:
+ ... # pragma: no cover
+
+ # overload per python/importlib_metadata#435
+ @overload
+ def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]:
+ ... # pragma: no cover
+
+ @overload
+ def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]:
"""
Return all values associated with a possibly multi-valued key.
"""
@@ -30,18 +44,19 @@ def json(self) -> Dict[str, Union[str, List[str]]]:
"""
-class SimplePath(Protocol):
+class SimplePath(Protocol[_T]):
"""
A minimal subset of pathlib.Path required by PathDistribution.
"""
- def joinpath(self) -> 'SimplePath':
+ def joinpath(self, other: Union[str, _T]) -> _T:
... # pragma: no cover
- def __truediv__(self) -> 'SimplePath':
+ def __truediv__(self, other: Union[str, _T]) -> _T:
... # pragma: no cover
- def parent(self) -> 'SimplePath':
+ @property
+ def parent(self) -> _T:
... # pragma: no cover
def read_text(self) -> str:
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/__init__.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/__init__.py
index f17866f..af5d428 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/__init__.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/__init__.py
@@ -1,44 +1,37 @@
-# -*- coding: utf-8 -*-
"""Jinja is a template engine written in pure Python. It provides a
non-XML syntax that supports inline expressions and an optional
sandboxed environment.
"""
-from markupsafe import escape
-from markupsafe import Markup
+from .bccache import BytecodeCache as BytecodeCache
+from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache
+from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache
+from .environment import Environment as Environment
+from .environment import Template as Template
+from .exceptions import TemplateAssertionError as TemplateAssertionError
+from .exceptions import TemplateError as TemplateError
+from .exceptions import TemplateNotFound as TemplateNotFound
+from .exceptions import TemplateRuntimeError as TemplateRuntimeError
+from .exceptions import TemplatesNotFound as TemplatesNotFound
+from .exceptions import TemplateSyntaxError as TemplateSyntaxError
+from .exceptions import UndefinedError as UndefinedError
+from .loaders import BaseLoader as BaseLoader
+from .loaders import ChoiceLoader as ChoiceLoader
+from .loaders import DictLoader as DictLoader
+from .loaders import FileSystemLoader as FileSystemLoader
+from .loaders import FunctionLoader as FunctionLoader
+from .loaders import ModuleLoader as ModuleLoader
+from .loaders import PackageLoader as PackageLoader
+from .loaders import PrefixLoader as PrefixLoader
+from .runtime import ChainableUndefined as ChainableUndefined
+from .runtime import DebugUndefined as DebugUndefined
+from .runtime import make_logging_undefined as make_logging_undefined
+from .runtime import StrictUndefined as StrictUndefined
+from .runtime import Undefined as Undefined
+from .utils import clear_caches as clear_caches
+from .utils import is_undefined as is_undefined
+from .utils import pass_context as pass_context
+from .utils import pass_environment as pass_environment
+from .utils import pass_eval_context as pass_eval_context
+from .utils import select_autoescape as select_autoescape
-from .bccache import BytecodeCache
-from .bccache import FileSystemBytecodeCache
-from .bccache import MemcachedBytecodeCache
-from .environment import Environment
-from .environment import Template
-from .exceptions import TemplateAssertionError
-from .exceptions import TemplateError
-from .exceptions import TemplateNotFound
-from .exceptions import TemplateRuntimeError
-from .exceptions import TemplatesNotFound
-from .exceptions import TemplateSyntaxError
-from .exceptions import UndefinedError
-from .filters import contextfilter
-from .filters import environmentfilter
-from .filters import evalcontextfilter
-from .loaders import BaseLoader
-from .loaders import ChoiceLoader
-from .loaders import DictLoader
-from .loaders import FileSystemLoader
-from .loaders import FunctionLoader
-from .loaders import ModuleLoader
-from .loaders import PackageLoader
-from .loaders import PrefixLoader
-from .runtime import ChainableUndefined
-from .runtime import DebugUndefined
-from .runtime import make_logging_undefined
-from .runtime import StrictUndefined
-from .runtime import Undefined
-from .utils import clear_caches
-from .utils import contextfunction
-from .utils import environmentfunction
-from .utils import evalcontextfunction
-from .utils import is_undefined
-from .utils import select_autoescape
-
-__version__ = "2.11.3"
+__version__ = "3.1.3"
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/_compat.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/_compat.py
deleted file mode 100644
index 1f04495..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/_compat.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# -*- coding: utf-8 -*-
-# flake8: noqa
-import marshal
-import sys
-
-PY2 = sys.version_info[0] == 2
-PYPY = hasattr(sys, "pypy_translation_info")
-_identity = lambda x: x
-
-if not PY2:
- unichr = chr
- range_type = range
- text_type = str
- string_types = (str,)
- integer_types = (int,)
-
- iterkeys = lambda d: iter(d.keys())
- itervalues = lambda d: iter(d.values())
- iteritems = lambda d: iter(d.items())
-
- import pickle
- from io import BytesIO, StringIO
-
- NativeStringIO = StringIO
-
- def reraise(tp, value, tb=None):
- if value.__traceback__ is not tb:
- raise value.with_traceback(tb)
- raise value
-
- ifilter = filter
- imap = map
- izip = zip
- intern = sys.intern
-
- implements_iterator = _identity
- implements_to_string = _identity
- encode_filename = _identity
-
- marshal_dump = marshal.dump
- marshal_load = marshal.load
-
-else:
- unichr = unichr
- text_type = unicode
- range_type = xrange
- string_types = (str, unicode)
- integer_types = (int, long)
-
- iterkeys = lambda d: d.iterkeys()
- itervalues = lambda d: d.itervalues()
- iteritems = lambda d: d.iteritems()
-
- import cPickle as pickle
- from cStringIO import StringIO as BytesIO, StringIO
-
- NativeStringIO = BytesIO
-
- exec("def reraise(tp, value, tb=None):\n raise tp, value, tb")
-
- from itertools import imap, izip, ifilter
-
- intern = intern
-
- def implements_iterator(cls):
- cls.next = cls.__next__
- del cls.__next__
- return cls
-
- def implements_to_string(cls):
- cls.__unicode__ = cls.__str__
- cls.__str__ = lambda x: x.__unicode__().encode("utf-8")
- return cls
-
- def encode_filename(filename):
- if isinstance(filename, unicode):
- return filename.encode("utf-8")
- return filename
-
- def marshal_dump(code, f):
- if isinstance(f, file):
- marshal.dump(code, f)
- else:
- f.write(marshal.dumps(code))
-
- def marshal_load(f):
- if isinstance(f, file):
- return marshal.load(f)
- return marshal.loads(f.read())
-
-
-def with_metaclass(meta, *bases):
- """Create a base class with a metaclass."""
- # This requires a bit of explanation: the basic idea is to make a
- # dummy metaclass for one level of class instantiation that replaces
- # itself with the actual metaclass.
- class metaclass(type):
- def __new__(cls, name, this_bases, d):
- return meta(name, bases, d)
-
- return type.__new__(metaclass, "temporary_class", (), {})
-
-
-try:
- from urllib.parse import quote_from_bytes as url_quote
-except ImportError:
- from urllib import quote as url_quote
-
-
-try:
- from collections import abc
-except ImportError:
- import collections as abc
-
-
-try:
- from os import fspath
-except ImportError:
- try:
- from pathlib import PurePath
- except ImportError:
- PurePath = None
-
- def fspath(path):
- if hasattr(path, "__fspath__"):
- return path.__fspath__()
-
- # Python 3.5 doesn't have __fspath__ yet, use str.
- if PurePath is not None and isinstance(path, PurePath):
- return str(path)
-
- return path
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/_identifier.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/_identifier.py
index 224d544..928c150 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/_identifier.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/_identifier.py
@@ -2,5 +2,5 @@
# generated by scripts/generate_identifier_pattern.py
pattern = re.compile(
- r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣔ-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఃా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑅳𑄴𑆀-𑆂𑆳-𑇊𑇀-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950
+ r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950
)
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/asyncfilters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/asyncfilters.py
deleted file mode 100644
index 3d98dbc..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/asyncfilters.py
+++ /dev/null
@@ -1,158 +0,0 @@
-from functools import wraps
-
-from . import filters
-from .asyncsupport import auto_aiter
-from .asyncsupport import auto_await
-
-
-async def auto_to_seq(value):
- seq = []
- if hasattr(value, "__aiter__"):
- async for item in value:
- seq.append(item)
- else:
- for item in value:
- seq.append(item)
- return seq
-
-
-async def async_select_or_reject(args, kwargs, modfunc, lookup_attr):
- seq, func = filters.prepare_select_or_reject(args, kwargs, modfunc, lookup_attr)
- if seq:
- async for item in auto_aiter(seq):
- if func(item):
- yield item
-
-
-def dualfilter(normal_filter, async_filter):
- wrap_evalctx = False
- if getattr(normal_filter, "environmentfilter", False) is True:
-
- def is_async(args):
- return args[0].is_async
-
- wrap_evalctx = False
- else:
- has_evalctxfilter = getattr(normal_filter, "evalcontextfilter", False) is True
- has_ctxfilter = getattr(normal_filter, "contextfilter", False) is True
- wrap_evalctx = not has_evalctxfilter and not has_ctxfilter
-
- def is_async(args):
- return args[0].environment.is_async
-
- @wraps(normal_filter)
- def wrapper(*args, **kwargs):
- b = is_async(args)
- if wrap_evalctx:
- args = args[1:]
- if b:
- return async_filter(*args, **kwargs)
- return normal_filter(*args, **kwargs)
-
- if wrap_evalctx:
- wrapper.evalcontextfilter = True
-
- wrapper.asyncfiltervariant = True
-
- return wrapper
-
-
-def asyncfiltervariant(original):
- def decorator(f):
- return dualfilter(original, f)
-
- return decorator
-
-
-@asyncfiltervariant(filters.do_first)
-async def do_first(environment, seq):
- try:
- return await auto_aiter(seq).__anext__()
- except StopAsyncIteration:
- return environment.undefined("No first item, sequence was empty.")
-
-
-@asyncfiltervariant(filters.do_groupby)
-async def do_groupby(environment, value, attribute):
- expr = filters.make_attrgetter(environment, attribute)
- return [
- filters._GroupTuple(key, await auto_to_seq(values))
- for key, values in filters.groupby(
- sorted(await auto_to_seq(value), key=expr), expr
- )
- ]
-
-
-@asyncfiltervariant(filters.do_join)
-async def do_join(eval_ctx, value, d=u"", attribute=None):
- return filters.do_join(eval_ctx, await auto_to_seq(value), d, attribute)
-
-
-@asyncfiltervariant(filters.do_list)
-async def do_list(value):
- return await auto_to_seq(value)
-
-
-@asyncfiltervariant(filters.do_reject)
-async def do_reject(*args, **kwargs):
- return async_select_or_reject(args, kwargs, lambda x: not x, False)
-
-
-@asyncfiltervariant(filters.do_rejectattr)
-async def do_rejectattr(*args, **kwargs):
- return async_select_or_reject(args, kwargs, lambda x: not x, True)
-
-
-@asyncfiltervariant(filters.do_select)
-async def do_select(*args, **kwargs):
- return async_select_or_reject(args, kwargs, lambda x: x, False)
-
-
-@asyncfiltervariant(filters.do_selectattr)
-async def do_selectattr(*args, **kwargs):
- return async_select_or_reject(args, kwargs, lambda x: x, True)
-
-
-@asyncfiltervariant(filters.do_map)
-async def do_map(*args, **kwargs):
- seq, func = filters.prepare_map(args, kwargs)
- if seq:
- async for item in auto_aiter(seq):
- yield await auto_await(func(item))
-
-
-@asyncfiltervariant(filters.do_sum)
-async def do_sum(environment, iterable, attribute=None, start=0):
- rv = start
- if attribute is not None:
- func = filters.make_attrgetter(environment, attribute)
- else:
-
- def func(x):
- return x
-
- async for item in auto_aiter(iterable):
- rv += func(item)
- return rv
-
-
-@asyncfiltervariant(filters.do_slice)
-async def do_slice(value, slices, fill_with=None):
- return filters.do_slice(await auto_to_seq(value), slices, fill_with)
-
-
-ASYNC_FILTERS = {
- "first": do_first,
- "groupby": do_groupby,
- "join": do_join,
- "list": do_list,
- # we intentionally do not support do_last because that would be
- # ridiculous
- "reject": do_reject,
- "rejectattr": do_rejectattr,
- "map": do_map,
- "select": do_select,
- "selectattr": do_selectattr,
- "sum": do_sum,
- "slice": do_slice,
-}
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/asyncsupport.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/asyncsupport.py
deleted file mode 100644
index 78ba373..0000000
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/asyncsupport.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# -*- coding: utf-8 -*-
-"""The code for async support. Importing this patches Jinja on supported
-Python versions.
-"""
-import asyncio
-import inspect
-from functools import update_wrapper
-
-from markupsafe import Markup
-
-from .environment import TemplateModule
-from .runtime import LoopContext
-from .utils import concat
-from .utils import internalcode
-from .utils import missing
-
-
-async def concat_async(async_gen):
- rv = []
-
- async def collect():
- async for event in async_gen:
- rv.append(event)
-
- await collect()
- return concat(rv)
-
-
-async def generate_async(self, *args, **kwargs):
- vars = dict(*args, **kwargs)
- try:
- async for event in self.root_render_func(self.new_context(vars)):
- yield event
- except Exception:
- yield self.environment.handle_exception()
-
-
-def wrap_generate_func(original_generate):
- def _convert_generator(self, loop, args, kwargs):
- async_gen = self.generate_async(*args, **kwargs)
- try:
- while 1:
- yield loop.run_until_complete(async_gen.__anext__())
- except StopAsyncIteration:
- pass
-
- def generate(self, *args, **kwargs):
- if not self.environment.is_async:
- return original_generate(self, *args, **kwargs)
- return _convert_generator(self, asyncio.get_event_loop(), args, kwargs)
-
- return update_wrapper(generate, original_generate)
-
-
-async def render_async(self, *args, **kwargs):
- if not self.environment.is_async:
- raise RuntimeError("The environment was not created with async mode enabled.")
-
- vars = dict(*args, **kwargs)
- ctx = self.new_context(vars)
-
- try:
- return await concat_async(self.root_render_func(ctx))
- except Exception:
- return self.environment.handle_exception()
-
-
-def wrap_render_func(original_render):
- def render(self, *args, **kwargs):
- if not self.environment.is_async:
- return original_render(self, *args, **kwargs)
- loop = asyncio.get_event_loop()
- return loop.run_until_complete(self.render_async(*args, **kwargs))
-
- return update_wrapper(render, original_render)
-
-
-def wrap_block_reference_call(original_call):
- @internalcode
- async def async_call(self):
- rv = await concat_async(self._stack[self._depth](self._context))
- if self._context.eval_ctx.autoescape:
- rv = Markup(rv)
- return rv
-
- @internalcode
- def __call__(self):
- if not self._context.environment.is_async:
- return original_call(self)
- return async_call(self)
-
- return update_wrapper(__call__, original_call)
-
-
-def wrap_macro_invoke(original_invoke):
- @internalcode
- async def async_invoke(self, arguments, autoescape):
- rv = await self._func(*arguments)
- if autoescape:
- rv = Markup(rv)
- return rv
-
- @internalcode
- def _invoke(self, arguments, autoescape):
- if not self._environment.is_async:
- return original_invoke(self, arguments, autoescape)
- return async_invoke(self, arguments, autoescape)
-
- return update_wrapper(_invoke, original_invoke)
-
-
-@internalcode
-async def get_default_module_async(self):
- if self._module is not None:
- return self._module
- self._module = rv = await self.make_module_async()
- return rv
-
-
-def wrap_default_module(original_default_module):
- @internalcode
- def _get_default_module(self):
- if self.environment.is_async:
- raise RuntimeError("Template module attribute is unavailable in async mode")
- return original_default_module(self)
-
- return _get_default_module
-
-
-async def make_module_async(self, vars=None, shared=False, locals=None):
- context = self.new_context(vars, shared, locals)
- body_stream = []
- async for item in self.root_render_func(context):
- body_stream.append(item)
- return TemplateModule(self, context, body_stream)
-
-
-def patch_template():
- from . import Template
-
- Template.generate = wrap_generate_func(Template.generate)
- Template.generate_async = update_wrapper(generate_async, Template.generate_async)
- Template.render_async = update_wrapper(render_async, Template.render_async)
- Template.render = wrap_render_func(Template.render)
- Template._get_default_module = wrap_default_module(Template._get_default_module)
- Template._get_default_module_async = get_default_module_async
- Template.make_module_async = update_wrapper(
- make_module_async, Template.make_module_async
- )
-
-
-def patch_runtime():
- from .runtime import BlockReference, Macro
-
- BlockReference.__call__ = wrap_block_reference_call(BlockReference.__call__)
- Macro._invoke = wrap_macro_invoke(Macro._invoke)
-
-
-def patch_filters():
- from .filters import FILTERS
- from .asyncfilters import ASYNC_FILTERS
-
- FILTERS.update(ASYNC_FILTERS)
-
-
-def patch_all():
- patch_template()
- patch_runtime()
- patch_filters()
-
-
-async def auto_await(value):
- if inspect.isawaitable(value):
- return await value
- return value
-
-
-async def auto_aiter(iterable):
- if hasattr(iterable, "__aiter__"):
- async for item in iterable:
- yield item
- return
- for item in iterable:
- yield item
-
-
-class AsyncLoopContext(LoopContext):
- _to_iterator = staticmethod(auto_aiter)
-
- @property
- async def length(self):
- if self._length is not None:
- return self._length
-
- try:
- self._length = len(self._iterable)
- except TypeError:
- iterable = [x async for x in self._iterator]
- self._iterator = self._to_iterator(iterable)
- self._length = len(iterable) + self.index + (self._after is not missing)
-
- return self._length
-
- @property
- async def revindex0(self):
- return await self.length - self.index
-
- @property
- async def revindex(self):
- return await self.length - self.index0
-
- async def _peek_next(self):
- if self._after is not missing:
- return self._after
-
- try:
- self._after = await self._iterator.__anext__()
- except StopAsyncIteration:
- self._after = missing
-
- return self._after
-
- @property
- async def last(self):
- return await self._peek_next() is missing
-
- @property
- async def nextitem(self):
- rv = await self._peek_next()
-
- if rv is missing:
- return self._undefined("there is no next item")
-
- return rv
-
- def __aiter__(self):
- return self
-
- async def __anext__(self):
- if self._after is not missing:
- rv = self._after
- self._after = missing
- else:
- rv = await self._iterator.__anext__()
-
- self.index0 += 1
- self._before = self._current
- self._current = rv
- return rv, self
-
-
-async def make_async_loop_context(iterable, undefined, recurse=None, depth0=0):
- import warnings
-
- warnings.warn(
- "This template must be recompiled with at least Jinja 2.11, or"
- " it will fail in 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- return AsyncLoopContext(iterable, undefined, recurse, depth0)
-
-
-patch_all()
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/bccache.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/bccache.py
index 9c06610..d0ddf56 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/bccache.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/bccache.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""The optional bytecode cache system. This is useful if you have very
complex template situations and the compilation of all those templates
slows down your application too much.
@@ -8,22 +7,30 @@
"""
import errno
import fnmatch
+import marshal
import os
+import pickle
import stat
import sys
import tempfile
+import typing as t
from hashlib import sha1
-from os import listdir
-from os import path
+from io import BytesIO
+from types import CodeType
-from ._compat import BytesIO
-from ._compat import marshal_dump
-from ._compat import marshal_load
-from ._compat import pickle
-from ._compat import text_type
-from .utils import open_if_exists
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+ from .environment import Environment
-bc_version = 4
+ class _MemcachedClient(te.Protocol):
+ def get(self, key: str) -> bytes:
+ ...
+
+ def set(self, key: str, value: bytes, timeout: t.Optional[int] = None) -> None:
+ ...
+
+
+bc_version = 5
# Magic bytes to identify Jinja bytecode cache files. Contains the
# Python major and minor version to avoid loading incompatible bytecode
# if a project upgrades its Python version.
@@ -34,7 +41,7 @@
)
-class Bucket(object):
+class Bucket:
"""Buckets are used to store the bytecode for one template. It's created
and initialized by the bytecode cache and passed to the loading functions.
@@ -43,17 +50,17 @@ class Bucket(object):
cache subclasses don't have to care about cache invalidation.
"""
- def __init__(self, environment, key, checksum):
+ def __init__(self, environment: "Environment", key: str, checksum: str) -> None:
self.environment = environment
self.key = key
self.checksum = checksum
self.reset()
- def reset(self):
+ def reset(self) -> None:
"""Resets the bucket (unloads the bytecode)."""
- self.code = None
+ self.code: t.Optional[CodeType] = None
- def load_bytecode(self, f):
+ def load_bytecode(self, f: t.BinaryIO) -> None:
"""Loads bytecode from a file or file like object."""
# make sure the magic header is correct
magic = f.read(len(bc_magic))
@@ -67,31 +74,31 @@ def load_bytecode(self, f):
return
# if marshal_load fails then we need to reload
try:
- self.code = marshal_load(f)
+ self.code = marshal.load(f)
except (EOFError, ValueError, TypeError):
self.reset()
return
- def write_bytecode(self, f):
+ def write_bytecode(self, f: t.IO[bytes]) -> None:
"""Dump the bytecode into the file or file like object passed."""
if self.code is None:
raise TypeError("can't write empty bucket")
f.write(bc_magic)
pickle.dump(self.checksum, f, 2)
- marshal_dump(self.code, f)
+ marshal.dump(self.code, f)
- def bytecode_from_string(self, string):
- """Load bytecode from a string."""
+ def bytecode_from_string(self, string: bytes) -> None:
+ """Load bytecode from bytes."""
self.load_bytecode(BytesIO(string))
- def bytecode_to_string(self):
- """Return the bytecode as string."""
+ def bytecode_to_string(self) -> bytes:
+ """Return the bytecode as bytes."""
out = BytesIO()
self.write_bytecode(out)
return out.getvalue()
-class BytecodeCache(object):
+class BytecodeCache:
"""To implement your own bytecode cache you have to subclass this class
and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
these methods are passed a :class:`~jinja2.bccache.Bucket`.
@@ -120,41 +127,48 @@ def dump_bytecode(self, bucket):
Jinja.
"""
- def load_bytecode(self, bucket):
+ def load_bytecode(self, bucket: Bucket) -> None:
"""Subclasses have to override this method to load bytecode into a
bucket. If they are not able to find code in the cache for the
bucket, it must not do anything.
"""
raise NotImplementedError()
- def dump_bytecode(self, bucket):
+ def dump_bytecode(self, bucket: Bucket) -> None:
"""Subclasses have to override this method to write the bytecode
from a bucket back to the cache. If it unable to do so it must not
fail silently but raise an exception.
"""
raise NotImplementedError()
- def clear(self):
+ def clear(self) -> None:
"""Clears the cache. This method is not used by Jinja but should be
implemented to allow applications to clear the bytecode cache used
by a particular environment.
"""
- def get_cache_key(self, name, filename=None):
+ def get_cache_key(
+ self, name: str, filename: t.Optional[t.Union[str]] = None
+ ) -> str:
"""Returns the unique hash key for this template name."""
hash = sha1(name.encode("utf-8"))
+
if filename is not None:
- filename = "|" + filename
- if isinstance(filename, text_type):
- filename = filename.encode("utf-8")
- hash.update(filename)
+ hash.update(f"|{filename}".encode())
+
return hash.hexdigest()
- def get_source_checksum(self, source):
+ def get_source_checksum(self, source: str) -> str:
"""Returns a checksum for the source."""
return sha1(source.encode("utf-8")).hexdigest()
- def get_bucket(self, environment, name, filename, source):
+ def get_bucket(
+ self,
+ environment: "Environment",
+ name: str,
+ filename: t.Optional[str],
+ source: str,
+ ) -> Bucket:
"""Return a cache bucket for the given template. All arguments are
mandatory but filename may be `None`.
"""
@@ -164,7 +178,7 @@ def get_bucket(self, environment, name, filename, source):
self.load_bytecode(bucket)
return bucket
- def set_bucket(self, bucket):
+ def set_bucket(self, bucket: Bucket) -> None:
"""Put the bucket into the cache."""
self.dump_bytecode(bucket)
@@ -187,14 +201,16 @@ class FileSystemBytecodeCache(BytecodeCache):
This bytecode cache supports clearing of the cache using the clear method.
"""
- def __init__(self, directory=None, pattern="__jinja2_%s.cache"):
+ def __init__(
+ self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache"
+ ) -> None:
if directory is None:
directory = self._get_default_cache_dir()
self.directory = directory
self.pattern = pattern
- def _get_default_cache_dir(self):
- def _unsafe_dir():
+ def _get_default_cache_dir(self) -> str:
+ def _unsafe_dir() -> "te.NoReturn":
raise RuntimeError(
"Cannot determine safe temp directory. You "
"need to explicitly provide one."
@@ -209,7 +225,7 @@ def _unsafe_dir():
if not hasattr(os, "getuid"):
_unsafe_dir()
- dirname = "_jinja2-cache-%d" % os.getuid()
+ dirname = f"_jinja2-cache-{os.getuid()}"
actual_dir = os.path.join(tmpdir, dirname)
try:
@@ -240,34 +256,72 @@ def _unsafe_dir():
return actual_dir
- def _get_cache_filename(self, bucket):
- return path.join(self.directory, self.pattern % bucket.key)
+ def _get_cache_filename(self, bucket: Bucket) -> str:
+ return os.path.join(self.directory, self.pattern % (bucket.key,))
+
+ def load_bytecode(self, bucket: Bucket) -> None:
+ filename = self._get_cache_filename(bucket)
- def load_bytecode(self, bucket):
- f = open_if_exists(self._get_cache_filename(bucket), "rb")
- if f is not None:
+ # Don't test for existence before opening the file, since the
+ # file could disappear after the test before the open.
+ try:
+ f = open(filename, "rb")
+ except (FileNotFoundError, IsADirectoryError, PermissionError):
+ # PermissionError can occur on Windows when an operation is
+ # in progress, such as calling clear().
+ return
+
+ with f:
+ bucket.load_bytecode(f)
+
+ def dump_bytecode(self, bucket: Bucket) -> None:
+ # Write to a temporary file, then rename to the real name after
+ # writing. This avoids another process reading the file before
+ # it is fully written.
+ name = self._get_cache_filename(bucket)
+ f = tempfile.NamedTemporaryFile(
+ mode="wb",
+ dir=os.path.dirname(name),
+ prefix=os.path.basename(name),
+ suffix=".tmp",
+ delete=False,
+ )
+
+ def remove_silent() -> None:
try:
- bucket.load_bytecode(f)
- finally:
- f.close()
+ os.remove(f.name)
+ except OSError:
+ # Another process may have called clear(). On Windows,
+ # another program may be holding the file open.
+ pass
- def dump_bytecode(self, bucket):
- f = open(self._get_cache_filename(bucket), "wb")
try:
- bucket.write_bytecode(f)
- finally:
- f.close()
+ with f:
+ bucket.write_bytecode(f)
+ except BaseException:
+ remove_silent()
+ raise
- def clear(self):
+ try:
+ os.replace(f.name, name)
+ except OSError:
+ # Another process may have called clear(). On Windows,
+ # another program may be holding the file open.
+ remove_silent()
+ except BaseException:
+ remove_silent()
+ raise
+
+ def clear(self) -> None:
# imported lazily here because google app-engine doesn't support
# write access on the file system and the function does not exist
# normally.
from os import remove
- files = fnmatch.filter(listdir(self.directory), self.pattern % "*")
+ files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",))
for filename in files:
try:
- remove(path.join(self.directory, filename))
+ remove(os.path.join(self.directory, filename))
except OSError:
pass
@@ -284,7 +338,7 @@ class MemcachedBytecodeCache(BytecodeCache):
- `python-memcached `_
(Unfortunately the django cache interface is not compatible because it
- does not support storing binary data, only unicode. You can however pass
+ does not support storing binary data, only text. You can however pass
the underlying cache client to the bytecode cache which is available
as `django.core.cache.cache._client`.)
@@ -319,32 +373,34 @@ class MemcachedBytecodeCache(BytecodeCache):
def __init__(
self,
- client,
- prefix="jinja2/bytecode/",
- timeout=None,
- ignore_memcache_errors=True,
+ client: "_MemcachedClient",
+ prefix: str = "jinja2/bytecode/",
+ timeout: t.Optional[int] = None,
+ ignore_memcache_errors: bool = True,
):
self.client = client
self.prefix = prefix
self.timeout = timeout
self.ignore_memcache_errors = ignore_memcache_errors
- def load_bytecode(self, bucket):
+ def load_bytecode(self, bucket: Bucket) -> None:
try:
code = self.client.get(self.prefix + bucket.key)
except Exception:
if not self.ignore_memcache_errors:
raise
- code = None
- if code is not None:
+ else:
bucket.bytecode_from_string(code)
- def dump_bytecode(self, bucket):
- args = (self.prefix + bucket.key, bucket.bytecode_to_string())
- if self.timeout is not None:
- args += (self.timeout,)
+ def dump_bytecode(self, bucket: Bucket) -> None:
+ key = self.prefix + bucket.key
+ value = bucket.bytecode_to_string()
+
try:
- self.client.set(*args)
+ if self.timeout is not None:
+ self.client.set(key, value, self.timeout)
+ else:
+ self.client.set(key, value)
except Exception:
if not self.ignore_memcache_errors:
raise
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/compiler.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/compiler.py
index 63297b4..ff95c80 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/compiler.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/compiler.py
@@ -1,7 +1,8 @@
-# -*- coding: utf-8 -*-
"""Compiles nodes from the parser into Python code."""
-from collections import namedtuple
+import typing as t
+from contextlib import contextmanager
from functools import update_wrapper
+from io import StringIO
from itertools import chain
from keyword import iskeyword as is_python_keyword
@@ -9,13 +10,6 @@
from markupsafe import Markup
from . import nodes
-from ._compat import imap
-from ._compat import iteritems
-from ._compat import izip
-from ._compat import NativeStringIO
-from ._compat import range_type
-from ._compat import string_types
-from ._compat import text_type
from .exceptions import TemplateAssertionError
from .idtracking import Symbols
from .idtracking import VAR_LOAD_ALIAS
@@ -24,9 +18,16 @@
from .idtracking import VAR_LOAD_UNDEFINED
from .nodes import EvalContext
from .optimizer import Optimizer
+from .utils import _PassArg
from .utils import concat
from .visitor import NodeVisitor
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+ from .environment import Environment
+
+F = t.TypeVar("F", bound=t.Callable[..., t.Any])
+
operators = {
"eq": "==",
"ne": "!=",
@@ -38,79 +39,109 @@
"notin": "not in",
}
-# what method to iterate over items do we want to use for dict iteration
-# in generated code? on 2.x let's go with iteritems, on 3.x with items
-if hasattr(dict, "iteritems"):
- dict_item_iter = "iteritems"
-else:
- dict_item_iter = "items"
-
-code_features = ["division"]
-
-# does this python version support generator stops? (PEP 0479)
-try:
- exec("from __future__ import generator_stop")
- code_features.append("generator_stop")
-except SyntaxError:
- pass
-
-# does this python version support yield from?
-try:
- exec("def f(): yield from x()")
-except SyntaxError:
- supports_yield_from = False
-else:
- supports_yield_from = True
-
-
-def optimizeconst(f):
- def new_func(self, node, frame, **kwargs):
+
+def optimizeconst(f: F) -> F:
+ def new_func(
+ self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any
+ ) -> t.Any:
# Only optimize if the frame is not volatile
- if self.optimized and not frame.eval_ctx.volatile:
+ if self.optimizer is not None and not frame.eval_ctx.volatile:
new_node = self.optimizer.visit(node, frame.eval_ctx)
+
if new_node != node:
return self.visit(new_node, frame)
+
return f(self, node, frame, **kwargs)
- return update_wrapper(new_func, f)
+ return update_wrapper(t.cast(F, new_func), f)
+
+
+def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]:
+ @optimizeconst
+ def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
+ if (
+ self.environment.sandboxed
+ and op in self.environment.intercepted_binops # type: ignore
+ ):
+ self.write(f"environment.call_binop(context, {op!r}, ")
+ self.visit(node.left, frame)
+ self.write(", ")
+ self.visit(node.right, frame)
+ else:
+ self.write("(")
+ self.visit(node.left, frame)
+ self.write(f" {op} ")
+ self.visit(node.right, frame)
+
+ self.write(")")
+
+ return visitor
+
+
+def _make_unop(
+ op: str,
+) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]:
+ @optimizeconst
+ def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
+ if (
+ self.environment.sandboxed
+ and op in self.environment.intercepted_unops # type: ignore
+ ):
+ self.write(f"environment.call_unop(context, {op!r}, ")
+ self.visit(node.node, frame)
+ else:
+ self.write("(" + op)
+ self.visit(node.node, frame)
+
+ self.write(")")
+
+ return visitor
def generate(
- node, environment, name, filename, stream=None, defer_init=False, optimized=True
-):
+ node: nodes.Template,
+ environment: "Environment",
+ name: t.Optional[str],
+ filename: t.Optional[str],
+ stream: t.Optional[t.TextIO] = None,
+ defer_init: bool = False,
+ optimized: bool = True,
+) -> t.Optional[str]:
"""Generate the python source for a node tree."""
if not isinstance(node, nodes.Template):
raise TypeError("Can't compile non template nodes")
+
generator = environment.code_generator_class(
environment, name, filename, stream, defer_init, optimized
)
generator.visit(node)
+
if stream is None:
- return generator.stream.getvalue()
+ return generator.stream.getvalue() # type: ignore
+
+ return None
-def has_safe_repr(value):
+def has_safe_repr(value: t.Any) -> bool:
"""Does the node have a safe representation?"""
if value is None or value is NotImplemented or value is Ellipsis:
return True
- if type(value) in (bool, int, float, complex, range_type, Markup) + string_types:
- return True
- if type(value) in (tuple, list, set, frozenset):
- for item in value:
- if not has_safe_repr(item):
- return False
- return True
- elif type(value) is dict:
- for key, value in iteritems(value):
- if not has_safe_repr(key):
- return False
- if not has_safe_repr(value):
- return False
+
+ if type(value) in {bool, int, float, complex, range, str, Markup}:
return True
+
+ if type(value) in {tuple, list, set, frozenset}:
+ return all(has_safe_repr(v) for v in value)
+
+ if type(value) is dict:
+ return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items())
+
return False
-def find_undeclared(nodes, names):
+def find_undeclared(
+ nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
+) -> t.Set[str]:
"""Check if the names passed are accessed undeclared. The return value
is a set of all the undeclared names from the sequence of names found.
"""
@@ -123,20 +154,49 @@ def find_undeclared(nodes, names):
return visitor.undeclared
-class MacroRef(object):
- def __init__(self, node):
+class MacroRef:
+ def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None:
self.node = node
self.accesses_caller = False
self.accesses_kwargs = False
self.accesses_varargs = False
-class Frame(object):
+class Frame:
"""Holds compile time information for us."""
- def __init__(self, eval_ctx, parent=None, level=None):
+ def __init__(
+ self,
+ eval_ctx: EvalContext,
+ parent: t.Optional["Frame"] = None,
+ level: t.Optional[int] = None,
+ ) -> None:
self.eval_ctx = eval_ctx
- self.symbols = Symbols(parent and parent.symbols or None, level=level)
+
+ # the parent of this frame
+ self.parent = parent
+
+ if parent is None:
+ self.symbols = Symbols(level=level)
+
+ # in some dynamic inheritance situations the compiler needs to add
+ # write tests around output statements.
+ self.require_output_check = False
+
+ # inside some tags we are using a buffer rather than yield statements.
+ # this for example affects {% filter %} or {% macro %}. If a frame
+ # is buffered this variable points to the name of the list used as
+ # buffer.
+ self.buffer: t.Optional[str] = None
+
+ # the name of the block we're in, otherwise None.
+ self.block: t.Optional[str] = None
+
+ else:
+ self.symbols = Symbols(parent.symbols, level=level)
+ self.require_output_check = parent.require_output_check
+ self.buffer = parent.buffer
+ self.block = parent.block
# a toplevel frame is the root + soft frames such as if conditions.
self.toplevel = False
@@ -146,47 +206,40 @@ def __init__(self, eval_ctx, parent=None, level=None):
# situations.
self.rootlevel = False
- # in some dynamic inheritance situations the compiler needs to add
- # write tests around output statements.
- self.require_output_check = parent and parent.require_output_check
-
- # inside some tags we are using a buffer rather than yield statements.
- # this for example affects {% filter %} or {% macro %}. If a frame
- # is buffered this variable points to the name of the list used as
- # buffer.
- self.buffer = None
+ # variables set inside of loops and blocks should not affect outer frames,
+ # but they still needs to be kept track of as part of the active context.
+ self.loop_frame = False
+ self.block_frame = False
- # the name of the block we're in, otherwise None.
- self.block = parent and parent.block or None
+ # track whether the frame is being used in an if-statement or conditional
+ # expression as it determines which errors should be raised during runtime
+ # or compile time.
+ self.soft_frame = False
- # the parent of this frame
- self.parent = parent
-
- if parent is not None:
- self.buffer = parent.buffer
-
- def copy(self):
+ def copy(self) -> "Frame":
"""Create a copy of the current one."""
rv = object.__new__(self.__class__)
rv.__dict__.update(self.__dict__)
rv.symbols = self.symbols.copy()
return rv
- def inner(self, isolated=False):
+ def inner(self, isolated: bool = False) -> "Frame":
"""Return an inner frame."""
if isolated:
return Frame(self.eval_ctx, level=self.symbols.level + 1)
return Frame(self.eval_ctx, self)
- def soft(self):
+ def soft(self) -> "Frame":
"""Return a soft frame. A soft frame may not be modified as
standalone thing as it shares the resources with the frame it
was created of, but it's not a rootlevel frame any longer.
- This is only used to implement if-statements.
+ This is only used to implement if-statements and conditional
+ expressions.
"""
rv = self.copy()
rv.rootlevel = False
+ rv.soft_frame = True
return rv
__copy__ = copy
@@ -199,19 +252,19 @@ class VisitorExit(RuntimeError):
class DependencyFinderVisitor(NodeVisitor):
"""A visitor that collects filter and test calls."""
- def __init__(self):
- self.filters = set()
- self.tests = set()
+ def __init__(self) -> None:
+ self.filters: t.Set[str] = set()
+ self.tests: t.Set[str] = set()
- def visit_Filter(self, node):
+ def visit_Filter(self, node: nodes.Filter) -> None:
self.generic_visit(node)
self.filters.add(node.name)
- def visit_Test(self, node):
+ def visit_Test(self, node: nodes.Test) -> None:
self.generic_visit(node)
self.tests.add(node.name)
- def visit_Block(self, node):
+ def visit_Block(self, node: nodes.Block) -> None:
"""Stop visiting at blocks."""
@@ -221,11 +274,11 @@ class UndeclaredNameVisitor(NodeVisitor):
not stop at closure frames.
"""
- def __init__(self, names):
+ def __init__(self, names: t.Iterable[str]) -> None:
self.names = set(names)
- self.undeclared = set()
+ self.undeclared: t.Set[str] = set()
- def visit_Name(self, node):
+ def visit_Name(self, node: nodes.Name) -> None:
if node.ctx == "load" and node.name in self.names:
self.undeclared.add(node.name)
if self.undeclared == self.names:
@@ -233,7 +286,7 @@ def visit_Name(self, node):
else:
self.names.discard(node.name)
- def visit_Block(self, node):
+ def visit_Block(self, node: nodes.Block) -> None:
"""Stop visiting a blocks."""
@@ -246,26 +299,33 @@ class CompilerExit(Exception):
class CodeGenerator(NodeVisitor):
def __init__(
- self, environment, name, filename, stream=None, defer_init=False, optimized=True
- ):
+ self,
+ environment: "Environment",
+ name: t.Optional[str],
+ filename: t.Optional[str],
+ stream: t.Optional[t.TextIO] = None,
+ defer_init: bool = False,
+ optimized: bool = True,
+ ) -> None:
if stream is None:
- stream = NativeStringIO()
+ stream = StringIO()
self.environment = environment
self.name = name
self.filename = filename
self.stream = stream
self.created_block_context = False
self.defer_init = defer_init
- self.optimized = optimized
+ self.optimizer: t.Optional[Optimizer] = None
+
if optimized:
self.optimizer = Optimizer(environment)
# aliases for imports
- self.import_aliases = {}
+ self.import_aliases: t.Dict[str, str] = {}
# a registry for all blocks. Because blocks are moved out
# into the global python scope they are registered here
- self.blocks = {}
+ self.blocks: t.Dict[str, nodes.Block] = {}
# the number of extends statements so far
self.extends_so_far = 0
@@ -279,12 +339,12 @@ def __init__(
self.code_lineno = 1
# registry of all filters and tests (global, not block local)
- self.tests = {}
- self.filters = {}
+ self.tests: t.Dict[str, str] = {}
+ self.filters: t.Dict[str, str] = {}
# the debug information
- self.debug_info = []
- self._write_debug_info = None
+ self.debug_info: t.List[t.Tuple[int, int]] = []
+ self._write_debug_info: t.Optional[int] = None
# the number of new lines before the next write()
self._new_lines = 0
@@ -303,75 +363,83 @@ def __init__(
self._indentation = 0
# Tracks toplevel assignments
- self._assign_stack = []
+ self._assign_stack: t.List[t.Set[str]] = []
# Tracks parameter definition blocks
- self._param_def_block = []
+ self._param_def_block: t.List[t.Set[str]] = []
# Tracks the current context.
self._context_reference_stack = ["context"]
+ @property
+ def optimized(self) -> bool:
+ return self.optimizer is not None
+
# -- Various compilation helpers
- def fail(self, msg, lineno):
+ def fail(self, msg: str, lineno: int) -> "te.NoReturn":
"""Fail with a :exc:`TemplateAssertionError`."""
raise TemplateAssertionError(msg, lineno, self.name, self.filename)
- def temporary_identifier(self):
+ def temporary_identifier(self) -> str:
"""Get a new unique identifier."""
self._last_identifier += 1
- return "t_%d" % self._last_identifier
+ return f"t_{self._last_identifier}"
- def buffer(self, frame):
+ def buffer(self, frame: Frame) -> None:
"""Enable buffering for the frame from that point onwards."""
frame.buffer = self.temporary_identifier()
- self.writeline("%s = []" % frame.buffer)
+ self.writeline(f"{frame.buffer} = []")
- def return_buffer_contents(self, frame, force_unescaped=False):
+ def return_buffer_contents(
+ self, frame: Frame, force_unescaped: bool = False
+ ) -> None:
"""Return the buffer contents of the frame."""
if not force_unescaped:
if frame.eval_ctx.volatile:
self.writeline("if context.eval_ctx.autoescape:")
self.indent()
- self.writeline("return Markup(concat(%s))" % frame.buffer)
+ self.writeline(f"return Markup(concat({frame.buffer}))")
self.outdent()
self.writeline("else:")
self.indent()
- self.writeline("return concat(%s)" % frame.buffer)
+ self.writeline(f"return concat({frame.buffer})")
self.outdent()
return
elif frame.eval_ctx.autoescape:
- self.writeline("return Markup(concat(%s))" % frame.buffer)
+ self.writeline(f"return Markup(concat({frame.buffer}))")
return
- self.writeline("return concat(%s)" % frame.buffer)
+ self.writeline(f"return concat({frame.buffer})")
- def indent(self):
+ def indent(self) -> None:
"""Indent by one."""
self._indentation += 1
- def outdent(self, step=1):
+ def outdent(self, step: int = 1) -> None:
"""Outdent by step."""
self._indentation -= step
- def start_write(self, frame, node=None):
+ def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None:
"""Yield or write into the frame buffer."""
if frame.buffer is None:
self.writeline("yield ", node)
else:
- self.writeline("%s.append(" % frame.buffer, node)
+ self.writeline(f"{frame.buffer}.append(", node)
- def end_write(self, frame):
+ def end_write(self, frame: Frame) -> None:
"""End the writing process started by `start_write`."""
if frame.buffer is not None:
self.write(")")
- def simple_write(self, s, frame, node=None):
+ def simple_write(
+ self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None
+ ) -> None:
"""Simple shortcut for start_write + write + end_write."""
self.start_write(frame, node)
self.write(s)
self.end_write(frame)
- def blockvisit(self, nodes, frame):
+ def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None:
"""Visit a list of nodes as block in a frame. If the current frame
is no buffer a dummy ``if 0: yield None`` is written automatically.
"""
@@ -382,7 +450,7 @@ def blockvisit(self, nodes, frame):
except CompilerExit:
pass
- def write(self, x):
+ def write(self, x: str) -> None:
"""Write a string into the output stream."""
if self._new_lines:
if not self._first_write:
@@ -396,19 +464,26 @@ def write(self, x):
self._new_lines = 0
self.stream.write(x)
- def writeline(self, x, node=None, extra=0):
+ def writeline(
+ self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0
+ ) -> None:
"""Combination of newline and write."""
self.newline(node, extra)
self.write(x)
- def newline(self, node=None, extra=0):
+ def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None:
"""Add one or more newlines before the next write."""
self._new_lines = max(self._new_lines, 1 + extra)
if node is not None and node.lineno != self._last_line:
self._write_debug_info = node.lineno
self._last_line = node.lineno
- def signature(self, node, frame, extra_kwargs=None):
+ def signature(
+ self,
+ node: t.Union[nodes.Call, nodes.Filter, nodes.Test],
+ frame: Frame,
+ extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
+ ) -> None:
"""Writes a function call to the stream for the current node.
A leading comma is added automatically. The extra keyword
arguments may not include python keywords otherwise a syntax
@@ -417,11 +492,10 @@ def signature(self, node, frame, extra_kwargs=None):
"""
# if any of the given keyword arguments is a python keyword
# we have to make sure that no invalid call is created.
- kwarg_workaround = False
- for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
- if is_python_keyword(kwarg):
- kwarg_workaround = True
- break
+ kwarg_workaround = any(
+ is_python_keyword(t.cast(str, k))
+ for k in chain((x.key for x in node.kwargs), extra_kwargs or ())
+ )
for arg in node.args:
self.write(", ")
@@ -432,8 +506,8 @@ def signature(self, node, frame, extra_kwargs=None):
self.write(", ")
self.visit(kwarg, frame)
if extra_kwargs is not None:
- for key, value in iteritems(extra_kwargs):
- self.write(", %s=%s" % (key, value))
+ for key, value in extra_kwargs.items():
+ self.write(f", {key}={value}")
if node.dyn_args:
self.write(", *")
self.visit(node.dyn_args, frame)
@@ -444,12 +518,12 @@ def signature(self, node, frame, extra_kwargs=None):
else:
self.write(", **{")
for kwarg in node.kwargs:
- self.write("%r: " % kwarg.key)
+ self.write(f"{kwarg.key!r}: ")
self.visit(kwarg.value, frame)
self.write(", ")
if extra_kwargs is not None:
- for key, value in iteritems(extra_kwargs):
- self.write("%r: %s, " % (key, value))
+ for key, value in extra_kwargs.items():
+ self.write(f"{key!r}: {value}, ")
if node.dyn_kwargs is not None:
self.write("}, **")
self.visit(node.dyn_kwargs, frame)
@@ -461,50 +535,82 @@ def signature(self, node, frame, extra_kwargs=None):
self.write(", **")
self.visit(node.dyn_kwargs, frame)
- def pull_dependencies(self, nodes):
- """Pull all the dependencies."""
+ def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
+ """Find all filter and test names used in the template and
+ assign them to variables in the compiled namespace. Checking
+ that the names are registered with the environment is done when
+ compiling the Filter and Test nodes. If the node is in an If or
+ CondExpr node, the check is done at runtime instead.
+
+ .. versionchanged:: 3.0
+ Filters and tests in If and CondExpr nodes are checked at
+ runtime instead of compile time.
+ """
visitor = DependencyFinderVisitor()
+
for node in nodes:
visitor.visit(node)
- for dependency in "filters", "tests":
- mapping = getattr(self, dependency)
- for name in getattr(visitor, dependency):
- if name not in mapping:
- mapping[name] = self.temporary_identifier()
+
+ for id_map, names, dependency in (self.filters, visitor.filters, "filters"), (
+ self.tests,
+ visitor.tests,
+ "tests",
+ ):
+ for name in sorted(names):
+ if name not in id_map:
+ id_map[name] = self.temporary_identifier()
+
+ # add check during runtime that dependencies used inside of executed
+ # blocks are defined, as this step may be skipped during compile time
+ self.writeline("try:")
+ self.indent()
+ self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]")
+ self.outdent()
+ self.writeline("except KeyError:")
+ self.indent()
+ self.writeline("@internalcode")
+ self.writeline(f"def {id_map[name]}(*unused):")
+ self.indent()
self.writeline(
- "%s = environment.%s[%r]" % (mapping[name], dependency, name)
+ f'raise TemplateRuntimeError("No {dependency[:-1]}'
+ f' named {name!r} found.")'
)
+ self.outdent()
+ self.outdent()
- def enter_frame(self, frame):
+ def enter_frame(self, frame: Frame) -> None:
undefs = []
- for target, (action, param) in iteritems(frame.symbols.loads):
+ for target, (action, param) in frame.symbols.loads.items():
if action == VAR_LOAD_PARAMETER:
pass
elif action == VAR_LOAD_RESOLVE:
- self.writeline("%s = %s(%r)" % (target, self.get_resolve_func(), param))
+ self.writeline(f"{target} = {self.get_resolve_func()}({param!r})")
elif action == VAR_LOAD_ALIAS:
- self.writeline("%s = %s" % (target, param))
+ self.writeline(f"{target} = {param}")
elif action == VAR_LOAD_UNDEFINED:
undefs.append(target)
else:
raise NotImplementedError("unknown load instruction")
if undefs:
- self.writeline("%s = missing" % " = ".join(undefs))
+ self.writeline(f"{' = '.join(undefs)} = missing")
- def leave_frame(self, frame, with_python_scope=False):
+ def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None:
if not with_python_scope:
undefs = []
- for target, _ in iteritems(frame.symbols.loads):
+ for target in frame.symbols.loads:
undefs.append(target)
if undefs:
- self.writeline("%s = missing" % " = ".join(undefs))
+ self.writeline(f"{' = '.join(undefs)} = missing")
- def func(self, name):
- if self.environment.is_async:
- return "async def %s" % name
- return "def %s" % name
+ def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str:
+ return async_value if self.environment.is_async else sync_value
- def macro_body(self, node, frame):
+ def func(self, name: str) -> str:
+ return f"{self.choose_async()}def {name}"
+
+ def macro_body(
+ self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
+ ) -> t.Tuple[Frame, MacroRef]:
"""Dump the function def of a macro or call block."""
frame = frame.inner()
frame.symbols.analyze_node(node)
@@ -513,6 +619,7 @@ def macro_body(self, node, frame):
explicit_caller = None
skip_special_params = set()
args = []
+
for idx, arg in enumerate(node.args):
if arg.name == "caller":
explicit_caller = idx
@@ -552,7 +659,7 @@ def macro_body(self, node, frame):
# macros are delayed, they never require output checks
frame.require_output_check = False
frame.symbols.analyze_node(node)
- self.writeline("%s(%s):" % (self.func("macro"), ", ".join(args)), node)
+ self.writeline(f"{self.func('macro')}({', '.join(args)}):", node)
self.indent()
self.buffer(frame)
@@ -561,17 +668,17 @@ def macro_body(self, node, frame):
self.push_parameter_definitions(frame)
for idx, arg in enumerate(node.args):
ref = frame.symbols.ref(arg.name)
- self.writeline("if %s is missing:" % ref)
+ self.writeline(f"if {ref} is missing:")
self.indent()
try:
default = node.defaults[idx - len(node.args)]
except IndexError:
self.writeline(
- "%s = undefined(%r, name=%r)"
- % (ref, "parameter %r was not provided" % arg.name, arg.name)
+ f'{ref} = undefined("parameter {arg.name!r} was not provided",'
+ f" name={arg.name!r})"
)
else:
- self.writeline("%s = " % ref)
+ self.writeline(f"{ref} = ")
self.visit(default, frame)
self.mark_parameter_stored(ref)
self.outdent()
@@ -584,50 +691,46 @@ def macro_body(self, node, frame):
return frame, macro_ref
- def macro_def(self, macro_ref, frame):
+ def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
"""Dump the macro definition for the def created by macro_body."""
arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
name = getattr(macro_ref.node, "name", None)
if len(macro_ref.node.args) == 1:
arg_tuple += ","
self.write(
- "Macro(environment, macro, %r, (%s), %r, %r, %r, "
- "context.eval_ctx.autoescape)"
- % (
- name,
- arg_tuple,
- macro_ref.accesses_kwargs,
- macro_ref.accesses_varargs,
- macro_ref.accesses_caller,
- )
+ f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
+ f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
+ f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
)
- def position(self, node):
+ def position(self, node: nodes.Node) -> str:
"""Return a human readable position for the node."""
- rv = "line %d" % node.lineno
+ rv = f"line {node.lineno}"
if self.name is not None:
- rv += " in " + repr(self.name)
+ rv = f"{rv} in {self.name!r}"
return rv
- def dump_local_context(self, frame):
- return "{%s}" % ", ".join(
- "%r: %s" % (name, target)
- for name, target in iteritems(frame.symbols.dump_stores())
+ def dump_local_context(self, frame: Frame) -> str:
+ items_kv = ", ".join(
+ f"{name!r}: {target}"
+ for name, target in frame.symbols.dump_stores().items()
)
+ return f"{{{items_kv}}}"
- def write_commons(self):
+ def write_commons(self) -> None:
"""Writes a common preamble that is used by root and block functions.
Primarily this sets up common local helpers and enforces a generator
through a dead branch.
"""
self.writeline("resolve = context.resolve_or_missing")
self.writeline("undefined = environment.undefined")
+ self.writeline("concat = environment.concat")
# always use the standard Undefined class for the implicit else of
# conditional expressions
self.writeline("cond_expr_undefined = Undefined")
self.writeline("if 0: yield None")
- def push_parameter_definitions(self, frame):
+ def push_parameter_definitions(self, frame: Frame) -> None:
"""Pushes all parameter targets from the given frame into a local
stack that permits tracking of yet to be assigned parameters. In
particular this enables the optimization from `visit_Name` to skip
@@ -636,97 +739,108 @@ def push_parameter_definitions(self, frame):
"""
self._param_def_block.append(frame.symbols.dump_param_targets())
- def pop_parameter_definitions(self):
+ def pop_parameter_definitions(self) -> None:
"""Pops the current parameter definitions set."""
self._param_def_block.pop()
- def mark_parameter_stored(self, target):
+ def mark_parameter_stored(self, target: str) -> None:
"""Marks a parameter in the current parameter definitions as stored.
This will skip the enforced undefined checks.
"""
if self._param_def_block:
self._param_def_block[-1].discard(target)
- def push_context_reference(self, target):
+ def push_context_reference(self, target: str) -> None:
self._context_reference_stack.append(target)
- def pop_context_reference(self):
+ def pop_context_reference(self) -> None:
self._context_reference_stack.pop()
- def get_context_ref(self):
+ def get_context_ref(self) -> str:
return self._context_reference_stack[-1]
- def get_resolve_func(self):
+ def get_resolve_func(self) -> str:
target = self._context_reference_stack[-1]
if target == "context":
return "resolve"
- return "%s.resolve" % target
+ return f"{target}.resolve"
- def derive_context(self, frame):
- return "%s.derived(%s)" % (
- self.get_context_ref(),
- self.dump_local_context(frame),
- )
+ def derive_context(self, frame: Frame) -> str:
+ return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
- def parameter_is_undeclared(self, target):
+ def parameter_is_undeclared(self, target: str) -> bool:
"""Checks if a given target is an undeclared parameter."""
if not self._param_def_block:
return False
return target in self._param_def_block[-1]
- def push_assign_tracking(self):
+ def push_assign_tracking(self) -> None:
"""Pushes a new layer for assignment tracking."""
self._assign_stack.append(set())
- def pop_assign_tracking(self, frame):
+ def pop_assign_tracking(self, frame: Frame) -> None:
"""Pops the topmost level for assignment tracking and updates the
context variables if necessary.
"""
vars = self._assign_stack.pop()
- if not frame.toplevel or not vars:
+ if (
+ not frame.block_frame
+ and not frame.loop_frame
+ and not frame.toplevel
+ or not vars
+ ):
return
public_names = [x for x in vars if x[:1] != "_"]
if len(vars) == 1:
name = next(iter(vars))
ref = frame.symbols.ref(name)
- self.writeline("context.vars[%r] = %s" % (name, ref))
+ if frame.loop_frame:
+ self.writeline(f"_loop_vars[{name!r}] = {ref}")
+ return
+ if frame.block_frame:
+ self.writeline(f"_block_vars[{name!r}] = {ref}")
+ return
+ self.writeline(f"context.vars[{name!r}] = {ref}")
else:
- self.writeline("context.vars.update({")
+ if frame.loop_frame:
+ self.writeline("_loop_vars.update({")
+ elif frame.block_frame:
+ self.writeline("_block_vars.update({")
+ else:
+ self.writeline("context.vars.update({")
for idx, name in enumerate(vars):
if idx:
self.write(", ")
ref = frame.symbols.ref(name)
- self.write("%r: %s" % (name, ref))
+ self.write(f"{name!r}: {ref}")
self.write("})")
- if public_names:
+ if not frame.block_frame and not frame.loop_frame and public_names:
if len(public_names) == 1:
- self.writeline("context.exported_vars.add(%r)" % public_names[0])
+ self.writeline(f"context.exported_vars.add({public_names[0]!r})")
else:
- self.writeline(
- "context.exported_vars.update((%s))"
- % ", ".join(imap(repr, public_names))
- )
+ names_str = ", ".join(map(repr, public_names))
+ self.writeline(f"context.exported_vars.update(({names_str}))")
# -- Statement Visitors
- def visit_Template(self, node, frame=None):
+ def visit_Template(
+ self, node: nodes.Template, frame: t.Optional[Frame] = None
+ ) -> None:
assert frame is None, "no root frame allowed"
eval_ctx = EvalContext(self.environment, self.name)
- from .runtime import exported
-
- self.writeline("from __future__ import %s" % ", ".join(code_features))
- self.writeline("from jinja2.runtime import " + ", ".join(exported))
+ from .runtime import exported, async_exported
if self.environment.is_async:
- self.writeline(
- "from jinja2.asyncsupport import auto_await, "
- "auto_aiter, AsyncLoopContext"
- )
+ exported_names = sorted(exported + async_exported)
+ else:
+ exported_names = sorted(exported)
+
+ self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
# if we want a deferred initialization we cannot move the
# environment into a local name
- envenv = not self.defer_init and ", environment=environment" or ""
+ envenv = "" if self.defer_init else ", environment=environment"
# do we have an extends tag at all? If not, we can save some
# overhead by just not processing any inheritance code.
@@ -735,7 +849,7 @@ def visit_Template(self, node, frame=None):
# find all blocks
for block in node.find_all(nodes.Block):
if block.name in self.blocks:
- self.fail("block %r defined twice" % block.name, block.lineno)
+ self.fail(f"block {block.name!r} defined twice", block.lineno)
self.blocks[block.name] = block
# find all imports and import them
@@ -745,16 +859,16 @@ def visit_Template(self, node, frame=None):
self.import_aliases[imp] = alias = self.temporary_identifier()
if "." in imp:
module, obj = imp.rsplit(".", 1)
- self.writeline("from %s import %s as %s" % (module, obj, alias))
+ self.writeline(f"from {module} import {obj} as {alias}")
else:
- self.writeline("import %s as %s" % (imp, alias))
+ self.writeline(f"import {imp} as {alias}")
# add the load name
- self.writeline("name = %r" % self.name)
+ self.writeline(f"name = {self.name!r}")
# generate the root render function.
self.writeline(
- "%s(context, missing=missing%s):" % (self.func("root"), envenv), extra=1
+ f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
)
self.indent()
self.write_commons()
@@ -763,7 +877,7 @@ def visit_Template(self, node, frame=None):
frame = Frame(eval_ctx)
if "self" in find_undeclared(node.body, ("self",)):
ref = frame.symbols.declare_parameter("self")
- self.writeline("%s = TemplateReference(context)" % ref)
+ self.writeline(f"{ref} = TemplateReference(context)")
frame.symbols.analyze_node(node)
frame.toplevel = frame.rootlevel = True
frame.require_output_check = have_extends and not self.has_known_extends
@@ -781,13 +895,11 @@ def visit_Template(self, node, frame=None):
self.indent()
self.writeline("if parent_template is not None:")
self.indent()
- if supports_yield_from and not self.environment.is_async:
+ if not self.environment.is_async:
self.writeline("yield from parent_template.root_render_func(context)")
else:
self.writeline(
- "%sfor event in parent_template."
- "root_render_func(context):"
- % (self.environment.is_async and "async " or "")
+ "async for event in parent_template.root_render_func(context):"
)
self.indent()
self.writeline("yield event")
@@ -795,10 +907,9 @@ def visit_Template(self, node, frame=None):
self.outdent(1 + (not self.has_known_extends))
# at this point we now have the blocks collected and can visit them too.
- for name, block in iteritems(self.blocks):
+ for name, block in self.blocks.items():
self.writeline(
- "%s(context, missing=missing%s):"
- % (self.func("block_" + name), envenv),
+ f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
block,
1,
)
@@ -808,32 +919,29 @@ def visit_Template(self, node, frame=None):
# toplevel template. This would cause a variety of
# interesting issues with identifier tracking.
block_frame = Frame(eval_ctx)
+ block_frame.block_frame = True
undeclared = find_undeclared(block.body, ("self", "super"))
if "self" in undeclared:
ref = block_frame.symbols.declare_parameter("self")
- self.writeline("%s = TemplateReference(context)" % ref)
+ self.writeline(f"{ref} = TemplateReference(context)")
if "super" in undeclared:
ref = block_frame.symbols.declare_parameter("super")
- self.writeline("%s = context.super(%r, block_%s)" % (ref, name, name))
+ self.writeline(f"{ref} = context.super({name!r}, block_{name})")
block_frame.symbols.analyze_node(block)
block_frame.block = name
+ self.writeline("_block_vars = {}")
self.enter_frame(block_frame)
self.pull_dependencies(block.body)
self.blockvisit(block.body, block_frame)
self.leave_frame(block_frame, with_python_scope=True)
self.outdent()
- self.writeline(
- "blocks = {%s}" % ", ".join("%r: block_%s" % (x, x) for x in self.blocks),
- extra=1,
- )
+ blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
+ self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
+ debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
+ self.writeline(f"debug_info = {debug_kv_str!r}")
- # add a function that returns the debug info
- self.writeline(
- "debug_info = %r" % "&".join("%s=%s" % x for x in self.debug_info)
- )
-
- def visit_Block(self, node, frame):
+ def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
"""Call a block and register it for the template."""
level = 0
if frame.toplevel:
@@ -851,18 +959,23 @@ def visit_Block(self, node, frame):
else:
context = self.get_context_ref()
- if (
- supports_yield_from
- and not self.environment.is_async
- and frame.buffer is None
- ):
+ if node.required:
+ self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
+ self.indent()
+ self.writeline(
+ f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
+ node,
+ )
+ self.outdent()
+
+ if not self.environment.is_async and frame.buffer is None:
self.writeline(
- "yield from context.blocks[%r][0](%s)" % (node.name, context), node
+ f"yield from context.blocks[{node.name!r}][0]({context})", node
)
else:
- loop = self.environment.is_async and "async for" or "for"
self.writeline(
- "%s event in context.blocks[%r][0](%s):" % (loop, node.name, context),
+ f"{self.choose_async()}for event in"
+ f" context.blocks[{node.name!r}][0]({context}):",
node,
)
self.indent()
@@ -871,7 +984,7 @@ def visit_Block(self, node, frame):
self.outdent(level)
- def visit_Extends(self, node, frame):
+ def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
"""Calls the extender."""
if not frame.toplevel:
self.fail("cannot use extend from a non top-level scope", node.lineno)
@@ -880,7 +993,6 @@ def visit_Extends(self, node, frame):
# far, we don't have to add a check if something extended
# the template before this one.
if self.extends_so_far > 0:
-
# if we have a known extends we just add a template runtime
# error into the generated code. We could catch that at compile
# time too, but i welcome it not to confuse users by throwing the
@@ -888,7 +1000,7 @@ def visit_Extends(self, node, frame):
if not self.has_known_extends:
self.writeline("if parent_template is not None:")
self.indent()
- self.writeline("raise TemplateRuntimeError(%r)" % "extended multiple times")
+ self.writeline('raise TemplateRuntimeError("extended multiple times")')
# if we have a known extends already we don't need that code here
# as we know that the template execution will end here.
@@ -899,10 +1011,8 @@ def visit_Extends(self, node, frame):
self.writeline("parent_template = environment.get_template(", node)
self.visit(node.template, frame)
- self.write(", %r)" % self.name)
- self.writeline(
- "for name, parent_block in parent_template.blocks.%s():" % dict_item_iter
- )
+ self.write(f", {self.name!r})")
+ self.writeline("for name, parent_block in parent_template.blocks.items():")
self.indent()
self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
self.outdent()
@@ -916,7 +1026,7 @@ def visit_Extends(self, node, frame):
# and now we have one more
self.extends_so_far += 1
- def visit_Include(self, node, frame):
+ def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
"""Handles includes."""
if node.ignore_missing:
self.writeline("try:")
@@ -924,16 +1034,16 @@ def visit_Include(self, node, frame):
func_name = "get_or_select_template"
if isinstance(node.template, nodes.Const):
- if isinstance(node.template.value, string_types):
+ if isinstance(node.template.value, str):
func_name = "get_template"
elif isinstance(node.template.value, (tuple, list)):
func_name = "select_template"
elif isinstance(node.template, (nodes.Tuple, nodes.List)):
func_name = "select_template"
- self.writeline("template = environment.%s(" % func_name, node)
+ self.writeline(f"template = environment.{func_name}(", node)
self.visit(node.template, frame)
- self.write(", %r)" % self.name)
+ self.write(f", {self.name!r})")
if node.ignore_missing:
self.outdent()
self.writeline("except TemplateNotFound:")
@@ -945,26 +1055,19 @@ def visit_Include(self, node, frame):
skip_event_yield = False
if node.with_context:
- loop = self.environment.is_async and "async for" or "for"
self.writeline(
- "%s event in template.root_render_func("
- "template.new_context(context.get_all(), True, "
- "%s)):" % (loop, self.dump_local_context(frame))
+ f"{self.choose_async()}for event in template.root_render_func("
+ "template.new_context(context.get_all(), True,"
+ f" {self.dump_local_context(frame)})):"
)
elif self.environment.is_async:
self.writeline(
- "for event in (await "
- "template._get_default_module_async())"
+ "for event in (await template._get_default_module_async())"
"._body_stream:"
)
else:
- if supports_yield_from:
- self.writeline("yield from template._get_default_module()._body_stream")
- skip_event_yield = True
- else:
- self.writeline(
- "for event in template._get_default_module()._body_stream:"
- )
+ self.writeline("yield from template._get_default_module()._body_stream")
+ skip_event_yield = True
if not skip_event_yield:
self.indent()
@@ -974,53 +1077,37 @@ def visit_Include(self, node, frame):
if node.ignore_missing:
self.outdent()
- def visit_Import(self, node, frame):
- """Visit regular imports."""
- self.writeline("%s = " % frame.symbols.ref(node.target), node)
- if frame.toplevel:
- self.write("context.vars[%r] = " % node.target)
- if self.environment.is_async:
- self.write("await ")
- self.write("environment.get_template(")
+ def _import_common(
+ self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame
+ ) -> None:
+ self.write(f"{self.choose_async('await ')}environment.get_template(")
self.visit(node.template, frame)
- self.write(", %r)." % self.name)
+ self.write(f", {self.name!r}).")
+
if node.with_context:
+ f_name = f"make_module{self.choose_async('_async')}"
self.write(
- "make_module%s(context.get_all(), True, %s)"
- % (
- self.environment.is_async and "_async" or "",
- self.dump_local_context(frame),
- )
+ f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})"
)
- elif self.environment.is_async:
- self.write("_get_default_module_async()")
else:
- self.write("_get_default_module()")
+ self.write(f"_get_default_module{self.choose_async('_async')}(context)")
+
+ def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
+ """Visit regular imports."""
+ self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
+ if frame.toplevel:
+ self.write(f"context.vars[{node.target!r}] = ")
+
+ self._import_common(node, frame)
+
if frame.toplevel and not node.target.startswith("_"):
- self.writeline("context.exported_vars.discard(%r)" % node.target)
+ self.writeline(f"context.exported_vars.discard({node.target!r})")
- def visit_FromImport(self, node, frame):
+ def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
"""Visit named imports."""
self.newline(node)
- self.write(
- "included_template = %senvironment.get_template("
- % (self.environment.is_async and "await " or "")
- )
- self.visit(node.template, frame)
- self.write(", %r)." % self.name)
- if node.with_context:
- self.write(
- "make_module%s(context.get_all(), True, %s)"
- % (
- self.environment.is_async and "_async" or "",
- self.dump_local_context(frame),
- )
- )
- elif self.environment.is_async:
- self.write("_get_default_module_async()")
- else:
- self.write("_get_default_module()")
-
+ self.write("included_template = ")
+ self._import_common(node, frame)
var_names = []
discarded_names = []
for name in node.names:
@@ -1029,22 +1116,18 @@ def visit_FromImport(self, node, frame):
else:
alias = name
self.writeline(
- "%s = getattr(included_template, "
- "%r, missing)" % (frame.symbols.ref(alias), name)
+ f"{frame.symbols.ref(alias)} ="
+ f" getattr(included_template, {name!r}, missing)"
)
- self.writeline("if %s is missing:" % frame.symbols.ref(alias))
+ self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
self.indent()
+ message = (
+ "the template {included_template.__name__!r}"
+ f" (imported on {self.position(node)})"
+ f" does not export the requested name {name!r}"
+ )
self.writeline(
- "%s = undefined(%r %% "
- "included_template.__name__, "
- "name=%r)"
- % (
- frame.symbols.ref(alias),
- "the template %%r (imported on %s) does "
- "not export the requested name %s"
- % (self.position(node), repr(name)),
- name,
- )
+ f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
)
self.outdent()
if frame.toplevel:
@@ -1055,35 +1138,35 @@ def visit_FromImport(self, node, frame):
if var_names:
if len(var_names) == 1:
name = var_names[0]
- self.writeline(
- "context.vars[%r] = %s" % (name, frame.symbols.ref(name))
- )
+ self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
else:
- self.writeline(
- "context.vars.update({%s})"
- % ", ".join(
- "%r: %s" % (name, frame.symbols.ref(name)) for name in var_names
- )
+ names_kv = ", ".join(
+ f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
)
+ self.writeline(f"context.vars.update({{{names_kv}}})")
if discarded_names:
if len(discarded_names) == 1:
- self.writeline("context.exported_vars.discard(%r)" % discarded_names[0])
+ self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
else:
+ names_str = ", ".join(map(repr, discarded_names))
self.writeline(
- "context.exported_vars.difference_"
- "update((%s))" % ", ".join(imap(repr, discarded_names))
+ f"context.exported_vars.difference_update(({names_str}))"
)
- def visit_For(self, node, frame):
+ def visit_For(self, node: nodes.For, frame: Frame) -> None:
loop_frame = frame.inner()
+ loop_frame.loop_frame = True
test_frame = frame.inner()
else_frame = frame.inner()
# try to figure out if we have an extended loop. An extended loop
# is necessary if the loop is in recursive mode if the special loop
- # variable is accessed in the body.
- extended_loop = node.recursive or "loop" in find_undeclared(
- node.iter_child_nodes(only=("body",)), ("loop",)
+ # variable is accessed in the body if the body is a scoped block.
+ extended_loop = (
+ node.recursive
+ or "loop"
+ in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
+ or any(block.scoped for block in node.find_all(nodes.Block))
)
loop_ref = None
@@ -1097,13 +1180,13 @@ def visit_For(self, node, frame):
if node.test:
loop_filter_func = self.temporary_identifier()
test_frame.symbols.analyze_node(node, for_branch="test")
- self.writeline("%s(fiter):" % self.func(loop_filter_func), node.test)
+ self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
self.indent()
self.enter_frame(test_frame)
- self.writeline(self.environment.is_async and "async for " or "for ")
+ self.writeline(self.choose_async("async for ", "for "))
self.visit(node.target, loop_frame)
self.write(" in ")
- self.write(self.environment.is_async and "auto_aiter(fiter)" or "fiter")
+ self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
self.write(":")
self.indent()
self.writeline("if ", node.test)
@@ -1120,7 +1203,7 @@ def visit_For(self, node, frame):
# variable is a special one we have to enforce aliasing for it.
if node.recursive:
self.writeline(
- "%s(reciter, loop_render_func, depth=0):" % self.func("loop"), node
+ f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
)
self.indent()
self.buffer(loop_frame)
@@ -1131,7 +1214,7 @@ def visit_For(self, node, frame):
# make sure the loop variable is a special one and raise a template
# assertion error if a loop tries to write to loop
if extended_loop:
- self.writeline("%s = missing" % loop_ref)
+ self.writeline(f"{loop_ref} = missing")
for name in node.find_all(nodes.Name):
if name.ctx == "store" and name.name == "loop":
@@ -1142,20 +1225,17 @@ def visit_For(self, node, frame):
if node.else_:
iteration_indicator = self.temporary_identifier()
- self.writeline("%s = 1" % iteration_indicator)
+ self.writeline(f"{iteration_indicator} = 1")
- self.writeline(self.environment.is_async and "async for " or "for ", node)
+ self.writeline(self.choose_async("async for ", "for "), node)
self.visit(node.target, loop_frame)
if extended_loop:
- if self.environment.is_async:
- self.write(", %s in AsyncLoopContext(" % loop_ref)
- else:
- self.write(", %s in LoopContext(" % loop_ref)
+ self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
else:
self.write(" in ")
if node.test:
- self.write("%s(" % loop_filter_func)
+ self.write(f"{loop_filter_func}(")
if node.recursive:
self.write("reciter")
else:
@@ -1170,21 +1250,22 @@ def visit_For(self, node, frame):
if node.recursive:
self.write(", undefined, loop_render_func, depth):")
else:
- self.write(extended_loop and ", undefined):" or ":")
+ self.write(", undefined):" if extended_loop else ":")
self.indent()
self.enter_frame(loop_frame)
+ self.writeline("_loop_vars = {}")
self.blockvisit(node.body, loop_frame)
if node.else_:
- self.writeline("%s = 0" % iteration_indicator)
+ self.writeline(f"{iteration_indicator} = 0")
self.outdent()
self.leave_frame(
loop_frame, with_python_scope=node.recursive and not node.else_
)
if node.else_:
- self.writeline("if %s:" % iteration_indicator)
+ self.writeline(f"if {iteration_indicator}:")
self.indent()
self.enter_frame(else_frame)
self.blockvisit(node.else_, else_frame)
@@ -1197,9 +1278,7 @@ def visit_For(self, node, frame):
self.return_buffer_contents(loop_frame)
self.outdent()
self.start_write(frame, node)
- if self.environment.is_async:
- self.write("await ")
- self.write("loop(")
+ self.write(f"{self.choose_async('await ')}loop(")
if self.environment.is_async:
self.write("auto_aiter(")
self.visit(node.iter, frame)
@@ -1208,7 +1287,12 @@ def visit_For(self, node, frame):
self.write(", loop)")
self.end_write(frame)
- def visit_If(self, node, frame):
+ # at the end of the iteration, clear any assignments made in the
+ # loop from the top level
+ if self._assign_stack:
+ self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
+
+ def visit_If(self, node: nodes.If, frame: Frame) -> None:
if_frame = frame.soft()
self.writeline("if ", node)
self.visit(node.test, if_frame)
@@ -1229,17 +1313,17 @@ def visit_If(self, node, frame):
self.blockvisit(node.else_, if_frame)
self.outdent()
- def visit_Macro(self, node, frame):
+ def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
macro_frame, macro_ref = self.macro_body(node, frame)
self.newline()
if frame.toplevel:
if not node.name.startswith("_"):
- self.write("context.exported_vars.add(%r)" % node.name)
- self.writeline("context.vars[%r] = " % node.name)
- self.write("%s = " % frame.symbols.ref(node.name))
+ self.write(f"context.exported_vars.add({node.name!r})")
+ self.writeline(f"context.vars[{node.name!r}] = ")
+ self.write(f"{frame.symbols.ref(node.name)} = ")
self.macro_def(macro_ref, macro_frame)
- def visit_CallBlock(self, node, frame):
+ def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
call_frame, macro_ref = self.macro_body(node, frame)
self.writeline("caller = ")
self.macro_def(macro_ref, call_frame)
@@ -1247,7 +1331,7 @@ def visit_CallBlock(self, node, frame):
self.visit_Call(node.call, frame, forward_caller=True)
self.end_write(frame)
- def visit_FilterBlock(self, node, frame):
+ def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
filter_frame = frame.inner()
filter_frame.symbols.analyze_node(node)
self.enter_frame(filter_frame)
@@ -1258,11 +1342,11 @@ def visit_FilterBlock(self, node, frame):
self.end_write(frame)
self.leave_frame(filter_frame)
- def visit_With(self, node, frame):
+ def visit_With(self, node: nodes.With, frame: Frame) -> None:
with_frame = frame.inner()
with_frame.symbols.analyze_node(node)
self.enter_frame(with_frame)
- for target, expr in izip(node.targets, node.values):
+ for target, expr in zip(node.targets, node.values):
self.newline()
self.visit(target, with_frame)
self.write(" = ")
@@ -1270,18 +1354,25 @@ def visit_With(self, node, frame):
self.blockvisit(node.body, with_frame)
self.leave_frame(with_frame)
- def visit_ExprStmt(self, node, frame):
+ def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
self.newline(node)
self.visit(node.node, frame)
- _FinalizeInfo = namedtuple("_FinalizeInfo", ("const", "src"))
- #: The default finalize function if the environment isn't configured
- #: with one. Or if the environment has one, this is called on that
- #: function's output for constants.
- _default_finalize = text_type
- _finalize = None
+ class _FinalizeInfo(t.NamedTuple):
+ const: t.Optional[t.Callable[..., str]]
+ src: t.Optional[str]
- def _make_finalize(self):
+ @staticmethod
+ def _default_finalize(value: t.Any) -> t.Any:
+ """The default finalize function if the environment isn't
+ configured with one. Or, if the environment has one, this is
+ called on that function's output for constants.
+ """
+ return str(value)
+
+ _finalize: t.Optional[_FinalizeInfo] = None
+
+ def _make_finalize(self) -> _FinalizeInfo:
"""Build the finalize function to be used on constants and at
runtime. Cached so it's only created once for all output nodes.
@@ -1297,39 +1388,48 @@ def _make_finalize(self):
if self._finalize is not None:
return self._finalize
+ finalize: t.Optional[t.Callable[..., t.Any]]
finalize = default = self._default_finalize
src = None
if self.environment.finalize:
src = "environment.finalize("
env_finalize = self.environment.finalize
+ pass_arg = {
+ _PassArg.context: "context",
+ _PassArg.eval_context: "context.eval_ctx",
+ _PassArg.environment: "environment",
+ }.get(
+ _PassArg.from_obj(env_finalize) # type: ignore
+ )
+ finalize = None
- def finalize(value):
- return default(env_finalize(value))
+ if pass_arg is None:
- if getattr(env_finalize, "contextfunction", False) is True:
- src += "context, "
- finalize = None # noqa: F811
- elif getattr(env_finalize, "evalcontextfunction", False) is True:
- src += "context.eval_ctx, "
- finalize = None
- elif getattr(env_finalize, "environmentfunction", False) is True:
- src += "environment, "
+ def finalize(value: t.Any) -> t.Any: # noqa: F811
+ return default(env_finalize(value))
+
+ else:
+ src = f"{src}{pass_arg}, "
- def finalize(value):
- return default(env_finalize(self.environment, value))
+ if pass_arg == "environment":
+
+ def finalize(value: t.Any) -> t.Any: # noqa: F811
+ return default(env_finalize(self.environment, value))
self._finalize = self._FinalizeInfo(finalize, src)
return self._finalize
- def _output_const_repr(self, group):
+ def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
"""Given a group of constant values converted from ``Output``
child nodes, produce a string to write to the template module
source.
"""
return repr(concat(group))
- def _output_child_to_const(self, node, frame, finalize):
+ def _output_child_to_const(
+ self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
+ ) -> str:
"""Try to optimize a child of an ``Output`` node by trying to
convert it to constant, finalized data at compile time.
@@ -1344,25 +1444,29 @@ def _output_child_to_const(self, node, frame, finalize):
# Template data doesn't go through finalize.
if isinstance(node, nodes.TemplateData):
- return text_type(const)
+ return str(const)
- return finalize.const(const)
+ return finalize.const(const) # type: ignore
- def _output_child_pre(self, node, frame, finalize):
+ def _output_child_pre(
+ self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
+ ) -> None:
"""Output extra source code before visiting a child of an
``Output`` node.
"""
if frame.eval_ctx.volatile:
- self.write("(escape if context.eval_ctx.autoescape else to_string)(")
+ self.write("(escape if context.eval_ctx.autoescape else str)(")
elif frame.eval_ctx.autoescape:
self.write("escape(")
else:
- self.write("to_string(")
+ self.write("str(")
if finalize.src is not None:
self.write(finalize.src)
- def _output_child_post(self, node, frame, finalize):
+ def _output_child_post(
+ self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
+ ) -> None:
"""Output extra source code after visiting a child of an
``Output`` node.
"""
@@ -1371,7 +1475,7 @@ def _output_child_post(self, node, frame, finalize):
if finalize.src is not None:
self.write(")")
- def visit_Output(self, node, frame):
+ def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
# If an extends is active, don't render outside a block.
if frame.require_output_check:
# A top-level extends is known to exist at compile time.
@@ -1382,7 +1486,7 @@ def visit_Output(self, node, frame):
self.indent()
finalize = self._make_finalize()
- body = []
+ body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
# Evaluate constants at compile time if possible. Each item in
# body will be either a list of static data or a node to be
@@ -1414,9 +1518,9 @@ def visit_Output(self, node, frame):
if frame.buffer is not None:
if len(body) == 1:
- self.writeline("%s.append(" % frame.buffer)
+ self.writeline(f"{frame.buffer}.append(")
else:
- self.writeline("%s.extend((" % frame.buffer)
+ self.writeline(f"{frame.buffer}.extend((")
self.indent()
@@ -1450,7 +1554,7 @@ def visit_Output(self, node, frame):
if frame.require_output_check:
self.outdent()
- def visit_Assign(self, node, frame):
+ def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
self.push_assign_tracking()
self.newline(node)
self.visit(node.target, frame)
@@ -1458,7 +1562,7 @@ def visit_Assign(self, node, frame):
self.visit(node.node, frame)
self.pop_assign_tracking(frame)
- def visit_AssignBlock(self, node, frame):
+ def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
self.push_assign_tracking()
block_frame = frame.inner()
# This is a special case. Since a set block always captures we
@@ -1475,15 +1579,17 @@ def visit_AssignBlock(self, node, frame):
if node.filter is not None:
self.visit_Filter(node.filter, block_frame)
else:
- self.write("concat(%s)" % block_frame.buffer)
+ self.write(f"concat({block_frame.buffer})")
self.write(")")
self.pop_assign_tracking(frame)
self.leave_frame(block_frame)
# -- Expression Visitors
- def visit_Name(self, node, frame):
- if node.ctx == "store" and frame.toplevel:
+ def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
+ if node.ctx == "store" and (
+ frame.toplevel or frame.loop_frame or frame.block_frame
+ ):
if self._assign_stack:
self._assign_stack[-1].add(node.name)
ref = frame.symbols.ref(node.name)
@@ -1499,52 +1605,51 @@ def visit_Name(self, node, frame):
and not self.parameter_is_undeclared(ref)
):
self.write(
- "(undefined(name=%r) if %s is missing else %s)"
- % (node.name, ref, ref)
+ f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
)
return
self.write(ref)
- def visit_NSRef(self, node, frame):
+ def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
# NSRefs can only be used to store values; since they use the normal
# `foo.bar` notation they will be parsed as a normal attribute access
# when used anywhere but in a `set` context
ref = frame.symbols.ref(node.name)
- self.writeline("if not isinstance(%s, Namespace):" % ref)
+ self.writeline(f"if not isinstance({ref}, Namespace):")
self.indent()
self.writeline(
- "raise TemplateRuntimeError(%r)"
- % "cannot assign attribute on non-namespace object"
+ "raise TemplateRuntimeError"
+ '("cannot assign attribute on non-namespace object")'
)
self.outdent()
- self.writeline("%s[%r]" % (ref, node.attr))
+ self.writeline(f"{ref}[{node.attr!r}]")
- def visit_Const(self, node, frame):
+ def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
val = node.as_const(frame.eval_ctx)
if isinstance(val, float):
self.write(str(val))
else:
self.write(repr(val))
- def visit_TemplateData(self, node, frame):
+ def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
try:
self.write(repr(node.as_const(frame.eval_ctx)))
except nodes.Impossible:
self.write(
- "(Markup if context.eval_ctx.autoescape else identity)(%r)" % node.data
+ f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
)
- def visit_Tuple(self, node, frame):
+ def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
self.write("(")
idx = -1
for idx, item in enumerate(node.items):
if idx:
self.write(", ")
self.visit(item, frame)
- self.write(idx == 0 and ",)" or ")")
+ self.write(",)" if idx == 0 else ")")
- def visit_List(self, node, frame):
+ def visit_List(self, node: nodes.List, frame: Frame) -> None:
self.write("[")
for idx, item in enumerate(node.items):
if idx:
@@ -1552,7 +1657,7 @@ def visit_List(self, node, frame):
self.visit(item, frame)
self.write("]")
- def visit_Dict(self, node, frame):
+ def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
self.write("{")
for idx, item in enumerate(node.items):
if idx:
@@ -1562,96 +1667,59 @@ def visit_Dict(self, node, frame):
self.visit(item.value, frame)
self.write("}")
- def binop(operator, interceptable=True): # noqa: B902
- @optimizeconst
- def visitor(self, node, frame):
- if (
- self.environment.sandboxed
- and operator in self.environment.intercepted_binops
- ):
- self.write("environment.call_binop(context, %r, " % operator)
- self.visit(node.left, frame)
- self.write(", ")
- self.visit(node.right, frame)
- else:
- self.write("(")
- self.visit(node.left, frame)
- self.write(" %s " % operator)
- self.visit(node.right, frame)
- self.write(")")
-
- return visitor
-
- def uaop(operator, interceptable=True): # noqa: B902
- @optimizeconst
- def visitor(self, node, frame):
- if (
- self.environment.sandboxed
- and operator in self.environment.intercepted_unops
- ):
- self.write("environment.call_unop(context, %r, " % operator)
- self.visit(node.node, frame)
- else:
- self.write("(" + operator)
- self.visit(node.node, frame)
- self.write(")")
-
- return visitor
-
- visit_Add = binop("+")
- visit_Sub = binop("-")
- visit_Mul = binop("*")
- visit_Div = binop("/")
- visit_FloorDiv = binop("//")
- visit_Pow = binop("**")
- visit_Mod = binop("%")
- visit_And = binop("and", interceptable=False)
- visit_Or = binop("or", interceptable=False)
- visit_Pos = uaop("+")
- visit_Neg = uaop("-")
- visit_Not = uaop("not ", interceptable=False)
- del binop, uaop
+ visit_Add = _make_binop("+")
+ visit_Sub = _make_binop("-")
+ visit_Mul = _make_binop("*")
+ visit_Div = _make_binop("/")
+ visit_FloorDiv = _make_binop("//")
+ visit_Pow = _make_binop("**")
+ visit_Mod = _make_binop("%")
+ visit_And = _make_binop("and")
+ visit_Or = _make_binop("or")
+ visit_Pos = _make_unop("+")
+ visit_Neg = _make_unop("-")
+ visit_Not = _make_unop("not ")
@optimizeconst
- def visit_Concat(self, node, frame):
+ def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
if frame.eval_ctx.volatile:
- func_name = "(context.eval_ctx.volatile and markup_join or unicode_join)"
+ func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
elif frame.eval_ctx.autoescape:
func_name = "markup_join"
else:
- func_name = "unicode_join"
- self.write("%s((" % func_name)
+ func_name = "str_join"
+ self.write(f"{func_name}((")
for arg in node.nodes:
self.visit(arg, frame)
self.write(", ")
self.write("))")
@optimizeconst
- def visit_Compare(self, node, frame):
+ def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
self.write("(")
self.visit(node.expr, frame)
for op in node.ops:
self.visit(op, frame)
self.write(")")
- def visit_Operand(self, node, frame):
- self.write(" %s " % operators[node.op])
+ def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
+ self.write(f" {operators[node.op]} ")
self.visit(node.expr, frame)
@optimizeconst
- def visit_Getattr(self, node, frame):
+ def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
if self.environment.is_async:
self.write("(await auto_await(")
self.write("environment.getattr(")
self.visit(node.node, frame)
- self.write(", %r)" % node.attr)
+ self.write(f", {node.attr!r})")
if self.environment.is_async:
self.write("))")
@optimizeconst
- def visit_Getitem(self, node, frame):
+ def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
# slices bypass the environment getitem method.
if isinstance(node.arg, nodes.Slice):
self.visit(node.node, frame)
@@ -1671,7 +1739,7 @@ def visit_Getitem(self, node, frame):
if self.environment.is_async:
self.write("))")
- def visit_Slice(self, node, frame):
+ def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
if node.start is not None:
self.visit(node.start, frame)
self.write(":")
@@ -1681,60 +1749,83 @@ def visit_Slice(self, node, frame):
self.write(":")
self.visit(node.step, frame)
- @optimizeconst
- def visit_Filter(self, node, frame):
+ @contextmanager
+ def _filter_test_common(
+ self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool
+ ) -> t.Iterator[None]:
if self.environment.is_async:
- self.write("await auto_await(")
- self.write(self.filters[node.name] + "(")
- func = self.environment.filters.get(node.name)
- if func is None:
- self.fail("no filter named %r" % node.name, node.lineno)
- if getattr(func, "contextfilter", False) is True:
- self.write("context, ")
- elif getattr(func, "evalcontextfilter", False) is True:
- self.write("context.eval_ctx, ")
- elif getattr(func, "environmentfilter", False) is True:
- self.write("environment, ")
-
- # if the filter node is None we are inside a filter block
- # and want to write to the current buffer
- if node.node is not None:
- self.visit(node.node, frame)
- elif frame.eval_ctx.volatile:
- self.write(
- "(context.eval_ctx.autoescape and"
- " Markup(concat(%s)) or concat(%s))" % (frame.buffer, frame.buffer)
- )
- elif frame.eval_ctx.autoescape:
- self.write("Markup(concat(%s))" % frame.buffer)
+ self.write("(await auto_await(")
+
+ if is_filter:
+ self.write(f"{self.filters[node.name]}(")
+ func = self.environment.filters.get(node.name)
else:
- self.write("concat(%s)" % frame.buffer)
+ self.write(f"{self.tests[node.name]}(")
+ func = self.environment.tests.get(node.name)
+
+ # When inside an If or CondExpr frame, allow the filter to be
+ # undefined at compile time and only raise an error if it's
+ # actually called at runtime. See pull_dependencies.
+ if func is None and not frame.soft_frame:
+ type_name = "filter" if is_filter else "test"
+ self.fail(f"No {type_name} named {node.name!r}.", node.lineno)
+
+ pass_arg = {
+ _PassArg.context: "context",
+ _PassArg.eval_context: "context.eval_ctx",
+ _PassArg.environment: "environment",
+ }.get(
+ _PassArg.from_obj(func) # type: ignore
+ )
+
+ if pass_arg is not None:
+ self.write(f"{pass_arg}, ")
+
+ # Back to the visitor function to handle visiting the target of
+ # the filter or test.
+ yield
+
self.signature(node, frame)
self.write(")")
+
if self.environment.is_async:
- self.write(")")
+ self.write("))")
@optimizeconst
- def visit_Test(self, node, frame):
- self.write(self.tests[node.name] + "(")
- if node.name not in self.environment.tests:
- self.fail("no test named %r" % node.name, node.lineno)
- self.visit(node.node, frame)
- self.signature(node, frame)
- self.write(")")
+ def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
+ with self._filter_test_common(node, frame, True):
+ # if the filter node is None we are inside a filter block
+ # and want to write to the current buffer
+ if node.node is not None:
+ self.visit(node.node, frame)
+ elif frame.eval_ctx.volatile:
+ self.write(
+ f"(Markup(concat({frame.buffer}))"
+ f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
+ )
+ elif frame.eval_ctx.autoescape:
+ self.write(f"Markup(concat({frame.buffer}))")
+ else:
+ self.write(f"concat({frame.buffer})")
+
+ @optimizeconst
+ def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
+ with self._filter_test_common(node, frame, False):
+ self.visit(node.node, frame)
@optimizeconst
- def visit_CondExpr(self, node, frame):
- def write_expr2():
+ def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
+ frame = frame.soft()
+
+ def write_expr2() -> None:
if node.expr2 is not None:
- return self.visit(node.expr2, frame)
+ self.visit(node.expr2, frame)
+ return
+
self.write(
- "cond_expr_undefined(%r)"
- % (
- "the inline if-"
- "expression on %s evaluated to false and "
- "no else section was defined." % self.position(node)
- )
+ f'cond_expr_undefined("the inline if-expression on'
+ f" {self.position(node)} evaluated to false and no else"
+ f' section was defined.")'
)
self.write("(")
@@ -1746,71 +1837,89 @@ def write_expr2():
self.write(")")
@optimizeconst
- def visit_Call(self, node, frame, forward_caller=False):
+ def visit_Call(
+ self, node: nodes.Call, frame: Frame, forward_caller: bool = False
+ ) -> None:
if self.environment.is_async:
- self.write("await auto_await(")
+ self.write("(await auto_await(")
if self.environment.sandboxed:
self.write("environment.call(context, ")
else:
self.write("context.call(")
self.visit(node.node, frame)
- extra_kwargs = forward_caller and {"caller": "caller"} or None
+ extra_kwargs = {"caller": "caller"} if forward_caller else None
+ loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
+ block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
+ if extra_kwargs:
+ extra_kwargs.update(loop_kwargs, **block_kwargs)
+ elif loop_kwargs or block_kwargs:
+ extra_kwargs = dict(loop_kwargs, **block_kwargs)
self.signature(node, frame, extra_kwargs)
self.write(")")
if self.environment.is_async:
- self.write(")")
+ self.write("))")
- def visit_Keyword(self, node, frame):
+ def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
self.write(node.key + "=")
self.visit(node.value, frame)
# -- Unused nodes for extensions
- def visit_MarkSafe(self, node, frame):
+ def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
self.write("Markup(")
self.visit(node.expr, frame)
self.write(")")
- def visit_MarkSafeIfAutoescape(self, node, frame):
- self.write("(context.eval_ctx.autoescape and Markup or identity)(")
+ def visit_MarkSafeIfAutoescape(
+ self, node: nodes.MarkSafeIfAutoescape, frame: Frame
+ ) -> None:
+ self.write("(Markup if context.eval_ctx.autoescape else identity)(")
self.visit(node.expr, frame)
self.write(")")
- def visit_EnvironmentAttribute(self, node, frame):
+ def visit_EnvironmentAttribute(
+ self, node: nodes.EnvironmentAttribute, frame: Frame
+ ) -> None:
self.write("environment." + node.name)
- def visit_ExtensionAttribute(self, node, frame):
- self.write("environment.extensions[%r].%s" % (node.identifier, node.name))
+ def visit_ExtensionAttribute(
+ self, node: nodes.ExtensionAttribute, frame: Frame
+ ) -> None:
+ self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
- def visit_ImportedName(self, node, frame):
+ def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
self.write(self.import_aliases[node.importname])
- def visit_InternalName(self, node, frame):
+ def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
self.write(node.name)
- def visit_ContextReference(self, node, frame):
+ def visit_ContextReference(
+ self, node: nodes.ContextReference, frame: Frame
+ ) -> None:
self.write("context")
- def visit_DerivedContextReference(self, node, frame):
+ def visit_DerivedContextReference(
+ self, node: nodes.DerivedContextReference, frame: Frame
+ ) -> None:
self.write(self.derive_context(frame))
- def visit_Continue(self, node, frame):
+ def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
self.writeline("continue", node)
- def visit_Break(self, node, frame):
+ def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
self.writeline("break", node)
- def visit_Scope(self, node, frame):
+ def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
scope_frame = frame.inner()
scope_frame.symbols.analyze_node(node)
self.enter_frame(scope_frame)
self.blockvisit(node.body, scope_frame)
self.leave_frame(scope_frame)
- def visit_OverlayScope(self, node, frame):
+ def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
ctx = self.temporary_identifier()
- self.writeline("%s = %s" % (ctx, self.derive_context(frame)))
- self.writeline("%s.vars = " % ctx)
+ self.writeline(f"{ctx} = {self.derive_context(frame)}")
+ self.writeline(f"{ctx}.vars = ")
self.visit(node.context, frame)
self.push_context_reference(ctx)
@@ -1821,9 +1930,11 @@ def visit_OverlayScope(self, node, frame):
self.leave_frame(scope_frame)
self.pop_context_reference()
- def visit_EvalContextModifier(self, node, frame):
+ def visit_EvalContextModifier(
+ self, node: nodes.EvalContextModifier, frame: Frame
+ ) -> None:
for keyword in node.options:
- self.writeline("context.eval_ctx.%s = " % keyword.key)
+ self.writeline(f"context.eval_ctx.{keyword.key} = ")
self.visit(keyword.value, frame)
try:
val = keyword.value.as_const(frame.eval_ctx)
@@ -1832,12 +1943,14 @@ def visit_EvalContextModifier(self, node, frame):
else:
setattr(frame.eval_ctx, keyword.key, val)
- def visit_ScopedEvalContextModifier(self, node, frame):
+ def visit_ScopedEvalContextModifier(
+ self, node: nodes.ScopedEvalContextModifier, frame: Frame
+ ) -> None:
old_ctx_name = self.temporary_identifier()
saved_ctx = frame.eval_ctx.save()
- self.writeline("%s = context.eval_ctx.save()" % old_ctx_name)
+ self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
self.visit_EvalContextModifier(node, frame)
for child in node.body:
self.visit(child, frame)
frame.eval_ctx.revert(saved_ctx)
- self.writeline("context.eval_ctx.revert(%s)" % old_ctx_name)
+ self.writeline(f"context.eval_ctx.revert({old_ctx_name})")
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/constants.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/constants.py
index bf7f2ca..41a1c23 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/constants.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/constants.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#: list of lorem ipsum words used by the lipsum() helper function
-LOREM_IPSUM_WORDS = u"""\
+LOREM_IPSUM_WORDS = """\
a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
auctor augue bibendum blandit class commodo condimentum congue consectetuer
consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/debug.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/debug.py
index 5d8aec3..7ed7e92 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/debug.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/debug.py
@@ -1,38 +1,37 @@
import sys
+import typing as t
from types import CodeType
+from types import TracebackType
-from . import TemplateSyntaxError
-from ._compat import PYPY
+from .exceptions import TemplateSyntaxError
from .utils import internal_code
from .utils import missing
+if t.TYPE_CHECKING:
+ from .runtime import Context
-def rewrite_traceback_stack(source=None):
+
+def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException:
"""Rewrite the current exception to replace any tracebacks from
within compiled template code with tracebacks that look like they
came from the template source.
This must be called within an ``except`` block.
- :param exc_info: A :meth:`sys.exc_info` tuple. If not provided,
- the current ``exc_info`` is used.
:param source: For ``TemplateSyntaxError``, the original source if
known.
- :return: A :meth:`sys.exc_info` tuple that can be re-raised.
+ :return: The original exception with the rewritten traceback.
"""
- exc_type, exc_value, tb = sys.exc_info()
+ _, exc_value, tb = sys.exc_info()
+ exc_value = t.cast(BaseException, exc_value)
+ tb = t.cast(TracebackType, tb)
if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated:
exc_value.translated = True
exc_value.source = source
-
- try:
- # Remove the old traceback on Python 3, otherwise the frames
- # from the compiler still show up.
- exc_value.with_traceback(None)
- except AttributeError:
- pass
-
+ # Remove the old traceback, otherwise the frames from the
+ # compiler still show up.
+ exc_value.with_traceback(None)
# Outside of runtime, so the frame isn't executing template
# code, but it still needs to point at the template.
tb = fake_traceback(
@@ -68,12 +67,15 @@ def rewrite_traceback_stack(source=None):
# Assign tb_next in reverse to avoid circular references.
for tb in reversed(stack):
- tb_next = tb_set_next(tb, tb_next)
+ tb.tb_next = tb_next
+ tb_next = tb
- return exc_type, exc_value, tb_next
+ return exc_value.with_traceback(tb_next)
-def fake_traceback(exc_value, tb, filename, lineno):
+def fake_traceback( # type: ignore
+ exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int
+) -> TracebackType:
"""Produce a new traceback object that looks like it came from the
template source instead of the compiled code. The filename, line
number, and location name will point to the template, and the local
@@ -100,79 +102,60 @@ def fake_traceback(exc_value, tb, filename, lineno):
"__jinja_exception__": exc_value,
}
# Raise an exception at the correct line number.
- code = compile("\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec")
+ code: CodeType = compile(
+ "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec"
+ )
# Build a new code object that points to the template file and
# replaces the location with a block name.
- try:
- location = "template"
-
- if tb is not None:
- function = tb.tb_frame.f_code.co_name
-
- if function == "root":
- location = "top-level template code"
- elif function.startswith("block_"):
- location = 'block "%s"' % function[6:]
-
- # Collect arguments for the new code object. CodeType only
- # accepts positional arguments, and arguments were inserted in
- # new Python versions.
- code_args = []
-
- for attr in (
- "argcount",
- "posonlyargcount", # Python 3.8
- "kwonlyargcount", # Python 3
- "nlocals",
- "stacksize",
- "flags",
- "code", # codestring
- "consts", # constants
- "names",
- "varnames",
- ("filename", filename),
- ("name", location),
- "firstlineno",
- "lnotab",
- "freevars",
- "cellvars",
- ):
- if isinstance(attr, tuple):
- # Replace with given value.
- code_args.append(attr[1])
- continue
-
- try:
- # Copy original value if it exists.
- code_args.append(getattr(code, "co_" + attr))
- except AttributeError:
- # Some arguments were added later.
- continue
-
- code = CodeType(*code_args)
- except Exception:
- # Some environments such as Google App Engine don't support
- # modifying code objects.
- pass
+ location = "template"
+
+ if tb is not None:
+ function = tb.tb_frame.f_code.co_name
+
+ if function == "root":
+ location = "top-level template code"
+ elif function.startswith("block_"):
+ location = f"block {function[6:]!r}"
+
+ if sys.version_info >= (3, 8):
+ code = code.replace(co_name=location)
+ else:
+ code = CodeType(
+ code.co_argcount,
+ code.co_kwonlyargcount,
+ code.co_nlocals,
+ code.co_stacksize,
+ code.co_flags,
+ code.co_code,
+ code.co_consts,
+ code.co_names,
+ code.co_varnames,
+ code.co_filename,
+ location,
+ code.co_firstlineno,
+ code.co_lnotab,
+ code.co_freevars,
+ code.co_cellvars,
+ )
# Execute the new code, which is guaranteed to raise, and return
# the new traceback without this frame.
try:
exec(code, globals, locals)
except BaseException:
- return sys.exc_info()[2].tb_next
+ return sys.exc_info()[2].tb_next # type: ignore
-def get_template_locals(real_locals):
+def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]:
"""Based on the runtime locals, get the context that would be
available at that point in the template.
"""
# Start with the current template context.
- ctx = real_locals.get("context")
+ ctx: "t.Optional[Context]" = real_locals.get("context")
- if ctx:
- data = ctx.get_all().copy()
+ if ctx is not None:
+ data: t.Dict[str, t.Any] = ctx.get_all().copy()
else:
data = {}
@@ -180,7 +163,7 @@ def get_template_locals(real_locals):
# rather than pushing a context. Local variables follow the scheme
# l_depth_name. Find the highest-depth local that has a value for
# each name.
- local_overrides = {}
+ local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {}
for name, value in real_locals.items():
if not name.startswith("l_") or value is missing:
@@ -188,8 +171,8 @@ def get_template_locals(real_locals):
continue
try:
- _, depth, name = name.split("_", 2)
- depth = int(depth)
+ _, depth_str, name = name.split("_", 2)
+ depth = int(depth_str)
except ValueError:
continue
@@ -206,63 +189,3 @@ def get_template_locals(real_locals):
data[name] = value
return data
-
-
-if sys.version_info >= (3, 7):
- # tb_next is directly assignable as of Python 3.7
- def tb_set_next(tb, tb_next):
- tb.tb_next = tb_next
- return tb
-
-
-elif PYPY:
- # PyPy might have special support, and won't work with ctypes.
- try:
- import tputil
- except ImportError:
- # Without tproxy support, use the original traceback.
- def tb_set_next(tb, tb_next):
- return tb
-
- else:
- # With tproxy support, create a proxy around the traceback that
- # returns the new tb_next.
- def tb_set_next(tb, tb_next):
- def controller(op):
- if op.opname == "__getattribute__" and op.args[0] == "tb_next":
- return tb_next
-
- return op.delegate()
-
- return tputil.make_proxy(controller, obj=tb)
-
-
-else:
- # Use ctypes to assign tb_next at the C level since it's read-only
- # from Python.
- import ctypes
-
- class _CTraceback(ctypes.Structure):
- _fields_ = [
- # Extra PyObject slots when compiled with Py_TRACE_REFS.
- ("PyObject_HEAD", ctypes.c_byte * object().__sizeof__()),
- # Only care about tb_next as an object, not a traceback.
- ("tb_next", ctypes.py_object),
- ]
-
- def tb_set_next(tb, tb_next):
- c_tb = _CTraceback.from_address(id(tb))
-
- # Clear out the old tb_next.
- if tb.tb_next is not None:
- c_tb_next = ctypes.py_object(tb.tb_next)
- c_tb.tb_next = ctypes.py_object()
- ctypes.pythonapi.Py_DecRef(c_tb_next)
-
- # Assign the new tb_next.
- if tb_next is not None:
- c_tb_next = ctypes.py_object(tb_next)
- ctypes.pythonapi.Py_IncRef(c_tb_next)
- c_tb.tb_next = c_tb_next
-
- return tb
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/defaults.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/defaults.py
index 8e0e7d7..638cad3 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/defaults.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/defaults.py
@@ -1,5 +1,5 @@
-# -*- coding: utf-8 -*-
-from ._compat import range_type
+import typing as t
+
from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401
from .tests import TESTS as DEFAULT_TESTS # noqa: F401
from .utils import Cycler
@@ -7,6 +7,9 @@
from .utils import Joiner
from .utils import Namespace
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+
# defaults for the parser / lexer
BLOCK_START_STRING = "{%"
BLOCK_END_STRING = "%}"
@@ -14,17 +17,17 @@
VARIABLE_END_STRING = "}}"
COMMENT_START_STRING = "{#"
COMMENT_END_STRING = "#}"
-LINE_STATEMENT_PREFIX = None
-LINE_COMMENT_PREFIX = None
+LINE_STATEMENT_PREFIX: t.Optional[str] = None
+LINE_COMMENT_PREFIX: t.Optional[str] = None
TRIM_BLOCKS = False
LSTRIP_BLOCKS = False
-NEWLINE_SEQUENCE = "\n"
+NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n"
KEEP_TRAILING_NEWLINE = False
# default filters, tests and namespace
DEFAULT_NAMESPACE = {
- "range": range_type,
+ "range": range,
"dict": dict,
"lipsum": generate_lorem_ipsum,
"cycler": Cycler,
@@ -33,10 +36,11 @@
}
# default policies
-DEFAULT_POLICIES = {
+DEFAULT_POLICIES: t.Dict[str, t.Any] = {
"compiler.ascii_str": True,
"urlize.rel": "noopener",
"urlize.target": None,
+ "urlize.extra_schemes": None,
"truncate.leeway": 5,
"json.dumps_function": None,
"json.dumps_kwargs": {"sort_keys": True},
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/environment.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/environment.py
index 8430390..185d332 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/environment.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/environment.py
@@ -1,25 +1,19 @@
-# -*- coding: utf-8 -*-
"""Classes for managing templates and their runtime and compile time
options.
"""
import os
-import sys
+import typing
+import typing as t
import weakref
+from collections import ChainMap
+from functools import lru_cache
from functools import partial
from functools import reduce
+from types import CodeType
from markupsafe import Markup
from . import nodes
-from ._compat import encode_filename
-from ._compat import implements_iterator
-from ._compat import implements_to_string
-from ._compat import iteritems
-from ._compat import PY2
-from ._compat import PYPY
-from ._compat import reraise
-from ._compat import string_types
-from ._compat import text_type
from .compiler import CodeGenerator
from .compiler import generate
from .defaults import BLOCK_END_STRING
@@ -44,25 +38,33 @@
from .exceptions import TemplateSyntaxError
from .exceptions import UndefinedError
from .lexer import get_lexer
+from .lexer import Lexer
from .lexer import TokenStream
from .nodes import EvalContext
from .parser import Parser
from .runtime import Context
from .runtime import new_context
from .runtime import Undefined
+from .utils import _PassArg
from .utils import concat
from .utils import consume
-from .utils import have_async_gen
from .utils import import_string
from .utils import internalcode
from .utils import LRUCache
from .utils import missing
-# for direct template usage we have up to ten living environments
-_spontaneous_environments = LRUCache(10)
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+ from .bccache import BytecodeCache
+ from .ext import Extension
+ from .loaders import BaseLoader
+
+_env_bound = t.TypeVar("_env_bound", bound="Environment")
-def get_spontaneous_environment(cls, *args):
+# for direct template usage we have up to ten living environments
+@lru_cache(maxsize=10)
+def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound:
"""Return a new spontaneous environment. A spontaneous environment
is used for templates created directly rather than through an
existing environment.
@@ -70,75 +72,74 @@ def get_spontaneous_environment(cls, *args):
:param cls: Environment class to create.
:param args: Positional arguments passed to environment.
"""
- key = (cls, args)
+ env = cls(*args)
+ env.shared = True
+ return env
- try:
- return _spontaneous_environments[key]
- except KeyError:
- _spontaneous_environments[key] = env = cls(*args)
- env.shared = True
- return env
-
-def create_cache(size):
+def create_cache(
+ size: int,
+) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
"""Return the cache class for the given size."""
if size == 0:
return None
+
if size < 0:
return {}
- return LRUCache(size)
+ return LRUCache(size) # type: ignore
-def copy_cache(cache):
+
+def copy_cache(
+ cache: t.Optional[t.MutableMapping],
+) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
"""Create an empty copy of the given cache."""
if cache is None:
return None
- elif type(cache) is dict:
+
+ if type(cache) is dict:
return {}
- return LRUCache(cache.capacity)
+
+ return LRUCache(cache.capacity) # type: ignore
-def load_extensions(environment, extensions):
+def load_extensions(
+ environment: "Environment",
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]],
+) -> t.Dict[str, "Extension"]:
"""Load the extensions from the list and bind it to the environment.
- Returns a dict of instantiated environments.
+ Returns a dict of instantiated extensions.
"""
result = {}
+
for extension in extensions:
- if isinstance(extension, string_types):
- extension = import_string(extension)
- result[extension.identifier] = extension(environment)
- return result
+ if isinstance(extension, str):
+ extension = t.cast(t.Type["Extension"], import_string(extension))
+ result[extension.identifier] = extension(environment)
-def fail_for_missing_callable(string, name):
- msg = string % name
- if isinstance(name, Undefined):
- try:
- name._fail_with_undefined_error()
- except Exception as e:
- msg = "%s (%s; did you forget to quote the callable name?)" % (msg, e)
- raise TemplateRuntimeError(msg)
+ return result
-def _environment_sanity_check(environment):
+def _environment_config_check(environment: "Environment") -> "Environment":
"""Perform a sanity check on the environment."""
assert issubclass(
environment.undefined, Undefined
- ), "undefined must be a subclass of undefined because filters depend on it."
+ ), "'undefined' must be a subclass of 'jinja2.Undefined'."
assert (
environment.block_start_string
!= environment.variable_start_string
!= environment.comment_start_string
- ), "block, variable and comment start strings must be different"
- assert environment.newline_sequence in (
+ ), "block, variable and comment start strings must be different."
+ assert environment.newline_sequence in {
"\r",
"\r\n",
"\n",
- ), "newline_sequence set to unknown line ending string."
+ }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'."
return environment
-class Environment(object):
+class Environment:
r"""The core component of Jinja is the `Environment`. It contains
important shared variables like configuration, filters, tests,
globals and others. Instances of this class may be modified if
@@ -256,9 +257,8 @@ class Environment(object):
See :ref:`bytecode-cache` for more information.
`enable_async`
- If set to true this enables async template execution which allows
- you to take advantage of newer Python features. This requires
- Python 3.6 or later.
+ If set to true this enables async template execution which
+ allows using async functions and generators.
"""
#: if this environment is sandboxed. Modifying this variable won't make
@@ -271,7 +271,7 @@ class Environment(object):
overlayed = False
#: the environment this environment is linked to if it is an overlay
- linked_to = None
+ linked_to: t.Optional["Environment"] = None
#: shared environments have this set to `True`. A shared environment
#: must not be modified
@@ -279,36 +279,40 @@ class Environment(object):
#: the class that is used for code generation. See
#: :class:`~jinja2.compiler.CodeGenerator` for more information.
- code_generator_class = CodeGenerator
+ code_generator_class: t.Type["CodeGenerator"] = CodeGenerator
+
+ concat = "".join
- #: the context class thatis used for templates. See
+ #: the context class that is used for templates. See
#: :class:`~jinja2.runtime.Context` for more information.
- context_class = Context
+ context_class: t.Type[Context] = Context
+
+ template_class: t.Type["Template"]
def __init__(
self,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- lstrip_blocks=LSTRIP_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- keep_trailing_newline=KEEP_TRAILING_NEWLINE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False,
- loader=None,
- cache_size=400,
- auto_reload=True,
- bytecode_cache=None,
- enable_async=False,
+ block_start_string: str = BLOCK_START_STRING,
+ block_end_string: str = BLOCK_END_STRING,
+ variable_start_string: str = VARIABLE_START_STRING,
+ variable_end_string: str = VARIABLE_END_STRING,
+ comment_start_string: str = COMMENT_START_STRING,
+ comment_end_string: str = COMMENT_END_STRING,
+ line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,
+ line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,
+ trim_blocks: bool = TRIM_BLOCKS,
+ lstrip_blocks: bool = LSTRIP_BLOCKS,
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
+ keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
+ optimized: bool = True,
+ undefined: t.Type[Undefined] = Undefined,
+ finalize: t.Optional[t.Callable[..., t.Any]] = None,
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
+ loader: t.Optional["BaseLoader"] = None,
+ cache_size: int = 400,
+ auto_reload: bool = True,
+ bytecode_cache: t.Optional["BytecodeCache"] = None,
+ enable_async: bool = False,
):
# !!Important notice!!
# The constructor accepts quite a few arguments that should be
@@ -336,7 +340,7 @@ def __init__(
self.keep_trailing_newline = keep_trailing_newline
# runtime information
- self.undefined = undefined
+ self.undefined: t.Type[Undefined] = undefined
self.optimized = optimized
self.finalize = finalize
self.autoescape = autoescape
@@ -358,52 +362,50 @@ def __init__(
# load extensions
self.extensions = load_extensions(self, extensions)
- self.enable_async = enable_async
- self.is_async = self.enable_async and have_async_gen
- if self.is_async:
- # runs patch_all() to enable async support
- from . import asyncsupport # noqa: F401
-
- _environment_sanity_check(self)
+ self.is_async = enable_async
+ _environment_config_check(self)
- def add_extension(self, extension):
+ def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None:
"""Adds an extension after the environment was created.
.. versionadded:: 2.5
"""
self.extensions.update(load_extensions(self, [extension]))
- def extend(self, **attributes):
+ def extend(self, **attributes: t.Any) -> None:
"""Add the items to the instance of the environment if they do not exist
yet. This is used by :ref:`extensions ` to register
callbacks and configuration values without breaking inheritance.
"""
- for key, value in iteritems(attributes):
+ for key, value in attributes.items():
if not hasattr(self, key):
setattr(self, key, value)
def overlay(
self,
- block_start_string=missing,
- block_end_string=missing,
- variable_start_string=missing,
- variable_end_string=missing,
- comment_start_string=missing,
- comment_end_string=missing,
- line_statement_prefix=missing,
- line_comment_prefix=missing,
- trim_blocks=missing,
- lstrip_blocks=missing,
- extensions=missing,
- optimized=missing,
- undefined=missing,
- finalize=missing,
- autoescape=missing,
- loader=missing,
- cache_size=missing,
- auto_reload=missing,
- bytecode_cache=missing,
- ):
+ block_start_string: str = missing,
+ block_end_string: str = missing,
+ variable_start_string: str = missing,
+ variable_end_string: str = missing,
+ comment_start_string: str = missing,
+ comment_end_string: str = missing,
+ line_statement_prefix: t.Optional[str] = missing,
+ line_comment_prefix: t.Optional[str] = missing,
+ trim_blocks: bool = missing,
+ lstrip_blocks: bool = missing,
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing,
+ keep_trailing_newline: bool = missing,
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing,
+ optimized: bool = missing,
+ undefined: t.Type[Undefined] = missing,
+ finalize: t.Optional[t.Callable[..., t.Any]] = missing,
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing,
+ loader: t.Optional["BaseLoader"] = missing,
+ cache_size: int = missing,
+ auto_reload: bool = missing,
+ bytecode_cache: t.Optional["BytecodeCache"] = missing,
+ enable_async: bool = False,
+ ) -> "Environment":
"""Create a new overlay environment that shares all the data with the
current environment except for cache and the overridden attributes.
Extensions cannot be removed for an overlayed environment. An overlayed
@@ -414,16 +416,20 @@ def overlay(
up completely. Not all attributes are truly linked, some are just
copied over so modifications on the original environment may not shine
through.
+
+ .. versionchanged:: 3.1.2
+ Added the ``newline_sequence``,, ``keep_trailing_newline``,
+ and ``enable_async`` parameters to match ``__init__``.
"""
args = dict(locals())
- del args["self"], args["cache_size"], args["extensions"]
+ del args["self"], args["cache_size"], args["extensions"], args["enable_async"]
rv = object.__new__(self.__class__)
rv.__dict__.update(self.__dict__)
rv.overlayed = True
rv.linked_to = self
- for key, value in iteritems(args):
+ for key, value in args.items():
if value is not missing:
setattr(rv, key, value)
@@ -433,25 +439,33 @@ def overlay(
rv.cache = copy_cache(self.cache)
rv.extensions = {}
- for key, value in iteritems(self.extensions):
+ for key, value in self.extensions.items():
rv.extensions[key] = value.bind(rv)
if extensions is not missing:
rv.extensions.update(load_extensions(rv, extensions))
- return _environment_sanity_check(rv)
+ if enable_async is not missing:
+ rv.is_async = enable_async
- lexer = property(get_lexer, doc="The lexer for this environment.")
+ return _environment_config_check(rv)
- def iter_extensions(self):
+ @property
+ def lexer(self) -> Lexer:
+ """The lexer for this environment."""
+ return get_lexer(self)
+
+ def iter_extensions(self) -> t.Iterator["Extension"]:
"""Iterates over the extensions by priority."""
return iter(sorted(self.extensions.values(), key=lambda x: x.priority))
- def getitem(self, obj, argument):
+ def getitem(
+ self, obj: t.Any, argument: t.Union[str, t.Any]
+ ) -> t.Union[t.Any, Undefined]:
"""Get an item or attribute of an object but prefer the item."""
try:
return obj[argument]
except (AttributeError, TypeError, LookupError):
- if isinstance(argument, string_types):
+ if isinstance(argument, str):
try:
attr = str(argument)
except Exception:
@@ -463,9 +477,9 @@ def getitem(self, obj, argument):
pass
return self.undefined(obj=obj, name=argument)
- def getattr(self, obj, attribute):
+ def getattr(self, obj: t.Any, attribute: str) -> t.Any:
"""Get an item or attribute of an object but prefer the attribute.
- Unlike :meth:`getitem` the attribute *must* be a bytestring.
+ Unlike :meth:`getitem` the attribute *must* be a string.
"""
try:
return getattr(obj, attribute)
@@ -476,51 +490,113 @@ def getattr(self, obj, attribute):
except (TypeError, LookupError, AttributeError):
return self.undefined(obj=obj, name=attribute)
- def call_filter(
- self, name, value, args=None, kwargs=None, context=None, eval_ctx=None
- ):
- """Invokes a filter on a value the same way the compiler does it.
+ def _filter_test_common(
+ self,
+ name: t.Union[str, Undefined],
+ value: t.Any,
+ args: t.Optional[t.Sequence[t.Any]],
+ kwargs: t.Optional[t.Mapping[str, t.Any]],
+ context: t.Optional[Context],
+ eval_ctx: t.Optional[EvalContext],
+ is_filter: bool,
+ ) -> t.Any:
+ if is_filter:
+ env_map = self.filters
+ type_name = "filter"
+ else:
+ env_map = self.tests
+ type_name = "test"
- Note that on Python 3 this might return a coroutine in case the
- filter is running from an environment in async mode and the filter
- supports async execution. It's your responsibility to await this
- if needed.
+ func = env_map.get(name) # type: ignore
- .. versionadded:: 2.7
- """
- func = self.filters.get(name)
if func is None:
- fail_for_missing_callable("no filter named %r", name)
- args = [value] + list(args or ())
- if getattr(func, "contextfilter", False) is True:
+ msg = f"No {type_name} named {name!r}."
+
+ if isinstance(name, Undefined):
+ try:
+ name._fail_with_undefined_error()
+ except Exception as e:
+ msg = f"{msg} ({e}; did you forget to quote the callable name?)"
+
+ raise TemplateRuntimeError(msg)
+
+ args = [value, *(args if args is not None else ())]
+ kwargs = kwargs if kwargs is not None else {}
+ pass_arg = _PassArg.from_obj(func)
+
+ if pass_arg is _PassArg.context:
if context is None:
raise TemplateRuntimeError(
- "Attempted to invoke context filter without context"
+ f"Attempted to invoke a context {type_name} without context."
)
+
args.insert(0, context)
- elif getattr(func, "evalcontextfilter", False) is True:
+ elif pass_arg is _PassArg.eval_context:
if eval_ctx is None:
if context is not None:
eval_ctx = context.eval_ctx
else:
eval_ctx = EvalContext(self)
+
args.insert(0, eval_ctx)
- elif getattr(func, "environmentfilter", False) is True:
+ elif pass_arg is _PassArg.environment:
args.insert(0, self)
- return func(*args, **(kwargs or {}))
- def call_test(self, name, value, args=None, kwargs=None):
- """Invokes a test on a value the same way the compiler does it.
+ return func(*args, **kwargs)
+
+ def call_filter(
+ self,
+ name: str,
+ value: t.Any,
+ args: t.Optional[t.Sequence[t.Any]] = None,
+ kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
+ context: t.Optional[Context] = None,
+ eval_ctx: t.Optional[EvalContext] = None,
+ ) -> t.Any:
+ """Invoke a filter on a value the same way the compiler does.
+
+ This might return a coroutine if the filter is running from an
+ environment in async mode and the filter supports async
+ execution. It's your responsibility to await this if needed.
+
+ .. versionadded:: 2.7
+ """
+ return self._filter_test_common(
+ name, value, args, kwargs, context, eval_ctx, True
+ )
+
+ def call_test(
+ self,
+ name: str,
+ value: t.Any,
+ args: t.Optional[t.Sequence[t.Any]] = None,
+ kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
+ context: t.Optional[Context] = None,
+ eval_ctx: t.Optional[EvalContext] = None,
+ ) -> t.Any:
+ """Invoke a test on a value the same way the compiler does.
+
+ This might return a coroutine if the test is running from an
+ environment in async mode and the test supports async execution.
+ It's your responsibility to await this if needed.
+
+ .. versionchanged:: 3.0
+ Tests support ``@pass_context``, etc. decorators. Added
+ the ``context`` and ``eval_ctx`` parameters.
.. versionadded:: 2.7
"""
- func = self.tests.get(name)
- if func is None:
- fail_for_missing_callable("no test named %r", name)
- return func(value, *(args or ()), **(kwargs or {}))
+ return self._filter_test_common(
+ name, value, args, kwargs, context, eval_ctx, False
+ )
@internalcode
- def parse(self, source, name=None, filename=None):
+ def parse(
+ self,
+ source: str,
+ name: t.Optional[str] = None,
+ filename: t.Optional[str] = None,
+ ) -> nodes.Template:
"""Parse the sourcecode and return the abstract syntax tree. This
tree of nodes is used by the compiler to convert the template into
executable source- or bytecode. This is useful for debugging or to
@@ -534,11 +610,18 @@ def parse(self, source, name=None, filename=None):
except TemplateSyntaxError:
self.handle_exception(source=source)
- def _parse(self, source, name, filename):
+ def _parse(
+ self, source: str, name: t.Optional[str], filename: t.Optional[str]
+ ) -> nodes.Template:
"""Internal parsing function used by `parse` and `compile`."""
- return Parser(self, source, name, encode_filename(filename)).parse()
+ return Parser(self, source, name, filename).parse()
- def lex(self, source, name=None, filename=None):
+ def lex(
+ self,
+ source: str,
+ name: t.Optional[str] = None,
+ filename: t.Optional[str] = None,
+ ) -> t.Iterator[t.Tuple[int, str, str]]:
"""Lex the given sourcecode and return a generator that yields
tokens as tuples in the form ``(lineno, token_type, value)``.
This can be useful for :ref:`extension development `
@@ -548,13 +631,18 @@ def lex(self, source, name=None, filename=None):
of the extensions to be applied you have to filter source through
the :meth:`preprocess` method.
"""
- source = text_type(source)
+ source = str(source)
try:
return self.lexer.tokeniter(source, name, filename)
except TemplateSyntaxError:
self.handle_exception(source=source)
- def preprocess(self, source, name=None, filename=None):
+ def preprocess(
+ self,
+ source: str,
+ name: t.Optional[str] = None,
+ filename: t.Optional[str] = None,
+ ) -> str:
"""Preprocesses the source with all extensions. This is automatically
called for all parsing and compiling methods but *not* for :meth:`lex`
because there you usually only want the actual source tokenized.
@@ -562,28 +650,43 @@ def preprocess(self, source, name=None, filename=None):
return reduce(
lambda s, e: e.preprocess(s, name, filename),
self.iter_extensions(),
- text_type(source),
+ str(source),
)
- def _tokenize(self, source, name, filename=None, state=None):
+ def _tokenize(
+ self,
+ source: str,
+ name: t.Optional[str],
+ filename: t.Optional[str] = None,
+ state: t.Optional[str] = None,
+ ) -> TokenStream:
"""Called by the parser to do the preprocessing and filtering
for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`.
"""
source = self.preprocess(source, name, filename)
stream = self.lexer.tokenize(source, name, filename, state)
+
for ext in self.iter_extensions():
- stream = ext.filter_stream(stream)
+ stream = ext.filter_stream(stream) # type: ignore
+
if not isinstance(stream, TokenStream):
- stream = TokenStream(stream, name, filename)
+ stream = TokenStream(stream, name, filename) # type: ignore
+
return stream
- def _generate(self, source, name, filename, defer_init=False):
+ def _generate(
+ self,
+ source: nodes.Template,
+ name: t.Optional[str],
+ filename: t.Optional[str],
+ defer_init: bool = False,
+ ) -> str:
"""Internal hook that can be overridden to hook a different generate
method in.
.. versionadded:: 2.5
"""
- return generate(
+ return generate( # type: ignore
source,
self,
name,
@@ -592,7 +695,7 @@ def _generate(self, source, name, filename, defer_init=False):
optimized=self.optimized,
)
- def _compile(self, source, filename):
+ def _compile(self, source: str, filename: str) -> CodeType:
"""Internal hook that can be overridden to hook a different compile
method in.
@@ -600,8 +703,37 @@ def _compile(self, source, filename):
"""
return compile(source, filename, "exec")
+ @typing.overload
+ def compile( # type: ignore
+ self,
+ source: t.Union[str, nodes.Template],
+ name: t.Optional[str] = None,
+ filename: t.Optional[str] = None,
+ raw: "te.Literal[False]" = False,
+ defer_init: bool = False,
+ ) -> CodeType:
+ ...
+
+ @typing.overload
+ def compile(
+ self,
+ source: t.Union[str, nodes.Template],
+ name: t.Optional[str] = None,
+ filename: t.Optional[str] = None,
+ raw: "te.Literal[True]" = ...,
+ defer_init: bool = False,
+ ) -> str:
+ ...
+
@internalcode
- def compile(self, source, name=None, filename=None, raw=False, defer_init=False):
+ def compile(
+ self,
+ source: t.Union[str, nodes.Template],
+ name: t.Optional[str] = None,
+ filename: t.Optional[str] = None,
+ raw: bool = False,
+ defer_init: bool = False,
+ ) -> t.Union[str, CodeType]:
"""Compile a node or template source code. The `name` parameter is
the load name of the template after it was joined using
:meth:`join_path` if necessary, not the filename on the file system.
@@ -623,7 +755,7 @@ def compile(self, source, name=None, filename=None, raw=False, defer_init=False)
"""
source_hint = None
try:
- if isinstance(source, string_types):
+ if isinstance(source, str):
source_hint = source
source = self._parse(source, name, filename)
source = self._generate(source, name, filename, defer_init=defer_init)
@@ -631,13 +763,13 @@ def compile(self, source, name=None, filename=None, raw=False, defer_init=False)
return source
if filename is None:
filename = ""
- else:
- filename = encode_filename(filename)
return self._compile(source, filename)
except TemplateSyntaxError:
self.handle_exception(source=source_hint)
- def compile_expression(self, source, undefined_to_none=True):
+ def compile_expression(
+ self, source: str, undefined_to_none: bool = True
+ ) -> "TemplateExpression":
"""A handy helper method that returns a callable that accepts keyword
arguments that appear as variables in the expression. If called it
returns the result of the expression.
@@ -674,8 +806,7 @@ def compile_expression(self, source, undefined_to_none=True):
)
expr.set_environment(self)
except TemplateSyntaxError:
- if sys.exc_info() is not None:
- self.handle_exception(source=source)
+ self.handle_exception(source=source)
body = [nodes.Assign(nodes.Name("result", "store"), expr, lineno=1)]
template = self.from_string(nodes.Template(body, lineno=1))
@@ -683,14 +814,13 @@ def compile_expression(self, source, undefined_to_none=True):
def compile_templates(
self,
- target,
- extensions=None,
- filter_func=None,
- zip="deflated",
- log_function=None,
- ignore_errors=True,
- py_compile=False,
- ):
+ target: t.Union[str, os.PathLike],
+ extensions: t.Optional[t.Collection[str]] = None,
+ filter_func: t.Optional[t.Callable[[str], bool]] = None,
+ zip: t.Optional[str] = "deflated",
+ log_function: t.Optional[t.Callable[[str], None]] = None,
+ ignore_errors: bool = True,
+ ) -> None:
"""Finds all the templates the loader can find, compiles them
and stores them in `target`. If `zip` is `None`, instead of in a
zipfile, the templates will be stored in a directory.
@@ -706,52 +836,26 @@ def compile_templates(
syntax errors to abort the compilation you can set `ignore_errors`
to `False` and you will get an exception on syntax errors.
- If `py_compile` is set to `True` .pyc files will be written to the
- target instead of standard .py files. This flag does not do anything
- on pypy and Python 3 where pyc files are not picked up by itself and
- don't give much benefit.
-
.. versionadded:: 2.4
"""
from .loaders import ModuleLoader
if log_function is None:
- def log_function(x):
+ def log_function(x: str) -> None:
pass
- if py_compile:
- if not PY2 or PYPY:
- import warnings
-
- warnings.warn(
- "'py_compile=True' has no effect on PyPy or Python"
- " 3 and will be removed in version 3.0",
- DeprecationWarning,
- stacklevel=2,
- )
- py_compile = False
- else:
- import imp
- import marshal
-
- py_header = imp.get_magic() + u"\xff\xff\xff\xff".encode("iso-8859-15")
+ assert log_function is not None
+ assert self.loader is not None, "No loader configured."
- # Python 3.3 added a source filesize to the header
- if sys.version_info >= (3, 3):
- py_header += u"\x00\x00\x00\x00".encode("iso-8859-15")
-
- def write_file(filename, data):
+ def write_file(filename: str, data: str) -> None:
if zip:
info = ZipInfo(filename)
info.external_attr = 0o755 << 16
zip_file.writestr(info, data)
else:
- if isinstance(data, text_type):
- data = data.encode("utf8")
-
with open(os.path.join(target, filename), "wb") as f:
- f.write(data)
+ f.write(data.encode("utf8"))
if zip is not None:
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
@@ -759,11 +863,11 @@ def write_file(filename, data):
zip_file = ZipFile(
target, "w", dict(deflated=ZIP_DEFLATED, stored=ZIP_STORED)[zip]
)
- log_function('Compiling into Zip archive "%s"' % target)
+ log_function(f"Compiling into Zip archive {target!r}")
else:
if not os.path.isdir(target):
os.makedirs(target)
- log_function('Compiling into folder "%s"' % target)
+ log_function(f"Compiling into folder {target!r}")
try:
for name in self.list_templates(extensions, filter_func):
@@ -773,25 +877,24 @@ def write_file(filename, data):
except TemplateSyntaxError as e:
if not ignore_errors:
raise
- log_function('Could not compile "%s": %s' % (name, e))
+ log_function(f'Could not compile "{name}": {e}')
continue
filename = ModuleLoader.get_module_filename(name)
- if py_compile:
- c = self._compile(code, encode_filename(filename))
- write_file(filename + "c", py_header + marshal.dumps(c))
- log_function('Byte-compiled "%s" as %s' % (name, filename + "c"))
- else:
- write_file(filename, code)
- log_function('Compiled "%s" as %s' % (name, filename))
+ write_file(filename, code)
+ log_function(f'Compiled "{name}" as {filename}')
finally:
if zip:
zip_file.close()
log_function("Finished compiling templates")
- def list_templates(self, extensions=None, filter_func=None):
+ def list_templates(
+ self,
+ extensions: t.Optional[t.Collection[str]] = None,
+ filter_func: t.Optional[t.Callable[[str], bool]] = None,
+ ) -> t.List[str]:
"""Returns a list of templates for this environment. This requires
that the loader supports the loader's
:meth:`~BaseLoader.list_templates` method.
@@ -807,6 +910,7 @@ def list_templates(self, extensions=None, filter_func=None):
.. versionadded:: 2.4
"""
+ assert self.loader is not None, "No loader configured."
names = self.loader.list_templates()
if extensions is not None:
@@ -815,7 +919,7 @@ def list_templates(self, extensions=None, filter_func=None):
"either extensions or filter_func can be passed, but not both"
)
- def filter_func(x):
+ def filter_func(x: str) -> bool:
return "." in x and x.rsplit(".", 1)[1] in extensions
if filter_func is not None:
@@ -823,15 +927,15 @@ def filter_func(x):
return names
- def handle_exception(self, source=None):
+ def handle_exception(self, source: t.Optional[str] = None) -> "te.NoReturn":
"""Exception handling helper. This is used internally to either raise
rewritten exceptions or return a rendered traceback for the template.
"""
from .debug import rewrite_traceback_stack
- reraise(*rewrite_traceback_stack(source=source))
+ raise rewrite_traceback_stack(source=source)
- def join_path(self, template, parent):
+ def join_path(self, template: str, parent: str) -> str:
"""Join a template with the parent. By default all the lookups are
relative to the loader root so this method returns the `template`
parameter unchanged, but if the paths should be relative to the
@@ -844,7 +948,9 @@ def join_path(self, template, parent):
return template
@internalcode
- def _load_template(self, name, globals):
+ def _load_template(
+ self, name: str, globals: t.Optional[t.MutableMapping[str, t.Any]]
+ ) -> "Template":
if self.loader is None:
raise TypeError("no loader for this environment specified")
cache_key = (weakref.ref(self.loader), name)
@@ -853,49 +959,88 @@ def _load_template(self, name, globals):
if template is not None and (
not self.auto_reload or template.is_up_to_date
):
+ # template.globals is a ChainMap, modifying it will only
+ # affect the template, not the environment globals.
+ if globals:
+ template.globals.update(globals)
+
return template
- template = self.loader.load(self, name, globals)
+
+ template = self.loader.load(self, name, self.make_globals(globals))
+
if self.cache is not None:
self.cache[cache_key] = template
return template
@internalcode
- def get_template(self, name, parent=None, globals=None):
- """Load a template from the loader. If a loader is configured this
- method asks the loader for the template and returns a :class:`Template`.
- If the `parent` parameter is not `None`, :meth:`join_path` is called
- to get the real template name before loading.
-
- The `globals` parameter can be used to provide template wide globals.
- These variables are available in the context at render time.
-
- If the template does not exist a :exc:`TemplateNotFound` exception is
- raised.
+ def get_template(
+ self,
+ name: t.Union[str, "Template"],
+ parent: t.Optional[str] = None,
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
+ ) -> "Template":
+ """Load a template by name with :attr:`loader` and return a
+ :class:`Template`. If the template does not exist a
+ :exc:`TemplateNotFound` exception is raised.
+
+ :param name: Name of the template to load. When loading
+ templates from the filesystem, "/" is used as the path
+ separator, even on Windows.
+ :param parent: The name of the parent template importing this
+ template. :meth:`join_path` can be used to implement name
+ transformations with this.
+ :param globals: Extend the environment :attr:`globals` with
+ these extra variables available for all renders of this
+ template. If the template has already been loaded and
+ cached, its globals are updated with any new items.
+
+ .. versionchanged:: 3.0
+ If a template is loaded from cache, ``globals`` will update
+ the template's globals instead of ignoring the new values.
.. versionchanged:: 2.4
- If `name` is a :class:`Template` object it is returned from the
- function unchanged.
+ If ``name`` is a :class:`Template` object it is returned
+ unchanged.
"""
if isinstance(name, Template):
return name
if parent is not None:
name = self.join_path(name, parent)
- return self._load_template(name, self.make_globals(globals))
+
+ return self._load_template(name, globals)
@internalcode
- def select_template(self, names, parent=None, globals=None):
- """Works like :meth:`get_template` but tries a number of templates
- before it fails. If it cannot find any of the templates, it will
- raise a :exc:`TemplatesNotFound` exception.
+ def select_template(
+ self,
+ names: t.Iterable[t.Union[str, "Template"]],
+ parent: t.Optional[str] = None,
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
+ ) -> "Template":
+ """Like :meth:`get_template`, but tries loading multiple names.
+ If none of the names can be loaded a :exc:`TemplatesNotFound`
+ exception is raised.
+
+ :param names: List of template names to try loading in order.
+ :param parent: The name of the parent template importing this
+ template. :meth:`join_path` can be used to implement name
+ transformations with this.
+ :param globals: Extend the environment :attr:`globals` with
+ these extra variables available for all renders of this
+ template. If the template has already been loaded and
+ cached, its globals are updated with any new items.
+
+ .. versionchanged:: 3.0
+ If a template is loaded from cache, ``globals`` will update
+ the template's globals instead of ignoring the new values.
.. versionchanged:: 2.11
- If names is :class:`Undefined`, an :exc:`UndefinedError` is
- raised instead. If no templates were found and names
+ If ``names`` is :class:`Undefined`, an :exc:`UndefinedError`
+ is raised instead. If no templates were found and ``names``
contains :class:`Undefined`, the message is more helpful.
.. versionchanged:: 2.4
- If `names` contains a :class:`Template` object it is returned
- from the function unchanged.
+ If ``names`` contains a :class:`Template` object it is
+ returned unchanged.
.. versionadded:: 2.3
"""
@@ -904,9 +1049,9 @@ def select_template(self, names, parent=None, globals=None):
if not names:
raise TemplatesNotFound(
- message=u"Tried to select from an empty list " u"of templates."
+ message="Tried to select from an empty list of templates."
)
- globals = self.make_globals(globals)
+
for name in names:
if isinstance(name, Template):
return name
@@ -916,95 +1061,127 @@ def select_template(self, names, parent=None, globals=None):
return self._load_template(name, globals)
except (TemplateNotFound, UndefinedError):
pass
- raise TemplatesNotFound(names)
+ raise TemplatesNotFound(names) # type: ignore
@internalcode
- def get_or_select_template(self, template_name_or_list, parent=None, globals=None):
- """Does a typecheck and dispatches to :meth:`select_template`
- if an iterable of template names is given, otherwise to
- :meth:`get_template`.
+ def get_or_select_template(
+ self,
+ template_name_or_list: t.Union[
+ str, "Template", t.List[t.Union[str, "Template"]]
+ ],
+ parent: t.Optional[str] = None,
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
+ ) -> "Template":
+ """Use :meth:`select_template` if an iterable of template names
+ is given, or :meth:`get_template` if one name is given.
.. versionadded:: 2.3
"""
- if isinstance(template_name_or_list, (string_types, Undefined)):
+ if isinstance(template_name_or_list, (str, Undefined)):
return self.get_template(template_name_or_list, parent, globals)
elif isinstance(template_name_or_list, Template):
return template_name_or_list
return self.select_template(template_name_or_list, parent, globals)
- def from_string(self, source, globals=None, template_class=None):
- """Load a template from a string. This parses the source given and
- returns a :class:`Template` object.
+ def from_string(
+ self,
+ source: t.Union[str, nodes.Template],
+ globals: t.Optional[t.MutableMapping[str, t.Any]] = None,
+ template_class: t.Optional[t.Type["Template"]] = None,
+ ) -> "Template":
+ """Load a template from a source string without using
+ :attr:`loader`.
+
+ :param source: Jinja source to compile into a template.
+ :param globals: Extend the environment :attr:`globals` with
+ these extra variables available for all renders of this
+ template. If the template has already been loaded and
+ cached, its globals are updated with any new items.
+ :param template_class: Return an instance of this
+ :class:`Template` class.
"""
- globals = self.make_globals(globals)
+ gs = self.make_globals(globals)
cls = template_class or self.template_class
- return cls.from_code(self, self.compile(source), globals, None)
-
- def make_globals(self, d):
- """Return a dict for the globals."""
- if not d:
- return self.globals
- return dict(self.globals, **d)
-
-
-class Template(object):
- """The central template object. This class represents a compiled template
- and is used to evaluate it.
-
- Normally the template object is generated from an :class:`Environment` but
- it also has a constructor that makes it possible to create a template
- instance directly using the constructor. It takes the same arguments as
- the environment constructor but it's not possible to specify a loader.
-
- Every template object has a few methods and members that are guaranteed
- to exist. However it's important that a template object should be
- considered immutable. Modifications on the object are not supported.
-
- Template objects created from the constructor rather than an environment
- do have an `environment` attribute that points to a temporary environment
- that is probably shared with other templates created with the constructor
- and compatible settings.
-
- >>> template = Template('Hello {{ name }}!')
- >>> template.render(name='John Doe') == u'Hello John Doe!'
- True
- >>> stream = template.stream(name='John Doe')
- >>> next(stream) == u'Hello John Doe!'
- True
- >>> next(stream)
- Traceback (most recent call last):
- ...
- StopIteration
+ return cls.from_code(self, self.compile(source), gs, None)
+
+ def make_globals(
+ self, d: t.Optional[t.MutableMapping[str, t.Any]]
+ ) -> t.MutableMapping[str, t.Any]:
+ """Make the globals map for a template. Any given template
+ globals overlay the environment :attr:`globals`.
+
+ Returns a :class:`collections.ChainMap`. This allows any changes
+ to a template's globals to only affect that template, while
+ changes to the environment's globals are still reflected.
+ However, avoid modifying any globals after a template is loaded.
+
+ :param d: Dict of template-specific globals.
+
+ .. versionchanged:: 3.0
+ Use :class:`collections.ChainMap` to always prevent mutating
+ environment globals.
+ """
+ if d is None:
+ d = {}
+
+ return ChainMap(d, self.globals)
+
+
+class Template:
+ """A compiled template that can be rendered.
+
+ Use the methods on :class:`Environment` to create or load templates.
+ The environment is used to configure how templates are compiled and
+ behave.
+
+ It is also possible to create a template object directly. This is
+ not usually recommended. The constructor takes most of the same
+ arguments as :class:`Environment`. All templates created with the
+ same environment arguments share the same ephemeral ``Environment``
+ instance behind the scenes.
+
+ A template object should be considered immutable. Modifications on
+ the object are not supported.
"""
#: Type of environment to create when creating a template directly
#: rather than through an existing environment.
- environment_class = Environment
+ environment_class: t.Type[Environment] = Environment
+
+ environment: Environment
+ globals: t.MutableMapping[str, t.Any]
+ name: t.Optional[str]
+ filename: t.Optional[str]
+ blocks: t.Dict[str, t.Callable[[Context], t.Iterator[str]]]
+ root_render_func: t.Callable[[Context], t.Iterator[str]]
+ _module: t.Optional["TemplateModule"]
+ _debug_info: str
+ _uptodate: t.Optional[t.Callable[[], bool]]
def __new__(
cls,
- source,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- lstrip_blocks=LSTRIP_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- keep_trailing_newline=KEEP_TRAILING_NEWLINE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False,
- enable_async=False,
- ):
+ source: t.Union[str, nodes.Template],
+ block_start_string: str = BLOCK_START_STRING,
+ block_end_string: str = BLOCK_END_STRING,
+ variable_start_string: str = VARIABLE_START_STRING,
+ variable_end_string: str = VARIABLE_END_STRING,
+ comment_start_string: str = COMMENT_START_STRING,
+ comment_end_string: str = COMMENT_END_STRING,
+ line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX,
+ line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX,
+ trim_blocks: bool = TRIM_BLOCKS,
+ lstrip_blocks: bool = LSTRIP_BLOCKS,
+ newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE,
+ keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE,
+ extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (),
+ optimized: bool = True,
+ undefined: t.Type[Undefined] = Undefined,
+ finalize: t.Optional[t.Callable[..., t.Any]] = None,
+ autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False,
+ enable_async: bool = False,
+ ) -> t.Any: # it returns a `Template`, but this breaks the sphinx build...
env = get_spontaneous_environment(
- cls.environment_class,
+ cls.environment_class, # type: ignore
block_start_string,
block_end_string,
variable_start_string,
@@ -1019,7 +1196,7 @@ def __new__(
keep_trailing_newline,
frozenset(extensions),
optimized,
- undefined,
+ undefined, # type: ignore
finalize,
autoescape,
None,
@@ -1031,7 +1208,13 @@ def __new__(
return env.from_string(source, template_class=cls)
@classmethod
- def from_code(cls, environment, code, globals, uptodate=None):
+ def from_code(
+ cls,
+ environment: Environment,
+ code: CodeType,
+ globals: t.MutableMapping[str, t.Any],
+ uptodate: t.Optional[t.Callable[[], bool]] = None,
+ ) -> "Template":
"""Creates a template object from compiled code and the globals. This
is used by the loaders and environment to create a template object.
"""
@@ -1042,7 +1225,12 @@ def from_code(cls, environment, code, globals, uptodate=None):
return rv
@classmethod
- def from_module_dict(cls, environment, module_dict, globals):
+ def from_module_dict(
+ cls,
+ environment: Environment,
+ module_dict: t.MutableMapping[str, t.Any],
+ globals: t.MutableMapping[str, t.Any],
+ ) -> "Template":
"""Creates a template object from a module. This is used by the
module loader to create a template object.
@@ -1051,8 +1239,13 @@ def from_module_dict(cls, environment, module_dict, globals):
return cls._from_namespace(environment, module_dict, globals)
@classmethod
- def _from_namespace(cls, environment, namespace, globals):
- t = object.__new__(cls)
+ def _from_namespace(
+ cls,
+ environment: Environment,
+ namespace: t.MutableMapping[str, t.Any],
+ globals: t.MutableMapping[str, t.Any],
+ ) -> "Template":
+ t: "Template" = object.__new__(cls)
t.environment = environment
t.globals = globals
t.name = namespace["name"]
@@ -1073,7 +1266,7 @@ def _from_namespace(cls, environment, namespace, globals):
return t
- def render(self, *args, **kwargs):
+ def render(self, *args: t.Any, **kwargs: t.Any) -> str:
"""This method accepts the same arguments as the `dict` constructor:
A dict, a dict subclass or some keyword arguments. If no arguments
are given the context will be empty. These two calls do the same::
@@ -1081,15 +1274,33 @@ def render(self, *args, **kwargs):
template.render(knights='that say nih')
template.render({'knights': 'that say nih'})
- This will return the rendered template as unicode string.
+ This will return the rendered template as a string.
"""
- vars = dict(*args, **kwargs)
+ if self.environment.is_async:
+ import asyncio
+
+ close = False
+
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.new_event_loop()
+ close = True
+
+ try:
+ return loop.run_until_complete(self.render_async(*args, **kwargs))
+ finally:
+ if close:
+ loop.close()
+
+ ctx = self.new_context(dict(*args, **kwargs))
+
try:
- return concat(self.root_render_func(self.new_context(vars)))
+ return self.environment.concat(self.root_render_func(ctx)) # type: ignore
except Exception:
self.environment.handle_exception()
- def render_async(self, *args, **kwargs):
+ async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str:
"""This works similar to :meth:`render` but returns a coroutine
that when awaited returns the entire rendered template string. This
requires the async feature to be enabled.
@@ -1098,42 +1309,75 @@ def render_async(self, *args, **kwargs):
await template.render_async(knights='that say nih; asynchronously')
"""
- # see asyncsupport for the actual implementation
- raise NotImplementedError(
- "This feature is not available for this version of Python"
- )
+ if not self.environment.is_async:
+ raise RuntimeError(
+ "The environment was not created with async mode enabled."
+ )
+
+ ctx = self.new_context(dict(*args, **kwargs))
+
+ try:
+ return self.environment.concat( # type: ignore
+ [n async for n in self.root_render_func(ctx)] # type: ignore
+ )
+ except Exception:
+ return self.environment.handle_exception()
- def stream(self, *args, **kwargs):
+ def stream(self, *args: t.Any, **kwargs: t.Any) -> "TemplateStream":
"""Works exactly like :meth:`generate` but returns a
:class:`TemplateStream`.
"""
return TemplateStream(self.generate(*args, **kwargs))
- def generate(self, *args, **kwargs):
+ def generate(self, *args: t.Any, **kwargs: t.Any) -> t.Iterator[str]:
"""For very large templates it can be useful to not render the whole
template at once but evaluate each statement after another and yield
piece for piece. This method basically does exactly that and returns
- a generator that yields one item after another as unicode strings.
+ a generator that yields one item after another as strings.
It accepts the same arguments as :meth:`render`.
"""
- vars = dict(*args, **kwargs)
+ if self.environment.is_async:
+ import asyncio
+
+ async def to_list() -> t.List[str]:
+ return [x async for x in self.generate_async(*args, **kwargs)]
+
+ yield from asyncio.run(to_list())
+ return
+
+ ctx = self.new_context(dict(*args, **kwargs))
+
try:
- for event in self.root_render_func(self.new_context(vars)):
- yield event
+ yield from self.root_render_func(ctx)
except Exception:
yield self.environment.handle_exception()
- def generate_async(self, *args, **kwargs):
+ async def generate_async(
+ self, *args: t.Any, **kwargs: t.Any
+ ) -> t.AsyncIterator[str]:
"""An async version of :meth:`generate`. Works very similarly but
returns an async iterator instead.
"""
- # see asyncsupport for the actual implementation
- raise NotImplementedError(
- "This feature is not available for this version of Python"
- )
+ if not self.environment.is_async:
+ raise RuntimeError(
+ "The environment was not created with async mode enabled."
+ )
+
+ ctx = self.new_context(dict(*args, **kwargs))
- def new_context(self, vars=None, shared=False, locals=None):
+ try:
+ async for event in self.root_render_func(ctx): # type: ignore
+ yield event
+ except Exception:
+ yield self.environment.handle_exception()
+
+ def new_context(
+ self,
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
+ shared: bool = False,
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
+ ) -> Context:
"""Create a new :class:`Context` for this template. The vars
provided will be passed to the template. Per default the globals
are added to the context. If shared is set to `True` the data
@@ -1145,35 +1389,80 @@ def new_context(self, vars=None, shared=False, locals=None):
self.environment, self.name, self.blocks, vars, shared, self.globals, locals
)
- def make_module(self, vars=None, shared=False, locals=None):
+ def make_module(
+ self,
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
+ shared: bool = False,
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
+ ) -> "TemplateModule":
"""This method works like the :attr:`module` attribute when called
without arguments but it will evaluate the template on every call
rather than caching it. It's also possible to provide
a dict which is then used as context. The arguments are the same
as for the :meth:`new_context` method.
"""
- return TemplateModule(self, self.new_context(vars, shared, locals))
+ ctx = self.new_context(vars, shared, locals)
+ return TemplateModule(self, ctx)
- def make_module_async(self, vars=None, shared=False, locals=None):
+ async def make_module_async(
+ self,
+ vars: t.Optional[t.Dict[str, t.Any]] = None,
+ shared: bool = False,
+ locals: t.Optional[t.Mapping[str, t.Any]] = None,
+ ) -> "TemplateModule":
"""As template module creation can invoke template code for
asynchronous executions this method must be used instead of the
normal :meth:`make_module` one. Likewise the module attribute
becomes unavailable in async mode.
"""
- # see asyncsupport for the actual implementation
- raise NotImplementedError(
- "This feature is not available for this version of Python"
+ ctx = self.new_context(vars, shared, locals)
+ return TemplateModule(
+ self, ctx, [x async for x in self.root_render_func(ctx)] # type: ignore
)
@internalcode
- def _get_default_module(self):
- if self._module is not None:
- return self._module
- self._module = rv = self.make_module()
- return rv
+ def _get_default_module(self, ctx: t.Optional[Context] = None) -> "TemplateModule":
+ """If a context is passed in, this means that the template was
+ imported. Imported templates have access to the current
+ template's globals by default, but they can only be accessed via
+ the context during runtime.
+
+ If there are new globals, we need to create a new module because
+ the cached module is already rendered and will not have access
+ to globals from the current context. This new module is not
+ cached because the template can be imported elsewhere, and it
+ should have access to only the current template's globals.
+ """
+ if self.environment.is_async:
+ raise RuntimeError("Module is not available in async mode.")
+
+ if ctx is not None:
+ keys = ctx.globals_keys - self.globals.keys()
+
+ if keys:
+ return self.make_module({k: ctx.parent[k] for k in keys})
+
+ if self._module is None:
+ self._module = self.make_module()
+
+ return self._module
+
+ async def _get_default_module_async(
+ self, ctx: t.Optional[Context] = None
+ ) -> "TemplateModule":
+ if ctx is not None:
+ keys = ctx.globals_keys - self.globals.keys()
+
+ if keys:
+ return await self.make_module_async({k: ctx.parent[k] for k in keys})
+
+ if self._module is None:
+ self._module = await self.make_module_async()
+
+ return self._module
@property
- def module(self):
+ def module(self) -> "TemplateModule":
"""The template as module. This is used for imports in the
template runtime but is also useful if one wants to access
exported template variables from the Python layer:
@@ -1188,7 +1477,7 @@ def module(self):
"""
return self._get_default_module()
- def get_corresponding_lineno(self, lineno):
+ def get_corresponding_lineno(self, lineno: int) -> int:
"""Return the source line number of a line number in the
generated bytecode as they are not in sync.
"""
@@ -1198,73 +1487,82 @@ def get_corresponding_lineno(self, lineno):
return 1
@property
- def is_up_to_date(self):
+ def is_up_to_date(self) -> bool:
"""If this variable is `False` there is a newer version available."""
if self._uptodate is None:
return True
return self._uptodate()
@property
- def debug_info(self):
+ def debug_info(self) -> t.List[t.Tuple[int, int]]:
"""The debug info mapping."""
if self._debug_info:
- return [tuple(map(int, x.split("="))) for x in self._debug_info.split("&")]
+ return [
+ tuple(map(int, x.split("="))) # type: ignore
+ for x in self._debug_info.split("&")
+ ]
+
return []
- def __repr__(self):
+ def __repr__(self) -> str:
if self.name is None:
- name = "memory:%x" % id(self)
+ name = f"memory:{id(self):x}"
else:
name = repr(self.name)
- return "<%s %s>" % (self.__class__.__name__, name)
+ return f"<{type(self).__name__} {name}>"
-@implements_to_string
-class TemplateModule(object):
+class TemplateModule:
"""Represents an imported template. All the exported names of the
template are available as attributes on this object. Additionally
- converting it into an unicode- or bytestrings renders the contents.
+ converting it into a string renders the contents.
"""
- def __init__(self, template, context, body_stream=None):
+ def __init__(
+ self,
+ template: Template,
+ context: Context,
+ body_stream: t.Optional[t.Iterable[str]] = None,
+ ) -> None:
if body_stream is None:
if context.environment.is_async:
raise RuntimeError(
- "Async mode requires a body stream "
- "to be passed to a template module. Use "
- "the async methods of the API you are "
- "using."
+ "Async mode requires a body stream to be passed to"
+ " a template module. Use the async methods of the"
+ " API you are using."
)
+
body_stream = list(template.root_render_func(context))
+
self._body_stream = body_stream
self.__dict__.update(context.get_exported())
self.__name__ = template.name
- def __html__(self):
+ def __html__(self) -> Markup:
return Markup(concat(self._body_stream))
- def __str__(self):
+ def __str__(self) -> str:
return concat(self._body_stream)
- def __repr__(self):
+ def __repr__(self) -> str:
if self.__name__ is None:
- name = "memory:%x" % id(self)
+ name = f"memory:{id(self):x}"
else:
name = repr(self.__name__)
- return "<%s %s>" % (self.__class__.__name__, name)
+ return f"<{type(self).__name__} {name}>"
-class TemplateExpression(object):
+class TemplateExpression:
"""The :meth:`jinja2.Environment.compile_expression` method returns an
instance of this object. It encapsulates the expression-like access
to the template with an expression it wraps.
"""
- def __init__(self, template, undefined_to_none):
+ def __init__(self, template: Template, undefined_to_none: bool) -> None:
self._template = template
self._undefined_to_none = undefined_to_none
- def __call__(self, *args, **kwargs):
+ def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Optional[t.Any]:
context = self._template.new_context(dict(*args, **kwargs))
consume(self._template.root_render_func(context))
rv = context.vars["result"]
@@ -1273,25 +1571,29 @@ def __call__(self, *args, **kwargs):
return rv
-@implements_iterator
-class TemplateStream(object):
+class TemplateStream:
"""A template stream works pretty much like an ordinary python generator
but it can buffer multiple items to reduce the number of total iterations.
Per default the output is unbuffered which means that for every unbuffered
- instruction in the template one unicode string is yielded.
+ instruction in the template one string is yielded.
If buffering is enabled with a buffer size of 5, five items are combined
- into a new unicode string. This is mainly useful if you are streaming
+ into a new string. This is mainly useful if you are streaming
big templates to a client via WSGI which flushes after each iteration.
"""
- def __init__(self, gen):
+ def __init__(self, gen: t.Iterator[str]) -> None:
self._gen = gen
self.disable_buffering()
- def dump(self, fp, encoding=None, errors="strict"):
+ def dump(
+ self,
+ fp: t.Union[str, t.IO],
+ encoding: t.Optional[str] = None,
+ errors: t.Optional[str] = "strict",
+ ) -> None:
"""Dump the complete stream into a file or file-like object.
- Per default unicode strings are written, if you want to encode
+ Per default strings are written, if you want to encode
before writing specify an `encoding`.
Example usage::
@@ -1299,16 +1601,19 @@ def dump(self, fp, encoding=None, errors="strict"):
Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
"""
close = False
- if isinstance(fp, string_types):
+
+ if isinstance(fp, str):
if encoding is None:
encoding = "utf-8"
+
fp = open(fp, "wb")
close = True
try:
if encoding is not None:
- iterable = (x.encode(encoding, errors) for x in self)
+ iterable = (x.encode(encoding, errors) for x in self) # type: ignore
else:
- iterable = self
+ iterable = self # type: ignore
+
if hasattr(fp, "writelines"):
fp.writelines(iterable)
else:
@@ -1318,17 +1623,17 @@ def dump(self, fp, encoding=None, errors="strict"):
if close:
fp.close()
- def disable_buffering(self):
+ def disable_buffering(self) -> None:
"""Disable the output buffering."""
self._next = partial(next, self._gen)
self.buffered = False
- def _buffered_generator(self, size):
- buf = []
+ def _buffered_generator(self, size: int) -> t.Iterator[str]:
+ buf: t.List[str] = []
c_size = 0
push = buf.append
- while 1:
+ while True:
try:
while c_size < size:
c = next(self._gen)
@@ -1342,7 +1647,7 @@ def _buffered_generator(self, size):
del buf[:]
c_size = 0
- def enable_buffering(self, size=5):
+ def enable_buffering(self, size: int = 5) -> None:
"""Enable buffering. Buffer `size` items before yielding them."""
if size <= 1:
raise ValueError("buffer size too small")
@@ -1350,11 +1655,11 @@ def enable_buffering(self, size=5):
self.buffered = True
self._next = partial(next, self._buffered_generator(size))
- def __iter__(self):
+ def __iter__(self) -> "TemplateStream":
return self
- def __next__(self):
- return self._next()
+ def __next__(self) -> str:
+ return self._next() # type: ignore
# hook in default template class. if anyone reads this comment: ignore that
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/exceptions.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/exceptions.py
index 0bf2003..082ebe8 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/exceptions.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/exceptions.py
@@ -1,44 +1,20 @@
-# -*- coding: utf-8 -*-
-from ._compat import imap
-from ._compat import implements_to_string
-from ._compat import PY2
-from ._compat import text_type
+import typing as t
+
+if t.TYPE_CHECKING:
+ from .runtime import Undefined
class TemplateError(Exception):
"""Baseclass for all template errors."""
- if PY2:
-
- def __init__(self, message=None):
- if message is not None:
- message = text_type(message).encode("utf-8")
- Exception.__init__(self, message)
-
- @property
- def message(self):
- if self.args:
- message = self.args[0]
- if message is not None:
- return message.decode("utf-8", "replace")
-
- def __unicode__(self):
- return self.message or u""
+ def __init__(self, message: t.Optional[str] = None) -> None:
+ super().__init__(message)
- else:
+ @property
+ def message(self) -> t.Optional[str]:
+ return self.args[0] if self.args else None
- def __init__(self, message=None):
- Exception.__init__(self, message)
- @property
- def message(self):
- if self.args:
- message = self.args[0]
- if message is not None:
- return message
-
-
-@implements_to_string
class TemplateNotFound(IOError, LookupError, TemplateError):
"""Raised if a template does not exist.
@@ -47,11 +23,15 @@ class TemplateNotFound(IOError, LookupError, TemplateError):
provided, an :exc:`UndefinedError` is raised.
"""
- # looks weird, but removes the warning descriptor that just
- # bogusly warns us about message being deprecated
- message = None
+ # Silence the Python warning about message being deprecated since
+ # it's not valid here.
+ message: t.Optional[str] = None
- def __init__(self, name, message=None):
+ def __init__(
+ self,
+ name: t.Optional[t.Union[str, "Undefined"]],
+ message: t.Optional[str] = None,
+ ) -> None:
IOError.__init__(self, name)
if message is None:
@@ -66,8 +46,8 @@ def __init__(self, name, message=None):
self.name = name
self.templates = [name]
- def __str__(self):
- return self.message
+ def __str__(self) -> str:
+ return str(self.message)
class TemplatesNotFound(TemplateNotFound):
@@ -82,7 +62,11 @@ class TemplatesNotFound(TemplateNotFound):
.. versionadded:: 2.2
"""
- def __init__(self, names=(), message=None):
+ def __init__(
+ self,
+ names: t.Sequence[t.Union[str, "Undefined"]] = (),
+ message: t.Optional[str] = None,
+ ) -> None:
if message is None:
from .runtime import Undefined
@@ -94,52 +78,57 @@ def __init__(self, names=(), message=None):
else:
parts.append(name)
- message = u"none of the templates given were found: " + u", ".join(
- imap(text_type, parts)
- )
- TemplateNotFound.__init__(self, names and names[-1] or None, message)
+ parts_str = ", ".join(map(str, parts))
+ message = f"none of the templates given were found: {parts_str}"
+
+ super().__init__(names[-1] if names else None, message)
self.templates = list(names)
-@implements_to_string
class TemplateSyntaxError(TemplateError):
"""Raised to tell the user that there is a problem with the template."""
- def __init__(self, message, lineno, name=None, filename=None):
- TemplateError.__init__(self, message)
+ def __init__(
+ self,
+ message: str,
+ lineno: int,
+ name: t.Optional[str] = None,
+ filename: t.Optional[str] = None,
+ ) -> None:
+ super().__init__(message)
self.lineno = lineno
self.name = name
self.filename = filename
- self.source = None
+ self.source: t.Optional[str] = None
# this is set to True if the debug.translate_syntax_error
# function translated the syntax error into a new traceback
self.translated = False
- def __str__(self):
+ def __str__(self) -> str:
# for translated errors we only return the message
if self.translated:
- return self.message
+ return t.cast(str, self.message)
# otherwise attach some stuff
- location = "line %d" % self.lineno
+ location = f"line {self.lineno}"
name = self.filename or self.name
if name:
- location = 'File "%s", %s' % (name, location)
- lines = [self.message, " " + location]
+ location = f'File "{name}", {location}'
+ lines = [t.cast(str, self.message), " " + location]
# if the source is set, add the line to the output
if self.source is not None:
try:
line = self.source.splitlines()[self.lineno - 1]
except IndexError:
- line = None
- if line:
+ pass
+ else:
lines.append(" " + line.strip())
- return u"\n".join(lines)
+ return "\n".join(lines)
- def __reduce__(self):
+ def __reduce__(self): # type: ignore
# https://bugs.python.org/issue1692335 Exceptions that take
# multiple required arguments have problems with pickling.
# Without this, raises TypeError: __init__() missing 1 required
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/ext.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/ext.py
index 9141be4..fade1fa 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/ext.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/ext.py
@@ -1,53 +1,57 @@
-# -*- coding: utf-8 -*-
"""Extension API for adding custom tags and behavior."""
import pprint
import re
-from sys import version_info
+import typing as t
from markupsafe import Markup
+from . import defaults
from . import nodes
-from ._compat import iteritems
-from ._compat import string_types
-from ._compat import with_metaclass
-from .defaults import BLOCK_END_STRING
-from .defaults import BLOCK_START_STRING
-from .defaults import COMMENT_END_STRING
-from .defaults import COMMENT_START_STRING
-from .defaults import KEEP_TRAILING_NEWLINE
-from .defaults import LINE_COMMENT_PREFIX
-from .defaults import LINE_STATEMENT_PREFIX
-from .defaults import LSTRIP_BLOCKS
-from .defaults import NEWLINE_SEQUENCE
-from .defaults import TRIM_BLOCKS
-from .defaults import VARIABLE_END_STRING
-from .defaults import VARIABLE_START_STRING
from .environment import Environment
from .exceptions import TemplateAssertionError
from .exceptions import TemplateSyntaxError
-from .nodes import ContextReference
-from .runtime import concat
-from .utils import contextfunction
+from .runtime import concat # type: ignore
+from .runtime import Context
+from .runtime import Undefined
from .utils import import_string
+from .utils import pass_context
-# the only real useful gettext functions for a Jinja template. Note
-# that ugettext must be assigned to gettext as Jinja doesn't support
-# non unicode strings.
-GETTEXT_FUNCTIONS = ("_", "gettext", "ngettext")
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+ from .lexer import Token
+ from .lexer import TokenStream
+ from .parser import Parser
-_ws_re = re.compile(r"\s*\n\s*")
+ class _TranslationsBasic(te.Protocol):
+ def gettext(self, message: str) -> str:
+ ...
+ def ngettext(self, singular: str, plural: str, n: int) -> str:
+ pass
-class ExtensionRegistry(type):
- """Gives the extension an unique identifier."""
+ class _TranslationsContext(_TranslationsBasic):
+ def pgettext(self, context: str, message: str) -> str:
+ ...
+
+ def npgettext(self, context: str, singular: str, plural: str, n: int) -> str:
+ ...
+
+ _SupportedTranslations = t.Union[_TranslationsBasic, _TranslationsContext]
- def __new__(mcs, name, bases, d):
- rv = type.__new__(mcs, name, bases, d)
- rv.identifier = rv.__module__ + "." + rv.__name__
- return rv
+
+# I18N functions available in Jinja templates. If the I18N library
+# provides ugettext, it will be assigned to gettext.
+GETTEXT_FUNCTIONS: t.Tuple[str, ...] = (
+ "_",
+ "gettext",
+ "ngettext",
+ "pgettext",
+ "npgettext",
+)
+_ws_re = re.compile(r"\s*\n\s*")
-class Extension(with_metaclass(ExtensionRegistry, object)):
+class Extension:
"""Extensions can be used to add extra functionality to the Jinja template
system at the parser level. Custom extensions are bound to an environment
but may not store environment specific data on `self`. The reason for
@@ -66,8 +70,13 @@ class Extension(with_metaclass(ExtensionRegistry, object)):
name as includes the name of the extension (fragment cache).
"""
+ identifier: t.ClassVar[str]
+
+ def __init_subclass__(cls) -> None:
+ cls.identifier = f"{cls.__module__}.{cls.__name__}"
+
#: if this extension parses this is the list of tags it's listening to.
- tags = set()
+ tags: t.Set[str] = set()
#: the priority of that extension. This is especially useful for
#: extensions that preprocess values. A lower value means higher
@@ -76,24 +85,28 @@ class Extension(with_metaclass(ExtensionRegistry, object)):
#: .. versionadded:: 2.4
priority = 100
- def __init__(self, environment):
+ def __init__(self, environment: Environment) -> None:
self.environment = environment
- def bind(self, environment):
+ def bind(self, environment: Environment) -> "Extension":
"""Create a copy of this extension bound to another environment."""
rv = object.__new__(self.__class__)
rv.__dict__.update(self.__dict__)
rv.environment = environment
return rv
- def preprocess(self, source, name, filename=None):
+ def preprocess(
+ self, source: str, name: t.Optional[str], filename: t.Optional[str] = None
+ ) -> str:
"""This method is called before the actual lexing and can be used to
preprocess the source. The `filename` is optional. The return value
must be the preprocessed source.
"""
return source
- def filter_stream(self, stream):
+ def filter_stream(
+ self, stream: "TokenStream"
+ ) -> t.Union["TokenStream", t.Iterable["Token"]]:
"""It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
to filter tokens returned. This method has to return an iterable of
:class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a
@@ -101,7 +114,7 @@ def filter_stream(self, stream):
"""
return stream
- def parse(self, parser):
+ def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
"""If any of the :attr:`tags` matched this method is called with the
parser as first argument. The token the parser stream is pointing at
is the name token that matched. This method has to return one or a
@@ -109,7 +122,9 @@ def parse(self, parser):
"""
raise NotImplementedError()
- def attr(self, name, lineno=None):
+ def attr(
+ self, name: str, lineno: t.Optional[int] = None
+ ) -> nodes.ExtensionAttribute:
"""Return an attribute node for the current extension. This is useful
to pass constants on extensions to generated template code.
@@ -120,8 +135,14 @@ def attr(self, name, lineno=None):
return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
def call_method(
- self, name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None
- ):
+ self,
+ name: str,
+ args: t.Optional[t.List[nodes.Expr]] = None,
+ kwargs: t.Optional[t.List[nodes.Keyword]] = None,
+ dyn_args: t.Optional[nodes.Expr] = None,
+ dyn_kwargs: t.Optional[nodes.Expr] = None,
+ lineno: t.Optional[int] = None,
+ ) -> nodes.Call:
"""Call a method of the extension. This is a shortcut for
:meth:`attr` + :class:`jinja2.nodes.Call`.
"""
@@ -139,38 +160,88 @@ def call_method(
)
-@contextfunction
-def _gettext_alias(__context, *args, **kwargs):
+@pass_context
+def _gettext_alias(
+ __context: Context, *args: t.Any, **kwargs: t.Any
+) -> t.Union[t.Any, Undefined]:
return __context.call(__context.resolve("gettext"), *args, **kwargs)
-def _make_new_gettext(func):
- @contextfunction
- def gettext(__context, __string, **variables):
+def _make_new_gettext(func: t.Callable[[str], str]) -> t.Callable[..., str]:
+ @pass_context
+ def gettext(__context: Context, __string: str, **variables: t.Any) -> str:
rv = __context.call(func, __string)
if __context.eval_ctx.autoescape:
rv = Markup(rv)
# Always treat as a format string, even if there are no
# variables. This makes translation strings more consistent
# and predictable. This requires escaping
- return rv % variables
+ return rv % variables # type: ignore
return gettext
-def _make_new_ngettext(func):
- @contextfunction
- def ngettext(__context, __singular, __plural, __num, **variables):
+def _make_new_ngettext(func: t.Callable[[str, str, int], str]) -> t.Callable[..., str]:
+ @pass_context
+ def ngettext(
+ __context: Context,
+ __singular: str,
+ __plural: str,
+ __num: int,
+ **variables: t.Any,
+ ) -> str:
variables.setdefault("num", __num)
rv = __context.call(func, __singular, __plural, __num)
if __context.eval_ctx.autoescape:
rv = Markup(rv)
# Always treat as a format string, see gettext comment above.
- return rv % variables
+ return rv % variables # type: ignore
return ngettext
+def _make_new_pgettext(func: t.Callable[[str, str], str]) -> t.Callable[..., str]:
+ @pass_context
+ def pgettext(
+ __context: Context, __string_ctx: str, __string: str, **variables: t.Any
+ ) -> str:
+ variables.setdefault("context", __string_ctx)
+ rv = __context.call(func, __string_ctx, __string)
+
+ if __context.eval_ctx.autoescape:
+ rv = Markup(rv)
+
+ # Always treat as a format string, see gettext comment above.
+ return rv % variables # type: ignore
+
+ return pgettext
+
+
+def _make_new_npgettext(
+ func: t.Callable[[str, str, str, int], str]
+) -> t.Callable[..., str]:
+ @pass_context
+ def npgettext(
+ __context: Context,
+ __string_ctx: str,
+ __singular: str,
+ __plural: str,
+ __num: int,
+ **variables: t.Any,
+ ) -> str:
+ variables.setdefault("context", __string_ctx)
+ variables.setdefault("num", __num)
+ rv = __context.call(func, __string_ctx, __singular, __plural, __num)
+
+ if __context.eval_ctx.autoescape:
+ rv = Markup(rv)
+
+ # Always treat as a format string, see gettext comment above.
+ return rv % variables # type: ignore
+
+ return npgettext
+
+
class InternationalizationExtension(Extension):
"""This extension adds gettext support to Jinja."""
@@ -183,8 +254,8 @@ class InternationalizationExtension(Extension):
# something is called twice here. One time for the gettext value and
# the other time for the n-parameter of the ngettext function.
- def __init__(self, environment):
- Extension.__init__(self, environment)
+ def __init__(self, environment: Environment) -> None:
+ super().__init__(environment)
environment.globals["_"] = _gettext_alias
environment.extend(
install_gettext_translations=self._install,
@@ -195,48 +266,108 @@ def __init__(self, environment):
newstyle_gettext=False,
)
- def _install(self, translations, newstyle=None):
+ def _install(
+ self, translations: "_SupportedTranslations", newstyle: t.Optional[bool] = None
+ ) -> None:
+ # ugettext and ungettext are preferred in case the I18N library
+ # is providing compatibility with older Python versions.
gettext = getattr(translations, "ugettext", None)
if gettext is None:
gettext = translations.gettext
ngettext = getattr(translations, "ungettext", None)
if ngettext is None:
ngettext = translations.ngettext
- self._install_callables(gettext, ngettext, newstyle)
- def _install_null(self, newstyle=None):
+ pgettext = getattr(translations, "pgettext", None)
+ npgettext = getattr(translations, "npgettext", None)
+ self._install_callables(
+ gettext, ngettext, newstyle=newstyle, pgettext=pgettext, npgettext=npgettext
+ )
+
+ def _install_null(self, newstyle: t.Optional[bool] = None) -> None:
+ import gettext
+
+ translations = gettext.NullTranslations()
+
+ if hasattr(translations, "pgettext"):
+ # Python < 3.8
+ pgettext = translations.pgettext
+ else:
+
+ def pgettext(c: str, s: str) -> str:
+ return s
+
+ if hasattr(translations, "npgettext"):
+ npgettext = translations.npgettext
+ else:
+
+ def npgettext(c: str, s: str, p: str, n: int) -> str:
+ return s if n == 1 else p
+
self._install_callables(
- lambda x: x, lambda s, p, n: (n != 1 and (p,) or (s,))[0], newstyle
+ gettext=translations.gettext,
+ ngettext=translations.ngettext,
+ newstyle=newstyle,
+ pgettext=pgettext,
+ npgettext=npgettext,
)
- def _install_callables(self, gettext, ngettext, newstyle=None):
+ def _install_callables(
+ self,
+ gettext: t.Callable[[str], str],
+ ngettext: t.Callable[[str, str, int], str],
+ newstyle: t.Optional[bool] = None,
+ pgettext: t.Optional[t.Callable[[str, str], str]] = None,
+ npgettext: t.Optional[t.Callable[[str, str, str, int], str]] = None,
+ ) -> None:
if newstyle is not None:
- self.environment.newstyle_gettext = newstyle
- if self.environment.newstyle_gettext:
+ self.environment.newstyle_gettext = newstyle # type: ignore
+ if self.environment.newstyle_gettext: # type: ignore
gettext = _make_new_gettext(gettext)
ngettext = _make_new_ngettext(ngettext)
- self.environment.globals.update(gettext=gettext, ngettext=ngettext)
- def _uninstall(self, translations):
- for key in "gettext", "ngettext":
+ if pgettext is not None:
+ pgettext = _make_new_pgettext(pgettext)
+
+ if npgettext is not None:
+ npgettext = _make_new_npgettext(npgettext)
+
+ self.environment.globals.update(
+ gettext=gettext, ngettext=ngettext, pgettext=pgettext, npgettext=npgettext
+ )
+
+ def _uninstall(self, translations: "_SupportedTranslations") -> None:
+ for key in ("gettext", "ngettext", "pgettext", "npgettext"):
self.environment.globals.pop(key, None)
- def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
- if isinstance(source, string_types):
+ def _extract(
+ self,
+ source: t.Union[str, nodes.Template],
+ gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
+ ) -> t.Iterator[
+ t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
+ ]:
+ if isinstance(source, str):
source = self.environment.parse(source)
return extract_from_ast(source, gettext_functions)
- def parse(self, parser):
+ def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
"""Parse a translatable tag."""
lineno = next(parser.stream).lineno
- num_called_num = False
+
+ context = None
+ context_token = parser.stream.next_if("string")
+
+ if context_token is not None:
+ context = context_token.value
# find all the variables referenced. Additionally a variable can be
# defined in the body of the trans block too, but this is checked at
# a later state.
- plural_expr = None
- plural_expr_assignment = None
- variables = {}
+ plural_expr: t.Optional[nodes.Expr] = None
+ plural_expr_assignment: t.Optional[nodes.Assign] = None
+ num_called_num = False
+ variables: t.Dict[str, nodes.Expr] = {}
trimmed = None
while parser.stream.current.type != "block_end":
if variables:
@@ -246,34 +377,34 @@ def parse(self, parser):
if parser.stream.skip_if("colon"):
break
- name = parser.stream.expect("name")
- if name.value in variables:
+ token = parser.stream.expect("name")
+ if token.value in variables:
parser.fail(
- "translatable variable %r defined twice." % name.value,
- name.lineno,
+ f"translatable variable {token.value!r} defined twice.",
+ token.lineno,
exc=TemplateAssertionError,
)
# expressions
if parser.stream.current.type == "assign":
next(parser.stream)
- variables[name.value] = var = parser.parse_expression()
- elif trimmed is None and name.value in ("trimmed", "notrimmed"):
- trimmed = name.value == "trimmed"
+ variables[token.value] = var = parser.parse_expression()
+ elif trimmed is None and token.value in ("trimmed", "notrimmed"):
+ trimmed = token.value == "trimmed"
continue
else:
- variables[name.value] = var = nodes.Name(name.value, "load")
+ variables[token.value] = var = nodes.Name(token.value, "load")
if plural_expr is None:
if isinstance(var, nodes.Call):
plural_expr = nodes.Name("_trans", "load")
- variables[name.value] = plural_expr
+ variables[token.value] = plural_expr
plural_expr_assignment = nodes.Assign(
nodes.Name("_trans", "store"), var
)
else:
plural_expr = var
- num_called_num = name.value == "num"
+ num_called_num = token.value == "num"
parser.stream.expect("block_end")
@@ -294,15 +425,15 @@ def parse(self, parser):
have_plural = True
next(parser.stream)
if parser.stream.current.type != "block_end":
- name = parser.stream.expect("name")
- if name.value not in variables:
+ token = parser.stream.expect("name")
+ if token.value not in variables:
parser.fail(
- "unknown variable %r for pluralization" % name.value,
- name.lineno,
+ f"unknown variable {token.value!r} for pluralization",
+ token.lineno,
exc=TemplateAssertionError,
)
- plural_expr = variables[name.value]
- num_called_num = name.value == "num"
+ plural_expr = variables[token.value]
+ num_called_num = token.value == "num"
parser.stream.expect("block_end")
plural_names, plural = self._parse_block(parser, False)
next(parser.stream)
@@ -311,9 +442,9 @@ def parse(self, parser):
next(parser.stream)
# register free names as simple name expressions
- for var in referenced:
- if var not in variables:
- variables[var] = nodes.Name(var, "load")
+ for name in referenced:
+ if name not in variables:
+ variables[name] = nodes.Name(name, "load")
if not have_plural:
plural_expr = None
@@ -330,6 +461,7 @@ def parse(self, parser):
node = self._make_node(
singular,
plural,
+ context,
variables,
plural_expr,
bool(referenced),
@@ -341,14 +473,17 @@ def parse(self, parser):
else:
return node
- def _trim_whitespace(self, string, _ws_re=_ws_re):
+ def _trim_whitespace(self, string: str, _ws_re: t.Pattern[str] = _ws_re) -> str:
return _ws_re.sub(" ", string.strip())
- def _parse_block(self, parser, allow_pluralize):
+ def _parse_block(
+ self, parser: "Parser", allow_pluralize: bool
+ ) -> t.Tuple[t.List[str], str]:
"""Parse until the next block tag with a given name."""
referenced = []
buf = []
- while 1:
+
+ while True:
if parser.stream.current.type == "data":
buf.append(parser.stream.current.value.replace("%", "%%"))
next(parser.stream)
@@ -356,20 +491,30 @@ def _parse_block(self, parser, allow_pluralize):
next(parser.stream)
name = parser.stream.expect("name").value
referenced.append(name)
- buf.append("%%(%s)s" % name)
+ buf.append(f"%({name})s")
parser.stream.expect("variable_end")
elif parser.stream.current.type == "block_begin":
next(parser.stream)
- if parser.stream.current.test("name:endtrans"):
+ block_name = (
+ parser.stream.current.value
+ if parser.stream.current.type == "name"
+ else None
+ )
+ if block_name == "endtrans":
break
- elif parser.stream.current.test("name:pluralize"):
+ elif block_name == "pluralize":
if allow_pluralize:
break
parser.fail(
"a translatable section can have only one pluralize section"
)
+ elif block_name == "trans":
+ parser.fail(
+ "trans blocks can't be nested; did you mean `endtrans`?"
+ )
parser.fail(
- "control structures in translatable sections are not allowed"
+ f"control structures in translatable sections are not allowed; "
+ f"saw `{block_name}`"
)
elif parser.stream.eos:
parser.fail("unclosed translation block")
@@ -379,37 +524,44 @@ def _parse_block(self, parser, allow_pluralize):
return referenced, concat(buf)
def _make_node(
- self, singular, plural, variables, plural_expr, vars_referenced, num_called_num
- ):
+ self,
+ singular: str,
+ plural: t.Optional[str],
+ context: t.Optional[str],
+ variables: t.Dict[str, nodes.Expr],
+ plural_expr: t.Optional[nodes.Expr],
+ vars_referenced: bool,
+ num_called_num: bool,
+ ) -> nodes.Output:
"""Generates a useful node from the data provided."""
+ newstyle = self.environment.newstyle_gettext # type: ignore
+ node: nodes.Expr
+
# no variables referenced? no need to escape for old style
# gettext invocations only if there are vars.
- if not vars_referenced and not self.environment.newstyle_gettext:
+ if not vars_referenced and not newstyle:
singular = singular.replace("%%", "%")
if plural:
plural = plural.replace("%%", "%")
- # singular only:
- if plural_expr is None:
- gettext = nodes.Name("gettext", "load")
- node = nodes.Call(gettext, [nodes.Const(singular)], [], None, None)
+ func_name = "gettext"
+ func_args: t.List[nodes.Expr] = [nodes.Const(singular)]
- # singular and plural
- else:
- ngettext = nodes.Name("ngettext", "load")
- node = nodes.Call(
- ngettext,
- [nodes.Const(singular), nodes.Const(plural), plural_expr],
- [],
- None,
- None,
- )
+ if context is not None:
+ func_args.insert(0, nodes.Const(context))
+ func_name = f"p{func_name}"
+
+ if plural_expr is not None:
+ func_name = f"n{func_name}"
+ func_args.extend((nodes.Const(plural), plural_expr))
+
+ node = nodes.Call(nodes.Name(func_name, "load"), func_args, [], None, None)
# in case newstyle gettext is used, the method is powerful
# enough to handle the variable expansion and autoescape
# handling itself
- if self.environment.newstyle_gettext:
- for key, value in iteritems(variables):
+ if newstyle:
+ for key, value in variables.items():
# the function adds that later anyways in case num was
# called num, so just skip it.
if num_called_num and key == "num":
@@ -439,9 +591,9 @@ class ExprStmtExtension(Extension):
that it doesn't print the return value.
"""
- tags = set(["do"])
+ tags = {"do"}
- def parse(self, parser):
+ def parse(self, parser: "Parser") -> nodes.ExprStmt:
node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
node.node = parser.parse_tuple()
return node
@@ -450,23 +602,15 @@ def parse(self, parser):
class LoopControlExtension(Extension):
"""Adds break and continue to the template engine."""
- tags = set(["break", "continue"])
+ tags = {"break", "continue"}
- def parse(self, parser):
+ def parse(self, parser: "Parser") -> t.Union[nodes.Break, nodes.Continue]:
token = next(parser.stream)
if token.value == "break":
return nodes.Break(lineno=token.lineno)
return nodes.Continue(lineno=token.lineno)
-class WithExtension(Extension):
- pass
-
-
-class AutoEscapeExtension(Extension):
- pass
-
-
class DebugExtension(Extension):
"""A ``{% debug %}`` tag that dumps the available variables,
filters, and tests.
@@ -490,13 +634,13 @@ class DebugExtension(Extension):
tags = {"debug"}
- def parse(self, parser):
+ def parse(self, parser: "Parser") -> nodes.Output:
lineno = parser.stream.expect("name:debug").lineno
- context = ContextReference()
+ context = nodes.ContextReference()
result = self.call_method("_render", [context], lineno=lineno)
return nodes.Output([result], lineno=lineno)
- def _render(self, context):
+ def _render(self, context: Context) -> str:
result = {
"context": context.get_all(),
"filters": sorted(self.environment.filters.keys()),
@@ -504,13 +648,16 @@ def _render(self, context):
}
# Set the depth since the intent is to show the top few names.
- if version_info[:2] >= (3, 4):
- return pprint.pformat(result, depth=3, compact=True)
- else:
- return pprint.pformat(result, depth=3)
+ return pprint.pformat(result, depth=3, compact=True)
-def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True):
+def extract_from_ast(
+ ast: nodes.Template,
+ gettext_functions: t.Sequence[str] = GETTEXT_FUNCTIONS,
+ babel_style: bool = True,
+) -> t.Iterator[
+ t.Tuple[int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]]
+]:
"""Extract localizable strings from the given template node. Per
default this function returns matches in babel style that means non string
parameters as well as keyword arguments are returned as `None`. This
@@ -538,23 +685,26 @@ def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True
* ``lineno`` is the number of the line on which the string was found,
* ``function`` is the name of the ``gettext`` function used (if the
string was extracted from embedded Python code), and
- * ``message`` is the string itself (a ``unicode`` object, or a tuple
- of ``unicode`` objects for functions with multiple string arguments).
+ * ``message`` is the string, or a tuple of strings for functions
+ with multiple string arguments.
This extraction function operates on the AST and is because of that unable
to extract any comments. For comment support you have to use the babel
extraction interface or extract comments yourself.
"""
- for node in node.find_all(nodes.Call):
+ out: t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]]
+
+ for node in ast.find_all(nodes.Call):
if (
not isinstance(node.node, nodes.Name)
or node.node.name not in gettext_functions
):
continue
- strings = []
+ strings: t.List[t.Optional[str]] = []
+
for arg in node.args:
- if isinstance(arg, nodes.Const) and isinstance(arg.value, string_types):
+ if isinstance(arg, nodes.Const) and isinstance(arg.value, str):
strings.append(arg.value)
else:
strings.append(None)
@@ -567,31 +717,35 @@ def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, babel_style=True
strings.append(None)
if not babel_style:
- strings = tuple(x for x in strings if x is not None)
- if not strings:
+ out = tuple(x for x in strings if x is not None)
+
+ if not out:
continue
else:
if len(strings) == 1:
- strings = strings[0]
+ out = strings[0]
else:
- strings = tuple(strings)
- yield node.lineno, node.node.name, strings
+ out = tuple(strings)
+ yield node.lineno, node.node.name, out
-class _CommentFinder(object):
+
+class _CommentFinder:
"""Helper class to find comments in a token stream. Can only
find comments for gettext calls forwards. Once the comment
from line 4 is found, a comment for line 1 will not return a
usable value.
"""
- def __init__(self, tokens, comment_tags):
+ def __init__(
+ self, tokens: t.Sequence[t.Tuple[int, str, str]], comment_tags: t.Sequence[str]
+ ) -> None:
self.tokens = tokens
self.comment_tags = comment_tags
self.offset = 0
self.last_lineno = 0
- def find_backwards(self, offset):
+ def find_backwards(self, offset: int) -> t.List[str]:
try:
for _, token_type, token_value in reversed(
self.tokens[self.offset : offset]
@@ -607,7 +761,7 @@ def find_backwards(self, offset):
finally:
self.offset = offset
- def find_comments(self, lineno):
+ def find_comments(self, lineno: int) -> t.List[str]:
if not self.comment_tags or self.last_lineno > lineno:
return []
for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset :]):
@@ -616,7 +770,16 @@ def find_comments(self, lineno):
return self.find_backwards(len(self.tokens))
-def babel_extract(fileobj, keywords, comment_tags, options):
+def babel_extract(
+ fileobj: t.BinaryIO,
+ keywords: t.Sequence[str],
+ comment_tags: t.Sequence[str],
+ options: t.Dict[str, t.Any],
+) -> t.Iterator[
+ t.Tuple[
+ int, str, t.Union[t.Optional[str], t.Tuple[t.Optional[str], ...]], t.List[str]
+ ]
+]:
"""Babel extraction method for Jinja templates.
.. versionchanged:: 2.3
@@ -644,33 +807,37 @@ def babel_extract(fileobj, keywords, comment_tags, options):
:return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
(comments will be empty currently)
"""
- extensions = set()
- for extension in options.get("extensions", "").split(","):
- extension = extension.strip()
- if not extension:
+ extensions: t.Dict[t.Type[Extension], None] = {}
+
+ for extension_name in options.get("extensions", "").split(","):
+ extension_name = extension_name.strip()
+
+ if not extension_name:
continue
- extensions.add(import_string(extension))
+
+ extensions[import_string(extension_name)] = None
+
if InternationalizationExtension not in extensions:
- extensions.add(InternationalizationExtension)
+ extensions[InternationalizationExtension] = None
- def getbool(options, key, default=False):
- return options.get(key, str(default)).lower() in ("1", "on", "yes", "true")
+ def getbool(options: t.Mapping[str, str], key: str, default: bool = False) -> bool:
+ return options.get(key, str(default)).lower() in {"1", "on", "yes", "true"}
silent = getbool(options, "silent", True)
environment = Environment(
- options.get("block_start_string", BLOCK_START_STRING),
- options.get("block_end_string", BLOCK_END_STRING),
- options.get("variable_start_string", VARIABLE_START_STRING),
- options.get("variable_end_string", VARIABLE_END_STRING),
- options.get("comment_start_string", COMMENT_START_STRING),
- options.get("comment_end_string", COMMENT_END_STRING),
- options.get("line_statement_prefix") or LINE_STATEMENT_PREFIX,
- options.get("line_comment_prefix") or LINE_COMMENT_PREFIX,
- getbool(options, "trim_blocks", TRIM_BLOCKS),
- getbool(options, "lstrip_blocks", LSTRIP_BLOCKS),
- NEWLINE_SEQUENCE,
- getbool(options, "keep_trailing_newline", KEEP_TRAILING_NEWLINE),
- frozenset(extensions),
+ options.get("block_start_string", defaults.BLOCK_START_STRING),
+ options.get("block_end_string", defaults.BLOCK_END_STRING),
+ options.get("variable_start_string", defaults.VARIABLE_START_STRING),
+ options.get("variable_end_string", defaults.VARIABLE_END_STRING),
+ options.get("comment_start_string", defaults.COMMENT_START_STRING),
+ options.get("comment_end_string", defaults.COMMENT_END_STRING),
+ options.get("line_statement_prefix") or defaults.LINE_STATEMENT_PREFIX,
+ options.get("line_comment_prefix") or defaults.LINE_COMMENT_PREFIX,
+ getbool(options, "trim_blocks", defaults.TRIM_BLOCKS),
+ getbool(options, "lstrip_blocks", defaults.LSTRIP_BLOCKS),
+ defaults.NEWLINE_SEQUENCE,
+ getbool(options, "keep_trailing_newline", defaults.KEEP_TRAILING_NEWLINE),
+ tuple(extensions),
cache_size=0,
auto_reload=False,
)
@@ -678,7 +845,7 @@ def getbool(options, key, default=False):
if getbool(options, "trimmed"):
environment.policies["ext.i18n.trimmed"] = True
if getbool(options, "newstyle_gettext"):
- environment.newstyle_gettext = True
+ environment.newstyle_gettext = True # type: ignore
source = fileobj.read().decode(options.get("encoding", "utf-8"))
try:
@@ -699,6 +866,4 @@ def getbool(options, key, default=False):
i18n = InternationalizationExtension
do = ExprStmtExtension
loopcontrols = LoopControlExtension
-with_ = WithExtension
-autoescape = AutoEscapeExtension
debug = DebugExtension
diff --git a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/filters.py b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/filters.py
index 74b108d..c7ecc9b 100644
--- a/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/filters.py
+++ b/add-on/TA-Demisto/bin/ta_demisto/aob_py3/jinja2/filters.py
@@ -1,79 +1,75 @@
-# -*- coding: utf-8 -*-
"""Built-in template filters used with the ``|`` operator."""
import math
import random
import re
-import warnings
-from collections import namedtuple
+import typing
+import typing as t
+from collections import abc
from itertools import chain
from itertools import groupby
from markupsafe import escape
from markupsafe import Markup
-from markupsafe import soft_unicode
+from markupsafe import soft_str
-from ._compat import abc
-from ._compat import imap
-from ._compat import iteritems
-from ._compat import string_types
-from ._compat import text_type
+from .async_utils import async_variant
+from .async_utils import auto_aiter
+from .async_utils import auto_await
+from .async_utils import auto_to_list
from .exceptions import FilterArgumentError
from .runtime import Undefined
from .utils import htmlsafe_json_dumps
+from .utils import pass_context
+from .utils import pass_environment
+from .utils import pass_eval_context
from .utils import pformat
-from .utils import unicode_urlencode
+from .utils import url_quote
from .utils import urlize
-_word_re = re.compile(r"\w+", re.UNICODE)
-_word_beginning_split_re = re.compile(r"([-\s\(\{\[\<]+)", re.UNICODE)
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+ from .environment import Environment
+ from .nodes import EvalContext
+ from .runtime import Context
+ from .sandbox import SandboxedEnvironment # noqa: F401
-
-def contextfilter(f):
- """Decorator for marking context dependent filters. The current
- :class:`Context` will be passed as first argument.
- """
- f.contextfilter = True
- return f
-
-
-def evalcontextfilter(f):
- """Decorator for marking eval-context dependent filters. An eval
- context object is passed as first argument. For more information
- about the eval context, see :ref:`eval-context`.
-
- .. versionadded:: 2.4
- """
- f.evalcontextfilter = True
- return f
+ class HasHTML(te.Protocol):
+ def __html__(self) -> str:
+ pass
-def environmentfilter(f):
- """Decorator for marking environment dependent filters. The current
- :class:`Environment` is passed to the filter as first argument.
- """
- f.environmentfilter = True
- return f
+F = t.TypeVar("F", bound=t.Callable[..., t.Any])
+K = t.TypeVar("K")
+V = t.TypeVar("V")
-def ignore_case(value):
+def ignore_case(value: V) -> V:
"""For use as a postprocessor for :func:`make_attrgetter`. Converts strings
to lowercase and returns other types as-is."""
- return value.lower() if isinstance(value, string_types) else value
+ if isinstance(value, str):
+ return t.cast(V, value.lower())
+ return value
-def make_attrgetter(environment, attribute, postprocess=None, default=None):
+
+def make_attrgetter(
+ environment: "Environment",
+ attribute: t.Optional[t.Union[str, int]],
+ postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
+ default: t.Optional[t.Any] = None,
+) -> t.Callable[[t.Any], t.Any]:
"""Returns a callable that looks up the given attribute from a
passed object with the rules of the environment. Dots are allowed
to access attributes of attributes. Integer parts in paths are
looked up as integers.
"""
- attribute = _prepare_attribute_parts(attribute)
+ parts = _prepare_attribute_parts(attribute)
- def attrgetter(item):
- for part in attribute:
+ def attrgetter(item: t.Any) -> t.Any:
+ for part in parts:
item = environment.getitem(item, part)
- if default and isinstance(item, Undefined):
+ if default is not None and isinstance(item, Undefined):
item = default
if postprocess is not None:
@@ -84,7 +80,11 @@ def attrgetter(item):
return attrgetter
-def make_multi_attrgetter(environment, attribute, postprocess=None):
+def make_multi_attrgetter(
+ environment: "Environment",
+ attribute: t.Optional[t.Union[str, int]],
+ postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None,
+) -> t.Callable[[t.Any], t.List[t.Any]]:
"""Returns a callable that looks up the given comma separated
attributes from a passed object with the rules of the environment.
Dots are allowed to access attributes of each attribute. Integer
@@ -95,17 +95,19 @@ def make_multi_attrgetter(environment, attribute, postprocess=None):
Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc.
"""
- attribute_parts = (
- attribute.split(",") if isinstance(attribute, string_types) else [attribute]
- )
- attribute = [
- _prepare_attribute_parts(attribute_part) for attribute_part in attribute_parts
- ]
+ if isinstance(attribute, str):
+ split: t.Sequence[t.Union[str, int, None]] = attribute.split(",")
+ else:
+ split = [attribute]
+
+ parts = [_prepare_attribute_parts(item) for item in split]
- def attrgetter(item):
- items = [None] * len(attribute)
- for i, attribute_part in enumerate(attribute):
+ def attrgetter(item: t.Any) -> t.List[t.Any]:
+ items = [None] * len(parts)
+
+ for i, attribute_part in enumerate(parts):
item_i = item
+
for part in attribute_part:
item_i = environment.getitem(item_i, part)
@@ -113,28 +115,35 @@ def attrgetter(item):
item_i = postprocess(item_i)
items[i] = item_i
+
return items
return attrgetter
-def _prepare_attribute_parts(attr):
+def _prepare_attribute_parts(
+ attr: t.Optional[t.Union[str, int]]
+) -> t.List[t.Union[str, int]]:
if attr is None:
return []
- elif isinstance(attr, string_types):
+
+ if isinstance(attr, str):
return [int(x) if x.isdigit() else x for x in attr.split(".")]
- else:
- return [attr]
+
+ return [attr]
-def do_forceescape(value):
+def do_forceescape(value: "t.Union[str, HasHTML]") -> Markup:
"""Enforce HTML escaping. This will probably double escape variables."""
if hasattr(value, "__html__"):
- value = value.__html__()
- return escape(text_type(value))
+ value = t.cast("HasHTML", value).__html__()
+ return escape(str(value))
-def do_urlencode(value):
+
+def do_urlencode(
+ value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]]
+) -> str:
"""Quote data for use in a URL path or query using UTF-8.
Basic wrapper around :func:`urllib.parse.quote` when given a
@@ -150,22 +159,23 @@ def do_urlencode(value):
.. versionadded:: 2.7
"""
- if isinstance(value, string_types) or not isinstance(value, abc.Iterable):
- return unicode_urlencode(value)
+ if isinstance(value, str) or not isinstance(value, abc.Iterable):
+ return url_quote(value)
if isinstance(value, dict):
- items = iteritems(value)
+ items: t.Iterable[t.Tuple[str, t.Any]] = value.items()
else:
- items = iter(value)
+ items = value # type: ignore
- return u"&".join(
- "%s=%s" % (unicode_urlencode(k, for_qs=True), unicode_urlencode(v, for_qs=True))
- for k, v in items
+ return "&".join(
+ f"{url_quote(k, for_qs=True)}={url_quote(v, for_qs=True)}" for k, v in items
)
-@evalcontextfilter
-def do_replace(eval_ctx, s, old, new, count=None):
+@pass_eval_context
+def do_replace(
+ eval_ctx: "EvalContext", s: str, old: str, new: str, count: t.Optional[int] = None
+) -> str:
"""Return a copy of the value with all occurrences of a substring
replaced with a new one. The first argument is the substring
that should be replaced, the second is the replacement string.
@@ -182,8 +192,10 @@ def do_replace(eval_ctx, s, old, new, count=None):
"""
if count is None:
count = -1
+
if not eval_ctx.autoescape:
- return text_type(s).replace(text_type(old), text_type(new), count)
+ return str(s).replace(str(old), str(new), count)
+
if (
hasattr(old, "__html__")
or hasattr(new, "__html__")
@@ -191,25 +203,62 @@ def do_replace(eval_ctx, s, old, new, count=None):
):
s = escape(s)
else:
- s = soft_unicode(s)
- return s.replace(soft_unicode(old), soft_unicode(new), count)
+ s = soft_str(s)
+
+ return s.replace(soft_str(old), soft_str(new), count)
-def do_upper(s):
+def do_upper(s: str) -> str:
"""Convert a value to uppercase."""
- return soft_unicode(s).upper()
+ return soft_str(s).upper()
-def do_lower(s):
+def do_lower(s: str) -> str:
"""Convert a value to lowercase."""
- return soft_unicode(s).lower()
+ return soft_str(s).lower()
+
+
+def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]:
+ """Return an iterator over the ``(key, value)`` items of a mapping.
+
+ ``x|items`` is the same as ``x.items()``, except if ``x`` is
+ undefined an empty iterator is returned.
+
+ This filter is useful if you expect the template to be rendered with
+ an implementation of Jinja in another programming language that does
+ not have a ``.items()`` method on its mapping type.
+
+ .. code-block:: html+jinja
+
+
+ {% for key, value in my_dict|items %}
+ - {{ key }}
+
- {{ value }}
+ {% endfor %}
+
+
+ .. versionadded:: 3.1
+ """
+ if isinstance(value, Undefined):
+ return
+ if not isinstance(value, abc.Mapping):
+ raise TypeError("Can only get item pairs from a mapping.")
-@evalcontextfilter
-def do_xmlattr(_eval_ctx, d, autospace=True):
+ yield from value.items()
+
+
+_space_re = re.compile(r"\s", flags=re.ASCII)
+
+
+@pass_eval_context
+def do_xmlattr(
+ eval_ctx: "EvalContext", d: t.Mapping[str, t.Any], autospace: bool = True
+) -> str:
"""Create an SGML/XML attribute string based on the items in a dict.
- All values that are neither `none` nor `undefined` are automatically
- escaped:
+
+ If any key contains a space, this fails with a ``ValueError``. Values that
+ are neither ``none`` nor ``undefined`` are automatically escaped.
.. sourcecode:: html+jinja
@@ -228,43 +277,63 @@ def do_xmlattr(_eval_ctx, d, autospace=True):
As you can see it automatically prepends a space in front of the item
if the filter returned something unless the second parameter is false.
+
+ .. versionchanged:: 3.1.3
+ Keys with spaces are not allowed.
"""
- rv = u" ".join(
- u'%s="%s"' % (escape(key), escape(value))
- for key, value in iteritems(d)
- if value is not None and not isinstance(value, Undefined)
- )
+ items = []
+
+ for key, value in d.items():
+ if value is None or isinstance(value, Undefined):
+ continue
+
+ if _space_re.search(key) is not None:
+ raise ValueError(f"Spaces are not allowed in attributes: '{key}'")
+
+ items.append(f'{escape(key)}="{escape(value)}"')
+
+ rv = " ".join(items)
+
if autospace and rv:
- rv = u" " + rv
- if _eval_ctx.autoescape:
+ rv = " " + rv
+
+ if eval_ctx.autoescape:
rv = Markup(rv)
+
return rv
-def do_capitalize(s):
+def do_capitalize(s: str) -> str:
"""Capitalize a value. The first character will be uppercase, all others
lowercase.
"""
- return soft_unicode(s).capitalize()
+ return soft_str(s).capitalize()
+
+_word_beginning_split_re = re.compile(r"([-\s({\[<]+)")
-def do_title(s):
+
+def do_title(s: str) -> str:
"""Return a titlecased version of the value. I.e. words will start with
uppercase letters, all remaining characters are lowercase.
"""
return "".join(
[
item[0].upper() + item[1:].lower()
- for item in _word_beginning_split_re.split(soft_unicode(s))
+ for item in _word_beginning_split_re.split(soft_str(s))
if item
]
)
-def do_dictsort(value, case_sensitive=False, by="key", reverse=False):
- """Sort a dict and yield (key, value) pairs. Because python dicts are
- unsorted you may want to use this function to order them by either
- key or value:
+def do_dictsort(
+ value: t.Mapping[K, V],
+ case_sensitive: bool = False,
+ by: 'te.Literal["key", "value"]' = "key",
+ reverse: bool = False,
+) -> t.List[t.Tuple[K, V]]:
+ """Sort a dict and yield (key, value) pairs. Python dicts may not
+ be in the order you want to display them in, so sort them first.
.. sourcecode:: jinja
@@ -287,7 +356,7 @@ def do_dictsort(value, case_sensitive=False, by="key", reverse=False):
else:
raise FilterArgumentError('You can only sort by either "key" or "value"')
- def sort_func(item):
+ def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any:
value = item[pos]
if not case_sensitive:
@@ -298,8 +367,14 @@ def sort_func(item):
return sorted(value.items(), key=sort_func, reverse=reverse)
-@environmentfilter
-def do_sort(environment, value, reverse=False, case_sensitive=False, attribute=None):
+@pass_environment
+def do_sort(
+ environment: "Environment",
+ value: "t.Iterable[V]",
+ reverse: bool = False,
+ case_sensitive: bool = False,
+ attribute: t.Optional[t.Union[str, int]] = None,
+) -> "t.List[V]":
"""Sort an iterable using Python's :func:`sorted`.
.. sourcecode:: jinja
@@ -331,7 +406,7 @@ def do_sort(environment, value, reverse=False, case_sensitive=False, attribute=N
.. sourcecode:: jinja
- {% for user users|sort(attribute="age,name") %}
+ {% for user in users|sort(attribute="age,name") %}
...
{% endfor %}
@@ -348,8 +423,13 @@ def do_sort(environment, value, reverse=False, case_sensitive=False, attribute=N
return sorted(value, key=key_func, reverse=reverse)
-@environmentfilter
-def do_unique(environment, value, case_sensitive=False, attribute=None):
+@pass_environment
+def do_unique(
+ environment: "Environment",
+ value: "t.Iterable[V]",
+ case_sensitive: bool = False,
+ attribute: t.Optional[t.Union[str, int]] = None,
+) -> "t.Iterator[V]":
"""Returns a list of unique items from the given iterable.
.. sourcecode:: jinja
@@ -376,7 +456,13 @@ def do_unique(environment, value, case_sensitive=False, attribute=None):
yield item
-def _min_or_max(environment, value, func, case_sensitive, attribute):
+def _min_or_max(
+ environment: "Environment",
+ value: "t.Iterable[V]",
+ func: "t.Callable[..., V]",
+ case_sensitive: bool,
+ attribute: t.Optional[t.Union[str, int]],
+) -> "t.Union[V, Undefined]":
it = iter(value)
try:
@@ -390,8 +476,13 @@ def _min_or_max(environment, value, func, case_sensitive, attribute):
return func(chain([first], it), key=key_func)
-@environmentfilter
-def do_min(environment, value, case_sensitive=False, attribute=None):
+@pass_environment
+def do_min(
+ environment: "Environment",
+ value: "t.Iterable[V]",
+ case_sensitive: bool = False,
+ attribute: t.Optional[t.Union[str, int]] = None,
+) -> "t.Union[V, Undefined]":
"""Return the smallest item from the sequence.
.. sourcecode:: jinja
@@ -405,8 +496,13 @@ def do_min(environment, value, case_sensitive=False, attribute=None):
return _min_or_max(environment, value, min, case_sensitive, attribute)
-@environmentfilter
-def do_max(environment, value, case_sensitive=False, attribute=None):
+@pass_environment
+def do_max(
+ environment: "Environment",
+ value: "t.Iterable[V]",
+ case_sensitive: bool = False,
+ attribute: t.Optional[t.Union[str, int]] = None,
+) -> "t.Union[V, Undefined]":
"""Return the largest item from the sequence.
.. sourcecode:: jinja
@@ -420,7 +516,11 @@ def do_max(environment, value, case_sensitive=False, attribute=None):
return _min_or_max(environment, value, max, case_sensitive, attribute)
-def do_default(value, default_value=u"", boolean=False):
+def do_default(
+ value: V,
+ default_value: V = "", # type: ignore
+ boolean: bool = False,
+) -> V:
"""If the value is undefined it will return the passed default value,
otherwise the value of the variable:
@@ -445,11 +545,17 @@ def do_default(value, default_value=u"", boolean=False):
"""
if isinstance(value, Undefined) or (boolean and not value):
return default_value
+
return value
-@evalcontextfilter
-def do_join(eval_ctx, value, d=u"", attribute=None):
+@pass_eval_context
+def sync_do_join(
+ eval_ctx: "EvalContext",
+ value: t.Iterable,
+ d: str = "",
+ attribute: t.Optional[t.Union[str, int]] = None,
+) -> str:
"""Return a string which is the concatenation of the strings in the
sequence. The separator between elements is an empty string per
default, you can define it with the optional parameter:
@@ -472,39 +578,54 @@ def do_join(eval_ctx, value, d=u"", attribute=None):
The `attribute` parameter was added.
"""
if attribute is not None:
- value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
+ value = map(make_attrgetter(eval_ctx.environment, attribute), value)
# no automatic escaping? joining is a lot easier then
if not eval_ctx.autoescape:
- return text_type(d).join(imap(text_type, value))
+ return str(d).join(map(str, value))
# if the delimiter doesn't have an html representation we check
# if any of the items has. If yes we do a coercion to Markup
if not hasattr(d, "__html__"):
value = list(value)
do_escape = False
+
for idx, item in enumerate(value):
if hasattr(item, "__html__"):
do_escape = True
else:
- value[idx] = text_type(item)
+ value[idx] = str(item)
+
if do_escape:
d = escape(d)
else:
- d = text_type(d)
+ d = str(d)
+
return d.join(value)
# no html involved, to normal joining
- return soft_unicode(d).join(imap(soft_unicode, value))
+ return soft_str(d).join(map(soft_str, value))
-def do_center(value, width=80):
+@async_variant(sync_do_join) # type: ignore
+async def do_join(
+ eval_ctx: "EvalContext",
+ value: t.Union[t.AsyncIterable, t.Iterable],
+ d: str = "",
+ attribute: t.Optional[t.Union[str, int]] = None,
+) -> str:
+ return sync_do_join(eval_ctx, await auto_to_list(value), d, attribute)
+
+
+def do_center(value: str, width: int = 80) -> str:
"""Centers the value in a field of a given width."""
- return text_type(value).center(width)
+ return soft_str(value).center(width)
-@environmentfilter
-def do_first(environment, seq):
+@pass_environment
+def sync_do_first(
+ environment: "Environment", seq: "t.Iterable[V]"
+) -> "t.Union[V, Undefined]":
"""Return the first item of a sequence."""
try:
return next(iter(seq))
@@ -512,10 +633,21 @@ def do_first(environment, seq):
return environment.undefined("No first item, sequence was empty.")
-@environmentfilter
-def do_last(environment, seq):
- """
- Return the last item of a sequence.
+@async_variant(sync_do_first) # type: ignore
+async def do_first(
+ environment: "Environment", seq: "t.Union[t.AsyncIterable[V], t.Iterable[V]]"
+) -> "t.Union[V, Undefined]":
+ try:
+ return await auto_aiter(seq).__anext__()
+ except StopAsyncIteration:
+ return environment.undefined("No first item, sequence was empty.")
+
+
+@pass_environment
+def do_last(
+ environment: "Environment", seq: "t.Reversible[V]"
+) -> "t.Union[V, Undefined]":
+ """Return the last item of a sequence.
Note: Does not work with generators. You may want to explicitly
convert it to a list:
@@ -530,8 +662,11 @@ def do_last(environment, seq):
return environment.undefined("No last item, sequence was empty.")
-@contextfilter
-def do_random(context, seq):
+# No async do_last, it may not be safe in async mode.
+
+
+@pass_context
+def do_random(context: "Context", seq: "t.Sequence[V]") -> "t.Union[V, Undefined]":
"""Return a random item from the sequence."""
try:
return random.choice(seq)
@@ -539,108 +674,151 @@ def do_random(context, seq):
return context.environment.undefined("No random item, sequence was empty.")
-def do_filesizeformat(value, binary=False):
+def do_filesizeformat(value: t.Union[str, float, int], binary: bool = False) -> str:
"""Format the value like a 'human-readable' file size (i.e. 13 kB,
4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
Giga, etc.), if the second parameter is set to `True` the binary
prefixes are used (Mebi, Gibi).
"""
bytes = float(value)
- base = binary and 1024 or 1000
+ base = 1024 if binary else 1000
prefixes = [
- (binary and "KiB" or "kB"),
- (binary and "MiB" or "MB"),
- (binary and "GiB" or "GB"),
- (binary and "TiB" or "TB"),
- (binary and "PiB" or "PB"),
- (binary and "EiB" or "EB"),
- (binary and "ZiB" or "ZB"),
- (binary and "YiB" or "YB"),
+ ("KiB" if binary else "kB"),
+ ("MiB" if binary else "MB"),
+ ("GiB" if binary else "GB"),
+ ("TiB" if binary else "TB"),
+ ("PiB" if binary else "PB"),
+ ("EiB" if binary else "EB"),
+ ("ZiB" if binary else "ZB"),
+ ("YiB" if binary else "YB"),
]
+
if bytes == 1:
return "1 Byte"
elif bytes < base:
- return "%d Bytes" % bytes
+ return f"{int(bytes)} Bytes"
else:
for i, prefix in enumerate(prefixes):
unit = base ** (i + 2)
+
if bytes < unit:
- return "%.1f %s" % ((base * bytes / unit), prefix)
- return "%.1f %s" % ((base * bytes / unit), prefix)
+ return f"{base * bytes / unit:.1f} {prefix}"
+ return f"{base * bytes / unit:.1f} {prefix}"
-def do_pprint(value, verbose=False):
- """Pretty print a variable. Useful for debugging.
- With Jinja 1.2 onwards you can pass it a parameter. If this parameter
- is truthy the output will be more verbose (this requires `pretty`)
- """
- return pformat(value, verbose=verbose)
+def do_pprint(value: t.Any) -> str:
+ """Pretty print a variable. Useful for debugging."""
+ return pformat(value)
+
+_uri_scheme_re = re.compile(r"^([\w.+-]{2,}:(/){0,2})$")
-@evalcontextfilter
+
+@pass_eval_context
def do_urlize(
- eval_ctx, value, trim_url_limit=None, nofollow=False, target=None, rel=None
-):
- """Converts URLs in plain text into clickable links.
+ eval_ctx: "EvalContext",
+ value: str,
+ trim_url_limit: t.Optional[int] = None,
+ nofollow: bool = False,
+ target: t.Optional[str] = None,
+ rel: t.Optional[str] = None,
+ extra_schemes: t.Optional[t.Iterable[str]] = None,
+) -> str:
+ """Convert URLs in text into clickable links.
+
+ This may not recognize links in some situations. Usually, a more
+ comprehensive formatter, such as a Markdown library, is a better
+ choice.
+
+ Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email
+ addresses. Links with trailing punctuation (periods, commas, closing
+ parentheses) and leading punctuation (opening parentheses) are
+ recognized excluding the punctuation. Email addresses that include
+ header fields are not recognized (for example,
+ ``mailto:address@example.com?cc=copy@example.com``).
+
+ :param value: Original text containing URLs to link.
+ :param trim_url_limit: Shorten displayed URL values to this length.
+ :param nofollow: Add the ``rel=nofollow`` attribute to links.
+ :param target: Add the ``target`` attribute to links.
+ :param rel: Add the ``rel`` attribute to links.
+ :param extra_schemes: Recognize URLs that start with these schemes
+ in addition to the default behavior. Defaults to
+ ``env.policies["urlize.extra_schemes"]``, which defaults to no
+ extra schemes.
+
+ .. versionchanged:: 3.0
+ The ``extra_schemes`` parameter was added.
+
+ .. versionchanged:: 3.0
+ Generate ``https://`` links for URLs without a scheme.
+
+ .. versionchanged:: 3.0
+ The parsing rules were updated. Recognize email addresses with
+ or without the ``mailto:`` scheme. Validate IP addresses. Ignore
+ parentheses and brackets in more cases.
+
+ .. versionchanged:: 2.8
+ The ``target`` parameter was added.
+ """
+ policies = eval_ctx.environment.policies
+ rel_parts = set((rel or "").split())
- If you pass the filter an additional integer it will shorten the urls
- to that number. Also a third argument exists that makes the urls
- "nofollow":
+ if nofollow:
+ rel_parts.add("nofollow")
- .. sourcecode:: jinja
+ rel_parts.update((policies["urlize.rel"] or "").split())
+ rel = " ".join(sorted(rel_parts)) or None
- {{ mytext|urlize(40, true) }}
- links are shortened to 40 chars and defined with rel="nofollow"
+ if target is None:
+ target = policies["urlize.target"]
- If *target* is specified, the ``target`` attribute will be added to the
- ```` tag:
+ if extra_schemes is None:
+ extra_schemes = policies["urlize.extra_schemes"] or ()
- .. sourcecode:: jinja
+ for scheme in extra_schemes:
+ if _uri_scheme_re.fullmatch(scheme) is None:
+ raise FilterArgumentError(f"{scheme!r} is not a valid URI scheme prefix.")
- {{ mytext|urlize(40, target='_blank') }}
+ rv = urlize(
+ value,
+ trim_url_limit=trim_url_limit,
+ rel=rel,
+ target=target,
+ extra_schemes=extra_schemes,
+ )
- .. versionchanged:: 2.8+
- The *target* parameter was added.
- """
- policies = eval_ctx.environment.policies
- rel = set((rel or "").split() or [])
- if nofollow:
- rel.add("nofollow")
- rel.update((policies["urlize.rel"] or "").split())
- if target is None:
- target = policies["urlize.target"]
- rel = " ".join(sorted(rel)) or None
- rv = urlize(value, trim_url_limit, rel=rel, target=target)
if eval_ctx.autoescape:
rv = Markup(rv)
+
return rv
-def do_indent(s, width=4, first=False, blank=False, indentfirst=None):
+def do_indent(
+ s: str, width: t.Union[int, str] = 4, first: bool = False, blank: bool = False
+) -> str:
"""Return a copy of the string with each line indented by 4 spaces. The
first line and blank lines are not indented by default.
- :param width: Number of spaces to indent by.
+ :param width: Number of spaces, or a string, to indent by.
:param first: Don't skip indenting the first line.
:param blank: Don't skip indenting empty lines.
+ .. versionchanged:: 3.0
+ ``width`` can be a string.
+
.. versionchanged:: 2.10
Blank lines are not indented by default.
Rename the ``indentfirst`` argument to ``first``.
"""
- if indentfirst is not None:
- warnings.warn(
- "The 'indentfirst' argument is renamed to 'first' and will"
- " be removed in version 3.0.",
- DeprecationWarning,
- stacklevel=2,
- )
- first = indentfirst
+ if isinstance(width, str):
+ indention = width
+ else:
+ indention = " " * width
- indention = u" " * width
- newline = u"\n"
+ newline = "\n"
if isinstance(s, Markup):
indention = Markup(indention)
@@ -665,8 +843,15 @@ def do_indent(s, width=4, first=False, blank=False, indentfirst=None):
return rv
-@environmentfilter
-def do_truncate(env, s, length=255, killwords=False, end="...", leeway=None):
+@pass_environment
+def do_truncate(
+ env: "Environment",
+ s: str,
+ length: int = 255,
+ killwords: bool = False,
+ end: str = "...",
+ leeway: t.Optional[int] = None,
+) -> str:
"""Return a truncated copy of the string. The length is specified
with the first parameter which defaults to ``255``. If the second
parameter is ``true`` the filter will cut the text at length. Otherwise
@@ -692,25 +877,29 @@ def do_truncate(env, s, length=255, killwords=False, end="...", leeway=None):
"""
if leeway is None:
leeway = env.policies["truncate.leeway"]
- assert length >= len(end), "expected length >= %s, got %s" % (len(end), length)
- assert leeway >= 0, "expected leeway >= 0, got %s" % leeway
+
+ assert length >= len(end), f"expected length >= {len(end)}, got {length}"
+ assert leeway >= 0, f"expected leeway >= 0, got {leeway}"
+
if len(s) <= length + leeway:
return s
+
if killwords:
return s[: length - len(end)] + end
+
result = s[: length - len(end)].rsplit(" ", 1)[0]
return result + end
-@environmentfilter
+@pass_environment
def do_wordwrap(
- environment,
- s,
- width=79,
- break_long_words=True,
- wrapstring=None,
- break_on_hyphens=True,
-):
+ environment: "Environment",
+ s: str,
+ width: int = 79,
+ break_long_words: bool = True,
+ wrapstring: t.Optional[str] = None,
+ break_on_hyphens: bool = True,
+) -> str:
"""Wrap a string to the given width. Existing newlines are treated
as paragraphs to be wrapped separately.
@@ -732,10 +921,9 @@ def do_wordwrap(
.. versionchanged:: 2.7
Added the ``wrapstring`` parameter.
"""
-
import textwrap
- if not wrapstring:
+ if wrapstring is None:
wrapstring = environment.newline_sequence
# textwrap.wrap doesn't consider existing newlines when wrapping.
@@ -759,12 +947,15 @@ def do_wordwrap(
)
-def do_wordcount(s):
+_word_re = re.compile(r"\w+")
+
+
+def do_wordcount(s: str) -> int:
"""Count the words in that string."""
- return len(_word_re.findall(soft_unicode(s)))
+ return len(_word_re.findall(soft_str(s)))
-def do_int(value, default=0, base=10):
+def do_int(value: t.Any, default: int = 0, base: int = 10) -> int:
"""Convert the value into an integer. If the
conversion doesn't work it will return ``0``. You can
override this default using the first parameter. You
@@ -774,8 +965,9 @@ def do_int(value, default=0, base=10):
The base is ignored for decimal numbers and non-string values.
"""
try:
- if isinstance(value, string_types):
+ if isinstance(value, str):
return int(value, base)
+
return int(value)
except (TypeError, ValueError):
# this quirk is necessary so that "42.23"|int gives 42.
@@ -785,7 +977,7 @@ def do_int(value, default=0, base=10):
return default
-def do_float(value, default=0.0):
+def do_float(value: t.Any, default: float = 0.0) -> float:
"""Convert the value into a floating point number. If the
conversion doesn't work it will return ``0.0``. You can
override this default using the first parameter.
@@ -796,7 +988,7 @@ def do_float(value, default=0.0):
return default
-def do_format(value, *args, **kwargs):
+def do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str:
"""Apply the given values to a `printf-style`_ format string, like
``string % values``.
@@ -820,22 +1012,26 @@ def do_format(value, *args, **kwargs):
raise FilterArgumentError(
"can't handle positional and keyword arguments at the same time"
)
- return soft_unicode(value) % (kwargs or args)
+
+ return soft_str(value) % (kwargs or args)
-def do_trim(value, chars=None):
+def do_trim(value: str, chars: t.Optional[str] = None) -> str:
"""Strip leading and trailing characters, by default whitespace."""
- return soft_unicode(value).strip(chars)
+ return soft_str(value).strip(chars)
-def do_striptags(value):
+def do_striptags(value: "t.Union[str, HasHTML]") -> str:
"""Strip SGML/XML tags and replace adjacent whitespace by one space."""
if hasattr(value, "__html__"):
- value = value.__html__()
- return Markup(text_type(value)).striptags()
+ value = t.cast("HasHTML", value).__html__()
+ return Markup(str(value)).striptags()
-def do_slice(value, slices, fill_with=None):
+
+def sync_do_slice(
+ value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None
+) -> "t.Iterator[t.List[V]]":
"""Slice an iterator and return a list of lists containing
those items. Useful if you want to create a div containing
three ul tags that represent columns:
@@ -860,18 +1056,34 @@ def do_slice(value, slices, fill_with=None):
items_per_slice = length // slices
slices_with_extra = length % slices
offset = 0
+
for slice_number in range(slices):
start = offset + slice_number * items_per_slice
+
if slice_number < slices_with_extra:
offset += 1
+
end = offset + (slice_number + 1) * items_per_slice
tmp = seq[start:end]
+
if fill_with is not None and slice_number >= slices_with_extra:
tmp.append(fill_with)
+
yield tmp
-def do_batch(value, linecount, fill_with=None):
+@async_variant(sync_do_slice) # type: ignore
+async def do_slice(
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
+ slices: int,
+ fill_with: t.Optional[t.Any] = None,
+) -> "t.Iterator[t.List[V]]":
+ return sync_do_slice(await auto_to_list(value), slices, fill_with)
+
+
+def do_batch(
+ value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None
+) -> "t.Iterator[t.List[V]]":
"""
A filter that batches items. It works pretty much like `slice`
just the other way round. It returns a list of lists with the
@@ -890,19 +1102,27 @@ def do_batch(value, linecount, fill_with=None):
{%- endfor %}
"""
- tmp = []
+ tmp: "t.List[V]" = []
+
for item in value:
if len(tmp) == linecount:
yield tmp
tmp = []
+
tmp.append(item)
+
if tmp:
if fill_with is not None and len(tmp) < linecount:
tmp += [fill_with] * (linecount - len(tmp))
+
yield tmp
-def do_round(value, precision=0, method="common"):
+def do_round(
+ value: float,
+ precision: int = 0,
+ method: 'te.Literal["common", "ceil", "floor"]' = "common",
+) -> float:
"""Round the number to a given precision. The first
parameter specifies the precision (default is ``0``), the
second the rounding method:
@@ -930,24 +1150,35 @@ def do_round(value, precision=0, method="common"):
"""
if method not in {"common", "ceil", "floor"}:
raise FilterArgumentError("method must be common, ceil or floor")
+
if method == "common":
return round(value, precision)
+
func = getattr(math, method)
- return func(value * (10 ** precision)) / (10 ** precision)
+ return t.cast(float, func(value * (10**precision)) / (10**precision))
+
+
+class _GroupTuple(t.NamedTuple):
+ grouper: t.Any
+ list: t.List
+ # Use the regular tuple repr to hide this subclass if users print
+ # out the value during debugging.
+ def __repr__(self) -> str:
+ return tuple.__repr__(self)
-# Use a regular tuple repr here. This is what we did in the past and we
-# really want to hide this custom type as much as possible. In particular
-# we do not want to accidentally expose an auto generated repr in case
-# people start to print this out in comments or something similar for
-# debugging.
-_GroupTuple = namedtuple("_GroupTuple", ["grouper", "list"])
-_GroupTuple.__repr__ = tuple.__repr__
-_GroupTuple.__str__ = tuple.__str__
+ def __str__(self) -> str:
+ return tuple.__str__(self)
-@environmentfilter
-def do_groupby(environment, value, attribute):
+@pass_environment
+def sync_do_groupby(
+ environment: "Environment",
+ value: "t.Iterable[V]",
+ attribute: t.Union[str, int],
+ default: t.Optional[t.Any] = None,
+ case_sensitive: bool = False,
+) -> "t.List[_GroupTuple]":
"""Group a sequence of objects by an attribute using Python's
:func:`itertools.groupby`. The attribute can use dot notation for
nested access, like ``"address.city"``. Unlike Python's ``groupby``,
@@ -978,18 +1209,86 @@ def do_groupby(environment, value, attribute):
{{ group.grouper }}: {{ group.list|join(", ") }}
{% endfor %}
+ You can specify a ``default`` value to use if an object in the list
+ does not have the given attribute.
+
+ .. sourcecode:: jinja
+
+ {% for city, items in users|groupby("city", default="NY") %}
+ - {{ city }}: {{ items|map(attribute="name")|join(", ") }}
+ {% endfor %}
+
+ Like the :func:`~jinja-filters.sort` filter, sorting and grouping is
+ case-insensitive by default. The ``key`` for each group will have
+ the case of the first item in that group of values. For example, if
+ a list of users has cities ``["CA", "NY", "ca"]``, the "CA" group
+ will have two values. This can be disabled by passing
+ ``case_sensitive=True``.
+
+ .. versionchanged:: 3.1
+ Added the ``case_sensitive`` parameter. Sorting and grouping is
+ case-insensitive by default, matching other filters that do
+ comparisons.
+
+ .. versionchanged:: 3.0
+ Added the ``default`` parameter.
+
.. versionchanged:: 2.6
The attribute supports dot notation for nested access.
"""
- expr = make_attrgetter(environment, attribute)
- return [
+ expr = make_attrgetter(
+ environment,
+ attribute,
+ postprocess=ignore_case if not case_sensitive else None,
+ default=default,
+ )
+ out = [
_GroupTuple(key, list(values))
for key, values in groupby(sorted(value, key=expr), expr)
]
+ if not case_sensitive:
+ # Return the real key from the first value instead of the lowercase key.
+ output_expr = make_attrgetter(environment, attribute, default=default)
+ out = [_GroupTuple(output_expr(values[0]), values) for _, values in out]
+
+ return out
+
+
+@async_variant(sync_do_groupby) # type: ignore
+async def do_groupby(
+ environment: "Environment",
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
+ attribute: t.Union[str, int],
+ default: t.Optional[t.Any] = None,
+ case_sensitive: bool = False,
+) -> "t.List[_GroupTuple]":
+ expr = make_attrgetter(
+ environment,
+ attribute,
+ postprocess=ignore_case if not case_sensitive else None,
+ default=default,
+ )
+ out = [
+ _GroupTuple(key, await auto_to_list(values))
+ for key, values in groupby(sorted(await auto_to_list(value), key=expr), expr)
+ ]
+
+ if not case_sensitive:
+ # Return the real key from the first value instead of the lowercase key.
+ output_expr = make_attrgetter(environment, attribute, default=default)
+ out = [_GroupTuple(output_expr(values[0]), values) for _, values in out]
+
+ return out
-@environmentfilter
-def do_sum(environment, iterable, attribute=None, start=0):
+
+@pass_environment
+def sync_do_sum(
+ environment: "Environment",
+ iterable: "t.Iterable[V]",
+ attribute: t.Optional[t.Union[str, int]] = None,
+ start: V = 0, # type: ignore
+) -> V:
"""Returns the sum of a sequence of numbers plus the value of parameter
'start' (which defaults to 0). When the sequence is empty it returns
start.
@@ -1001,52 +1300,93 @@ def do_sum(environment, iterable, attribute=None, start=0):
Total: {{ items|sum(attribute='price') }}
.. versionchanged:: 2.6
- The `attribute` parameter was added to allow suming up over
- attributes. Also the `start` parameter was moved on to the right.
+ The ``attribute`` parameter was added to allow summing up over
+ attributes. Also the ``start`` parameter was moved on to the right.
"""
if attribute is not None:
- iterable = imap(make_attrgetter(environment, attribute), iterable)
- return sum(iterable, start)
+ iterable = map(make_attrgetter(environment, attribute), iterable)
+
+ return sum(iterable, start) # type: ignore[no-any-return, call-overload]
-def do_list(value):
+@async_variant(sync_do_sum) # type: ignore
+async def do_sum(
+ environment: "Environment",
+ iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
+ attribute: t.Optional[t.Union[str, int]] = None,
+ start: V = 0, # type: ignore
+) -> V:
+ rv = start
+
+ if attribute is not None:
+ func = make_attrgetter(environment, attribute)
+ else:
+
+ def func(x: V) -> V:
+ return x
+
+ async for item in auto_aiter(iterable):
+ rv += func(item)
+
+ return rv
+
+
+def sync_do_list(value: "t.Iterable[V]") -> "t.List[V]":
"""Convert the value into a list. If it was a string the returned list
will be a list of characters.
"""
return list(value)
-def do_mark_safe(value):
+@async_variant(sync_do_list) # type: ignore
+async def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]":
+ return await auto_to_list(value)
+
+
+def do_mark_safe(value: str) -> Markup:
"""Mark the value as safe which means that in an environment with automatic
escaping enabled this variable will not be escaped.
"""
return Markup(value)
-def do_mark_unsafe(value):
+def do_mark_unsafe(value: str) -> str:
"""Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
- return text_type(value)
+ return str(value)
+
+
+@typing.overload
+def do_reverse(value: str) -> str:
+ ...
+
+
+@typing.overload
+def do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]":
+ ...
-def do_reverse(value):
+def do_reverse(value: t.Union[str, t.Iterable[V]]) -> t.Union[str, t.Iterable[V]]:
"""Reverse the object or return an iterator that iterates over it the other
way round.
"""
- if isinstance(value, string_types):
+ if isinstance(value, str):
return value[::-1]
+
try:
- return reversed(value)
+ return reversed(value) # type: ignore
except TypeError:
try:
rv = list(value)
rv.reverse()
return rv
- except TypeError:
- raise FilterArgumentError("argument must be iterable")
+ except TypeError as e:
+ raise FilterArgumentError("argument must be iterable") from e
-@environmentfilter
-def do_attr(environment, obj, name):
+@pass_environment
+def do_attr(
+ environment: "Environment", obj: t.Any, name: str
+) -> t.Union[Undefined, t.Any]:
"""Get an attribute of an object. ``foo|attr("bar")`` works like
``foo.bar`` just that always an attribute is returned and items are not
looked up.
@@ -1063,16 +1403,39 @@ def do_attr(environment, obj, name):
except AttributeError:
pass
else:
- if environment.sandboxed and not environment.is_safe_attribute(
- obj, name, value
- ):
- return environment.unsafe_undefined(obj, name)
+ if environment.sandboxed:
+ environment = t.cast("SandboxedEnvironment", environment)
+
+ if not environment.is_safe_attribute(obj, name, value):
+ return environment.unsafe_undefined(obj, name)
+
return value
+
return environment.undefined(obj=obj, name=name)
-@contextfilter
-def do_map(*args, **kwargs):
+@typing.overload
+def sync_do_map(
+ context: "Context", value: t.Iterable, name: str, *args: t.Any, **kwargs: t.Any
+) -> t.Iterable:
+ ...
+
+
+@typing.overload
+def sync_do_map(
+ context: "Context",
+ value: t.Iterable,
+ *,
+ attribute: str = ...,
+ default: t.Optional[t.Any] = None,
+) -> t.Iterable:
+ ...
+
+
+@pass_context
+def sync_do_map(
+ context: "Context", value: t.Iterable, *args: t.Any, **kwargs: t.Any
+) -> t.Iterable:
"""Applies a filter on a sequence of objects or looks up an attribute.
This is useful when dealing with lists of objects but you are really
only interested in a certain value of it.
@@ -1104,7 +1467,7 @@ def do_map(*args, **kwargs):
.. code-block:: python
(u.username for u in users)
- (u.username or "Anonymous" for u in users)
+ (getattr(u, "username", "Anonymous") for u in users)
(do_lower(x) for x in titles)
.. versionchanged:: 2.11.0
@@ -1112,14 +1475,53 @@ def do_map(*args, **kwargs):
.. versionadded:: 2.7
"""
- seq, func = prepare_map(args, kwargs)
- if seq:
- for item in seq:
+ if value:
+ func = prepare_map(context, args, kwargs)
+
+ for item in value:
yield func(item)
-@contextfilter
-def do_select(*args, **kwargs):
+@typing.overload
+def do_map(
+ context: "Context",
+ value: t.Union[t.AsyncIterable, t.Iterable],
+ name: str,
+ *args: t.Any,
+ **kwargs: t.Any,
+) -> t.Iterable:
+ ...
+
+
+@typing.overload
+def do_map(
+ context: "Context",
+ value: t.Union[t.AsyncIterable, t.Iterable],
+ *,
+ attribute: str = ...,
+ default: t.Optional[t.Any] = None,
+) -> t.Iterable:
+ ...
+
+
+@async_variant(sync_do_map) # type: ignore
+async def do_map(
+ context: "Context",
+ value: t.Union[t.AsyncIterable, t.Iterable],
+ *args: t.Any,
+ **kwargs: t.Any,
+) -> t.AsyncIterable:
+ if value:
+ func = prepare_map(context, args, kwargs)
+
+ async for item in auto_aiter(value):
+ yield await auto_await(func(item))
+
+
+@pass_context
+def sync_do_select(
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
+) -> "t.Iterator[V]":
"""Filters a sequence of objects by applying a test to each object,
and only selecting the objects with the test succeeding.
@@ -1144,11 +1546,23 @@ def do_select(*args, **kwargs):
.. versionadded:: 2.7
"""
- return select_or_reject(args, kwargs, lambda x: x, False)
+ return select_or_reject(context, value, args, kwargs, lambda x: x, False)
+
+
+@async_variant(sync_do_select) # type: ignore
+async def do_select(
+ context: "Context",
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
+ *args: t.Any,
+ **kwargs: t.Any,
+) -> "t.AsyncIterator[V]":
+ return async_select_or_reject(context, value, args, kwargs, lambda x: x, False)
-@contextfilter
-def do_reject(*args, **kwargs):
+@pass_context
+def sync_do_reject(
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
+) -> "t.Iterator[V]":
"""Filters a sequence of objects by applying a test to each object,
and rejecting the objects with the test succeeding.
@@ -1168,11 +1582,23 @@ def do_reject(*args, **kwargs):
.. versionadded:: 2.7
"""
- return select_or_reject(args, kwargs, lambda x: not x, False)
+ return select_or_reject(context, value, args, kwargs, lambda x: not x, False)
-@contextfilter
-def do_selectattr(*args, **kwargs):
+@async_variant(sync_do_reject) # type: ignore
+async def do_reject(
+ context: "Context",
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
+ *args: t.Any,
+ **kwargs: t.Any,
+) -> "t.AsyncIterator[V]":
+ return async_select_or_reject(context, value, args, kwargs, lambda x: not x, False)
+
+
+@pass_context
+def sync_do_selectattr(
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
+) -> "t.Iterator[V]":
"""Filters a sequence of objects by applying a test to the specified
attribute of each object, and only selecting the objects with the
test succeeding.
@@ -1196,11 +1622,23 @@ def do_selectattr(*args, **kwargs):
.. versionadded:: 2.7
"""
- return select_or_reject(args, kwargs, lambda x: x, True)
+ return select_or_reject(context, value, args, kwargs, lambda x: x, True)
+
+@async_variant(sync_do_selectattr) # type: ignore
+async def do_selectattr(
+ context: "Context",
+ value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
+ *args: t.Any,
+ **kwargs: t.Any,
+) -> "t.AsyncIterator[V]":
+ return async_select_or_reject(context, value, args, kwargs, lambda x: x, True)
-@contextfilter
-def do_rejectattr(*args, **kwargs):
+
+@pass_context
+def sync_do_rejectattr(
+ context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any
+) -> "t.Iterator[V]":
"""Filters a sequence of objects by applying a test to the specified
attribute of each object, and rejecting the objects with the test
succeeding.
@@ -1222,105 +1660,138 @@ def do_rejectattr(*args, **kwargs):
.. versionadded:: 2.7
"""
- return select_or_reject(args, kwargs, lambda x: not x, True)
-
+ return select_or_reject(context, value, args, kwargs, lambda x: not x, True)
-@evalcontextfilter
-def do_tojson(eval_ctx, value, indent=None):
- """Dumps a structure to JSON so that it's safe to use in ``