glob.translate: Convert Glob Patterns to Regex

Published on: September 24, 2026
Reading time: 5 minutes
Code and file structure for Python glob.translate filters

The glob module is usually associated with finding files through patterns such as *.py, data/**/*.csv, or report-??.pdf. The glob.translate() function turns a glob pattern into a regular expression. This is valuable when you want the readable syntax of globs but need to match paths that come from an API, an archive, a database, cloud storage, or any in-memory collection rather than the local file system.

In this guide, you will learn how glob.translate works, how it differs from fnmatch.translate, how recursive matching and hidden files are handled, and how to build safe, testable path filters.

What glob.translate does

glob.translate(pattern) returns a regular-expression string compatible with Python’s re module. You can compile that expression once and reuse it across many path values.

import glob
import re

regex = re.compile(glob.translate("src/**/*.py", recursive=True))

paths = [
    "src/app.py",
    "src/api/routes.py",
    "tests/test_app.py",
]

selected = [path for path in paths if regex.match(path)]
print(selected)

The pattern is interpreted as a path pattern, not as arbitrary text. Directory separators therefore matter, and a normal * does not usually cross a separator.

Glob patterns versus regular expressions

Glob patterns are intentionally simple. An asterisk represents a sequence of characters, a question mark represents one character, and brackets define a character class. Regular expressions provide much more control, but they are harder to write and maintain. glob.translate offers a practical bridge: users can provide a simple glob, while your program works with a compiled regex.

This approach is especially helpful in configuration files, command-line tools, build systems, backup software, and dashboards where non-specialists need to define filters.

Recursive matching with double stars

With recursive=True, the ** pattern can represent multiple directory levels. This lets a single filter match deeply nested paths.

pattern = "project/**/test_*.py"
regex = re.compile(glob.translate(pattern, recursive=True))

for path in paths:
    if regex.match(path):
        print(path)

Without the recursive option, you should not assume that ** will traverse an arbitrary number of directories. Set the option explicitly so the behavior remains obvious to future maintainers.

Hidden files and directories

On Unix-like systems, names beginning with a dot are hidden. The include_hidden option controls whether wildcard segments may match those names. This matters in project scanners because directories such as .git, .venv, and .cache may contain thousands of files that you do not want to process.

regex = re.compile(
    glob.translate("**/*.toml", recursive=True, include_hidden=False)
)

Even when your paths are only strings, preserving the hidden-file rule makes the filter behave consistently with normal glob operations.

Custom path separators

The function can work with explicit separators. This is useful when paths come from ZIP archives, object storage, remote systems, or a database. ZIP members, for example, normally use forward slashes even on Windows.

regex = re.compile(
    glob.translate("assets/**/*.png", recursive=True, seps="/")
)

Fixing the separator can make test results predictable across operating systems. It also prevents a filter created on one platform from behaving differently on another.

glob.translate versus fnmatch.translate

fnmatch.translate is designed primarily for filename-style matching, whereas glob.translate understands path segments. In path matching, separators are special boundaries. In filename matching, the value is often treated as a single sequence.

Use fnmatch for simple names such as photo-*.jpg. Use glob.translate when directories, recursion, hidden components, or separator control are part of the requirement.

Compile the expression once

If a filter will be applied to thousands of paths, compile it once. Reusing a compiled regex avoids repeated translation and makes the filtering step easy to read.

def build_filter(pattern, *, recursive=False):
    expression = glob.translate(pattern, recursive=recursive)
    return re.compile(expression)

filter_regex = build_filter("logs/**/*.json", recursive=True)
results = [path for path in paths if filter_regex.match(path)]

If your application accepts many dynamic patterns, consider a bounded cache. Do not keep an unlimited number of compiled patterns because that can waste memory.

Validation and security

Glob patterns do not execute code, but broad patterns can still create performance problems. A pattern matching an entire storage bucket or a huge file tree may trigger excessive work. In a web application, never let an untrusted pattern scan the whole server. Restrict the root directory, maximum depth, pattern length, execution time, and result count.

When matching an in-memory list, file-system exposure is reduced, but denial-of-service style workloads are still possible. Limit input size and reject patterns that are abnormally long or complex.

Filtering cloud object keys

Suppose an API returns object keys from cloud storage, and you want only images inside one customer’s directory.

objects = [
    "customers/acme/logo.png",
    "customers/acme/docs/manual.pdf",
    "customers/acme/screens/home.webp",
    "customers/other/logo.png",
]

