Skip to content

route_rules ¤

Modules:

Classes:

Functions:

ArtifactMeta ¤

Bases: Struct

Parameters:

Attributes:

behavior instance-attribute ¤

behavior: Behavior

format instance-attribute ¤

format: Format

path instance-attribute ¤

path: Path

size instance-attribute ¤

size: int

Behavior ¤

Bases: StrEnum

Attributes:

CLASSICAL class-attribute instance-attribute ¤

CLASSICAL = auto()

DOMAIN class-attribute instance-attribute ¤

DOMAIN = auto()

IPCIDR class-attribute instance-attribute ¤

IPCIDR = auto()

Builder ¤

Parameters:

  • dist_dir (Path, default: PosixPath('dist') ) –
  • exporters (list[ExporterMihomo], default: [ExporterMihomo(behavior=<Behavior.DOMAIN: 'domain'>, format=<Format.MRS: 'mrs'>, export_path_template='mihomo/{slug}.{behavior}{format.ext}'), ExporterMihomo(behavior=<Behavior.DOMAIN: 'domain'>, format=<Format.TEXT: 'text'>, export_path_template='mihomo/{slug}.{behavior}{format.ext}'), ExporterMihomo(behavior=<Behavior.IPCIDR: 'ipcidr'>, format=<Format.MRS: 'mrs'>, export_path_template='mihomo/{slug}.{behavior}{format.ext}'), ExporterMihomo(behavior=<Behavior.IPCIDR: 'ipcidr'>, format=<Format.TEXT: 'text'>, export_path_template='mihomo/{slug}.{behavior}{format.ext}'), ExporterMihomo(behavior=<Behavior.CLASSICAL: 'classical'>, format=<Format.TEXT: 'text'>, export_path_template='mihomo/{slug}.{behavior}{format.ext}')] ) –
  • recipes (list[RecipeWrapper], default: <dynamic> ) –

    Built-in mutable sequence.

    If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

Methods:

Attributes:

dist_dir class-attribute instance-attribute ¤

dist_dir: Path = field(default=Path('dist'))

exporters class-attribute instance-attribute ¤

exporters: list[ExporterMihomo] = field(
    factory=_default_exporters
)

recipes class-attribute instance-attribute ¤

recipes: list[RecipeWrapper] = field(factory=list)

build async ¤

build() -> None
Source code in src/route_rules/gen/_builder.py
42
43
44
45
46
async def build(self) -> None:
    meta = Meta(build_time=datetime.datetime.now().astimezone())
    for recipe in self.recipes:
        meta.recipes.append(await self.build_recipe(recipe))
    (self.dist_dir / "meta.json").write_bytes(meta.json_encode())

build_recipe async ¤

build_recipe(recipe: RecipeWrapper) -> RecipeMeta
Source code in src/route_rules/gen/_builder.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
async def build_recipe(self, recipe: RecipeWrapper) -> RecipeMeta:
    ruleset: RuleSet = await recipe.build()
    meta = RecipeMeta(
        name=recipe.name,
        slug=recipe.slug,
        statistics=await self._build_statistics(recipe, ruleset),
    )
    for provider in recipe.providers:
        meta.providers.append(
            ProviderMeta(
                name=provider,
                download_url=recipe.registry.download_url(provider),
                preview_url=recipe.registry.preview_url(provider),
            )
        )
    for exporter in self.exporters:
        path: Path = exporter.export_path(recipe.slug)
        size: int = exporter.export(self.dist_dir / path, ruleset)
        if size == 0:
            continue
        meta.artifacts.append(
            ArtifactMeta(
                behavior=exporter.behavior,
                format=exporter.format,
                path=path,
                size=size,
            )
        )
    return meta

load classmethod ¤

load(file: str | PathLike[str]) -> Self
Source code in src/route_rules/gen/_builder.py
34
35
36
37
38
39
40
@classmethod
def load(cls, file: str | os.PathLike[str]) -> Self:
    config: Config = Config.load(file)
    self: Self = cls()
    for recipe_config in config.recipes:
        self.recipes.append(RecipeWrapper.from_config(recipe_config))
    return self

Config pydantic-model ¤

Bases: BaseModel

Parameters:

  • recipes (list[RecipeConfig]) –
