# Path: src/tidypy/util.py
# def read_file(filepath):
#     """
#     Retrieves the contents of the specified file.
# 
#     This function performs simple caching so that the same file isn't read more
#     than once per process.
# 
#     :param filepath: the file to read
#     :type filepath: str
#     :returns: str
#     """
# 
#     with _FILE_CACHE_LOCK:
#         if filepath not in _FILE_CACHE:
#             _FILE_CACHE[filepath] = _read_file(filepath)
#     return _FILE_CACHE[filepath]


import re
from collections import OrderedDict, defaultdict
from threading import Lock
from .util import read_file
        if sortby is None:
            sortby = self.DEFAULT_SORT

        for attr in reversed(sortby):
            if attr in ('line', 'character'):
                keyfunc = lambda i, attr=attr: getattr(i, attr) or 0
            else:
                keyfunc = lambda i, attr=attr: getattr(i, attr) or ''

            issues = sorted(issues, key=keyfunc)

        return issues

    def _group_issues(self, issues, keyfunc, sortby):
        grouped = defaultdict(list)
        for issue in issues:
            grouped[keyfunc(issue)].append(issue)

        return OrderedDict([
            (key, self._sort_issues(grouped[key], sortby))
            for key in sorted(grouped)
        ])

    def _parse_noqa(self, filename):
        lines = {}

        if not RE_PYTHON_FILE.search(filename):
            return lines

        try:

# Path: src/tidypy/util.py
# def read_file(filepath):
#     """
#     Retrieves the contents of the specified file.
# 
#     This function performs simple caching so that the same file isn't read more
#     than once per process.
# 
#     :param filepath: the file to read
#     :type filepath: str
#     :returns: str
#     """
# 
#     with _FILE_CACHE_LOCK:
#         if filepath not in _FILE_CACHE:
#             _FILE_CACHE[filepath] = _read_file(filepath)
#     return _FILE_CACHE[filepath]


# Path: src/tidypy/collector.py
import re
from collections import OrderedDict, defaultdict
from threading import Lock
from .util import read_file

        if sortby is None:
            sortby = self.DEFAULT_SORT

        for attr in reversed(sortby):
            if attr in ('line', 'character'):
                keyfunc = lambda i, attr=attr: getattr(i, attr) or 0
            else:
                keyfunc = lambda i, attr=attr: getattr(i, attr) or ''

            issues = sorted(issues, key=keyfunc)

        return issues

    def _group_issues(self, issues, keyfunc, sortby):
        grouped = defaultdict(list)
        for issue in issues:
            grouped[keyfunc(issue)].append(issue)

        return OrderedDict([
            (key, self._sort_issues(grouped[key], sortby))
            for key in sorted(grouped)
        ])

    def _parse_noqa(self, filename):
        lines = {}

        if not RE_PYTHON_FILE.search(filename):
            return lines

        try:

            content = read_file(filename)