TensorPlay
latest (dev)
Copy
View Markdown

Latest development documentation · Updated 2026-10-08

Source code for tensorplay.package.glob_group

# mypy: allow-untyped-defs
from __future__ import annotations

import re
from collections.abc import Iterable


GlobPattern = str | Iterable[str]



[docs]
class GlobGroup:
    """A set of patterns that candidate strings will be matched against.

    A candidate is composed of a list of segments separated by ``separator``,
    e.g. ``"foo.bar.baz"``.

    A pattern contains one or more segments. Segments can be:
        - A literal string (e.g. ``"foo"``), which matches exactly.
        - A string containing a wildcard (e.g. ``"tensorplay*"``, or
          ``"foo*baz*"``). The wildcard matches any string, including the
          empty string.
        - A double wildcard (``"**"``). This matches against zero or more
          complete segments.

    Examples:
        ``tensorplay.**``: matches ``tensorplay`` and all its submodules, e.g.
        ``tensorplay.nn`` and ``tensorplay.nn.functional``.
        ``tensorplay.*``: matches ``tensorplay.nn`` or ``tensorplay.utils``,
        but not ``tensorplay.nn.functional``.
        ``tensorplay*.**``: matches ``tensorplay``, ``tensorplay_vision``, and
        all their submodules.

    A candidate will match the ``GlobGroup`` if it matches any of the
    ``include`` patterns and none of the ``exclude`` patterns.

    Args:
        include (str | Iterable[str]): A string or list of strings,
            each representing a pattern to be matched against. A candidate
            will match if it matches *any* include pattern
        exclude (str | Iterable[str]): A string or list of strings,
            each representing a pattern to be matched against. A candidate
            will be excluded from matching if it matches *any* exclude pattern.
        separator (str): A string that delimits segments in candidates and
            patterns. By default this is "." which corresponds to how modules
            are named in Python. Another common value for this is "/", which
            is the Unix path separator.
    """

    def __init__(
        self, include: GlobPattern, *, exclude: GlobPattern = (), separator: str = "."
    ):
        self._dbg = f"GlobGroup(include={include}, exclude={exclude})"
        self.include = GlobGroup._glob_list(include, separator)
        self.exclude = GlobGroup._glob_list(exclude, separator)
        self.separator = separator

    def __str__(self):
        return self._dbg

    def __repr__(self):
        return self._dbg

    def matches(self, candidate: str) -> bool:
        candidate = self.separator + candidate
        return any(p.fullmatch(candidate) for p in self.include) and all(
            not p.fullmatch(candidate) for p in self.exclude
        )

    @staticmethod
    def _glob_list(elems: GlobPattern, separator: str = "."):
        if isinstance(elems, str):
            return [GlobGroup._glob_to_re(elems, separator)]
        else:
            return [GlobGroup._glob_to_re(e, separator) for e in elems]

    @staticmethod
    def _glob_to_re(pattern: str, separator: str = "."):
        # To avoid corner cases for the first component, the candidate string
        # is prefixed with the separator so that e.g. `import tensorplay`
        # matches against `.tensorplay` (assuming '.' is the separator).
        def component_to_re(component):
            if "**" in component:
                if component == "**":
                    return "(" + re.escape(separator) + "[^" + separator + "]+)*"
                else:
                    raise ValueError("** can only appear as an entire path segment")
            else:
                return re.escape(separator) + ("[^" + separator + "]*").join(
                    re.escape(x) for x in component.split("*")
                )

        result = "".join(component_to_re(c) for c in pattern.split(separator))
        return re.compile(result)
Ask DeepWiki