def get_selector(query):
        """Returns a callable object that returns true when `query` matches dictionary.

        Args:
            query: An object that specifies the query. It can be one of the following:
                - A string:
                    - Load JSON object from this string if possible ELSE
                    - Treat it as a file name and load JSON objects from there.
                  The parsed/loaded object must be either dict or list.
                - A list of dict. Wrap it into a function that calls match method of a DictUtils class.
                - Callable object. Return as is.

        Returns:
            Callable object.
        """
        # If it's a string, assume it's a JSON parsable string and if not - assume it's a JSON file name.
        if isinstance(query, Six.string_types):
            try:
                query = json.loads(query)
            except ValueError:
                query = IOUtils.read_json(query)

        selector = query
        # If it's a list of dict, wrap it into a function.
        if isinstance(query, (list, dict)):
            def dict_matcher(bench): return DictUtils.match(bench, query, policy='strict')
            selector = dict_matcher
        # Here, it must be a callable object.
        if not callable(selector):
            raise ValueError("Invalid type of object that holds parameters (%s)" % type(selector))
        return selector
    def load(inputs, **kwargs):
        """Load benchmark data (parsed from log files) from a JSON file.

        A file name is a JSON file that contains object with 'data' field. This field
        is a list with dictionaries, each dictionary contains parameters for one benchmark:
        {"data":[{...}, {...}, {...}]}

        Args:
            inputs (str): File name of a JSON (*.json) or a compressed JSON (.json.gz) file.

        Returns:
            Instance of this class.
        """
        is_json_file = IOUtils.is_json_file(inputs)
        if not is_json_file and isinstance(inputs, list) and len(inputs) == 1:
            is_json_file = IOUtils.is_json_file(inputs[0])
            inputs = inputs[0] if is_json_file else inputs
        if is_json_file:
            benchmarks = IOUtils.read_json(inputs, check_extension=True)
            if 'data' not in benchmarks:
                benchmarks = {'data': []}
                print("[WARNING]: No benchmark data found in '{}'".format(
                    inputs))
            return BenchData(benchmarks['data'], create_copy=False)
        #
        is_csv_file = IOUtils.is_csv_file(inputs)
        if not is_csv_file and isinstance(inputs, list) and len(inputs) == 1:
            is_csv_file = IOUtils.is_csv_file(inputs[0])
            inputs = inputs[0] if is_csv_file else inputs
        if is_csv_file:
            with OpenFile(inputs, 'r') as fobj:
                reader = csv.DictReader(fobj)
                benchmarks = list(reader)
            return BenchData(benchmarks, create_copy=False)
        #
        is_compressed_tarball = IOUtils.is_compressed_tarball(inputs)
        if not is_compressed_tarball and isinstance(inputs,
                                                    list) and len(inputs) == 1:
            is_compressed_tarball = IOUtils.is_json_file(inputs[0])
            inputs = inputs[0] if is_compressed_tarball else inputs
        if is_compressed_tarball:
            benchmarks = []
            with tarfile.open(inputs, "r:gz") as archive:
                for member in archive.getmembers():
                    if member.isfile() and member.name.endswith('.log'):
                        log_file = archive.extractfile(member)
                        if log_file is not None:
                            parameters = {}
                            DictUtils.add(
                                parameters,
                                log_file,
                                pattern=
                                '[ \t]*__(.+?(?=__[ \t]*[=]))__[ \t]*=(.+)',
                                must_match=False,
                                ignore_errors=True)
                            benchmarks.append(parameters)
            return BenchData(benchmarks, create_copy=False)
        #
        return BenchData.parse(inputs, **kwargs)
    def load_data(**kwargs):
        is_dir = os.path.isdir(kwargs['input'])
        is_file = os.path.isfile(kwargs['input'])
        is_log_file = is_file and kwargs['input'].endswith('.log')
        is_json_file = is_file and (kwargs['input'].endswith('.json')
                                    or kwargs['input'].endswith('.json.gz'))

        if is_dir or is_log_file:
            files = IOUtils.find_files(config['input'], "*.log",
                                       config['recursive'])
            benchmarks, failed_benchmarks = LogParser.parse_log_files(files)
            benchmarks.extend(failed_benchmarks)
        elif is_json_file:
            benchmarks = IOUtils.read_json(kwargs['input'])
            benchmarks = benchmarks['data']
        else:
            raise ValueError("Invalid input descriptor: {}".format(
                kwargs['input']))
        return benchmarks
