tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

schema.py (4296B)


      1 from enum import Enum
      2 from dataclasses import dataclass
      3 from fnmatch import fnmatchcase
      4 from functools import cached_property
      5 from typing import Any, Dict, Sequence, Union
      6 
      7 from ..schema import SchemaValue, validate_dict
      8 
      9 """
     10 YAML filename for meta files
     11 """
     12 WEB_FEATURES_YML_FILENAME = "WEB_FEATURES.yml"
     13 
     14 # File prefix to indicate that this FeatureFile should run in EXCLUDE mode.
     15 EXCLUSION_PREFIX = "!"
     16 
     17 
     18 class SpecialFileEnum(Enum):
     19    """All files recursively"""
     20    RECURSIVE = "**"
     21 
     22 
     23 class FileMatchingMode(Enum):
     24    """Defines how a FeatureFile pattern is used for matching."""
     25    INCLUDE = 1  # Include files that match the pattern
     26    EXCLUDE = 2  # Exclude files that match the pattern
     27 
     28 class FeatureFile(str):
     29    @cached_property
     30    def matching_mode(self) -> FileMatchingMode:
     31        """Determines if the pattern should include or exclude matches."""
     32        return FileMatchingMode.EXCLUDE if self.startswith(EXCLUSION_PREFIX) else FileMatchingMode.INCLUDE
     33 
     34    @cached_property
     35    def processed_filename(self) -> str:
     36        """Removes the exclusion prefix "!" from the pattern."""
     37        # TODO. After moving to Python3.9, use: return self.removeprefix(EXCLUSION_PREFIX)
     38        return self[len(EXCLUSION_PREFIX):] if self.startswith(EXCLUSION_PREFIX) else self
     39 
     40    def match_files(self, base_filenames: Sequence[str]) -> Sequence[str]:
     41        """
     42        Given the input base file names, returns the subset of base file names
     43        that match the given FeatureFile based on matching_mode.
     44        If the FeatureFile contains any number of "*" characters, fnmatch is
     45        used check each file name.
     46        If the FeatureFile does not contain any "*" characters, the base file name
     47        must match the FeatureFile exactly
     48        :param base_filenames: The list of filenames to check against the FeatureFile
     49        :return: List of matching file names that match FeatureFile
     50        """
     51        result = []
     52        # If our file name contains a wildcard, use fnmatch
     53        if "*" in self:
     54            for base_filename in base_filenames:
     55                if fnmatchcase(base_filename, self.processed_filename):
     56                    result.append(base_filename)
     57        elif self.processed_filename in base_filenames:
     58            result.append(self.processed_filename)
     59        return result
     60 
     61 
     62 @dataclass
     63 class FeatureEntry:
     64    files: Union[Sequence[FeatureFile], SpecialFileEnum]
     65    """The web-features key"""
     66    name: str
     67 
     68    _required_keys = {"files", "name"}
     69 
     70    def __init__(self, obj: Dict[str, Any]):
     71        """
     72        Converts the provided dictionary to an instance of FeatureEntry
     73        :param obj: The object that will be converted to a FeatureEntry.
     74        :return: An instance of FeatureEntry
     75        :raises ValueError: If there are unexpected keys or missing required keys.
     76        """
     77        validate_dict(obj, FeatureEntry._required_keys)
     78        self.files = SchemaValue.from_union([
     79            lambda x: SchemaValue.from_list(SchemaValue.from_class(FeatureFile), x),
     80            SpecialFileEnum], obj.get("files"))
     81        self.name = SchemaValue.from_str(obj.get("name"))
     82        # If "**" is used, it should be the only item. Not in a list.
     83        if isinstance(self.files, list) and SpecialFileEnum.RECURSIVE.value in self.files:
     84            raise ValueError(f'Feature {self.name} contains "**" in a list. It should be `files: "**"`')
     85 
     86 
     87    def does_feature_apply_recursively(self) -> bool:
     88        if isinstance(self.files, SpecialFileEnum) and self.files == SpecialFileEnum.RECURSIVE:
     89            return True
     90        return False
     91 
     92 
     93 @dataclass
     94 class WebFeaturesFile:
     95    """List of features"""
     96    features: Sequence[FeatureEntry]
     97 
     98    _required_keys = {"features"}
     99 
    100    def __init__(self, obj: Dict[str, Any]):
    101        """
    102        Converts the provided dictionary to an instance of WebFeaturesFile
    103        :param obj: The object that will be converted to a WebFeaturesFile.
    104        :return: An instance of WebFeaturesFile
    105        :raises ValueError: If there are unexpected keys or missing required keys.
    106        """
    107        validate_dict(obj, WebFeaturesFile._required_keys)
    108        self.features = SchemaValue.from_list(
    109            lambda raw_feature: FeatureEntry(SchemaValue.from_dict(raw_feature)), obj.get("features"))