Browse Source

!687 clean lineage code after removing lineage public APIs, delete get_summary_lineage(), modify related st and ut

Merge pull request !687 from luopengting/lineage
tags/v1.1.0
mindspore-ci-bot Gitee 5 years ago
parent
commit
c96e7471c5
20 changed files with 228 additions and 1126 deletions
  1. +20
    -41
      mindinsight/backend/lineagemgr/lineage_api.py
  2. +5
    -0
      mindinsight/datavisual/data_transform/data_manager.py
  3. +2
    -1
      mindinsight/lineagemgr/cache_item_updater.py
  4. +0
    -3
      mindinsight/lineagemgr/common/exceptions/error_code.py
  5. +0
    -20
      mindinsight/lineagemgr/common/exceptions/exceptions.py
  6. +1
    -37
      mindinsight/lineagemgr/common/utils.py
  7. +1
    -60
      mindinsight/lineagemgr/common/validator/validate.py
  8. +4
    -43
      mindinsight/lineagemgr/lineage_parser.py
  9. +9
    -136
      mindinsight/lineagemgr/model.py
  10. +2
    -49
      mindinsight/lineagemgr/querier/querier.py
  11. +0
    -14
      tests/st/func/lineagemgr/cache/__init__.py
  12. +0
    -100
      tests/st/func/lineagemgr/cache/test_lineage_cache.py
  13. +25
    -12
      tests/st/func/lineagemgr/collection/model/test_model_lineage.py
  14. +6
    -0
      tests/st/func/lineagemgr/conftest.py
  15. +72
    -277
      tests/st/func/lineagemgr/test_model.py
  16. +10
    -9
      tests/ut/backend/lineagemgr/test_lineage_api.py
  17. +24
    -136
      tests/ut/lineagemgr/querier/test_querier.py
  18. +1
    -0
      tests/ut/lineagemgr/querier/test_query_model.py
  19. +21
    -187
      tests/ut/lineagemgr/test_model.py
  20. +25
    -1
      tests/utils/tools.py

+ 20
- 41
mindinsight/backend/lineagemgr/lineage_api.py View File

@@ -14,16 +14,15 @@
# ============================================================================ # ============================================================================
"""Lineage restful api.""" """Lineage restful api."""
import json import json
import os


from flask import Blueprint, jsonify, request from flask import Blueprint, jsonify, request


from mindinsight.conf import settings from mindinsight.conf import settings
from mindinsight.datavisual.utils.tools import get_train_id from mindinsight.datavisual.utils.tools import get_train_id
from mindinsight.datavisual.data_transform.data_manager import DATA_MANAGER from mindinsight.datavisual.data_transform.data_manager import DATA_MANAGER
from mindinsight.lineagemgr.model import filter_summary_lineage, get_summary_lineage
from mindinsight.utils.exceptions import MindInsightException, ParamValueError
from mindinsight.lineagemgr.cache_item_updater import update_lineage_object from mindinsight.lineagemgr.cache_item_updater import update_lineage_object
from mindinsight.lineagemgr.model import filter_summary_lineage
from mindinsight.utils.exceptions import MindInsightException, ParamValueError


BLUEPRINT = Blueprint("lineage", __name__, url_prefix=settings.URL_PATH_PREFIX+settings.API_PREFIX) BLUEPRINT = Blueprint("lineage", __name__, url_prefix=settings.URL_PATH_PREFIX+settings.API_PREFIX)


@@ -67,26 +66,8 @@ def _get_lineage_info(search_condition):
Raises: Raises:
MindInsightException: If method fails to be called. MindInsightException: If method fails to be called.
""" """
summary_base_dir = str(settings.SUMMARY_BASE_DIR)
try: try:
lineage_info = filter_summary_lineage(
data_manager=DATA_MANAGER,
search_condition=search_condition,
added=True)

lineages = lineage_info['object']

summary_base_dir = os.path.realpath(summary_base_dir)
length = len(summary_base_dir)

for lineage in lineages:
summary_dir = lineage['summary_dir']
summary_dir = os.path.realpath(summary_dir)
if summary_base_dir == summary_dir:
relative_dir = './'
else:
relative_dir = os.path.join(os.curdir, summary_dir[length+1:])
lineage['summary_dir'] = relative_dir
lineage_info = filter_summary_lineage(data_manager=DATA_MANAGER, search_condition=search_condition)


except MindInsightException as exception: except MindInsightException as exception:
raise MindInsightException(exception.error, exception.message, http_code=400) raise MindInsightException(exception.error, exception.message, http_code=400)
@@ -134,29 +115,27 @@ def get_dataset_graph():
>>> GET http://xxxx/v1/mindinsight/datasets/dataset_graph?train_id=xxx >>> GET http://xxxx/v1/mindinsight/datasets/dataset_graph?train_id=xxx
""" """


summary_base_dir = str(settings.SUMMARY_BASE_DIR)
summary_dir = get_train_id(request)
train_id = get_train_id(request)
search_condition = {
'summary_dir': {
'in': [train_id]
}
}
result = {}
try: try:
dataset_graph = get_summary_lineage(
DATA_MANAGER,
summary_dir=summary_dir,
keys=['dataset_graph']
)
objects = filter_summary_lineage(DATA_MANAGER, search_condition).get('object')
except MindInsightException as exception: except MindInsightException as exception:
raise MindInsightException(exception.error, exception.message, http_code=400) raise MindInsightException(exception.error, exception.message, http_code=400)


if dataset_graph:
summary_dir_result = dataset_graph.get('summary_dir')
base_dir_len = len(summary_base_dir)
if summary_base_dir == summary_dir_result:
relative_dir = './'
else:
relative_dir = os.path.join(
os.curdir, summary_dir[base_dir_len + 1:]
)
dataset_graph['summary_dir'] = relative_dir

return jsonify(dataset_graph)
if objects:
lineage_obj = objects[0]
dataset_graph = lineage_obj.get('dataset_graph')

if dataset_graph:
result.update({'dataset_graph': dataset_graph})
result.update({'summary_dir': lineage_obj.get('summary_dir')})

return jsonify(result)




def init_module(app): def init_module(app):


+ 5
- 0
mindinsight/datavisual/data_transform/data_manager.py View File

@@ -210,6 +210,11 @@ class CachedTrainJob:
"""Threading lock with given key.""" """Threading lock with given key."""
return self._key_locks.setdefault(key, threading.Lock()) return self._key_locks.setdefault(key, threading.Lock())


@property
def train_id(self):
"""Get train id."""
return self._basic_info.train_id



class TrainJob: class TrainJob:
""" """


+ 2
- 1
mindinsight/lineagemgr/cache_item_updater.py View File

@@ -72,12 +72,13 @@ class LineageCacheItemUpdater(BaseCacheItemUpdater):


def _lineage_parsing(self, cache_item): def _lineage_parsing(self, cache_item):
"""Parse summaries and return lineage parser.""" """Parse summaries and return lineage parser."""
train_id = cache_item.train_id
summary_dir = cache_item.abs_summary_dir summary_dir = cache_item.abs_summary_dir
update_time = cache_item.basic_info.update_time update_time = cache_item.basic_info.update_time


cached_lineage_item = cache_item.get(key=LINEAGE, raise_exception=False) cached_lineage_item = cache_item.get(key=LINEAGE, raise_exception=False)
if cached_lineage_item is None: if cached_lineage_item is None:
lineage_parser = LineageParser(summary_dir, update_time)
lineage_parser = LineageParser(train_id, summary_dir, update_time)
else: else:
lineage_parser = cached_lineage_item lineage_parser = cached_lineage_item
with cache_item.lock_key(LINEAGE): with cache_item.lock_key(LINEAGE):


+ 0
- 3
mindinsight/lineagemgr/common/exceptions/error_code.py View File

@@ -57,7 +57,6 @@ class LineageErrors(LineageErrorCodes):
PARAM_STEP_NUM_ERROR = 18 | _MINDSPORE_COLLECTOR_ERROR PARAM_STEP_NUM_ERROR = 18 | _MINDSPORE_COLLECTOR_ERROR


# Model lineage error codes. # Model lineage error codes.
LINEAGE_PARAM_OPERATION_ERROR = 0 | _MODEL_LINEAGE_API_ERROR_MASK
LINEAGE_PARAM_METRIC_ERROR = 1 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_PARAM_METRIC_ERROR = 1 | _MODEL_LINEAGE_API_ERROR_MASK
LINEAGE_PARAM_LOSS_FUNCTION_ERROR = 4 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_PARAM_LOSS_FUNCTION_ERROR = 4 | _MODEL_LINEAGE_API_ERROR_MASK
LINEAGE_PARAM_TRAIN_DATASET_PATH_ERROR = 5 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_PARAM_TRAIN_DATASET_PATH_ERROR = 5 | _MODEL_LINEAGE_API_ERROR_MASK
@@ -79,7 +78,6 @@ class LineageErrors(LineageErrorCodes):
LINEAGE_DIR_NOT_EXIST_ERROR = 20 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_DIR_NOT_EXIST_ERROR = 20 | _MODEL_LINEAGE_API_ERROR_MASK
LINEAGE_SUMMARY_DATA_ERROR = 21 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_SUMMARY_DATA_ERROR = 21 | _MODEL_LINEAGE_API_ERROR_MASK
LINEAGE_FILE_NOT_FOUND_ERROR = 22 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_FILE_NOT_FOUND_ERROR = 22 | _MODEL_LINEAGE_API_ERROR_MASK
LINEAGE_PARAM_SUMMARY_PATH_ERROR = 23 | _MODEL_LINEAGE_API_ERROR_MASK
LINEAGE_SEARCH_CONDITION_PARAM_ERROR = 24 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_SEARCH_CONDITION_PARAM_ERROR = 24 | _MODEL_LINEAGE_API_ERROR_MASK


LINEAGE_PARAM_LINEAGE_TYPE_ERROR = 25 | _MODEL_LINEAGE_API_ERROR_MASK LINEAGE_PARAM_LINEAGE_TYPE_ERROR = 25 | _MODEL_LINEAGE_API_ERROR_MASK
@@ -110,7 +108,6 @@ class LineageErrorMsg(Enum):
"mindspore.train.summary.SummaryRecord" "mindspore.train.summary.SummaryRecord"
PARAM_RAISE_EXCEPTION_ERROR = "Invalid value for raise_exception. It should be True or False." PARAM_RAISE_EXCEPTION_ERROR = "Invalid value for raise_exception. It should be True or False."
# Lineage error messages. # Lineage error messages.
LINEAGE_PARAM_SUMMARY_PATH_ERROR = "The parameter summary path error: {}"
LINEAGE_SUMMARY_DATA_ERROR = "Query summary data error: {}" LINEAGE_SUMMARY_DATA_ERROR = "Query summary data error: {}"
LINEAGE_FILE_NOT_FOUND_ERROR = "File not found error: {}" LINEAGE_FILE_NOT_FOUND_ERROR = "File not found error: {}"
LINEAGE_DIR_NOT_EXIST_ERROR = "Dir not exist error: {}" LINEAGE_DIR_NOT_EXIST_ERROR = "Dir not exist error: {}"


+ 0
- 20
mindinsight/lineagemgr/common/exceptions/exceptions.py View File

@@ -106,16 +106,6 @@ class LineageEventFieldNotExistException(MindInsightException):
) )




class LineageParamSummaryPathError(MindInsightException):
"""The lineage parameter summary path error."""
def __init__(self, msg):
super(LineageParamSummaryPathError, self).__init__(
error=LineageErrors.LINEAGE_PARAM_SUMMARY_PATH_ERROR,
message=LineageErrorMsg.LINEAGE_PARAM_SUMMARY_PATH_ERROR.value.format(msg),
http_code=400
)


