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.