def main():
    """Entry point when invoking this scrip from a command line."""
    parser = argparse.ArgumentParser()
    parser.add_argument('inputs',
                        nargs='*',
                        help='Log directory or a JSON file')
    parser.add_argument(
        '--recursive',
        required=False,
        default=False,
        action='store_true',
        help='If input is folder, scan it recursively for log files.')
    parser.add_argument('--xparam',
                        type=str,
                        required=True,
                        default=None,
                        help='A parameter that is associated with x axis.')
    parser.add_argument('--yparam',
                        type=str,
                        required=True,
                        default=None,
                        help='A parameter that is associated with y axis.')
    parser.add_argument('--series',
                        type=str,
                        required=True,
                        default=None,
                        help='A json array with filters for series.')
    parser.add_argument(
        '--aggregation',
        type=str,
        required=True,
        default="avg",
        help=
        'In case of multiple matches, use this to aggregate values (min, max, avg)'
    )
    parser.add_argument('--chart_file',
                        '--chart-file',
                        type=str,
                        required=False,
                        default=None,
                        help='If present, write chart into this file.')
    parser.add_argument(
        '--series_file',
        '--series-file',
        type=str,
        required=False,
        default=None,
        help='If present, write series JSON data into this file.')
    parser.add_argument(
        '--chart_opts',
        '--chart-opts',
        type=str,
        required=False,
        default=None,
        help='If present, a json object specifying chart options.')
    parser.add_argument('--chart_type',
                        '--chart-type',
                        type=str,
                        required=False,
                        default='line',
                        help='Type of a chart ("line" or "bar").')
    parser.add_argument(
        '--baseline_xvalue',
        '--baseline-xvalue',
        type=str,
        required=False,
        default=None,
        help=
        "A value that's used to normalize one series. Useful to plot speedup charts."
    )
    parser.add_argument(
        '--baseline_series',
        '--baseline-series',
        type=int,
        required=False,
        default=None,
        help="An index of a baseline series to use to normalize all series.")
    args = parser.parse_args()

    if len(args.inputs) == 0:
        raise ValueError("Must be at least one input ('--input')")

    # Parse log files and load benchmark data
    logfiles = []  # Original raw log files with benchmark data
    benchmarks = []  # Parsed benchmarks
    for input_path in args.inputs:
        if os.path.isdir(input_path):
            logfiles.extend(
                IOUtils.find_files(input_path, "*.log", args.recursive))
        elif os.path.isfile(input_path) and input_path.endswith(
            ('.json', '.json.gz')):
            file_benchmarks = IOUtils.read_json(input_path)
            if 'data' in file_benchmarks and isinstance(
                    file_benchmarks['data'], list):
                benchmarks.extend(file_benchmarks['data'])
            else:
                logging.warn("Cannot parse file (%s). Invalid content.",
                             input_path)
        else:
            logging.warn("Cannot parse file (%s). Unknown extension. ",
                         input_path)
    if len(logfiles) > 0:
        benchmarks.extend(LogParser.parse_log_files(logfiles))
    else:
        logging.warn("No input log files have been found")
    if len(benchmarks) == 0:
        raise ValueError("No benchmarks have been loaded.")
    # Build data for series
    chart_data = SeriesBuilder.build(benchmarks, args)
    # Write it
    if args.series_file:
        DictUtils.dump_json_to_file(chart_data, args)
    # Plot it
    if args.chart_file:
        SeriesBuilder.plot(chart_data, args)