class LineageQuerySummaryDataError(MindInsightException): class LineageQuerySummaryDataError(MindInsightException):
"""Query summary data error in lineage module.""" """Query summary data error in lineage module."""
def __init__(self, msg): def __init__(self, msg):
@@ -136,16 +126,6 @@ class LineageFileNotFoundError(MindInsightException):
) )




class LineageDirNotExistError(MindInsightException):
"""Directory not exist in lineage module."""
def __init__(self, msg):
super(LineageDirNotExistError, self).__init__(
error=LineageErrors.LINEAGE_DIR_NOT_EXIST_ERROR,
message=LineageErrorMsg.LINEAGE_DIR_NOT_EXIST_ERROR.value.format(msg),
http_code=400
)


class LineageSearchConditionParamError(MindInsightException): class LineageSearchConditionParamError(MindInsightException):
"""Search condition param is invalid in lineage module.""" """Search condition param is invalid in lineage module."""
def __init__(self, msg): def __init__(self, msg):


+ 1
- 37
mindinsight/lineagemgr/common/utils.py View File

@@ -15,30 +15,16 @@
"""Lineage utils.""" """Lineage utils."""
import os import os
import re import re
from pathlib import Path


from mindinsight.datavisual.data_transform.summary_watcher import SummaryWatcher from mindinsight.datavisual.data_transform.summary_watcher import SummaryWatcher
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamValueError, LineageParamTypeError, \
LineageDirNotExistError, LineageParamSummaryPathError
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamTypeError
from mindinsight.lineagemgr.common.log import logger as log from mindinsight.lineagemgr.common.log import logger as log
from mindinsight.lineagemgr.common.validator.validate import validate_path




def enum_to_list(enum): def enum_to_list(enum):
return [enum_ele.value for enum_ele in enum] return [enum_ele.value for enum_ele in enum]




def normalize_summary_dir(summary_dir):
"""Normalize summary dir."""
try:
summary_dir = validate_path(summary_dir)
except (LineageParamValueError, LineageDirNotExistError) as error:
log.error(str(error))
log.exception(error)
raise LineageParamSummaryPathError(str(error.message))
return summary_dir


def get_timestamp(filename): def get_timestamp(filename):
"""Get timestamp from filename.""" """Get timestamp from filename."""
timestamp = int(re.search(SummaryWatcher().SUMMARY_FILENAME_REGEX, filename)[1]) timestamp = int(re.search(SummaryWatcher().SUMMARY_FILENAME_REGEX, filename)[1])
@@ -68,25 +54,3 @@ def make_directory(path):
log.error("No write permission on the directory(%r), error = %r", path, err) log.error("No write permission on the directory(%r), error = %r", path, err)
raise LineageParamTypeError("No write permission on the directory.") raise LineageParamTypeError("No write permission on the directory.")
return real_path return real_path


def get_relative_path(path, base_path):
"""
Get relative path based on base_path.

Args:
path (str): absolute path.
base_path: absolute base path.

Returns:
str, relative path based on base_path.

"""
try:
r_path = str(Path(path).relative_to(Path(base_path)))
except ValueError:
raise LineageParamValueError("The path %r does not start with %r." % (path, base_path))

if r_path == ".":
r_path = ""
return os.path.join("./", r_path)

+ 1
- 60
mindinsight/lineagemgr/common/validator/validate.py View File

@@ -13,13 +13,11 @@
# limitations under the License. # limitations under the License.
# ============================================================================ # ============================================================================
"""Validate the parameters.""" """Validate the parameters."""
import os
import re import re
from marshmallow import ValidationError from marshmallow import ValidationError


from mindinsight.lineagemgr.common.exceptions.error_code import LineageErrors, LineageErrorMsg from mindinsight.lineagemgr.common.exceptions.error_code import LineageErrors, LineageErrorMsg
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamTypeError, \
LineageParamValueError, LineageDirNotExistError
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamTypeError, LineageParamValueError
from mindinsight.lineagemgr.common.log import logger as log from mindinsight.lineagemgr.common.log import logger as log
from mindinsight.lineagemgr.common.validator.validate_path import safe_normalize_path from mindinsight.lineagemgr.common.validator.validate_path import safe_normalize_path
from mindinsight.lineagemgr.querier.query_model import FIELD_MAPPING from mindinsight.lineagemgr.querier.query_model import FIELD_MAPPING
@@ -224,38 +222,6 @@ def validate_raise_exception(raise_exception):
) )




def validate_filter_key(keys):
"""
Verify the keys of filtering is valid or not.

Args:
keys (list): The keys to get the relative lineage info.

Raises:
LineageParamTypeError: If keys is not list.
LineageParamValueError: If the value of keys is invalid.
"""
filter_keys = [
'metric', 'hyper_parameters', 'algorithm',
'train_dataset', 'model', 'valid_dataset',
'dataset_graph'
]

if not isinstance(keys, list):
log.error("Keys must be list.")
raise LineageParamTypeError("Keys must be list.")

for element in keys:
if not isinstance(element, str):
log.error("Element of keys must be str.")
raise LineageParamTypeError("Element of keys must be str.")

if not set(keys).issubset(filter_keys):
err_msg = "Keys must be in {}.".format(filter_keys)
log.error(err_msg)
raise LineageParamValueError(err_msg)


def validate_condition(search_condition): def validate_condition(search_condition):
""" """
Verify the param in search_condition is valid or not. Verify the param in search_condition is valid or not.
@@ -310,31 +276,6 @@ def validate_condition(search_condition):
raise LineageParamValueError(err_msg) raise LineageParamValueError(err_msg)




def validate_path(summary_path):
"""
Verify the summary path is valid or not.

Args:
summary_path (str): The summary path which is a dir.

Raises:
LineageParamValueError: If the input param value is invalid.
LineageDirNotExistError: If the summary path is invalid.
"""
try:
summary_path = safe_normalize_path(
summary_path, "summary_path", None, check_absolute_path=True
)
except ValidationError:
log.error("The summary path is invalid.")
raise LineageParamValueError("The summary path is invalid.")
if not os.path.isdir(summary_path):
log.error("The summary path does not exist or is not a dir.")
raise LineageDirNotExistError("The summary path does not exist or is not a dir.")

return summary_path


def validate_user_defined_info(user_defined_info): def validate_user_defined_info(user_defined_info):
""" """
Validate user defined info, delete the item if its key is in lineage. Validate user defined info, delete the item if its key is in lineage.


+ 4
- 43
mindinsight/lineagemgr/lineage_parser.py View File

@@ -15,7 +15,6 @@
"""This file is used to parse lineage info.""" """This file is used to parse lineage info."""
import os import os


from mindinsight.datavisual.data_transform.summary_watcher import SummaryWatcher
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageSummaryAnalyzeException, \ from mindinsight.lineagemgr.common.exceptions.exceptions import LineageSummaryAnalyzeException, \
LineageEventNotExistException, LineageEventFieldNotExistException, LineageFileNotFoundError, \ LineageEventNotExistException, LineageEventFieldNotExistException, LineageFileNotFoundError, \
MindInsightException MindInsightException
@@ -64,8 +63,9 @@ class SuperLineageObj:


class LineageParser: class LineageParser:
"""Lineage parser.""" """Lineage parser."""
def __init__(self, summary_dir, update_time=None, added_info=None):
def __init__(self, train_id, summary_dir, update_time=None, added_info=None):
self._summary_dir = summary_dir self._summary_dir = summary_dir
self._train_id = train_id
self._update_time = update_time self._update_time = update_time
self._added_info = added_info self._added_info = added_info