Show JSON schema:
{
  "$defs": {
    "RecipeConfig": {
      "properties": {
        "name": {
          "title": "Name",
          "type": "string"
        },
        "providers": {
          "items": {
            "type": "string"
          },
          "title": "Providers",
          "type": "array"
        }
      },
      "required": [
        "name",
        "providers"
      ],
      "title": "RecipeConfig",
      "type": "object"
    }
  },
  "properties": {
    "recipes": {
      "items": {
        "$ref": "#/$defs/RecipeConfig"
      },
      "title": "Recipes",
      "type": "array"
    }
  },
  "required": [
    "recipes"
  ],
  "title": "Config",
  "type": "object"
}

Fields:

recipes pydantic-field ¤

recipes: list[RecipeConfig]

load classmethod ¤

load(file: str | PathLike[str]) -> Self
Source code in src/route_rules/gen/_config.py
17
18
19
20
21
@classmethod
def load(cls, file: str | os.PathLike[str]) -> Self:
    file = Path(file)
    data: Any = msgspec.yaml.decode(file.read_bytes())
    return cls.model_validate(data)

Format ¤

Bases: StrEnum

Attributes:

MRS class-attribute instance-attribute ¤

MRS = auto()

TEXT class-attribute instance-attribute ¤

TEXT = auto()

YAML class-attribute instance-attribute ¤

YAML = auto()

ext property ¤

ext: str

Meta ¤

Bases: Struct

Parameters:

  • build_time (datetime) –
  • recipes (list[RecipeMeta], default: <dynamic> ) –

    Built-in mutable sequence.

    If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

Methods:

Attributes:

build_time instance-attribute ¤

build_time: datetime

recipes class-attribute instance-attribute ¤

recipes: list[RecipeMeta] = field(default_factory=list)

json_decode classmethod ¤

json_decode(data: Buffer | str) -> Self
Source code in src/route_rules/gen/_meta.py
41
42
43
@classmethod
def json_decode(cls, data: Buffer | str) -> Self:
    return msgspec.json.decode(data, type=cls, dec_hook=dec_hook)

json_encode ¤

json_encode() -> bytes
Source code in src/route_rules/gen/_meta.py
45
46
def json_encode(self) -> bytes:
    return msgspec.json.encode(self, enc_hook=enc_hook)

Provider ¤

Bases: ABC

Parameters:

  • name (str) –
  • download_url_template (str) –
  • preview_url_template (str, default: <dynamic> ) –

Methods:

Attributes:

download_url_template class-attribute instance-attribute ¤

download_url_template: str = field()

name class-attribute instance-attribute ¤

name: str = field()

preview_url_template class-attribute instance-attribute ¤

preview_url_template: str = field(
    default=Factory(
        _default_preview_url_template, takes_self=True
    )
)

download_url ¤

download_url(name: str) -> str
Source code in src/route_rules/provider/_abc.py
26
27
28
def download_url(self, name: str) -> str:
    name = urllib.parse.quote(name)
    return self.download_url_template.format(name=name)

load abstractmethod async ¤

load(name: str) -> RuleSet
Source code in src/route_rules/provider/_abc.py
30
31
32
@abc.abstractmethod
async def load(self, name: str) -> RuleSet:
    raise NotImplementedError

preview_url ¤

preview_url(name: str) -> str
Source code in src/route_rules/provider/_abc.py
34
35
36
def preview_url(self, name: str) -> str:
    name = urllib.parse.quote(name)
    return self.preview_url_template.format(name=name)

ProviderMeta ¤

Bases: Struct

Parameters:

  • name (str) –
  • download_url (str) –
  • preview_url (str) –

Attributes:

download_url instance-attribute ¤

download_url: str

name instance-attribute ¤

name: str

preview_url instance-attribute ¤

preview_url: str

ProviderMihomo ¤

Bases: Provider

Parameters:

  • name (str) –
  • download_url_template (str) –
  • preview_url_template (str, default: <dynamic> ) –
  • behavior (Behavior) –
  • format (Format, default: <Format.YAML: 'yaml'> ) –

Methods:

Attributes:

behavior class-attribute instance-attribute ¤

behavior: Behavior = field(kw_only=True)

download_url_template class-attribute instance-attribute ¤

download_url_template: str = field()

format class-attribute instance-attribute ¤

format: Format = field(default=YAML, kw_only=True)

name class-attribute instance-attribute ¤

name: str = field()

preview_url_template class-attribute instance-attribute ¤

