Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Filters

Filters can be used to restrict Ganzua to operate only on certain dependencies. Which filters are supported depends on the exact subcommand.

The following sections describe the syntax of filters, how filters are evaluated, and provide examples.

Syntax

A PEG-style grammar for the filter syntax would look as follows:

filter → ws? pattern ws? ( , ws? filter ws? )*

pattern → !? ( literal | * | ? )+

literal → /[a-zA-Z0-9._-]+/

ws → /\s+/

A filter consists of one or more glob patterns, separated by comma. Trailing commas are not allowed. Each pattern may be surrounded by whitespace.

Each pattern is a Unix glob expression, similar to what is supported in .gitignore files. A pattern can contain literal content, * asterisk metacharacters, and ? question mark metacharacters. The pattern must not be empty.

A negated pattern has a leading ! exclamation mark. There may not be any space between the exclamation mark and the rest of the pattern.

The * asterisk metacharacter matches zero or more arbitrary characters.

The ? question mark metacharacter matches a single arbitrary character.

Literal content can only consist of ASCII letters, ASCII digits, and PyPI name separators (._-). Filters are only used to match names (e.g. PyPI package names), so literal content is matched in a normalized manner. Literal content is not case sensitive, so the patterns A and a are equivalent. Separators all match each other as per the Python packaging name normalization rules, so the patterns a_b, a.b, and a--b are all equivalent.

Differences to .gitignore:

  • filter patterns are separated by commas, not by newlines
  • comments are not supported
  • escapes are not needed – metacharacters can never occur in a name
  • no special rules for directory separators – slashes can never occur in a name
  • other fnmatch syntax is not supported (see below)
  • no ** double asterisk metacharacter

Differences to fnmatch(3), glob(3), or glob(7):

  • no character classes [abc], ranges [a-z], or complementation [!a-z] (also called bracket expressions)
  • no brace expansion {a,b,c}-{1,2,3}
  • no special handling for leading . periods – names can never start with a separator
  • matching is always case-insensitive

References:

Evaluation

When matching a name against a filter, all patterns are evaluated in order. When a normal pattern matches, the name is explicitly included. When a negated pattern matches, the name is explicitly excluded. A name can flip between included and excluded state arbitrarily often, and only the final state matters. Filter matching does not short-circuit.

If a filter contains at least one normal (non-negated) pattern, each name is excluded by default, and at least one of the normal patterns must match. This is as-if such a filter started with a !* pattern. If a filter only contains negated patterns, names are included by default, and the negated patterns can exclude names. This is as-if the filter started with a * pattern.

These semantics are exactly how glob patterns work in ripgrep: https://github.com/BurntSushi/ripgrep/blob/4519153e5e461527f4bca45b042fff45c4ec6fb9/GUIDE.md#manual-filtering-globs.

Examples

Let's consider a project with the following lockfile:

full lockfile contents
nameversion
annotated-types0.7.0
click8.3.1
colorama0.4.6
coverage7.12.0
dirty-equals0.11
executing2.2.1
idna3.11
iniconfig2.3.0
inline-snapshot0.31.1
multidict6.7.0
mypy1.18.2
mypy-extensions1.1.0
packaging25.0
pluggy1.6.0
propcache0.4.1
pydantic2.12.4
pydantic-core2.41.5
pygments2.19.2
pytest9.0.1
pytest-cov7.0.0
tomlkit0.13.3
typing-extensions4.15.0
typing-inspection0.4.2
yarl1.22.0

We can use a name filter to select one or more specific packages from the lockfile:

$ ganzua inspect $EXAMPLE --name=pydantic --format=markdown
| package  | version |
|----------|---------|
| pydantic | 2.12.4  |
$ ganzua inspect $EXAMPLE --name=pydantic,mypy,pytest-cov --format=markdown
| package    | version |
|------------|---------|
| mypy       | 1.18.2  |
| pydantic   | 2.12.4  |
| pytest-cov | 7.0.0   |

We can use glob patterns to select all packages that start with py:

$ ganzua inspect $EXAMPLE --name='py*' --format=markdown
| package       | version |
|---------------|---------|
| pydantic      | 2.12.4  |
| pydantic-core | 2.41.5  |
| pygments      | 2.19.2  |
| pytest        | 9.0.1   |
| pytest-cov    | 7.0.0   |

Or all packages that contain py but do not start with py:

$ ganzua inspect $EXAMPLE --name='*py*, !py*' --format=markdown
| package         | version |
|-----------------|---------|
| mypy            | 1.18.2  |
| mypy-extensions | 1.1.0   |