@@ -154,7 +154,7 @@ class LineageParser:
"""Update lineage object.""" """Update lineage object."""
if self._super_lineage_obj is None: if self._super_lineage_obj is None:
lineage_obj = LineageObj( lineage_obj = LineageObj(
self._summary_dir,
self._train_id,
train_lineage=lineage_info.train_lineage, train_lineage=lineage_info.train_lineage,
evaluation_lineage=lineage_info.eval_lineage, evaluation_lineage=lineage_info.eval_lineage,
dataset_graph=lineage_info.dataset_graph, dataset_graph=lineage_info.dataset_graph,
@@ -177,52 +177,13 @@ class LineageParser:


class LineageOrganizer: class LineageOrganizer:
"""Lineage organizer.""" """Lineage organizer."""
def __init__(self, data_manager=None, summary_base_dir=None):
def __init__(self, data_manager):
self._data_manager = data_manager self._data_manager = data_manager
self._summary_base_dir = summary_base_dir
self._check_params()
self._super_lineage_objs = {} self._super_lineage_objs = {}
self._organize_from_cache() self._organize_from_cache()
self._organize_from_disk()

def _check_params(self):
"""Check params."""
if self._data_manager is not None and self._summary_base_dir is not None:
self._summary_base_dir = None

def _organize_from_disk(self):
"""Organize lineage objs from disk."""
if self._summary_base_dir is None:
return
summary_watcher = SummaryWatcher()
relative_dirs = summary_watcher.list_summary_directories(
summary_base_dir=self._summary_base_dir
)

no_lineage_count = 0
for item in relative_dirs:
relative_dir = item.get('relative_path')
update_time = item.get('update_time')
abs_summary_dir = os.path.realpath(os.path.join(self._summary_base_dir, relative_dir))

try:
lineage_parser = LineageParser(abs_summary_dir, update_time)
super_lineage_obj = lineage_parser.super_lineage_obj
if super_lineage_obj is not None:
self._super_lineage_objs.update({abs_summary_dir: super_lineage_obj})
except LineageFileNotFoundError:
no_lineage_count += 1

if no_lineage_count == len(relative_dirs):
logger.info('There is no summary log file under summary_base_dir.')
raise LineageFileNotFoundError(
'There is no summary log file under summary_base_dir.'
)


def _organize_from_cache(self): def _organize_from_cache(self):
"""Organize lineage objs from cache.""" """Organize lineage objs from cache."""
if self._data_manager is None:
return
brief_cache = self._data_manager.get_brief_cache() brief_cache = self._data_manager.get_brief_cache()
cache_items = brief_cache.cache_items cache_items = brief_cache.cache_items
for relative_dir, cache_train_job in cache_items.items(): for relative_dir, cache_train_job in cache_items.items():


+ 9
- 136
mindinsight/lineagemgr/model.py View File

@@ -13,20 +13,15 @@
# limitations under the License. # limitations under the License.
# ============================================================================ # ============================================================================
"""This file is used to define the model lineage python api.""" """This file is used to define the model lineage python api."""
import os
import numpy as np import numpy as np
import pandas as pd import pandas as pd


from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamValueError, \
LineageQuerySummaryDataError, LineageParamSummaryPathError, \
LineageQuerierParamException, LineageDirNotExistError, LineageSearchConditionParamError, \
LineageParamTypeError, LineageSummaryParseException
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageQuerySummaryDataError, \
LineageQuerierParamException, LineageSearchConditionParamError, LineageParamTypeError, LineageSummaryParseException
from mindinsight.lineagemgr.common.log import logger as log from mindinsight.lineagemgr.common.log import logger as log
from mindinsight.lineagemgr.common.utils import normalize_summary_dir, get_relative_path
from mindinsight.lineagemgr.common.validator.model_parameter import SearchModelConditionParameter from mindinsight.lineagemgr.common.validator.model_parameter import SearchModelConditionParameter
from mindinsight.lineagemgr.common.validator.validate import validate_filter_key, validate_search_model_condition, \
validate_condition, validate_path, validate_train_id
from mindinsight.lineagemgr.lineage_parser import LineageParser, LineageOrganizer
from mindinsight.lineagemgr.common.validator.validate import validate_search_model_condition, validate_condition
from mindinsight.lineagemgr.lineage_parser import LineageOrganizer
from mindinsight.lineagemgr.querier.querier import Querier from mindinsight.lineagemgr.querier.querier import Querier
from mindinsight.optimizer.common.enums import ReasonCode from mindinsight.optimizer.common.enums import ReasonCode
from mindinsight.optimizer.utils.utils import is_simple_numpy_number from mindinsight.optimizer.utils.utils import is_simple_numpy_number
@@ -38,62 +33,7 @@ _USER_DEFINED_PREFIX = "[U]"
USER_DEFINED_INFO_LIMIT = 100 USER_DEFINED_INFO_LIMIT = 100




def get_summary_lineage(data_manager=None, summary_dir=None, keys=None):
"""
Get summary lineage from data_manager or parsing from summaries.

One of data_manager or summary_dir needs to be specified. Support getting
super_lineage_obj from data_manager or parsing summaries by summary_dir.

Args:
data_manager (DataManager): Data manager defined as
mindinsight.datavisual.data_transform.data_manager.DataManager
summary_dir (str): The summary directory. It contains summary logs for
one training.
keys (list[str]): The filter keys of lineage information. The acceptable
keys are `metric`, `user_defined`, `hyper_parameters`, `algorithm`,
`train_dataset`, `model`, `valid_dataset` and `dataset_graph`.
If it is `None`, all information will be returned. Default: None.

Returns:
dict, the lineage information for one training.

Raises:
LineageParamSummaryPathError: If summary path is invalid.
LineageQuerySummaryDataError: If querying summary data fails.
LineageFileNotFoundError: If the summary log file is not found.

"""
default_result = {}
if data_manager is None and summary_dir is None:
raise LineageParamTypeError("One of data_manager or summary_dir needs to be specified.")
if data_manager is not None and summary_dir is None:
raise LineageParamTypeError("If data_manager is specified, the summary_dir needs to be "
"specified as relative path.")

if keys is not None:
validate_filter_key(keys)

if data_manager is None:
normalize_summary_dir(summary_dir)
super_lineage_obj = LineageParser(summary_dir).super_lineage_obj
else:
validate_train_id(summary_dir)
super_lineage_obj = LineageOrganizer(data_manager=data_manager).get_super_lineage_obj(summary_dir)

if super_lineage_obj is None:
return default_result

try:
result = Querier({summary_dir: super_lineage_obj}).get_summary_lineage(summary_dir, keys)
except (LineageQuerierParamException, LineageParamTypeError) as error:
log.error(str(error))
log.exception(error)
raise LineageQuerySummaryDataError("Get summary lineage failed.")
return result[0]


def filter_summary_lineage(data_manager=None, summary_base_dir=None, search_condition=None, added=False):
def filter_summary_lineage(data_manager, search_condition=None):
""" """
Filter summary lineage from data_manager or parsing from summaries. Filter summary lineage from data_manager or parsing from summaries.


@@ -103,18 +43,8 @@ def filter_summary_lineage(data_manager=None, summary_base_dir=None, search_cond
Args: Args:
data_manager (DataManager): Data manager defined as data_manager (DataManager): Data manager defined as
mindinsight.datavisual.data_transform.data_manager.DataManager mindinsight.datavisual.data_transform.data_manager.DataManager
summary_base_dir (str): The summary base directory. It contains summary
directories generated by training.
search_condition (dict): The search condition. search_condition (dict): The search condition.
""" """
if data_manager is None and summary_base_dir is None:
raise LineageParamTypeError("One of data_manager or summary_base_dir needs to be specified.")

if data_manager is None:
summary_base_dir = normalize_summary_dir(summary_base_dir)
else:
summary_base_dir = data_manager.summary_base_dir

search_condition = {} if search_condition is None else search_condition search_condition = {} if search_condition is None else search_condition


try: try:
@@ -126,18 +56,8 @@ def filter_summary_lineage(data_manager=None, summary_base_dir=None, search_cond
raise LineageSearchConditionParamError(str(error.message)) raise LineageSearchConditionParamError(str(error.message))


try: try:
search_condition = _convert_relative_path_to_abspath(summary_base_dir, search_condition)
except (LineageParamValueError, LineageDirNotExistError) as error:
log.error(str(error))
log.exception(error)
raise LineageParamSummaryPathError(str(error.message))

try:
lineage_objects = LineageOrganizer(data_manager, summary_base_dir).super_lineage_objs
result = Querier(lineage_objects).filter_summary_lineage(
condition=search_condition,
added=added
)
lineage_objects = LineageOrganizer(data_manager).super_lineage_objs
result = Querier(lineage_objects).filter_summary_lineage(condition=search_condition)
except LineageSummaryParseException: except LineageSummaryParseException:
result = {'object': [], 'count': 0} result = {'object': [], 'count': 0}
except (LineageQuerierParamException, LineageParamTypeError) as error: except (LineageQuerierParamException, LineageParamTypeError) as error:
@@ -148,53 +68,6 @@ def filter_summary_lineage(data_manager=None, summary_base_dir=None, search_cond
return result return result




def _convert_relative_path_to_abspath(summary_base_dir, search_condition):
"""
Convert relative path to absolute path.

Args:
summary_base_dir (str): The summary base directory.
search_condition (dict): The search condition.

Returns:
dict, the updated search_condition.

Raises:
LineageParamValueError: If the value of input_name is invalid.
"""
if ("summary_dir" not in search_condition) or (not search_condition.get("summary_dir")):
return search_condition

summary_dir_condition = search_condition.get("summary_dir")

for key in ['in', 'not_in']:
if key in summary_dir_condition:
summary_paths = []
for summary_dir in summary_dir_condition.get(key):
if summary_dir.startswith('./'):
abs_dir = os.path.join(
summary_base_dir, summary_dir[2:]
)
abs_dir = validate_path(abs_dir)
else:
abs_dir = validate_path(summary_dir)
summary_paths.append(abs_dir)
search_condition.get('summary_dir')[key] = summary_paths

if 'eq' in summary_dir_condition:
summary_dir = summary_dir_condition.get('eq')
if summary_dir.startswith('./'):
abs_dir = os.path.join(
summary_base_dir, summary_dir[2:]
)
abs_dir = validate_path(abs_dir)
else:
abs_dir = validate_path(summary_dir)
search_condition.get('summary_dir')['eq'] = abs_dir

return search_condition


def get_flattened_lineage(data_manager, search_condition=None): def get_flattened_lineage(data_manager, search_condition=None):
""" """
Get lineage data in a table from data manager. Get lineage data in a table from data manager.
@@ -207,10 +80,10 @@ def get_flattened_lineage(data_manager, search_condition=None):
Dict[str, list]: A dict contains keys and values from lineages. Dict[str, list]: A dict contains keys and values from lineages.


""" """
summary_base_dir, flatten_dict, user_count = data_manager.summary_base_dir, {'train_id': []}, 0
flatten_dict, user_count = {'train_id': []}, 0
lineages = filter_summary_lineage(data_manager=data_manager, search_condition=search_condition).get("object", []) lineages = filter_summary_lineage(data_manager=data_manager, search_condition=search_condition).get("object", [])
for index, lineage in enumerate(lineages): for index, lineage in enumerate(lineages):
flatten_dict['train_id'].append(get_relative_path(lineage.get("summary_dir"), summary_base_dir))
flatten_dict['train_id'].append(lineage.get("summary_dir"))
for key, val in _flatten_lineage(lineage.get('model_lineage', {})): for key, val in _flatten_lineage(lineage.get('model_lineage', {})):
if key.startswith(_USER_DEFINED_PREFIX) and key not in flatten_dict: if key.startswith(_USER_DEFINED_PREFIX) and key not in flatten_dict:
if user_count > USER_DEFINED_INFO_LIMIT: if user_count > USER_DEFINED_INFO_LIMIT:


+ 2
- 49
mindinsight/lineagemgr/querier/querier.py View File

@@ -189,53 +189,7 @@ class Querier:
raise LineageParamTypeError("Init param should be a dict.") raise LineageParamTypeError("Init param should be a dict.")
return super_lineage_objs return super_lineage_objs


def get_summary_lineage(self, summary_dir=None, filter_keys=None):
"""
Get summary lineage information.

If a summary dir is specified, the special summary lineage information
will be found. If the summary dir is `None`, all summary lineage
information will be found.

Returns the content corresponding to the specified field in the filter
key. The contents of the filter key include `metric`, `hyper_parameters`,
`algorithm`, `train_dataset`, `valid_dataset` and `model`. You can
specify multiple filter keys in the `filter_keys`. If the parameter is
`None`, complete information will be returned.

Args:
summary_dir (Union[str, None]): Summary log dir. Default: None.
filter_keys (Union[list[str], None]): Filter keys. Default: None.

Returns:
list[dict], summary lineage information.
"""

if filter_keys is None:
filter_keys = LineageFilterKey.get_key_list()
else:
for key in filter_keys:
if not LineageFilterKey.is_valid_filter_key(key):
raise LineageQuerierParamException(
filter_keys, 'The filter key {} is invalid.'.format(key)
)

if summary_dir is None:
result = [
item.lineage_obj.get_summary_info(filter_keys) for item in self._super_lineage_objs.values()
]
elif summary_dir in self._super_lineage_objs:
lineage_obj = self._super_lineage_objs[summary_dir].lineage_obj
result = [lineage_obj.get_summary_info(filter_keys)]
else:
raise LineageQuerierParamException(
'summary_dir',
'Summary dir {} does not exist.'.format(summary_dir)
)

return result

def filter_summary_lineage(self, condition=None, added=False):
def filter_summary_lineage(self, condition=None):
""" """
Filter and sort lineage information based on the specified condition. Filter and sort lineage information based on the specified condition.


@@ -298,8 +252,7 @@ class Querier:
lineage_object.update(item.lineage_obj.to_model_lineage_dict()) lineage_object.update(item.lineage_obj.to_model_lineage_dict())
if LineageType.DATASET.value in lineage_types: if LineageType.DATASET.value in lineage_types:
lineage_object.update(item.lineage_obj.to_dataset_lineage_dict()) lineage_object.update(item.lineage_obj.to_dataset_lineage_dict())
if added:
lineage_object.update({"added_info": item.added_info})
lineage_object.update({"added_info": item.added_info})
object_items.append(lineage_object) object_items.append(lineage_object)


lineage_info = { lineage_info = {


+ 0
- 14
tests/st/func/lineagemgr/cache/__init__.py View File

@@ -1,14 +0,0 @@
# Copyright 2020 Huawei Technologies Co., Ltd
#
# 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.
# ============================================================================

+ 0
- 100
tests/st/func/lineagemgr/cache/test_lineage_cache.py View File

@@ -1,100 +0,0 @@
# Copyright 2020 Huawei Technologies Co., Ltd
#
# 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.
# ============================================================================

"""
Function:
Test the query module about lineage information.
Usage:
The query module test should be run after lineagemgr/collection/model/test_model_lineage.py
pytest lineagemgr
"""
from unittest import TestCase

import pytest

from mindinsight.datavisual.data_transform.data_manager import DataManager
from mindinsight.lineagemgr.cache_item_updater import LineageCacheItemUpdater
from mindinsight.lineagemgr.model import filter_summary_lineage, get_summary_lineage

from ..test_model import LINEAGE_INFO_RUN1, LINEAGE_FILTRATION_EXCEPT_RUN, \
LINEAGE_FILTRATION_RUN1, LINEAGE_FILTRATION_RUN2
from ..conftest import BASE_SUMMARY_DIR
from .....ut.lineagemgr.querier import event_data
from .....utils.tools import assert_equal_lineages


@pytest.mark.usefixtures("create_summary_dir")
class TestModelApi(TestCase):
"""Test get lineage from data_manager."""
@classmethod
def setup_class(cls):
data_manager = DataManager(BASE_SUMMARY_DIR)
data_manager.register_brief_cache_item_updater(LineageCacheItemUpdater())
data_manager.start_load_data().join()

cls._data_manger = data_manager

@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_get_summary_lineage(self):
"""Test the interface of get_summary_lineage."""
total_res = get_summary_lineage(data_manager=self._data_manger, summary_dir="./run1")
expect_total_res = LINEAGE_INFO_RUN1
assert_equal_lineages(expect_total_res, total_res, self.assertDictEqual)

@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_filter_summary_lineage(self):
"""Test the interface of filter_summary_lineage."""
expect_result = {
'customized': event_data.CUSTOMIZED__1,
'object': [
LINEAGE_FILTRATION_EXCEPT_RUN,
LINEAGE_FILTRATION_RUN1,
LINEAGE_FILTRATION_RUN2
],
'count': 3
}

search_condition = {
'sorted_name': 'summary_dir'
}
res = filter_summary_lineage(data_manager=self._data_manger, search_condition=search_condition)
expect_objects = expect_result.get('object')
for idx, res_object in enumerate(res.get('object')):
expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark')
assert_equal_lineages(expect_result, res, self.assertDictEqual)

expect_result = {
'customized': {},
'object': [],
'count': 0
}

search_condition = {
'summary_dir': {
"in": ['./dir_with_empty_lineage']
}
}
res = filter_summary_lineage(data_manager=self._data_manger, search_condition=search_condition)
assert_equal_lineages(expect_result, res, self.assertDictEqual)

+ 25
- 12
tests/st/func/lineagemgr/collection/model/test_model_lineage.py View File

@@ -27,7 +27,7 @@ from unittest import TestCase, mock


import numpy as np import numpy as np
import pytest import pytest
from mindinsight.lineagemgr.model import get_summary_lineage
from mindinsight.lineagemgr.model import filter_summary_lineage
from mindinsight.lineagemgr.common.exceptions.error_code import LineageErrors from mindinsight.lineagemgr.common.exceptions.error_code import LineageErrors
from mindinsight.utils.exceptions import MindInsightException from mindinsight.utils.exceptions import MindInsightException


@@ -37,10 +37,11 @@ from mindspore.dataset.engine import MindDataset
from mindspore.nn import Momentum, SoftmaxCrossEntropyWithLogits, WithLossCell from mindspore.nn import Momentum, SoftmaxCrossEntropyWithLogits, WithLossCell
from mindspore.train.callback import ModelCheckpoint, RunContext, SummaryStep, _ListCallback from mindspore.train.callback import ModelCheckpoint, RunContext, SummaryStep, _ListCallback
from mindspore.train.summary import SummaryRecord from mindspore.train.summary import SummaryRecord
from tests.utils.lineage_writer.model_lineage import AnalyzeObject, EvalLineage, TrainLineage


from ...conftest import SUMMARY_DIR, SUMMARY_DIR_2, SUMMARY_DIR_3
from .train_one_step import TrainOneStep from .train_one_step import TrainOneStep
from ...conftest import SUMMARY_DIR, SUMMARY_DIR_2, SUMMARY_DIR_3, BASE_SUMMARY_DIR, LINEAGE_DATA_MANAGER
from ......utils.lineage_writer.model_lineage import AnalyzeObject, EvalLineage, TrainLineage
from ......utils.tools import get_relative_path




@pytest.mark.usefixtures("create_summary_dir") @pytest.mark.usefixtures("create_summary_dir")
@@ -76,6 +77,13 @@ class TestModelLineage(TestCase):
"version": "v1" "version": "v1"
} }


train_id = get_relative_path(SUMMARY_DIR, BASE_SUMMARY_DIR)
cls._search_condition = {
'summary_dir': {
'eq': train_id
}
}

@pytest.mark.scene_train(2) @pytest.mark.scene_train(2)
@pytest.mark.level0 @pytest.mark.level0
@pytest.mark.platform_arm_ascend_training @pytest.mark.platform_arm_ascend_training
@@ -90,13 +98,17 @@ class TestModelLineage(TestCase):
train_callback = TrainLineage(SUMMARY_DIR, True, self.user_defined_info) train_callback = TrainLineage(SUMMARY_DIR, True, self.user_defined_info)
train_callback.initial_learning_rate = 0.12 train_callback.initial_learning_rate = 0.12
train_callback.end(RunContext(self.run_context)) train_callback.end(RunContext(self.run_context))
res = get_summary_lineage(summary_dir=SUMMARY_DIR)
assert res.get('hyper_parameters', {}).get('epoch') == 10

LINEAGE_DATA_MANAGER.start_load_data().join()
res = filter_summary_lineage(LINEAGE_DATA_MANAGER, self._search_condition)
assert res.get('object')[0].get('model_lineage', {}).get('epoch') == 10
run_context = self.run_context run_context = self.run_context
run_context['epoch_num'] = 14 run_context['epoch_num'] = 14
train_callback.end(RunContext(run_context)) train_callback.end(RunContext(run_context))
res = get_summary_lineage(summary_dir=SUMMARY_DIR)
assert res.get('hyper_parameters', {}).get('epoch') == 14

LINEAGE_DATA_MANAGER.start_load_data().join()
res = filter_summary_lineage(LINEAGE_DATA_MANAGER, self._search_condition)
assert res.get('object')[0].get('model_lineage', {}).get('epoch') == 14


@pytest.mark.scene_eval(3) @pytest.mark.scene_eval(3)
@pytest.mark.level0 @pytest.mark.level0
@@ -186,12 +198,13 @@ class TestModelLineage(TestCase):
run_context_customized['train_network'] = net run_context_customized['train_network'] = net
train_callback.begin(RunContext(run_context_customized)) train_callback.begin(RunContext(run_context_customized))
train_callback.end(RunContext(run_context_customized)) train_callback.end(RunContext(run_context_customized))
res = get_summary_lineage(summary_dir=SUMMARY_DIR)
assert res.get('hyper_parameters', {}).get('loss_function') \
== 'SoftmaxCrossEntropyWithLogits'
assert res.get('algorithm', {}).get('network') == 'ResNet'
assert res.get('hyper_parameters', {}).get('optimizer') == 'Momentum'


LINEAGE_DATA_MANAGER.start_load_data().join()
res = filter_summary_lineage(LINEAGE_DATA_MANAGER, self._search_condition)
assert res.get('object')[0].get('model_lineage', {}).get('loss_function') \
== 'SoftmaxCrossEntropyWithLogits'
assert res.get('object')[0].get('model_lineage', {}).get('network') == 'ResNet'
assert res.get('object')[0].get('model_lineage', {}).get('optimizer') == 'Momentum'


@pytest.mark.scene_exception(1) @pytest.mark.scene_exception(1)
@pytest.mark.level0 @pytest.mark.level0


+ 6
- 0
tests/st/func/lineagemgr/conftest.py View File

@@ -21,6 +21,9 @@ import tempfile


import pytest import pytest


from mindinsight.datavisual.data_transform.data_manager import DataManager
from mindinsight.lineagemgr.cache_item_updater import LineageCacheItemUpdater

from ....utils import mindspore from ....utils import mindspore
from ....utils.mindspore.dataset.engine.serializer_deserializer import SERIALIZED_PIPELINE from ....utils.mindspore.dataset.engine.serializer_deserializer import SERIALIZED_PIPELINE


@@ -31,6 +34,9 @@ SUMMARY_DIR = os.path.join(BASE_SUMMARY_DIR, 'run1')
SUMMARY_DIR_2 = os.path.join(BASE_SUMMARY_DIR, 'run2') SUMMARY_DIR_2 = os.path.join(BASE_SUMMARY_DIR, 'run2')
SUMMARY_DIR_3 = os.path.join(BASE_SUMMARY_DIR, 'except_run') SUMMARY_DIR_3 = os.path.join(BASE_SUMMARY_DIR, 'except_run')


LINEAGE_DATA_MANAGER = DataManager(BASE_SUMMARY_DIR)
LINEAGE_DATA_MANAGER.register_brief_cache_item_updater(LineageCacheItemUpdater())

COLLECTION_MODULE = 'TestModelLineage' COLLECTION_MODULE = 'TestModelLineage'
API_MODULE = 'TestModelApi' API_MODULE = 'TestModelApi'
DATASET_GRAPH = SERIALIZED_PIPELINE DATASET_GRAPH = SERIALIZED_PIPELINE


+ 72
- 277
tests/st/func/lineagemgr/test_model.py View File

@@ -24,20 +24,18 @@ import os
from unittest import TestCase from unittest import TestCase
import pytest import pytest


from mindinsight.lineagemgr.model import filter_summary_lineage, get_summary_lineage
from mindinsight.lineagemgr.common.exceptions.exceptions import (LineageFileNotFoundError, LineageParamSummaryPathError,
LineageParamTypeError, LineageParamValueError,
LineageSearchConditionParamError)
from mindinsight.lineagemgr.model import filter_summary_lineage
from mindinsight.datavisual.data_transform import data_manager from mindinsight.datavisual.data_transform import data_manager
from mindinsight.lineagemgr.cache_item_updater import LineageCacheItemUpdater from mindinsight.lineagemgr.cache_item_updater import LineageCacheItemUpdater
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageSearchConditionParamError
from mindinsight.lineagemgr.model import get_flattened_lineage from mindinsight.lineagemgr.model import get_flattened_lineage


from .conftest import BASE_SUMMARY_DIR, DATASET_GRAPH, SUMMARY_DIR, SUMMARY_DIR_2
from .conftest import BASE_SUMMARY_DIR, DATASET_GRAPH, SUMMARY_DIR, SUMMARY_DIR_2, LINEAGE_DATA_MANAGER
from ....ut.lineagemgr.querier import event_data from ....ut.lineagemgr.querier import event_data
from ....utils.tools import assert_equal_lineages
from ....utils.tools import assert_equal_lineages, get_relative_path


LINEAGE_INFO_RUN1 = { LINEAGE_INFO_RUN1 = {
'summary_dir': os.path.join(BASE_SUMMARY_DIR, 'run1'),
'summary_dir': './run1',
'metric': { 'metric': {
'accuracy': 0.78 'accuracy': 0.78
}, },
@@ -67,7 +65,7 @@ LINEAGE_INFO_RUN1 = {
'dataset_graph': DATASET_GRAPH 'dataset_graph': DATASET_GRAPH
} }
LINEAGE_FILTRATION_EXCEPT_RUN = { LINEAGE_FILTRATION_EXCEPT_RUN = {
'summary_dir': os.path.join(BASE_SUMMARY_DIR, 'except_run'),
'summary_dir': './except_run',
'model_lineage': { 'model_lineage': {
'loss_function': 'SoftmaxCrossEntropyWithLogits', 'loss_function': 'SoftmaxCrossEntropyWithLogits',
'train_dataset_path': None, 'train_dataset_path': None,
@@ -86,10 +84,11 @@ LINEAGE_FILTRATION_EXCEPT_RUN = {
'metric': {}, 'metric': {},
'dataset_mark': 2 'dataset_mark': 2
}, },
'dataset_graph': DATASET_GRAPH
'dataset_graph': DATASET_GRAPH,
'added_info': {}
} }
LINEAGE_FILTRATION_RUN1 = { LINEAGE_FILTRATION_RUN1 = {
'summary_dir': os.path.join(BASE_SUMMARY_DIR, 'run1'),
'summary_dir': './run1',
'model_lineage': { 'model_lineage': {
'loss_function': 'SoftmaxCrossEntropyWithLogits', 'loss_function': 'SoftmaxCrossEntropyWithLogits',
'train_dataset_path': None, 'train_dataset_path': None,
@@ -114,10 +113,11 @@ LINEAGE_FILTRATION_RUN1 = {
}, },
'dataset_mark': 2 'dataset_mark': 2
}, },
'dataset_graph': DATASET_GRAPH
'dataset_graph': DATASET_GRAPH,
'added_info': {}
} }
LINEAGE_FILTRATION_RUN2 = { LINEAGE_FILTRATION_RUN2 = {
'summary_dir': os.path.join(BASE_SUMMARY_DIR, 'run2'),
'summary_dir': './run2',
'model_lineage': { 'model_lineage': {
'loss_function': "SoftmaxCrossEntropyWithLogits", 'loss_function': "SoftmaxCrossEntropyWithLogits",
'train_dataset_path': None, 'train_dataset_path': None,
@@ -138,7 +138,8 @@ LINEAGE_FILTRATION_RUN2 = {
}, },
'dataset_mark': 3 'dataset_mark': 3
}, },
'dataset_graph': DATASET_GRAPH
'dataset_graph': DATASET_GRAPH,
'added_info': {}
} }




@@ -149,12 +150,12 @@ class TestModelApi(TestCase):
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
"""The initial process.""" """The initial process."""
cls.dir_with_empty_lineage = os.path.join(
cls._dir_with_empty_lineage = os.path.join(
BASE_SUMMARY_DIR, 'dir_with_empty_lineage' BASE_SUMMARY_DIR, 'dir_with_empty_lineage'
) )
os.makedirs(cls.dir_with_empty_lineage)
os.makedirs(cls._dir_with_empty_lineage)
ms_file = os.path.join( ms_file = os.path.join(
cls.dir_with_empty_lineage, 'empty.summary.1581499502.bms_MS'
cls._dir_with_empty_lineage, 'empty.summary.1581499502.bms_MS'
) )
lineage_file = ms_file + '_lineage' lineage_file = ms_file + '_lineage'
with open(ms_file, mode='w'): with open(ms_file, mode='w'):
@@ -162,156 +163,11 @@ class TestModelApi(TestCase):
with open(lineage_file, mode='w'): with open(lineage_file, mode='w'):
pass pass


cls.empty_dir = os.path.join(BASE_SUMMARY_DIR, 'empty_dir')
os.makedirs(cls.empty_dir)

@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_get_summary_lineage(self):
"""Test the interface of get_summary_lineage."""
total_res = get_summary_lineage(None, SUMMARY_DIR)
partial_res1 = get_summary_lineage(None, SUMMARY_DIR, ['hyper_parameters'])
partial_res2 = get_summary_lineage(None, SUMMARY_DIR, ['metric', 'algorithm'])
expect_total_res = LINEAGE_INFO_RUN1
expect_partial_res1 = {
'summary_dir': os.path.join(BASE_SUMMARY_DIR, 'run1'),
'hyper_parameters': {
'optimizer': 'Momentum',
'learning_rate': 0.12,
'loss_function': 'SoftmaxCrossEntropyWithLogits',
'epoch': 14,
'parallel_mode': 'stand_alone',
'device_num': 2,
'batch_size': 32
}
}
expect_partial_res2 = {
'summary_dir': os.path.join(BASE_SUMMARY_DIR, 'run1'),
'metric': {
'accuracy': 0.78
},
'algorithm': {
'network': 'ResNet'
}
}
assert_equal_lineages(expect_total_res, total_res, self.assertDictEqual)
assert_equal_lineages(expect_partial_res1, partial_res1, self.assertDictEqual)
assert_equal_lineages(expect_partial_res2, partial_res2, self.assertDictEqual)

# the lineage summary file is empty
result = get_summary_lineage(None, self.dir_with_empty_lineage)
assert {} == result

# keys is empty list
expect_result = {
'summary_dir': SUMMARY_DIR
}
result = get_summary_lineage(None, SUMMARY_DIR, [])
assert expect_result == result

@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_get_summary_lineage_exception_1(self):
"""Test the interface of get_summary_lineage with exception."""
# summary path does not exist
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path does not exist or is not a dir.',
get_summary_lineage,
None,
'/tmp/fake/dir'
)

# summary path is relative path
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
get_summary_lineage,
None,
'tmp'
)

# the type of input param is invalid
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
get_summary_lineage,
None,
['/root/linage1', '/root/lineage2']
)

# summary path is empty str
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
get_summary_lineage,
None,
'',
keys=None
)
empty_dir = os.path.join(BASE_SUMMARY_DIR, 'empty_dir')
os.makedirs(empty_dir)
cls._empty_train_id = get_relative_path(empty_dir, LINEAGE_DATA_MANAGER.summary_base_dir)


# summary path invalid
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
get_summary_lineage,
None,
'\\',
keys=None
)