preview_url_template: str = field(
    default=Factory(
        _default_preview_url_template, takes_self=True
    )
)

download_url ¤

download_url(name: str) -> str
Source code in src/route_rules/provider/_abc.py
26
27
28
def download_url(self, name: str) -> str:
    name = urllib.parse.quote(name)
    return self.download_url_template.format(name=name)

load async ¤

load(name: str) -> RuleSet
Source code in src/route_rules/provider/mihomo/_provider.py
20
21
22
23
24
@override
@cta.cachedmethod(lambda self: self._cache)
async def load(self, name: str) -> RuleSet:
    response: httpx.Response = await utils.download(self.download_url(name))
    return decode(response.text, behavior=self.behavior, format=self.format)

preview_url ¤

preview_url(name: str) -> str
Source code in src/route_rules/provider/_abc.py
34
35
36
def preview_url(self, name: str) -> str:
    name = urllib.parse.quote(name)
    return self.preview_url_template.format(name=name)

ProviderRegistry ¤

Parameters:

  • registry (dict[str, Provider], default: <class 'dict'> ) –

    dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Methods:

Attributes:

registry class-attribute instance-attribute ¤

registry: dict[str, Provider] = field(factory=dict)

download_url ¤

download_url(name: str) -> str
Source code in src/route_rules/provider/_registry.py
57
58
59
60
61
def download_url(self, name: str) -> str:
    provider: Provider
    ruleset_name: str
    provider, ruleset_name = self._parse_name(name)
    return provider.download_url(ruleset_name)

load async ¤

load(name: str) -> RuleSet
Source code in src/route_rules/provider/_registry.py
63
64
65
66
67
async def load(self, name: str) -> RuleSet:
    provider: Provider
    ruleset_name: str
    provider, ruleset_name = self._parse_name(name)
    return await provider.load(ruleset_name)

presets cached classmethod ¤

presets() -> Self
Source code in src/route_rules/provider/_registry.py
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
@classmethod
@functools.cache
def presets(cls) -> Self:
    self: Self = cls()
    self.register(
        ProviderMihomo(
            "blackmatrix7",
            "https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/{name}/{name}.list",
            "https://github.com/blackmatrix7/ios_rule_script/tree/master/rule/Clash/{name}",
            behavior=Behavior.CLASSICAL,
            format=Format.TEXT,
        ),
        ProviderMihomo(
            "dler-io",
            "https://raw.githubusercontent.com/dler-io/Rules/main/Clash/Provider/{name}.yaml",
            "https://github.com/dler-io/Rules/blob/main/Clash/Provider/{name}.yaml",
            behavior=Behavior.CLASSICAL,
            format=Format.YAML,
        ),
        ProviderMihomo(
            "MetaCubeX/geosite",
            "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/{name}.yaml",
            "https://github.com/MetaCubeX/meta-rules-dat/blob/meta/geo/geosite/{name}.yaml",
            behavior=Behavior.DOMAIN,
            format=Format.YAML,
        ),
        ProviderMihomo(
            "SukkaW/classical",
            "https://ruleset.skk.moe/Clash/{name}.txt",
            behavior=Behavior.CLASSICAL,
            format=Format.TEXT,
        ),
        ProviderMihomo(
            "SukkaW/domain",
            "https://ruleset.skk.moe/Clash/{name}.txt",
            behavior=Behavior.DOMAIN,
            format=Format.TEXT,
        ),
    )
    return self

preview_url ¤

preview_url(name: str) -> str
Source code in src/route_rules/provider/_registry.py
69
70
71
72
73
def preview_url(self, name: str) -> str:
    provider: Provider
    ruleset_name: str
    provider, ruleset_name = self._parse_name(name)
    return provider.preview_url(ruleset_name)

register ¤

register(*providers: Provider) -> None
Source code in src/route_rules/provider/_registry.py
75
76
77
def register(self, *providers: Provider) -> None:
    for provider in providers:
        self.registry[provider.name] = provider

Recipe ¤