Using the question mark operator, we can select all packages that have a 4-letter name:

$ ganzua inspect $EXAMPLE --name='????' --format=markdown
| package | version |
|---------|---------|
| idna    | 3.11    |
| mypy    | 1.18.2  |
| yarl    | 1.22.0  |

When only using negated patterns, results are included by default. Here, we exclude all patterns that contain a hyphen, or start with the letters p or c:

$ ganzua inspect $EXAMPLE --name='!*-*, !p*, !c*' --format=markdown
| package   | version |
|-----------|---------|
| executing | 2.2.1   |
| idna      | 3.11    |
| iniconfig | 2.3.0   |
| multidict | 6.7.0   |
| mypy      | 1.18.2  |
| tomlkit   | 0.13.3  |
| yarl      | 1.22.0  |

Filters are case-insensitive, and hyphens/underscores/periods are all equivalent. Thus, all of these filters produce the same results:

  • $ ganzua inspect $EXAMPLE --name='*ex*,!*-*' --format=markdown
  • $ ganzua inspect $EXAMPLE --name='*EX*,!*...*' --format=markdown
  • $ ganzua inspect $EXAMPLE --name='*eX*,!*_*' --format=markdown
output for the above commands
| package   | version |
|-----------|---------|
| executing | 2.2.1   |

Some further filter syntax details that describe edge cases:

filters may be surrounded by spaces
$ ganzua inspect $EXAMPLE --name='  foo , bar, pytest  ' --format=markdown
| package | version |
|---------|---------|
| pytest  | 9.0.1   |
filters must not be empty
$ ganzua inspect $EXAMPLE --name='' --format=markdown
Usage: ganzua inspect [OPTIONS] [LOCKFILE]
Try 'ganzua inspect --help' for help.

Error: Invalid value for '--name': expected filter pattern
at offset 0 (EOF):
  |
  |^
[command exited with status 2]
$ ganzua inspect $EXAMPLE --name='foo,' --format=markdown
Usage: ganzua inspect [OPTIONS] [LOCKFILE]
Try 'ganzua inspect --help' for help.

Error: Invalid value for '--name': expected filter pattern
at offset 4 (EOF):
  |foo,
  |    ^
[command exited with status 2]
syntax errors
$ ganzua inspect $EXAMPLE --name='must not contain spaces' --format=markdown
Usage: ganzua inspect [OPTIONS] [LOCKFILE]
Try 'ganzua inspect --help' for help.

Error: Invalid value for '--name': unexpected content after filter pattern
at offset 5 (char 'n' U+006E LATIN SMALL LETTER N):
  |must not contain spaces
  |     ^
[command exited with status 2]
$ ganzua inspect $EXAMPLE --name='/slashes/not/supported/' --format=markdown
Usage: ganzua inspect [OPTIONS] [LOCKFILE]
Try 'ganzua inspect --help' for help.

Error: Invalid value for '--name': expected filter pattern
at offset 0 (char '/' U+002F SOLIDUS):
  |/slashes/not/supported/
  |^
[command exited with status 2]
$ ganzua inspect $EXAMPLE --name='Ÿñiçøðœ' --format=markdown
Usage: ganzua inspect [OPTIONS] [LOCKFILE]
Try 'ganzua inspect --help' for help.

Error: Invalid value for '--name': expected filter pattern
at offset 0 (char 'Ÿ' U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS):
  |Ÿñiçøðœ
  |^
[command exited with status 2]
Bracket expressions and brace expansion are not supported

Conventional fnmatch syntax allows character classes like [a-z] or [!a-z]. This doesn't seem overly helpful for matching package names, so hasn't been implemented yet.

$ ganzua inspect $EXAMPLE --name='ex[a-z]mple' --format=markdown
Usage: ganzua inspect [OPTIONS] [LOCKFILE]
Try 'ganzua inspect --help' for help.

Error: Invalid value for '--name': bracket expressions not supported
at offset 2 (char '[' U+005B LEFT SQUARE BRACKET):
  |ex[a-z]mple
  |  ^
[command exited with status 2]

Similarly, brace expansion is not supported. Instead, it's usually possible to write multiple separate filters.

$ ganzua inspect $EXAMPLE --name='foo{,-bar}' --format=markdown
Usage: ganzua inspect [OPTIONS] [LOCKFILE]
Try 'ganzua inspect --help' for help.

Error: Invalid value for '--name': brace expansion not supported
at offset 3 (char '{' U+007B LEFT CURLY BRACKET):
  |foo{,-bar}
  |   ^
[command exited with status 2]