@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_get_summary_lineage_exception_2(self):
"""Test the interface of get_summary_lineage with exception."""
# keys is invalid
self.assertRaisesRegex(
LineageParamValueError,
'Keys must be in',
get_summary_lineage,
None,
SUMMARY_DIR,
['metric', 'fake_name']
)

self.assertRaisesRegex(
LineageParamTypeError,
'Element of keys must be str.',
get_summary_lineage,
None,
SUMMARY_DIR,
[1, 2, 3]
)

@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_get_summary_lineage_exception_3(self):
"""Test the interface of get_summary_lineage with exception."""
for keys in [0, 0.1, True, (3, 4), {'a': 'b'}]:
self.assertRaisesRegex(
LineageParamTypeError,
'Keys must be list.',
get_summary_lineage,
None,
SUMMARY_DIR,
keys
)
LINEAGE_DATA_MANAGER.start_load_data().join()


@pytest.mark.level0 @pytest.mark.level0
@pytest.mark.platform_arm_ascend_training @pytest.mark.platform_arm_ascend_training
@@ -334,18 +190,7 @@ class TestModelApi(TestCase):
search_condition = { search_condition = {
'sorted_name': 'summary_dir' 'sorted_name': 'summary_dir'
} }
res = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition)
expect_objects = expect_result.get('object')
for idx, res_object in enumerate(res.get('object')):
expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark')
assert_equal_lineages(expect_result, res, self.assertDictEqual)