Parameters:

  • name (str) –
  • providers (list[str]) –
  • registry (ProviderRegistry, default: ProviderRegistry(registry={'blackmatrix7': ProviderMihomo(name='blackmatrix7', download_url_template='https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/{name}/{name}.list', preview_url_template='https://github.com/blackmatrix7/ios_rule_script/tree/master/rule/Clash/{name}', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.CLASSICAL: 'classical'>, format=<Format.TEXT: 'text'>), 'dler-io': ProviderMihomo(name='dler-io', download_url_template='https://raw.githubusercontent.com/dler-io/Rules/main/Clash/Provider/{name}.yaml', preview_url_template='https://github.com/dler-io/Rules/blob/main/Clash/Provider/{name}.yaml', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.CLASSICAL: 'classical'>, format=<Format.YAML: 'yaml'>), 'MetaCubeX/geosite': ProviderMihomo(name='MetaCubeX/geosite', download_url_template='https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/{name}.yaml', preview_url_template='https://github.com/MetaCubeX/meta-rules-dat/blob/meta/geo/geosite/{name}.yaml', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.DOMAIN: 'domain'>, format=<Format.YAML: 'yaml'>), 'SukkaW/classical': ProviderMihomo(name='SukkaW/classical', download_url_template='https://ruleset.skk.moe/Clash/{name}.txt', preview_url_template='https://ruleset.skk.moe/Clash/{name}.txt', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.CLASSICAL: 'classical'>, format=<Format.TEXT: 'text'>), 'SukkaW/domain': ProviderMihomo(name='SukkaW/domain', download_url_template='https://ruleset.skk.moe/Clash/{name}.txt', preview_url_template='https://ruleset.skk.moe/Clash/{name}.txt', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.DOMAIN: 'domain'>, format=<Format.TEXT: 'text'>)}) ) –
  • slug (str, default: <dynamic> ) –

Methods:

Attributes:

name class-attribute instance-attribute ¤

name: str = field()

providers class-attribute instance-attribute ¤

providers: list[str] = field()

registry class-attribute instance-attribute ¤

registry: ProviderRegistry = field(
    factory=presets, kw_only=True
)

slug class-attribute instance-attribute ¤

slug: str = field(
    default=Factory(default_slug, takes_self=True),
    kw_only=True,
)

build async ¤

build() -> RuleSet
Source code in src/route_rules/core/_recipe.py
28
29
30
31
32
33
34
@cta.cachedmethod(lambda self: self._cache)
async def build(self) -> RuleSet:
    ruleset: RuleSet = RuleSet.union(
        *(await asyncio.gather(*(self.registry.load(p) for p in self.providers)))
    )
    ruleset = ruleset.optimize()
    return ruleset

RecipeMeta ¤

Bases: Struct

Parameters:

  • name (str) –
  • slug (str) –
  • artifacts (list[ArtifactMeta], default: <dynamic> ) –

    Built-in mutable sequence.

    If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

  • providers (list[ProviderMeta], default: <dynamic> ) –

    Built-in mutable sequence.

    If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

  • statistics (RecipeStatistics, default: <dynamic> ) –

Attributes:

artifacts class-attribute instance-attribute ¤

artifacts: list[ArtifactMeta] = field(default_factory=list)

name instance-attribute ¤

name: str

providers class-attribute instance-attribute ¤

providers: list[ProviderMeta] = field(default_factory=list)

slug instance-attribute ¤

slug: str

statistics class-attribute instance-attribute ¤

statistics: RecipeStatistics = field(
    default_factory=RecipeStatistics
)

RecipeWrapper ¤

Bases: Recipe

Parameters:

  • name (str) –
  • providers (list[str]) –
  • registry (ProviderRegistry, default: ProviderRegistry(registry={'blackmatrix7': ProviderMihomo(name='blackmatrix7', download_url_template='https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/{name}/{name}.list', preview_url_template='https://github.com/blackmatrix7/ios_rule_script/tree/master/rule/Clash/{name}', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.CLASSICAL: 'classical'>, format=<Format.TEXT: 'text'>), 'dler-io': ProviderMihomo(name='dler-io', download_url_template='https://raw.githubusercontent.com/dler-io/Rules/main/Clash/Provider/{name}.yaml', preview_url_template='https://github.com/dler-io/Rules/blob/main/Clash/Provider/{name}.yaml', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.CLASSICAL: 'classical'>, format=<Format.YAML: 'yaml'>), 'MetaCubeX/geosite': ProviderMihomo(name='MetaCubeX/geosite', download_url_template='https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/{name}.yaml', preview_url_template='https://github.com/MetaCubeX/meta-rules-dat/blob/meta/geo/geosite/{name}.yaml', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.DOMAIN: 'domain'>, format=<Format.YAML: 'yaml'>), 'SukkaW/classical': ProviderMihomo(name='SukkaW/classical', download_url_template='https://ruleset.skk.moe/Clash/{name}.txt', preview_url_template='https://ruleset.skk.moe/Clash/{name}.txt', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.CLASSICAL: 'classical'>, format=<Format.TEXT: 'text'>), 'SukkaW/domain': ProviderMihomo(name='SukkaW/domain', download_url_template='https://ruleset.skk.moe/Clash/{name}.txt', preview_url_template='https://ruleset.skk.moe/Clash/{name}.txt', _cache=LRUCache({}, maxsize=65536, currsize=0), behavior=<Behavior.DOMAIN: 'domain'>, format=<Format.TEXT: 'text'>)}) ) –
  • slug (str, default: <dynamic> ) –

