Constructs an expectation using file lines map semantics.
        The file_lines_map_expectations decorator handles boilerplate issues
        surrounding the common pattern of evaluating truthiness of some
        condition on an line by line basis in a file.

        Args:
            func (function): \
                The function implementing an expectation that will be applied
                line by line across a file. The function should take a file
                and return information about how many lines met expectations.

        Notes:
            Users can specify skip value k that will cause the expectation
            function to disregard the first k lines of the file.

            file_lines_map_expectation will add a kwarg _lines to the called function with the nonnull lines \
            to process.

            null_lines_regex defines a regex used to skip lines, but can be overridden

        See also:
            :func:`expect_file_line_regex_match_count_to_be_between
            <great_expectations.data_asset.base.DataAsset.expect_file_line_regex_match_count_to_be_between>` \
            for an example of a file_lines_map_expectation

def file_lines_map_expectation(cls, func):
        """Constructs an expectation using file lines map semantics.
        The file_lines_map_expectations decorator handles boilerplate issues
        surrounding the common pattern of evaluating truthiness of some
        condition on an line by line basis in a file.

        Args:
            func (function): \
                The function implementing an expectation that will be applied
                line by line across a file. The function should take a file
                and return information about how many lines met expectations.

        Notes:
            Users can specify skip value k that will cause the expectation
            function to disregard the first k lines of the file.

            file_lines_map_expectation will add a kwarg _lines to the called function with the nonnull lines \
            to process.

            null_lines_regex defines a regex used to skip lines, but can be overridden

        See also:
            :func:`expect_file_line_regex_match_count_to_be_between
            <great_expectations.data_asset.base.DataAsset.expect_file_line_regex_match_count_to_be_between>` \
            for an example of a file_lines_map_expectation
        """
        if PY3:
            argspec = inspect.getfullargspec(func)[0][1:]
        else:
            argspec = inspect.getargspec(func)[0][1:]

        @cls.expectation(argspec)
        @wraps(func)
        def inner_wrapper(self, skip=None, mostly=None, null_lines_regex=r"^\s*$", result_format=None, *args, **kwargs):
            try:
                f = open(self._path, "r")
            except:
                raise

            if result_format is None:
                result_format = self.default_expectation_args["result_format"]

            result_format = parse_result_format(result_format)
            lines = f.readlines() #Read in file lines

            #Skip k initial lines designated by the user
            if skip is not None and skip <= len(lines):
                try:
                    assert float(skip).is_integer()
                    assert float(skip) >= 0
                except:
                    raise ValueError("skip must be a positive integer")

                for i in range(1, skip+1):
                    lines.pop(0)

            if lines:
                if null_lines_regex is not None:
                    null_lines = re.compile(null_lines_regex) #Ignore lines that are empty or have only white space ("null values" in the line-map context)
                    boolean_mapped_null_lines = np.array(
                        [bool(null_lines.match(line)) for line in lines])
                else:
                    boolean_mapped_null_lines = np.zeros(len(lines), dtype=bool)
                element_count = int(len(lines))
                if element_count > sum(boolean_mapped_null_lines):
                    nonnull_lines = list(compress(lines, np.invert(boolean_mapped_null_lines)))
                    nonnull_count = int((boolean_mapped_null_lines == False).sum())
                    boolean_mapped_success_lines = np.array(
                        func(self, _lines=nonnull_lines, *args, **kwargs))
                    success_count = np.count_nonzero(boolean_mapped_success_lines)
                    unexpected_list = list(compress(nonnull_lines, \
                        np.invert(boolean_mapped_success_lines)))
                    nonnull_lines_index = range(0, len(nonnull_lines)+1)
                    unexpected_index_list = list(compress(nonnull_lines_index,
                                                          np.invert(boolean_mapped_success_lines)))
                    success, percent_success = self._calc_map_expectation_success(
                        success_count, nonnull_count, mostly)
                    return_obj = self._format_map_output(
                        result_format, success,
                        element_count, nonnull_count,
                        len(unexpected_list),
                        unexpected_list, unexpected_index_list
                    )
                else:
                    return_obj = self._format_map_output(
                        result_format=result_format, success=None,
                        element_count=element_count, nonnull_count=0,
                        unexpected_count=0,
                        unexpected_list=[], unexpected_index_list=[]
                    )
            else:
                return_obj = self._format_map_output(result_format=result_format,
                                                     success=None,
                                                     element_count=0,
                                                     nonnull_count=0,
                                                     unexpected_count=0,
                                                     unexpected_list=[],
                                                     unexpected_index_list=[])
            f.close()
            return return_obj

        inner_wrapper.__name__ = func.__name__
        inner_wrapper.__doc__ = func.__doc__

        return inner_wrapper