expect_result = {
'customized': {},
'object': [],
'count': 0
}
res = filter_summary_lineage(None, self.dir_with_empty_lineage)
res = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition)
expect_objects = expect_result.get('object') expect_objects = expect_result.get('object')
for idx, res_object in enumerate(res.get('object')): for idx, res_object in enumerate(res.get('object')):
expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark') expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark')
@@ -362,8 +207,8 @@ class TestModelApi(TestCase):
search_condition = { search_condition = {
'summary_dir': { 'summary_dir': {
'in': [ 'in': [
SUMMARY_DIR,
SUMMARY_DIR_2
get_relative_path(SUMMARY_DIR, LINEAGE_DATA_MANAGER.summary_base_dir),
get_relative_path(SUMMARY_DIR_2, LINEAGE_DATA_MANAGER.summary_base_dir)
] ]
}, },
'metric/accuracy': { 'metric/accuracy': {
@@ -383,7 +228,7 @@ class TestModelApi(TestCase):
], ],
'count': 2 'count': 2
} }
partial_res = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition)
partial_res = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition)
expect_objects = expect_result.get('object') expect_objects = expect_result.get('object')
for idx, res_object in enumerate(partial_res.get('object')): for idx, res_object in enumerate(partial_res.get('object')):
expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark') expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark')
@@ -421,7 +266,7 @@ class TestModelApi(TestCase):
], ],
'count': 2 'count': 2
} }
partial_res = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition)
partial_res = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition)
expect_objects = expect_result.get('object') expect_objects = expect_result.get('object')
for idx, res_object in enumerate(partial_res.get('object')): for idx, res_object in enumerate(partial_res.get('object')):
expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark') expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark')
@@ -450,7 +295,7 @@ class TestModelApi(TestCase):
], ],
'count': 3 'count': 3
} }
partial_res1 = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition1)
partial_res1 = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition1)
expect_objects = expect_result.get('object') expect_objects = expect_result.get('object')
for idx, res_object in enumerate(partial_res1.get('object')): for idx, res_object in enumerate(partial_res1.get('object')):
expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark') expect_objects[idx]['model_lineage']['dataset_mark'] = res_object['model_lineage'].get('dataset_mark')
@@ -469,9 +314,30 @@ class TestModelApi(TestCase):
'object': [], 'object': [],
'count': 0 'count': 0
} }
partial_res2 = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition2)
partial_res2 = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition2)
assert expect_result == partial_res2 assert expect_result == partial_res2


@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_filter_summary_lineage_condition_4(self):
"""Test the abnormal execution of the filter_summary_lineage interface."""
expect_result = {
'customized': {},
'object': [],
'count': 0
}
search_condition = {
'summary_dir': {
'eq': self._empty_train_id
}
}
res = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition)
assert expect_result == res

@pytest.mark.level0 @pytest.mark.level0
@pytest.mark.platform_arm_ascend_training @pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training @pytest.mark.platform_x86_gpu_training
@@ -480,10 +346,10 @@ class TestModelApi(TestCase):
@pytest.mark.env_single @pytest.mark.env_single
def test_filter_summary_lineage_with_lineage_type(self): def test_filter_summary_lineage_with_lineage_type(self):
"""Test the interface of filter_summary_lineage with lineage_type.""" """Test the interface of filter_summary_lineage with lineage_type."""
summary_dir = os.path.join(BASE_SUMMARY_DIR, 'except_run')
train_id = './except_run'
search_condition = { search_condition = {
'summary_dir': { 'summary_dir': {
'in': [summary_dir]
'in': [train_id]
}, },
'lineage_type': { 'lineage_type': {
'eq': 'dataset' 'eq': 'dataset'
@@ -493,50 +359,16 @@ class TestModelApi(TestCase):
'customized': {}, 'customized': {},
'object': [ 'object': [
{ {
'summary_dir': summary_dir,
'dataset_graph': DATASET_GRAPH
'summary_dir': train_id,
'dataset_graph': DATASET_GRAPH,
'added_info': {}
} }
], ],
'count': 1 'count': 1
} }
res = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition)
res = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition)
assert expect_result == res assert expect_result == res


@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_filter_summary_lineage_exception_1(self):
"""Test the abnormal execution of the filter_summary_lineage interface."""
# summary base dir is relative path
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
filter_summary_lineage,
None,
'relative_path'
)

# summary base dir does not exist
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path does not exist or is not a dir.',
filter_summary_lineage,
None,
'/path/does/not/exist'
)

# no summary log file under summary_base_dir
self.assertRaisesRegex(
LineageFileNotFoundError,
'There is no summary log file under summary_base_dir.',
filter_summary_lineage,
None,
self.empty_dir
)