Methods:

Attributes:

name class-attribute instance-attribute ¤

name: str = field()

providers class-attribute instance-attribute ¤

providers: list[str] = field()

registry class-attribute instance-attribute ¤

registry: ProviderRegistry = field(
    factory=presets, kw_only=True
)

slug class-attribute instance-attribute ¤

slug: str = field(
    default=Factory(default_slug, takes_self=True),
    kw_only=True,
)

build async ¤

build() -> RuleSet
Source code in src/route_rules/core/_recipe.py
28
29
30
31
32
33
34
@cta.cachedmethod(lambda self: self._cache)
async def build(self) -> RuleSet:
    ruleset: RuleSet = RuleSet.union(
        *(await asyncio.gather(*(self.registry.load(p) for p in self.providers)))
    )
    ruleset = ruleset.optimize()
    return ruleset

from_config classmethod ¤

from_config(config: RecipeConfig) -> Self
Source code in src/route_rules/gen/_recipe.py
12
13
14
@classmethod
def from_config(cls, config: RecipeConfig) -> Self:
    return cls(name=config.name, providers=config.providers)

RuleSet ¤

Bases: UserDict[str, set[str]]

.

References
  1. https://wiki.metacubex.one/en/config/rules/

Methods:

Attributes:

domain property ¤

domain: set[str]

domain_suffix property ¤

domain_suffix: set[str]

ip_cidr property ¤

ip_cidr: set[str]

__missing__ ¤

__missing__(key: str) -> set[str]
Source code in src/route_rules/core/_ruleset.py
21
22
23
def __missing__(self, key: str) -> set[str]:
    self[key] = set()
    return self[key]

__or__ ¤

__or__(other: Mapping[str, Set[str]]) -> Self
Source code in src/route_rules/core/_ruleset.py
14
15
16
17
18
19
@override
def __or__(self, other: Mapping[str, AbstractSet[str]], /) -> Self:  # pyright: ignore[reportIncompatibleMethodOverride]
    result: Self = type(self)()
    for typ in self.keys() | other.keys():
        result[typ] = self.get(typ, set()) | other.get(typ, set())
    return result

add ¤

add(typ: str, value: str) -> None
Source code in src/route_rules/core/_ruleset.py
37
38
39
40
41
def add(self, typ: str, value: str) -> None:
    # IP-CIDR and IP-CIDR6 have the same effect, with IP-CIDR6 being an alias.
    if typ == "IP-CIDR6":
        typ = "IP-CIDR"
    self[typ].add(value)

optimize ¤

optimize() -> Self
Source code in src/route_rules/core/_ruleset.py
43
44
45
def optimize(self) -> Self:
    # TODO: implement
    return self

union ¤

union(*others: Mapping[str, Set[str]]) -> Self
Source code in src/route_rules/core/_ruleset.py
47
48
49
50
51
52
53
def union(self, *others: Mapping[str, AbstractSet[str]]) -> Self:
    result: Self = type(self)()
    for typ in set(self.keys()).union(*(m.keys() for m in others)):
        result[typ] = self.get(typ, set()).union(
            *(m.get(typ, set()) for m in others)
        )
    return result

download async ¤

download(url: str) -> Response
Source code in src/route_rules/utils/_download.py
 9
10
11
12
13
14
15
16
async def download(url: str) -> httpx.Response:
    response: httpx.Response = await client.get(url)
    response = response.raise_for_status()
    if response.extensions["from_cache"]:
        logger.success("Cache Hit: {}", url)
    else:
        logger.info("Cache Miss: {}", url)
    return response