1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
######################################################################
#
# File: b2sdk/_internal/filter.py
#
# Copyright 2024 Backblaze Inc. All Rights Reserved.
#
# License https://www.backblaze.com/using_b2_code.html
#
######################################################################
from __future__ import annotations
import fnmatch
from dataclasses import dataclass
from enum import Enum
from typing import Sequence
class FilterType(Enum):
INCLUDE = 'include'
EXCLUDE = 'exclude'
@dataclass
class Filter:
type: FilterType
pattern: str
@classmethod
def include(cls, pattern: str) -> Filter:
return cls(type=FilterType.INCLUDE, pattern=pattern)
@classmethod
def exclude(cls, pattern: str) -> Filter:
return cls(type=FilterType.EXCLUDE, pattern=pattern)
class FilterMatcher:
"""
Holds a list of filters and matches a string (i.e. file name) against them.
The order of filters matters. The *last* matching filter decides whether
the string is included or excluded. If no filter matches, the string is
included by default.
If the given list of filters contains only INCLUDE filters, then it is
assumed that all files are excluded by default. In this case, an additional
EXCLUDE filter is prepended to the list.
:param filters: list of filters
"""
def __init__(self, filters: Sequence[Filter]):
if filters and all(filter_.type == FilterType.INCLUDE for filter_ in filters):
filters = [Filter(type=FilterType.EXCLUDE, pattern='*'), *filters]
self.filters = filters
def match(self, s: str) -> bool:
include_file = True
for filter_ in self.filters:
matched = fnmatch.fnmatchcase(s, filter_.pattern)
if matched and filter_.type == FilterType.INCLUDE:
include_file = True
elif matched and filter_.type == FilterType.EXCLUDE:
include_file = False
return include_file
|