@pytest.mark.level0 @pytest.mark.level0
@pytest.mark.platform_arm_ascend_training @pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training @pytest.mark.platform_x86_gpu_training
@@ -553,8 +385,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The search_condition element summary_dir should be dict.', 'The search_condition element summary_dir should be dict.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -566,8 +397,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The sorted_name have to exist when sorted_type exists.', 'The sorted_name have to exist when sorted_type exists.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -577,8 +407,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'Invalid search_condition type, it should be dict.', 'Invalid search_condition type, it should be dict.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -590,8 +419,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The limit must be int.', 'The limit must be int.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -611,8 +439,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The offset must be int.', 'The offset must be int.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -626,8 +453,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The search attribute not supported.', 'The search attribute not supported.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -648,8 +474,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The sorted_type must be ascending or descending', 'The sorted_type must be ascending or descending',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -663,8 +488,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The compare condition should be in', 'The compare condition should be in',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -678,8 +502,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The parameter metric/accuracy is invalid.', 'The parameter metric/accuracy is invalid.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -690,31 +513,6 @@ class TestModelApi(TestCase):
@pytest.mark.platform_x86_cpu @pytest.mark.platform_x86_cpu
@pytest.mark.env_single @pytest.mark.env_single
def test_filter_summary_lineage_exception_5(self): def test_filter_summary_lineage_exception_5(self):
"""Test the abnormal execution of the filter_summary_lineage interface."""
# the summary dir is invalid in search condition
search_condition = {
'summary_dir': {
'in': [
'xxx'
]
}
}
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
search_condition
)

@pytest.mark.level0
@pytest.mark.platform_arm_ascend_training
@pytest.mark.platform_x86_gpu_training
@pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu
@pytest.mark.env_single
def test_filter_summary_lineage_exception_6(self):
"""Test the abnormal execution of the filter_summary_lineage interface.""" """Test the abnormal execution of the filter_summary_lineage interface."""
# gt > lt # gt > lt
search_condition1 = { search_condition1 = {
@@ -728,7 +526,7 @@ class TestModelApi(TestCase):
'object': [], 'object': [],
'count': 0 'count': 0
} }
partial_res1 = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition1)
partial_res1 = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition1)
assert expect_result == partial_res1 assert expect_result == partial_res1


# the (offset + 1) * limit > count # the (offset + 1) * limit > count
@@ -744,7 +542,7 @@ class TestModelApi(TestCase):
'object': [], 'object': [],
'count': 2 'count': 2
} }
partial_res2 = filter_summary_lineage(None, BASE_SUMMARY_DIR, search_condition2)
partial_res2 = filter_summary_lineage(LINEAGE_DATA_MANAGER, search_condition2)
assert expect_result == partial_res2 assert expect_result == partial_res2


@pytest.mark.level0 @pytest.mark.level0
@@ -753,7 +551,7 @@ class TestModelApi(TestCase):
@pytest.mark.platform_x86_ascend_training @pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu @pytest.mark.platform_x86_cpu
@pytest.mark.env_single @pytest.mark.env_single
def test_filter_summary_lineage_exception_7(self):
def test_filter_summary_lineage_exception_6(self):
"""Test the abnormal execution of the filter_summary_lineage interface.""" """Test the abnormal execution of the filter_summary_lineage interface."""
condition_keys = ["summary_dir", "lineage_type", "loss_function", "optimizer", "network", "dataset_mark"] condition_keys = ["summary_dir", "lineage_type", "loss_function", "optimizer", "network", "dataset_mark"]
for condition_key in condition_keys: for condition_key in condition_keys:
@@ -767,8 +565,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
f'The parameter {condition_key} is invalid. Its operation should be `eq`, `in` or `not_in`.', f'The parameter {condition_key} is invalid. Its operation should be `eq`, `in` or `not_in`.',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -778,7 +575,7 @@ class TestModelApi(TestCase):
@pytest.mark.platform_x86_ascend_training @pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu @pytest.mark.platform_x86_cpu
@pytest.mark.env_single @pytest.mark.env_single
def test_filter_summary_lineage_exception_8(self):
def test_filter_summary_lineage_exception_7(self):
"""Test the abnormal execution of the filter_summary_lineage interface.""" """Test the abnormal execution of the filter_summary_lineage interface."""
invalid_lineage_types = ['xxx', None] invalid_lineage_types = ['xxx', None]
for lineage_type in invalid_lineage_types: for lineage_type in invalid_lineage_types:
@@ -791,8 +588,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
"The parameter lineage_type is invalid. It should be 'dataset' or 'model'.", "The parameter lineage_type is invalid. It should be 'dataset' or 'model'.",
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )


@@ -802,7 +598,7 @@ class TestModelApi(TestCase):
@pytest.mark.platform_x86_ascend_training @pytest.mark.platform_x86_ascend_training
@pytest.mark.platform_x86_cpu @pytest.mark.platform_x86_cpu
@pytest.mark.env_single @pytest.mark.env_single
def test_filter_summary_lineage_exception_9(self):
def test_filter_summary_lineage_exception_8(self):
"""Test the abnormal execution of the filter_summary_lineage interface.""" """Test the abnormal execution of the filter_summary_lineage interface."""
invalid_sorted_names = ['xxx', 'metric_', 1] invalid_sorted_names = ['xxx', 'metric_', 1]
for sorted_name in invalid_sorted_names: for sorted_name in invalid_sorted_names:
@@ -813,8 +609,7 @@ class TestModelApi(TestCase):
LineageSearchConditionParamError, LineageSearchConditionParamError,
'The sorted_name must be in', 'The sorted_name must be in',
filter_summary_lineage, filter_summary_lineage,
None,
BASE_SUMMARY_DIR,
LINEAGE_DATA_MANAGER,
search_condition search_condition
) )




+ 10
- 9
tests/ut/backend/lineagemgr/test_lineage_api.py View File

@@ -14,7 +14,6 @@
# ============================================================================ # ============================================================================
"""Test the module of lineage_api.""" """Test the module of lineage_api."""
import json import json
import os
from unittest import TestCase, mock from unittest import TestCase, mock


from flask import Response from flask import Response
@@ -73,21 +72,21 @@ class TestSearchModel(TestCase):
@mock.patch('mindinsight.backend.lineagemgr.lineage_api.filter_summary_lineage') @mock.patch('mindinsight.backend.lineagemgr.lineage_api.filter_summary_lineage')
def test_search_model_success(self, *args): def test_search_model_success(self, *args):
"""Test the success of model_success.""" """Test the success of model_success."""
base_dir = '/path/to/test_lineage_summary_dir_base'
args[0].return_value = { args[0].return_value = {
'object': [ 'object': [
{ {
'summary_dir': base_dir,
'model_lineage': LINEAGE_FILTRATION_BASE
'summary_dir': './',
'model_lineage': LINEAGE_FILTRATION_BASE,
'added_info': {}
}, },
{ {
'summary_dir': os.path.join(base_dir, 'run1'),
'model_lineage': LINEAGE_FILTRATION_RUN1
'summary_dir': './run1',
'model_lineage': LINEAGE_FILTRATION_RUN1,
'added_info': {}
} }
], ],
'count': 2 'count': 2
} }
args[1].SUMMARY_BASE_DIR = base_dir


body_data = { body_data = {
'limit': 10, 'limit': 10,
@@ -101,11 +100,13 @@ class TestSearchModel(TestCase):
'object': [ 'object': [
{ {
'summary_dir': './', 'summary_dir': './',
'model_lineage': LINEAGE_FILTRATION_BASE
'model_lineage': LINEAGE_FILTRATION_BASE,
'added_info': {}
}, },
{ {
'summary_dir': './run1', 'summary_dir': './run1',
'model_lineage': LINEAGE_FILTRATION_RUN1
'model_lineage': LINEAGE_FILTRATION_RUN1,
'added_info': {}
} }
], ],
'count': 2 'count': 2


+ 24
- 136
tests/ut/lineagemgr/querier/test_querier.py View File

@@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
# ============================================================================ # ============================================================================
"""Test the querier module.""" """Test the querier module."""
import os
import time import time


from unittest import TestCase, mock from unittest import TestCase, mock
@@ -22,7 +23,7 @@ from google.protobuf.json_format import ParseDict


import mindinsight.datavisual.proto_files.mindinsight_lineage_pb2 as summary_pb2 import mindinsight.datavisual.proto_files.mindinsight_lineage_pb2 as summary_pb2
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamTypeError, LineageQuerierParamException from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamTypeError, LineageQuerierParamException
from mindinsight.lineagemgr.lineage_parser import LineageOrganizer
from mindinsight.lineagemgr.lineage_parser import LineageParser
from mindinsight.lineagemgr.querier.querier import Querier from mindinsight.lineagemgr.querier.querier import Querier
from mindinsight.lineagemgr.summary.lineage_summary_analyzer import LineageInfo from mindinsight.lineagemgr.summary.lineage_summary_analyzer import LineageInfo


@@ -104,6 +105,7 @@ def create_filtration_result(summary_dir, train_event_dict,
"user_defined": {} "user_defined": {}
}, },
"dataset_graph": dataset_dict, "dataset_graph": dataset_dict,
'added_info': {}
} }
return filtration_result return filtration_result


