from contextlib import suppress
import re


def get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None):
    """
    Returns context_lines before and after lineno from file.
    Returns (pre_context_lineno, pre_context, context_line, post_context).
    """
    source = None
    if loader is not None and hasattr(loader, "get_source"):
        with suppress(ImportError):
            source = loader.get_source(module_name)
        if source is not None:
            source = source.splitlines()
    if source is None:
        with suppress(OSError, IOError):
            with open(filename, "rb") as fp:
                source = fp.read().splitlines()
    if source is None:
        return None, [], None, []
    try:
        # If we just read the source from a file, or if the loader did not
        # apply tokenize.detect_encoding to decode the source into a Unicode
        # string, then we should do that ourselves.
        if isinstance(source[0], bytes):
            encoding = "ascii"
            for line in source[:2]:
                # File coding may be specified. Match pattern from PEP-263
                # (http://www.python.org/dev/peps/pep-0263/)
                match = re.search(br"coding[:=]\s*([-\w.]+)", line)
                if match:
                    encoding = match.group(1).decode("ascii")
                    break
            source = [str(sline, encoding, "replace") for sline in source]

        lower_bound = max(0, lineno - context_lines)
        upper_bound = lineno + context_lines

        pre_context = source[lower_bound:lineno]
        context_line = source[lineno]
        post_context = source[lineno + 1 : upper_bound]

        return lower_bound, pre_context, context_line, post_context
    except Exception as e:
        try:
            context_line = f'<There was an error displaying the source file: "{repr(e)}"  The loaded source has {len(source)} lines.>'
        except Exception:
            context_line = "<There was an error displaying the source file. Further, there was an error displaying that error>"
        return lineno, [], context_line, []