Repository: yt-dlp/yt-dlp
File: yt_dlp/utils.py (Python)

Code before:
                        fcntl.LOCK_SH if not exclusive
                        else fcntl.LOCK_EX if block
                        else fcntl.LOCK_EX | fcntl.LOCK_NB)

        def _unlock_file(f):
            fcntl.flock(f, fcntl.LOCK_UN)

    except ImportError:
        UNSUPPORTED_MSG = 'file locking is not supported on this platform'

        def _lock_file(f, exclusive, block):
            raise IOError(UNSUPPORTED_MSG)

        def _unlock_file(f):
            raise IOError(UNSUPPORTED_MSG)


class locked_file(object):
    def __init__(self, filename, mode, block=True, encoding=None):
        assert mode in ['r', 'rb', 'a', 'ab', 'w', 'wb']
        self.f = io.open(filename, mode, encoding=encoding)
        self.mode = mode
        self.block = block

    def __enter__(self):
        exclusive = self.mode not in ['r', 'rb']
        try:
            _lock_file(self.f, exclusive, self.block)
        except IOError:
            self.f.close()
            raise
        return self

    def __exit__(self, etype, value, traceback):
        try:
            _unlock_file(self.f)
        finally:
            self.f.close()

    def __iter__(self):
        return iter(self.f)

    def write(self, *args):
        return self.f.write(*args)

    def read(self, *args):
        return self.f.read(*args)

    def flush(self, *args):
        self.f.flush()


Review comment (suggestion):
```suggestion
        exclusive = 'r' not in self.mode
```

Code after:
        '$=': lambda attr, value: attr.endswith(value),
        '~=': lambda attr, value: re.search(value, attr),
    }
    COMPARISON_OPERATORS = {
        **STRING_OPERATORS,
        '<=': operator.le,  # "<=" must be defined above "<"
        '<': operator.lt,
        '>=': operator.ge,
        '>': operator.gt,
        '=': operator.eq,
    }

    operator_rex = re.compile(r'''(?x)\s*
        (?P<key>[a-z_]+)
        \s*(?P<negation>!\s*)?(?P<op>%s)(?P<none_inclusive>\s*\?)?\s*
        (?:
            (?P<quote>["\'])(?P<quotedstrval>.+?)(?P=quote)|
            (?P<strval>.+?)
        )
        \s*$
        ''' % '|'.join(map(re.escape, COMPARISON_OPERATORS.keys())))
    m = operator_rex.search(filter_part)
    if m:
        m = m.groupdict()
        unnegated_op = COMPARISON_OPERATORS[m['op']]
        if m['negation']:
            op = lambda attr, value: not unnegated_op(attr, value)
        else:
            op = unnegated_op
        comparison_value = m['quotedstrval'] or m['strval'] or m['intval']
        if m['quote']:
            comparison_value = comparison_value.replace(r'\%s' % m['quote'], m['quote'])
        actual_value = dct.get(m['key'])
        numeric_comparison = None
        if isinstance(actual_value, compat_numeric_types):
            # If the original field is a string and matching comparisonvalue is
            # a number we should respect the origin of the original field
            # and process comparison value as a string (see
            # https://github.com/ytdl-org/youtube-dl/issues/11082)
            try:
                numeric_comparison = int(comparison_value)
            except ValueError:
                numeric_comparison = parse_filesize(comparison_value)
                if numeric_comparison is None:
                    numeric_comparison = parse_filesize(f'{comparison_value}B')
                if numeric_comparison is None:
                    numeric_comparison = parse_duration(comparison_value)
        if numeric_comparison is not None and m['op'] in STRING_OPERATORS:
            raise ValueError('Operator %s only supports string values!' % m['op'])
        if actual_value is None:
            return incomplete or m['none_inclusive']