@@ -162,42 +164,42 @@ LINEAGE_INFO_1 = {
'dataset_graph': event_data.DATASET_DICT_0 'dataset_graph': event_data.DATASET_DICT_0
} }
LINEAGE_FILTRATION_0 = create_filtration_result( LINEAGE_FILTRATION_0 = create_filtration_result(
'/path/to/summary0',
'./summary0',
event_data.EVENT_TRAIN_DICT_0, event_data.EVENT_TRAIN_DICT_0,
event_data.EVENT_EVAL_DICT_0, event_data.EVENT_EVAL_DICT_0,
event_data.METRIC_0, event_data.METRIC_0,
event_data.DATASET_DICT_0 event_data.DATASET_DICT_0
) )
LINEAGE_FILTRATION_1 = create_filtration_result( LINEAGE_FILTRATION_1 = create_filtration_result(
'/path/to/summary1',
'./summary1',
event_data.EVENT_TRAIN_DICT_1, event_data.EVENT_TRAIN_DICT_1,
event_data.EVENT_EVAL_DICT_1, event_data.EVENT_EVAL_DICT_1,
event_data.METRIC_1, event_data.METRIC_1,
event_data.DATASET_DICT_0 event_data.DATASET_DICT_0
) )
LINEAGE_FILTRATION_2 = create_filtration_result( LINEAGE_FILTRATION_2 = create_filtration_result(
'/path/to/summary2',
'./summary2',
event_data.EVENT_TRAIN_DICT_2, event_data.EVENT_TRAIN_DICT_2,
event_data.EVENT_EVAL_DICT_2, event_data.EVENT_EVAL_DICT_2,
event_data.METRIC_2, event_data.METRIC_2,
event_data.DATASET_DICT_0 event_data.DATASET_DICT_0
) )
LINEAGE_FILTRATION_3 = create_filtration_result( LINEAGE_FILTRATION_3 = create_filtration_result(
'/path/to/summary3',
'./summary3',
event_data.EVENT_TRAIN_DICT_3, event_data.EVENT_TRAIN_DICT_3,
event_data.EVENT_EVAL_DICT_3, event_data.EVENT_EVAL_DICT_3,
event_data.METRIC_3, event_data.METRIC_3,
event_data.DATASET_DICT_0 event_data.DATASET_DICT_0
) )
LINEAGE_FILTRATION_4 = create_filtration_result( LINEAGE_FILTRATION_4 = create_filtration_result(
'/path/to/summary4',
'./summary4',
event_data.EVENT_TRAIN_DICT_4, event_data.EVENT_TRAIN_DICT_4,
event_data.EVENT_EVAL_DICT_4, event_data.EVENT_EVAL_DICT_4,
event_data.METRIC_4, event_data.METRIC_4,
event_data.DATASET_DICT_0 event_data.DATASET_DICT_0
) )
LINEAGE_FILTRATION_5 = { LINEAGE_FILTRATION_5 = {
"summary_dir": '/path/to/summary5',
"summary_dir": './summary5',
"model_lineage": { "model_lineage": {
"loss_function": "loss_function":
event_data.EVENT_TRAIN_DICT_5['train_lineage']['hyper_parameters']['loss_function'], event_data.EVENT_TRAIN_DICT_5['train_lineage']['hyper_parameters']['loss_function'],
@@ -219,10 +221,11 @@ LINEAGE_FILTRATION_5 = {
"dataset_mark": '2', "dataset_mark": '2',
"user_defined": {} "user_defined": {}
}, },
"dataset_graph": event_data.DATASET_DICT_0
"dataset_graph": event_data.DATASET_DICT_0,
"added_info": {}
} }
LINEAGE_FILTRATION_6 = { LINEAGE_FILTRATION_6 = {
"summary_dir": '/path/to/summary6',
"summary_dir": './summary6',
"model_lineage": { "model_lineage": {
"loss_function": None, "loss_function": None,
"train_dataset_path": None, "train_dataset_path": None,
@@ -243,15 +246,16 @@ LINEAGE_FILTRATION_6 = {
"dataset_mark": '2', "dataset_mark": '2',
"user_defined": {} "user_defined": {}
}, },
"dataset_graph": event_data.DATASET_DICT_0
"dataset_graph": event_data.DATASET_DICT_0,
"added_info": {}
} }




class TestQuerier(TestCase): class TestQuerier(TestCase):
"""Test the class of `Querier`.""" """Test the class of `Querier`."""
_MOCK_DATA_MANAGER = MagicMock()


@mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryPathParser.get_lineage_summaries') @mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryPathParser.get_lineage_summaries')
@mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryWatcher.list_summary_directories')
@mock.patch('mindinsight.lineagemgr.lineage_parser.LineageSummaryAnalyzer.get_user_defined_info') @mock.patch('mindinsight.lineagemgr.lineage_parser.LineageSummaryAnalyzer.get_user_defined_info')
@mock.patch('mindinsight.lineagemgr.lineage_parser.LineageSummaryAnalyzer.get_summary_infos') @mock.patch('mindinsight.lineagemgr.lineage_parser.LineageSummaryAnalyzer.get_summary_infos')
@mock.patch('mindinsight.lineagemgr.lineage_parser.FileHandler') @mock.patch('mindinsight.lineagemgr.lineage_parser.FileHandler')
@@ -263,139 +267,23 @@ class TestQuerier(TestCase):
event_data.EVENT_DATASET_DICT_0 event_data.EVENT_DATASET_DICT_0
) )
args[1].return_value = [] args[1].return_value = []
args[3].return_value = ['path']
args[2].return_value = ['path']
mock_file_handler = MagicMock() mock_file_handler = MagicMock()
mock_file_handler.size = 1 mock_file_handler.size = 1


args[2].return_value = [{'relative_path': './', 'update_time': 1}]
single_summary_path = '/path/to/summary0'
lineage_objects = LineageOrganizer(summary_base_dir=single_summary_path).super_lineage_objs
self.single_querier = Querier(lineage_objects)
summary_dir = '/path/test/'


lineage_infos = get_lineage_infos() lineage_infos = get_lineage_infos()
args[0].side_effect = lineage_infos args[0].side_effect = lineage_infos
summary_base_dir = '/path/to'
relative_dirs = []
lineage_objects = {}
for i in range(7): for i in range(7):
relative_dirs.append(dict(relative_path=f'./summary{i}', update_time=time.time() - i))
args[2].return_value = relative_dirs
lineage_objects = LineageOrganizer(summary_base_dir=summary_base_dir).super_lineage_objs
self.multi_querier = Querier(lineage_objects)

def test_get_summary_lineage_success_1(self):
"""Test the success of get_summary_lineage."""
expected_result = [LINEAGE_INFO_0]
result = self.single_querier.get_summary_lineage()
assert_equal_lineages(expected_result, result, self.assertListEqual)

def test_get_summary_lineage_success_2(self):
"""Test the success of get_summary_lineage."""
expected_result = [LINEAGE_INFO_0]
result = self.single_querier.get_summary_lineage()
assert_equal_lineages(expected_result, result, self.assertListEqual)

def test_get_summary_lineage_success_3(self):
"""Test the success of get_summary_lineage."""
expected_result = [
{
'summary_dir': '/path/to/summary0',
'model': event_data.EVENT_TRAIN_DICT_0['train_lineage']['model'],
'algorithm': event_data.EVENT_TRAIN_DICT_0['train_lineage']['algorithm']
}
]
result = self.single_querier.get_summary_lineage(
filter_keys=['model', 'algorithm']
)
assert_equal_lineages(expected_result, result, self.assertListEqual)

def test_get_summary_lineage_success_4(self):
"""Test the success of get_summary_lineage."""
expected_result = [
LINEAGE_INFO_0,
LINEAGE_INFO_1,
{
'summary_dir': '/path/to/summary2',
**event_data.EVENT_TRAIN_DICT_2['train_lineage'],
'metric': event_data.METRIC_2,
'valid_dataset': event_data.EVENT_EVAL_DICT_2['evaluation_lineage']['valid_dataset'],
'dataset_graph': event_data.DATASET_DICT_0
},
{
'summary_dir': '/path/to/summary3',
**event_data.EVENT_TRAIN_DICT_3['train_lineage'],
'metric': event_data.METRIC_3,
'valid_dataset': event_data.EVENT_EVAL_DICT_3['evaluation_lineage']['valid_dataset'],
'dataset_graph': event_data.DATASET_DICT_0
},
{
'summary_dir': '/path/to/summary4',
**event_data.EVENT_TRAIN_DICT_4['train_lineage'],
'metric': event_data.METRIC_4,
'valid_dataset': event_data.EVENT_EVAL_DICT_4['evaluation_lineage']['valid_dataset'],
'dataset_graph': event_data.DATASET_DICT_0
},
{
'summary_dir': '/path/to/summary5',
**event_data.EVENT_TRAIN_DICT_5['train_lineage'],
'metric': {},
'valid_dataset': {},
'dataset_graph': event_data.DATASET_DICT_0
},
{
'summary_dir': '/path/to/summary6',
'hyper_parameters': {},
'algorithm': {},
'model': {},
'train_dataset': {},
'metric': event_data.METRIC_5,
'valid_dataset': event_data.EVENT_EVAL_DICT_5['evaluation_lineage']['valid_dataset'],
'dataset_graph': event_data.DATASET_DICT_0
}
]
result = self.multi_querier.get_summary_lineage()
assert_equal_lineages(expected_result, result, self.assertListEqual)

def test_get_summary_lineage_success_5(self):
"""Test the success of get_summary_lineage."""
expected_result = [LINEAGE_INFO_1]
result = self.multi_querier.get_summary_lineage(
summary_dir='/path/to/summary1'
)
assert_equal_lineages(expected_result, result, self.assertListEqual)

def test_get_summary_lineage_success_6(self):
"""Test the success of get_summary_lineage."""
expected_result = [
{
'summary_dir': '/path/to/summary0',
'hyper_parameters': event_data.EVENT_TRAIN_DICT_0['train_lineage']['hyper_parameters'],
'train_dataset': event_data.EVENT_TRAIN_DICT_0['train_lineage']['train_dataset'],
'metric': event_data.METRIC_0,
'valid_dataset': event_data.EVENT_EVAL_DICT_0['evaluation_lineage']['valid_dataset']
}
]
filter_keys = [
'metric', 'hyper_parameters', 'train_dataset', 'valid_dataset'
]
result = self.multi_querier.get_summary_lineage(
summary_dir='/path/to/summary0', filter_keys=filter_keys
)
assert_equal_lineages(expected_result, result, self.assertListEqual)

def test_get_summary_lineage_fail(self):
"""Test the function of get_summary_lineage with exception."""
filter_keys = ['xxx']
self.assertRaises(
LineageQuerierParamException,
self.multi_querier.get_summary_lineage,
filter_keys=filter_keys
)
train_id = f'./summary{i}'
summary_dir = os.path.join(summary_dir, train_id)
update_time = time.time() - i
lineage_parser = LineageParser(train_id, summary_dir, update_time)
lineage_objects.update({train_id: lineage_parser.super_lineage_obj})


self.assertRaises(
LineageQuerierParamException,
self.multi_querier.get_summary_lineage,
summary_dir='xxx'
)
self.multi_querier = Querier(lineage_objects)


def test_filter_summary_lineage_success_1(self): def test_filter_summary_lineage_success_1(self):
"""Test the success of filter_summary_lineage.""" """Test the success of filter_summary_lineage."""


+ 1
- 0
tests/ut/lineagemgr/querier/test_query_model.py View File

@@ -133,6 +133,7 @@ class TestLineageObj(TestCase):
) )
expected_result['model_lineage']['dataset_mark'] = None expected_result['model_lineage']['dataset_mark'] = None
expected_result.pop('dataset_graph') expected_result.pop('dataset_graph')
expected_result.pop('added_info')
result = self.lineage_obj.to_model_lineage_dict() result = self.lineage_obj.to_model_lineage_dict()
assert_equal_lineages(expected_result, result, self.assertDictEqual) assert_equal_lineages(expected_result, result, self.assertDictEqual)




+ 21
- 187
tests/ut/lineagemgr/test_model.py View File

@@ -16,224 +16,60 @@
from unittest import TestCase, mock from unittest import TestCase, mock
from unittest.mock import MagicMock from unittest.mock import MagicMock


from mindinsight.lineagemgr.model import get_summary_lineage, filter_summary_lineage, \
_convert_relative_path_to_abspath, get_flattened_lineage
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamSummaryPathError, \
LineageFileNotFoundError, LineageSummaryParseException, LineageQuerierParamException, \
LineageQuerySummaryDataError, LineageSearchConditionParamError, LineageParamTypeError, \
LineageParamValueError
from mindinsight.lineagemgr.model import filter_summary_lineage, get_flattened_lineage
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageSummaryParseException, \
LineageQuerierParamException, LineageQuerySummaryDataError, LineageSearchConditionParamError, LineageParamTypeError
from mindinsight.lineagemgr.common.path_parser import SummaryPathParser from mindinsight.lineagemgr.common.path_parser import SummaryPathParser
from ...st.func.lineagemgr.test_model import LINEAGE_FILTRATION_RUN1, LINEAGE_FILTRATION_RUN2 from ...st.func.lineagemgr.test_model import LINEAGE_FILTRATION_RUN1, LINEAGE_FILTRATION_RUN2




class TestModel(TestCase):
"""Test the function of get_summary_lineage and filter_summary_lineage."""

@mock.patch('mindinsight.lineagemgr.model.Querier')
@mock.patch('mindinsight.lineagemgr.model.LineageParser')
@mock.patch('os.path.isdir')
def test_get_summary_lineage_success(self, isdir_mock, parser_mock, qurier_mock):
"""Test the function of get_summary_lineage."""
isdir_mock.return_value = True
parser_mock.return_value = MagicMock()

mock_querier = MagicMock()
qurier_mock.return_value = mock_querier
mock_querier.get_summary_lineage.return_value = [{'algorithm': {'network': 'ResNet'}}]
summary_dir = '/path/to/summary_dir'
result = get_summary_lineage(None, summary_dir, keys=['algorithm'])
self.assertEqual(result, {'algorithm': {'network': 'ResNet'}})

def test_get_summary_lineage_failed(self):
"""Test get_summary_lineage failed."""
invalid_path = '../fake_dir'
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
get_summary_lineage,
None,
invalid_path
)

