diff --git a/.mypy.ini b/.mypy.ini index aca91e2ed6f..994dc9f2cad 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,5 +1,5 @@ [mypy] -files = nncf/common/sparsity, nncf/common/graph, nncf/common/accuracy_aware_training/ +files = nncf/common/sparsity, nncf/common/graph, nncf/common/accuracy_aware_training/, nncf/common/utils/ follow_imports = silent strict = True diff --git a/nncf/common/accuracy_aware_training/training_loop.py b/nncf/common/accuracy_aware_training/training_loop.py index f4a7a3e57b5..2b376afc821 100644 --- a/nncf/common/accuracy_aware_training/training_loop.py +++ b/nncf/common/accuracy_aware_training/training_loop.py @@ -357,7 +357,7 @@ def remove_registry_prefix(algo_name: str) -> str: ) return { - remove_registry_prefix(algo_name): controller_cls + remove_registry_prefix(algo_name): cast(CompressionAlgorithmController, controller_cls) for algo_name, controller_cls in ADAPTIVE_COMPRESSION_CONTROLLERS.registry_dict.items() } diff --git a/nncf/common/deprecation.py b/nncf/common/deprecation.py index 352725c830c..240a22a2eda 100644 --- a/nncf/common/deprecation.py +++ b/nncf/common/deprecation.py @@ -17,7 +17,7 @@ from packaging import version -def warning_deprecated(msg): +def warning_deprecated(msg: str) -> None: # Note: must use FutureWarning in order not to get suppressed by default warnings.warn(msg, FutureWarning, stacklevel=2) diff --git a/nncf/common/graph/patterns/manager.py b/nncf/common/graph/patterns/manager.py index 824a3b4a4c5..08bae0000af 100644 --- a/nncf/common/graph/patterns/manager.py +++ b/nncf/common/graph/patterns/manager.py @@ -38,17 +38,19 @@ def _get_backend_hw_patterns_map(backend: BackendType) -> Dict[HWFusedPatternNam if backend == BackendType.ONNX: from nncf.onnx.hardware.fused_patterns import ONNX_HW_FUSED_PATTERNS - registry = ONNX_HW_FUSED_PATTERNS.registry_dict + registry = cast(Dict[HWFusedPatternNames, Callable[[], GraphPattern]], ONNX_HW_FUSED_PATTERNS.registry_dict) return registry if backend == BackendType.OPENVINO: from nncf.openvino.hardware.fused_patterns import OPENVINO_HW_FUSED_PATTERNS - registry = OPENVINO_HW_FUSED_PATTERNS.registry_dict + registry = cast( + Dict[HWFusedPatternNames, Callable[[], GraphPattern]], OPENVINO_HW_FUSED_PATTERNS.registry_dict + ) return registry if backend == BackendType.TORCH: from nncf.torch.hardware.fused_patterns import PT_HW_FUSED_PATTERNS - registry = PT_HW_FUSED_PATTERNS.registry_dict + registry = cast(Dict[HWFusedPatternNames, Callable[[], GraphPattern]], PT_HW_FUSED_PATTERNS.registry_dict) return registry raise ValueError(f"Hardware-fused patterns not implemented for {backend} backend.") @@ -66,17 +68,19 @@ def _get_backend_ignored_patterns_map( if backend == BackendType.ONNX: from nncf.onnx.quantization.ignored_patterns import ONNX_IGNORED_PATTERNS - registry = ONNX_IGNORED_PATTERNS.registry_dict + registry = cast(Dict[IgnoredPatternNames, Callable[[], GraphPattern]], ONNX_IGNORED_PATTERNS.registry_dict) return registry if backend == BackendType.OPENVINO: from nncf.openvino.quantization.ignored_patterns import OPENVINO_IGNORED_PATTERNS - registry = OPENVINO_IGNORED_PATTERNS.registry_dict + registry = cast( + Dict[IgnoredPatternNames, Callable[[], GraphPattern]], OPENVINO_IGNORED_PATTERNS.registry_dict + ) return registry if backend == BackendType.TORCH: from nncf.torch.quantization.ignored_patterns import PT_IGNORED_PATTERNS - registry = PT_IGNORED_PATTERNS.registry_dict + registry = cast(Dict[IgnoredPatternNames, Callable[[], GraphPattern]], PT_IGNORED_PATTERNS.registry_dict) return registry raise ValueError(f"Ignored patterns not implemented for {backend} backend.") diff --git a/nncf/common/utils/api_marker.py b/nncf/common/utils/api_marker.py index 90e12ff5013..1b6b346231c 100644 --- a/nncf/common/utils/api_marker.py +++ b/nncf/common/utils/api_marker.py @@ -8,6 +8,7 @@ # 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. +from typing import Any class api: @@ -17,7 +18,7 @@ class api: def __init__(self, canonical_alias: str = None): self._canonical_alias = canonical_alias - def __call__(self, obj): + def __call__(self, obj: Any) -> Any: # The value of the marker will be useful in determining # whether we are handling a base class or a derived one. setattr(obj, api.API_MARKER_ATTR, obj.__name__) @@ -26,5 +27,5 @@ def __call__(self, obj): return obj -def is_api(obj) -> bool: +def is_api(obj: Any) -> bool: return hasattr(obj, api.API_MARKER_ATTR) diff --git a/nncf/common/utils/backend.py b/nncf/common/utils/backend.py index 9dcd6a57d71..7589aabb739 100644 --- a/nncf/common/utils/backend.py +++ b/nncf/common/utils/backend.py @@ -56,7 +56,7 @@ def is_torch_model(model: TModel) -> bool: :param model: A target model. :return: True if the model is an instance of torch.nn.Module, otherwise False. """ - import torch + import torch # type: ignore return isinstance(model, torch.nn.Module) @@ -68,7 +68,7 @@ def is_tensorflow_model(model: TModel) -> bool: :param model: A target model. :return: True if the model is an instance of tensorflow.Module, otherwise False. """ - import tensorflow + import tensorflow # type: ignore return isinstance(model, tensorflow.Module) @@ -80,7 +80,7 @@ def is_onnx_model(model: TModel) -> bool: :param model: A target model. :return: True if the model is an instance of onnx.ModelProto, otherwise False. """ - import onnx + import onnx # type: ignore return isinstance(model, onnx.ModelProto) @@ -92,7 +92,7 @@ def is_openvino_model(model: TModel) -> bool: :param model: A target model. :return: True if the model is an instance of openvino.runtime.Model, otherwise False. """ - import openvino.runtime as ov + import openvino.runtime as ov # type: ignore return isinstance(model, ov.Model) @@ -147,7 +147,7 @@ def copy_model(model: TModel) -> TModel: model_backend = get_backend(model) if model_backend == BackendType.OPENVINO: # TODO(l-bat): Remove after fixing ticket: 100919 - return model.clone() + return model.clone() # type: ignore if model_backend == BackendType.TENSORFLOW: # deepcopy and tensorflow.keras.models.clone_model does not work correctly on 2.8.4 version from nncf.tensorflow.graph.model_transformer import TFModelTransformer diff --git a/nncf/common/utils/debug.py b/nncf/common/utils/debug.py index e88a2778030..f682919da53 100644 --- a/nncf/common/utils/debug.py +++ b/nncf/common/utils/debug.py @@ -11,23 +11,24 @@ import logging from contextlib import contextmanager +from typing import Generator from nncf.common.logging import nncf_logger DEBUG_LOG_DIR = "./nncf_debug" -def is_debug(): +def is_debug() -> bool: return nncf_logger.getEffectiveLevel() == logging.DEBUG -def set_debug_log_dir(dir_: str): +def set_debug_log_dir(dir_: str) -> None: global DEBUG_LOG_DIR DEBUG_LOG_DIR = dir_ @contextmanager -def nncf_debug(): +def nncf_debug() -> Generator[None, None, None]: from nncf.common.logging.logger import set_log_level set_log_level(logging.DEBUG) diff --git a/nncf/common/utils/decorators.py b/nncf/common/utils/decorators.py index 0556391e366..d47c78c473a 100644 --- a/nncf/common/utils/decorators.py +++ b/nncf/common/utils/decorators.py @@ -10,14 +10,14 @@ # limitations under the License. from importlib import import_module -from typing import Callable, List +from typing import Any, Callable, Dict, List from nncf.common.logging import nncf_logger -IMPORTED_DEPENDENCIES = {} +IMPORTED_DEPENDENCIES: Dict[str, bool] = {} -def skip_if_dependency_unavailable(dependencies: List[str]) -> Callable: +def skip_if_dependency_unavailable(dependencies: List[str]) -> Callable[[Callable[..., None]], Callable[..., None]]: """ Decorator factory to skip a noreturn function if dependencies are not met. @@ -26,7 +26,7 @@ def skip_if_dependency_unavailable(dependencies: List[str]) -> Callable: """ def wrap(func: Callable[..., None]) -> Callable[..., None]: - def wrapped_f(*args, **kwargs): + def wrapped_f(*args: Any, **kwargs: Any): # type: ignore for libname in dependencies: if libname in IMPORTED_DEPENDENCIES: if IMPORTED_DEPENDENCIES[libname]: diff --git a/nncf/common/utils/dot_file_rw.py b/nncf/common/utils/dot_file_rw.py index 4116fce7aa2..f3a1bdb3b98 100644 --- a/nncf/common/utils/dot_file_rw.py +++ b/nncf/common/utils/dot_file_rw.py @@ -13,10 +13,10 @@ from collections import defaultdict from typing import Dict -import networkx as nx +import networkx as nx # type: ignore -def write_dot_graph(G: nx.DiGraph, path: pathlib.Path): +def write_dot_graph(G: nx.DiGraph, path: pathlib.Path) -> None: # NOTE: writing dot files with colons even in labels or other node/edge/graph attributes leads to an # error. See https://github.com/networkx/networkx/issues/5962. If `relabel` is True in this function, # then the colons (:) will be replaced with (^) symbols. @@ -47,21 +47,21 @@ def read_dot_graph(path: pathlib.Path) -> nx.DiGraph: REPLACEMENT_CHAR = "^" -def _maybe_escape_colons_in_attrs(data: Dict): +def _maybe_escape_colons_in_attrs(data: Dict[str, str]) -> None: for attr_name in data: attr_val = str(data[attr_name]) if RESERVED_CHAR in attr_val and not (attr_val[0] == '"' or attr_val[-1] == '"'): data[attr_name] = '"' + data[attr_name] + '"' # escaped colons are allowed -def _unescape_colons_in_attrs_with_colons(data: Dict): +def _unescape_colons_in_attrs_with_colons(data: Dict[str, str]) -> None: for attr_name in data: attr_val = data[attr_name] if RESERVED_CHAR in attr_val and (attr_val[0] == '"' and attr_val[-1] == '"'): data[attr_name] = data[attr_name][1:-1] -def _remove_cosmetic_labels(graph: nx.DiGraph): +def _remove_cosmetic_labels(graph: nx.DiGraph) -> None: for node_name, node_data in graph.nodes(data=True): if "label" in node_data: label = node_data["label"] @@ -69,7 +69,7 @@ def _remove_cosmetic_labels(graph: nx.DiGraph): del node_data["label"] -def _add_cosmetic_labels(graph: nx.DiGraph, relabeled_node_mapping: Dict[str, str]): +def _add_cosmetic_labels(graph: nx.DiGraph, relabeled_node_mapping: Dict[str, str]) -> None: for original_name, dot_name in relabeled_node_mapping.items(): node_data = graph.nodes[dot_name] if "label" not in node_data: @@ -98,7 +98,7 @@ def relabel_graph_for_dot_visualization(nx_graph: nx.Graph, from_reference: bool __CHARACTER_REPLACE_FROM = REPLACEMENT_CHAR __CHARACTER_REPLACE_TO = RESERVED_CHAR - hits = defaultdict(lambda: 0) + hits: Dict[str, int] = defaultdict(lambda: 0) mapping = {} for original_name in nx_graph.nodes(): if not isinstance(original_name, str): diff --git a/nncf/common/utils/helpers.py b/nncf/common/utils/helpers.py index 4ac365fc504..6f1a6fe3557 100644 --- a/nncf/common/utils/helpers.py +++ b/nncf/common/utils/helpers.py @@ -59,7 +59,7 @@ def configure_accuracy_aware_paths(log_dir: Union[str, pathlib.Path]) -> Union[s return acc_aware_log_dir -def product_dict(d: Dict[Hashable, List]) -> Dict: +def product_dict(d: Dict[Hashable, List[str]]) -> Iterable[Dict[Hashable, str]]: """ Generates dicts which enumerate the options for keys given in the input dict; options are represented by list values in the input dict. diff --git a/nncf/common/utils/os.py b/nncf/common/utils/os.py index 471ed6e2a54..86cb0b5bd4d 100644 --- a/nncf/common/utils/os.py +++ b/nncf/common/utils/os.py @@ -11,19 +11,20 @@ import sys from contextlib import contextmanager from pathlib import Path +from typing import IO, Any, BinaryIO, Iterator, TextIO, Union import psutil import nncf -def fail_if_symlink(file: Path): +def fail_if_symlink(file: Path) -> None: if file.is_symlink(): raise nncf.ValidationError("File {} is a symbolic link, aborting.".format(str(file))) @contextmanager -def safe_open(file: Path, *args, **kwargs): +def safe_open(file: Path, *args, **kwargs) -> Iterator[Union[TextIO, BinaryIO, IO[Any]]]: # type: ignore """ Safe function to open file and return a stream. @@ -38,11 +39,11 @@ def safe_open(file: Path, *args, **kwargs): yield f -def is_windows(): +def is_windows() -> bool: return "win32" in sys.platform -def is_linux(): +def is_linux() -> bool: return "linux" in sys.platform @@ -61,7 +62,7 @@ def get_available_cpu_count(logical: bool = True) -> int: return 1 -def get_available_memory_amount() -> int: +def get_available_memory_amount() -> float: """ :return: Available memory amount (bytes) """ diff --git a/nncf/common/utils/patcher.py b/nncf/common/utils/patcher.py index d0f1c2731ed..659e76a2fe4 100644 --- a/nncf/common/utils/patcher.py +++ b/nncf/common/utils/patcher.py @@ -15,7 +15,7 @@ from collections import OrderedDict from functools import partial from functools import partialmethod -from typing import Callable +from typing import Any, Callable, Dict, List, Optional, Tuple, Union class Patcher: @@ -24,16 +24,16 @@ class Patcher: Note: imported from OTX. """ - def __init__(self): - self._patched = OrderedDict() + def __init__(self) -> None: + self._patched: Dict[Tuple[Any, str], List[Tuple[Any, Callable[..., Any]]]] = OrderedDict() def patch( # noqa: C901 self, - obj_cls, - wrapper: Callable, + obj_cls: Union[Any], + wrapper: Callable[..., Any], *, force: bool = True, - ): + ) -> None: """ Apply patching :param obj_cls: Function to be overriden. @@ -54,7 +54,7 @@ def patch( # noqa: C901 else: if inspect.isclass(obj_cls): try: - fn = obj_cls.__getattribute__(obj_cls, fn_name) + fn = obj_cls.__getattribute__(obj_cls, fn_name) # type: ignore except AttributeError: return self._patch_class_fn(obj_cls, fn_name, fn, wrapper, force) @@ -65,14 +65,14 @@ def patch( # noqa: C901 return self._patch_instance_fn(obj_cls, fn_name, fn, wrapper, force) - def unpatch(self, obj_cls=None, depth=0): + def unpatch(self, obj_cls: Any = None, depth: int = 0) -> None: """ Undo patching :param obj_cls: Function to be unpatched. :param depth: How many patches to undo, depth=0 to undo all of them """ - def _unpatch(obj, fn_name, key, depth): + def _unpatch(obj: Any, fn_name: str, key: Tuple[Any, str], depth: int) -> None: if depth == 0: depth = len(self._patched[key]) keep = len(self._patched[key]) - depth @@ -108,7 +108,7 @@ def _unpatch(obj, fn_name, key, depth): obj, fn_name = self.import_obj(".".join([obj, fn_name])) _unpatch(obj, fn_name, key, depth) - def import_obj(self, obj_cls): # noqa: C901 + def import_obj(self, obj_cls: Union[str, Any]) -> Tuple[Any, str]: # noqa: C901 """Object import helper.""" if isinstance(obj_cls, str): fn_name = obj_cls.split(".")[-1] @@ -144,7 +144,7 @@ def import_obj(self, obj_cls): # noqa: C901 obj_cls = getattr(importlib.import_module(module), obj_cls) return obj_cls, fn_name - def _patch_module_fn(self, obj_cls, fn_name, fn, wrapper, force): + def _patch_module_fn(self, obj_cls: Any, fn_name: str, fn: Any, wrapper: Callable[..., Any], force: bool) -> None: def helper(*args, **kwargs): # type: ignore obj_cls = kwargs.pop("__obj_cls") fn = kwargs.pop("__fn") @@ -160,7 +160,7 @@ def helper(*args, **kwargs): # type: ignore setattr(obj_cls, fn_name, partial(helper, __wrapper=wrapper, __fn=fn, __obj_cls=obj_cls)) self._patched[key].append((fn, wrapper)) - def _patch_class_fn(self, obj_cls, fn_name, fn, wrapper, force): + def _patch_class_fn(self, obj_cls: Any, fn_name: str, fn: Any, wrapper: Callable[..., Any], force: bool) -> None: if isinstance(fn, (staticmethod, classmethod)): def helper(*args, **kwargs): # type: ignore @@ -171,7 +171,7 @@ def helper(*args, **kwargs): # type: ignore return wrapper(args[0], fn.__get__(args[0]), *args[1:], **kwargs) return wrapper(obj_cls, fn.__get__(obj_cls), *args, **kwargs) - elif isinstance(fn, type(all.__call__)): + elif isinstance(fn, type(all.__call__)): # type: ignore def helper(self, *args, **kwargs): # type: ignore kwargs.pop("__obj_cls") @@ -200,7 +200,7 @@ def helper(self, *args, **kwargs): # type: ignore ) self._patched[key].append((fn, wrapper)) - def _patch_instance_fn(self, obj_cls, fn_name, fn, wrapper, force): + def _patch_instance_fn(self, obj_cls: Any, fn_name: str, fn: Any, wrapper: Callable[..., Any], force: bool) -> None: def helper(ctx, *args, **kwargs): # type: ignore fn = kwargs.pop("__fn") wrapper = kwargs.pop("__wrapper") @@ -215,7 +215,7 @@ def helper(ctx, *args, **kwargs): # type: ignore setattr(obj_cls, fn_name, partialmethod(helper, __wrapper=wrapper, __fn=fn).__get__(obj_cls)) self._patched[key].append((fn, wrapper)) - def _initialize(self, key, force): + def _initialize(self, key: Tuple[int, str], force: bool) -> Optional[Any]: fn = None if key not in self._patched: self._patched[key] = [] diff --git a/nncf/common/utils/registry.py b/nncf/common/utils/registry.py index e165a3b60f6..29e62169500 100644 --- a/nncf/common/utils/registry.py +++ b/nncf/common/utils/registry.py @@ -9,29 +9,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Any, Callable, Dict + class Registry: REGISTERED_NAME_ATTR = "_registered_name" def __init__(self, name: str, add_name_as_attr: bool = False): self._name = name - self._registry_dict = {} + self._registry_dict: Dict[str, Any] = {} self._add_name_as_attr = add_name_as_attr @property - def registry_dict(self): + def registry_dict(self) -> Dict[str, Any]: return self._registry_dict - def values(self): + def values(self) -> Any: return self._registry_dict.values() - def _register(self, obj, name: str): + def _register(self, obj: Any, name: str) -> None: if name in self._registry_dict: raise KeyError("{} is already registered in {}".format(name, self._name)) self._registry_dict[name] = obj - def register(self, name: str = None): - def wrap(obj): + def register(self, name: str = None) -> Callable[[Any], Any]: + def wrap(obj: Any) -> Any: cls_name = name if cls_name is None: cls_name = obj.__name__ @@ -42,13 +44,13 @@ def wrap(obj): return wrap - def get(self, name): + def get(self, name: str) -> Any: if name not in self._registry_dict: self._key_not_found(name) return self._registry_dict[name] - def _key_not_found(self, name): + def _key_not_found(self, name: str) -> None: raise KeyError("{} is unknown type of {} ".format(name, self._name)) - def __contains__(self, item): + def __contains__(self, item: Any) -> bool: return item in self._registry_dict.values() diff --git a/nncf/common/utils/tensorboard.py b/nncf/common/utils/tensorboard.py index 7a6fe1043c8..826721d1bad 100644 --- a/nncf/common/utils/tensorboard.py +++ b/nncf/common/utils/tensorboard.py @@ -10,7 +10,7 @@ # limitations under the License. from functools import singledispatch -from typing import Dict +from typing import Any, Dict, Union from nncf.common.pruning.statistics import FilterPruningStatistics from nncf.common.sparsity.statistics import ConstSparsityStatistics @@ -35,12 +35,21 @@ def prepare_for_tensorboard(nncf_stats: NNCFStatistics) -> Dict[str, float]: @singledispatch -def convert_to_dict(stats, algorithm_name: str): +def convert_to_dict( + stats: Union[ + FilterPruningStatistics, + MagnitudeSparsityStatistics, + RBSparsityStatistics, + ConstSparsityStatistics, + MovementSparsityStatistics, + ], + algorithm_name: str, +) -> Dict[Any, Any]: return {} @convert_to_dict.register(FilterPruningStatistics) -def _(stats, algorithm_name): +def _(stats: FilterPruningStatistics, algorithm_name: str) -> Dict[str, float]: tensorboard_stats = { f"{algorithm_name}/algo_current_pruning_level": stats.current_pruning_level, f"{algorithm_name}/model_FLOPS_pruning_level": stats.model_statistics.flops_pruning_level, @@ -53,7 +62,9 @@ def _(stats, algorithm_name): @convert_to_dict.register(MagnitudeSparsityStatistics) @convert_to_dict.register(RBSparsityStatistics) @convert_to_dict.register(ConstSparsityStatistics) -def _(stats, algorithm_name): +def _( + stats: Union[MagnitudeSparsityStatistics, RBSparsityStatistics, ConstSparsityStatistics], algorithm_name: str +) -> Dict[str, float]: tensorboard_stats = { f"{algorithm_name}/sparsity_level_for_model": stats.model_statistics.sparsity_level, f"{algorithm_name}/sparsity_level_for_sparsified_layers": stats.model_statistics.sparsity_level_for_layers, @@ -67,7 +78,7 @@ def _(stats, algorithm_name): @convert_to_dict.register(MovementSparsityStatistics) -def _(stats, algorithm_name): +def _(stats: MovementSparsityStatistics, algorithm_name: str) -> Dict[str, float]: tensorboard_stats = { f"{algorithm_name}/model_sparsity": stats.model_statistics.sparsity_level, f"{algorithm_name}/linear_layer_sparsity": stats.model_statistics.sparsity_level_for_layers, diff --git a/nncf/common/utils/timer.py b/nncf/common/utils/timer.py index 90b47b298be..b60d9953d30 100644 --- a/nncf/common/utils/timer.py +++ b/nncf/common/utils/timer.py @@ -11,12 +11,13 @@ import time from contextlib import contextmanager +from typing import Callable, Iterator from nncf.common.logging import nncf_logger @contextmanager -def timer(): +def timer() -> Iterator[Callable[[], float]]: """ Context manager to measure execution time. """ diff --git a/nncf/tensorflow/graph/model_transformer.py b/nncf/tensorflow/graph/model_transformer.py index bec81169048..ef028d0bd47 100644 --- a/nncf/tensorflow/graph/model_transformer.py +++ b/nncf/tensorflow/graph/model_transformer.py @@ -11,7 +11,7 @@ import copy from collections import OrderedDict from collections import namedtuple -from typing import Callable, Dict, List, Set, Union +from typing import Callable, Dict, List, Set, TypeVar, Union import tensorflow as tf @@ -37,6 +37,7 @@ from nncf.tensorflow.layers.wrapper import NNCFWrapper WeightOperations = namedtuple("WeightOperations", ("weights_attr_name", "operations")) +TModel = TypeVar("TModel") class TFModelTransformer(ModelTransformer): @@ -44,7 +45,7 @@ class TFModelTransformer(ModelTransformer): Applies transformations to a Keras model graph. """ - def __init__(self, model): + def __init__(self, model: TModel) -> None: """ Initializes Model Transformer diff --git a/tests/common/utils/test_timer.py b/tests/common/utils/test_timer.py index 53cbf118bba..bdb2e154d6a 100644 --- a/tests/common/utils/test_timer.py +++ b/tests/common/utils/test_timer.py @@ -22,4 +22,4 @@ def test_timer(nncf_caplog): t() with nncf_caplog.at_level(logging.INFO): - assert "nncf:timer.py:28 Elapsed Time: 00:00:01" in nncf_caplog.text + assert "nncf:timer.py:29 Elapsed Time: 00:00:01" in nncf_caplog.text