pattern = "customers/acme/**/*.[pw][ne][gb]"
regex = re.compile(glob.translate(pattern, recursive=True, seps="/"))
filtered = [item for item in objects if regex.match(item)]

For a large set of extensions, combining a path filter with a suffix check may be clearer than forcing all rules into one pattern. Readability is usually more important than clever syntax.

Using pathlib for normalization

pathlib provides object-oriented path handling. You can normalize remote-style paths with PurePosixPath before applying the regex.

from pathlib import PurePosixPath

normalized = [PurePosixPath(path).as_posix() for path in objects]
filtered = [path for path in normalized if regex.match(path)]

For related material, see our guides to pathlib in Python, regular expressions, the os module, and reading large files.

Testing path filters

Good tests should cover root-level files, deeply nested paths, hidden components, alternative separators, spaces, uppercase extensions, empty values, and near matches.

cases = {
    "src/main.py": True,
    "src/api/main.py": True,
    "src/.cache/main.py": False,
    "tests/main.py": False,
}

filter_regex = re.compile(glob.translate("src/**/*.py", recursive=True))
for path, expected in cases.items():
    assert bool(filter_regex.match(path)) is expected

Tests are particularly important when patterns are saved in configuration files, because a small change may silently include or exclude large groups of files.

Common mistakes

A common mistake is expecting * to cross directory boundaries. Another is forgetting that hidden names may require explicit handling. Developers also sometimes apply operating-system paths to a filter designed for archive paths without normalizing separators.

Another mistake is rebuilding the same regex inside a loop. Translation and compilation should happen before iteration. Finally, avoid relying on an undocumented detail of the generated regex. Treat the returned expression as an implementation output and use the public options to control behavior.

When glob.translate is a good fit

Use it when you need a user-friendly filter language and want to apply it outside direct disk traversal. Typical use cases include cloud storage listings, ZIP manifests, deployment packages, backup rules, code indexers, test discovery, asset pipelines, and build tools.

Consult the official glob documentation and the regular expression documentation for exact version details. Because function parameters can vary by Python version, verify compatibility when supporting older interpreters.

Conclusion

glob.translate combines the readability of glob syntax with the flexibility and reuse of compiled regular expressions. By understanding recursive matching, separators, hidden paths, validation, and testing, you can build filters that are portable and predictable. Keep patterns scoped, compile them once, normalize incoming paths, and enforce sensible limits whenever filters come from users or external systems.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Python code with annotations and type hints on a laptop
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: Resolve Deferred Annotations

    Learn Python annotationlib for safe annotation retrieval, forward references, deferred evaluation, and framework introspection.

    Ler mais

    Tempo de leitura: 7 minutos
    24/09/2026
    Developer programming in Python with SQLite and dbm.sqlite3
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    dbm.sqlite3: SQLite-Backed Key-Value Storage

    Learn Python dbm.sqlite3 for SQLite-backed key-value storage, safe serialization, migration, performance, and concurrency.

    Ler mais

    Tempo de leitura: 6 minutos
    23/09/2026
    Developer working with asynchronous tasks and Python TaskGroup eager_start
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    TaskGroup eager_start: Control Task Startup

    Learn how eager_start in asyncio.TaskGroup controls task startup, immediate execution, ordering, performance, cancellation, and compatibility.

    Ler mais

    Tempo de leitura: 6 minutos
    23/09/2026
    Developer working with static typing and Python typing.ReadOnly
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    typing.ReadOnly: Read-Only TypedDict Fields

    Learn Python typing.ReadOnly to declare read-only TypedDict keys and design safer, clearer data contracts.

    Ler mais

    Tempo de leitura: 6 minutos
    22/09/2026
    Python code representing positional arguments with functools.Placeholder
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: Fill Middle partial Arguments

    Learn Python functools.Placeholder to reserve middle arguments in partial, build clearer callbacks, and avoid unnecessary lambda wrappers.

    Ler mais

    Tempo de leitura: 5 minutos
    22/09/2026
    Python code with a deprecated API warning using warnings.deprecated
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    warnings.deprecated: Mark Deprecated APIs

    Learn Python warnings.deprecated to mark obsolete APIs, guide migrations, and integrate deprecation with typing, tests, documentation, and CI.

    Ler mais

    Tempo de leitura: 6 minutos
    21/09/2026