@mock.patch('mindinsight.lineagemgr.common.utils.validate_path')
@mock.patch.object(SummaryPathParser, 'get_lineage_summaries')
def test_get_summary_lineage_failed2(self, mock_summary, mock_valid):
"""Test get_summary_lineage failed."""
mock_summary.return_value = []
mock_valid.return_value = '/path/to/summary/dir'
self.assertRaisesRegex(
LineageFileNotFoundError,
'no summary log file under summary_dir',
get_summary_lineage,
None,
'/path/to/summary_dir'
)

@mock.patch('mindinsight.lineagemgr.lineage_parser.FileHandler')
@mock.patch('mindinsight.lineagemgr.lineage_parser.LineageParser._parse_summary_log')
@mock.patch('mindinsight.lineagemgr.common.utils.validate_path')
@mock.patch.object(SummaryPathParser, 'get_lineage_summaries')
def test_get_summary_lineage_failed3(self,
mock_summary,
mock_valid,
mock_parser,
mock_file_handler):
"""Test get_summary_lineage failed."""
mock_summary.return_value = ['/path/to/summary/file']
mock_valid.return_value = '/path/to/summary_dir'
mock_parser.return_value = None
mock_file_handler = MagicMock()
mock_file_handler.size = 1
result = get_summary_lineage(None, '/path/to/summary_dir')
assert {} == result

@mock.patch('mindinsight.lineagemgr.model.validate_path')
def test_convert_relative_path_to_abspath(self, validate_path_mock):
"""Test the function of converting realtive path to abspath."""
validate_path_mock.return_value = '/path/to/summary_base_dir/summary_dir'
summary_base_dir = '/path/to/summary_base_dir'
search_condition = {
'summary_dir': {
'in': ['/path/to/summary_base_dir']
}
}
result = _convert_relative_path_to_abspath(summary_base_dir,
search_condition)
self.assertDictEqual(
result, {'summary_dir': {'in': ['/path/to/summary_base_dir/summary_dir']}})

search_condition = {
'summary_dir': {
'in': ['./summary_dir']
}
}
result = _convert_relative_path_to_abspath(summary_base_dir, search_condition)
self.assertDictEqual(
result, {'summary_dir': {'in': ['/path/to/summary_base_dir/summary_dir']}}
)

search_condition = {
'summary_dir': {
'eq': '/summary_dir'
}
}
result = _convert_relative_path_to_abspath(summary_base_dir, search_condition)
self.assertDictEqual(
result, {'summary_dir': {'eq': '/path/to/summary_base_dir/summary_dir'}})

search_condition = {
'summary_dir': None
}
result = _convert_relative_path_to_abspath(summary_base_dir, search_condition)
self.assertDictEqual(
result, search_condition
)


class TestFilterAPI(TestCase): class TestFilterAPI(TestCase):
"""Test the function of filter_summary_lineage.""" """Test the function of filter_summary_lineage."""
@mock.patch('mindinsight.lineagemgr.model.LineageOrganizer')
_MOCK_DATA_MANAGER = MagicMock()

@mock.patch('mindinsight.lineagemgr.model.Querier') @mock.patch('mindinsight.lineagemgr.model.Querier')
@mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryPathParser.get_lineage_summaries') @mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryPathParser.get_lineage_summaries')
@mock.patch('mindinsight.lineagemgr.model._convert_relative_path_to_abspath')
@mock.patch('mindinsight.lineagemgr.model.normalize_summary_dir')
def test_filter_summary_lineage(self, validate_path_mock, convert_path_mock,
latest_summary_mock, qurier_mock, organizer_mock):
def test_filter_summary_lineage(self, latest_summary_mock, qurier_mock):
"""Test the function of filter_summary_lineage.""" """Test the function of filter_summary_lineage."""
convert_path_mock.return_value = {
'summary_dir': {
'in': ['/path/to/summary_base_dir']
},
'loss': {
'gt': 2.0
}
}
organizer_mock = MagicMock()
organizer_mock.super_lineage_objs = None
validate_path_mock.return_value = True
latest_summary_mock.return_value = ['/path/to/summary_base_dir/summary_dir'] latest_summary_mock.return_value = ['/path/to/summary_base_dir/summary_dir']
mock_querier = MagicMock() mock_querier = MagicMock()
qurier_mock.return_value = mock_querier qurier_mock.return_value = mock_querier
mock_querier.filter_summary_lineage.return_value = [{'loss': 3.0}] mock_querier.filter_summary_lineage.return_value = [{'loss': 3.0}]


summary_base_dir = '/path/to/summary_base_dir'
result = filter_summary_lineage(None, summary_base_dir)
result = filter_summary_lineage(self._MOCK_DATA_MANAGER)
self.assertEqual(result, [{'loss': 3.0}]) self.assertEqual(result, [{'loss': 3.0}])


def test_invalid_path(self):
"""Test filter_summary_lineage with invalid path."""
invalid_path = '../fake_dir'
self.assertRaisesRegex(
LineageParamSummaryPathError,
'The summary path is invalid.',
filter_summary_lineage,
None,
invalid_path
)

@mock.patch('mindinsight.lineagemgr.model.validate_condition') @mock.patch('mindinsight.lineagemgr.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.model.normalize_summary_dir')
def test_invalid_search_condition(self, mock_path, mock_valid):
def test_invalid_search_condition(self, mock_valid):
"""Test filter_summary_lineage with invalid invalid param.""" """Test filter_summary_lineage with invalid invalid param."""
mock_path.return_value = None
mock_valid.side_effect = LineageParamTypeError( mock_valid.side_effect = LineageParamTypeError(
'Invalid search_condition type.') 'Invalid search_condition type.')
self.assertRaisesRegex( self.assertRaisesRegex(
LineageSearchConditionParamError, LineageSearchConditionParamError,
'Invalid search_condition type.', 'Invalid search_condition type.',
filter_summary_lineage, filter_summary_lineage,
None,
'/path/to/summary/dir',
self._MOCK_DATA_MANAGER,
'invalid_condition' 'invalid_condition'
) )


@mock.patch('mindinsight.lineagemgr.model.validate_search_model_condition')
@mock.patch('mindinsight.lineagemgr.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.common.utils.validate_path')
@mock.patch('mindinsight.lineagemgr.model._convert_relative_path_to_abspath')
def test_failed_to_convert_path(self, mock_convert, *args):
def test_failed_to_get_summary_files(self):
"""Test filter_summary_lineage with invalid invalid param.""" """Test filter_summary_lineage with invalid invalid param."""
mock_convert.side_effect = LineageParamValueError('invalid path')
args[0].return_value = None
self.assertRaisesRegex(
LineageParamSummaryPathError,
'invalid path',
filter_summary_lineage,
None,
'/path/to/summary/dir',
{}
)

@mock.patch('mindinsight.lineagemgr.model._convert_relative_path_to_abspath')
@mock.patch('mindinsight.lineagemgr.model.validate_search_model_condition')
@mock.patch('mindinsight.lineagemgr.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.model.normalize_summary_dir')
@mock.patch.object(SummaryPathParser, 'get_lineage_summaries')
def test_failed_to_get_summary_filesh(self, mock_parse, *args):
"""Test filter_summary_lineage with invalid invalid param."""
path = '/path/to/summary/dir'
mock_parse.return_value = []
args[0].return_value = path
self.assertRaisesRegex(
LineageFileNotFoundError,
'There is no summary log file under summary_base_dir.',
filter_summary_lineage,
None,
path
)
default_result = {
'customized': {},
'object': [],
'count': 0
}
assert default_result == filter_summary_lineage(self._MOCK_DATA_MANAGER)


@mock.patch('mindinsight.lineagemgr.model._convert_relative_path_to_abspath')
@mock.patch('mindinsight.lineagemgr.model.validate_search_model_condition') @mock.patch('mindinsight.lineagemgr.model.validate_search_model_condition')
@mock.patch('mindinsight.lineagemgr.model.validate_condition') @mock.patch('mindinsight.lineagemgr.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.model.normalize_summary_dir')
@mock.patch.object(SummaryPathParser, 'get_lineage_summaries') @mock.patch.object(SummaryPathParser, 'get_lineage_summaries')
@mock.patch('mindinsight.lineagemgr.model.Querier') @mock.patch('mindinsight.lineagemgr.model.Querier')
def test_failed_to_querier(self, mock_query, mock_parse, *args):
def test_failed_to_querier(self, mock_query, *args):
"""Test filter_summary_lineage with invalid invalid param.""" """Test filter_summary_lineage with invalid invalid param."""
mock_query.side_effect = LineageSummaryParseException() mock_query.side_effect = LineageSummaryParseException()
mock_parse.return_value = ['/path/to/summary/file']
args[0].return_value = None args[0].return_value = None
res = filter_summary_lineage(None, '/path/to/summary')
res = filter_summary_lineage(self._MOCK_DATA_MANAGER)
assert res == {'object': [], 'count': 0} assert res == {'object': [], 'count': 0}


mock_query.side_effect = LineageQuerierParamException(['keys'], 'key') mock_query.side_effect = LineageQuerierParamException(['keys'], 'key')
@@ -241,8 +77,7 @@ class TestFilterAPI(TestCase):
LineageQuerySummaryDataError, LineageQuerySummaryDataError,
'Filter summary lineage failed.', 'Filter summary lineage failed.',
filter_summary_lineage, filter_summary_lineage,
None,
'/path/to/summary/dir'
self._MOCK_DATA_MANAGER
) )


@mock.patch('mindinsight.lineagemgr.model.filter_summary_lineage') @mock.patch('mindinsight.lineagemgr.model.filter_summary_lineage')
@@ -251,8 +86,7 @@ class TestFilterAPI(TestCase):
mock_data = { mock_data = {
'object': [LINEAGE_FILTRATION_RUN1, LINEAGE_FILTRATION_RUN2] 'object': [LINEAGE_FILTRATION_RUN1, LINEAGE_FILTRATION_RUN2]
} }
mock_datamanager = MagicMock()
mock_datamanager.summary_base_dir = '/tmp/'
mock_data_manager = MagicMock()
mock_filter_summary_lineage.return_value = mock_data mock_filter_summary_lineage.return_value = mock_data
result = get_flattened_lineage(mock_datamanager, None)
result = get_flattened_lineage(mock_data_manager)
assert result.get('[U]info') == ['info1', None] assert result.get('[U]info') == ['info1', None]

+ 25
- 1
tests/utils/tools.py View File

@@ -19,12 +19,14 @@ import io
import os import os
import shutil import shutil
import json import json
from pathlib import Path


from urllib.parse import urlencode from urllib.parse import urlencode

import numpy as np import numpy as np
from PIL import Image from PIL import Image


from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamValueError



def get_url(url, params): def get_url(url, params):
""" """
@@ -138,3 +140,25 @@ def assert_equal_lineages(lineages1, lineages2, assert_func, decimal_num=2):
else: else:
deal_float_for_dict(lineages1, lineages2, decimal_num) deal_float_for_dict(lineages1, lineages2, decimal_num)
assert_func(lineages1, lineages2) assert_func(lineages1, lineages2)


def get_relative_path(path, base_path):
"""
Get relative path based on base_path.

Args:
path (str): absolute path.
base_path: absolute base path.

Returns:
str, relative path based on base_path.

"""
try:
r_path = str(Path(path).relative_to(Path(base_path)))
except ValueError:
raise LineageParamValueError("The path %r does not start with %r." % (path, base_path))

if r_path == ".":
r_path = ""
return os.path.join("./", r_path)

Loading…
Cancel
Save