Sifty Dingo Filtering Language

The kojismokydingo.sift package implements a minimal filtering language tentatively named "sifty dingo". The language is based on predicates written as a sequence of s-expressions.

Language Example

(flag inactive (!status ACTIVE))
(flag old-guard (not (inactive?)) (joined-before 2000-01-01))
(flag noob (not (inactive?)) (joined-after 2020-01-01))
(!flagged noob old-guard inactive)

This example presumes three predicates have been created; joined-after, joined-before, and status. Each of these take a single argument at their initialization.

When a Sifter is compiled from the above source, it will contain four Sieve rules. When that sifter instance is invoked on a set of data, each of the four rules will be invoked in order. The first three rules will set their relevant flags on any data entries which matched their predicates. The fourth rule has no explicit flag, and so its results would be implicitly flagged with "default"

Loosely translated, this example means:

  • flag as inactive anyone whose status is not ACTIVE

  • flag as old-guard anyone that isn't flagged inactive and whose joined-before predicate is true for the value 2000-01-01

  • flag as a noob anyone that isn't flagged inactive and whose joined-after predicate is true for the value 2020-10-01

  • flag as default anyone what doesn't have the noob, old-guard, nor inactive flags

Basic Syntax

The language is composed of a series of nested sieve predicates, each with a symbol as its first element denoting the name of the sieve. Subsequent elements are the arguments to the predicate.

Sieve Predicate

(foo)
(foo 1 two "three" /four/ |five|)
(bar x y)
(bar x y optional: True)
(and (foo 1) (bar 2))
(or (baz 3) (and (qux) (quux 4)))

A sieve predicate bears the form of an S-Expression, with opening and closing parenthesis characters denoting the contents. The first element identifies the name of the predicate.

Sieve predicates are not used to evalute to a value -- they are True/False tests which are applied to the data which is fed to the fully compiled sifter. Because of this, it is rare that they are nested except in the case of the logical combining predicates.

Symbol

foo
Tacos-and-Pizza
1.2.34

Symbols are unquoted alphanumerics. They are white-space terminated. Symbols are used to resolve the class of a predicate when they are the first element of a sieve. Symbols are also a valid matching type, and they function similarly to a string. A symbol cannot be entirely numeric -- it would instead be interpreted as a Number in that case.

Symbols with a leading dollar-sign are interpreted as variables, and their value will be substituted from a sifter parameter at compile time.

When used as arguments to a sieve predicate, symbols with a trailing : character are treated as keyword markers. The value following a keyword marker is used as the keyword's argument. They keyword and its value form an option pair, which is fed into the sieve via its set_options method.

Number

123
002
-1

Numbers are unquoted series of digits, with an optional leading negative sign. They can be compared with both integer and string values.

Symbol Group

foo-{001..005}
{foo,bar}-001
{hello,goodbye}-{cruel,happy}-world
109{2,4,5}1
10{002..106..2}

A Symbol Group is an entity which represents a collection of symbols based on some substitutions. Substitutions are bounded in matching '{' and '}' characters. Substitutions may be either a comma-separated list of values, or a double-dotted range. A Symbol Group matches any string value that is represented by the product of its substitutions.

If a Symbol Group is entirely numeric, it will match with rules similar to Number.

If any part of a substitution is malformed, that substitution will be treated as a single symbol value. If a Symbol Group contains only one possible product, it will become a simple Symbol.

String

"Foo bar"

A string is quoted with matching '"' characters. Normal escape sequences are honored.

Strings may interpolate variables from the sifter at compile time using Python's str.format markup rules.

Regex

/^Foo.*Bar$/
/^FOO.*BAR$/i

A Regex is quoted with matching '/' characters. Optional flags can be appendes to the regex by specifying the characters immediately after the closing '/'

Glob

|foo*|
|FOO*|i

A Glob is quoted with matching '|' characters. An optional trailing 'i' can be used to indicate the glob matching is case-insensitive.

Item Path

.foo
.bar[].qux
[2::1].baz[{ping,pong}]

An item path is a way to select elements of the given data objects for matching.

Item paths can be used as the first argument to the built-in item predicate.

Using an item path as the first element in a sieve is also a shortcut for invoking the item predicate. These are equivalent expressions:

  • (.foo {100..200})

  • (item .foo {100..200})

Core Sieves

The language supports three logical expressions; and, or, and not. Each of these apply a logical constraint on top of other expressions. The language also provides a way to set flags via tha flag expression, and to check flags via the flagged predicate. There final built-in predicate is item which is used to do value comparisons against the data structures themselves.

Statement flag

(flag NAME EXPR [EXPR...])

Acts like the and logical expression. In addition to passing its matches, this expression will also set the given flag name on each data item that matched all sub-expressions.

Logical and

(and EXPR [EXPR...])

Matches data items which pass through all of the sub-expressions. Once a data item fails to match, it will not be passed along to further sub-expressions.

Logical or

(or EXPR [EXPR...])

Matches data items which pass through any of the sub-expressions. Once a data item has been matched, it will not be passed along to further sub-expressions.

Logical not

(not EXPR [EXPR...])

Matches data items which pass none of the sub-expressions. Once a data item has been matched, it will not be passed along to further sub-expressions.

As a convenience, ! is a synonym for not.

Any expression can be inverted by prefixing it with ! or not-. For example, all of these are equivalent expressions:

  • (not (foo 1))

  • (not-foo 1)

  • (! (foo 1))

  • (!foo 1)

Predicate flagged

(flagged NAME [NAME...])

Matches data items which have had any of the named flags applied to it previously.

As a convenience, ? is a synonym for flagged.

In addition, any flag can be used as its own predicate by appending a ? to its name. For example, the following are equivalent:

  • (flagged awesome)

  • (? awesome)

  • (awesome?)

Predicate item

(item PATH [VALUE...])

Resolves an ItemPath against each data item. If any values are supplied as an argument, then the predicate will pass any data items which has any path element that matches to any of the values. If no values are supplied then the path elements simply need to be present and non-null.

The item predicate may be specified implicitly by making the first element of the sieve an ItemPath. For example, the following are equivalent:

  • (item .foo[].bar {1..100})

  • (.foo[].bar {1..100})

Build Sieves

To facilitate filtering sequences of koji build info dicts, there are a number of available sieves provided in the kojismokydingo.sift.builds module.

A sifter instance with these and the core sieves available by default can be created via kojismokydingo.sift.builds.build_info_sifter()

Build EVR Comparison Predicates

(OP VER)

OP can be any of the following comparison operators:

  • ==

  • !=

  • >

  • >=

  • <

  • <=

VER can be in any of the following forms:

  • EPOCH:VERSION

  • EPOCH:VERSION-RELEASE

  • VERSION

  • VERSION-RELEASE

If EPOCH is omitted, it is presumed to be 0. If RELEASE is omitted, it is presumed to be equivalent.

These predicates filter by using RPM EVR comparison rules against the epoch, version, and release values of the builds.

Build Predicate cg-imported

(cg-imported [CGNAME...])

Filters for builds which were produced by a koji Content Generator via the CGImport API. Such builds would have no task ID associated with them.

If any optional CGNAME matchers are supplied, then filters for builds which are produced by matching content generators only.

Build Predicate compare-latest-id

(compare-latest-id OP TAG)

Filters for builds which have an ID that compares to the latest build of the same package name in the given tag. If there is no matching build in the tag, then the filtered build will not be included.

OP can be any of the following comparison operators: ==, !=, >, >=, <, <=

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate compare-latest-nvr

(compare-latest-nvr OP TAG)

Filters for builds which have an NVR that compares to the latest build of the same package name in the given tag. If there is no matching build in the tag, then the filtered build will not be included.

OP can be any of the following comparison operators: ==, !=, >, >=, <, <=

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate epoch

(epoch EPOCH [EPOCH...])

Filters for builds whose epoch value matches any of the given EPOCH patterns.

Build Predicate evr-high

(evr-high [count: COUNT])

Filters for builds with the highest EVR for each package name among the original series.

COUNT if specified must be a positive integer greater than zero, representing the maximum number of highest-EVR builds to match for each package name. Default behavior is a count of 1 (highest only).

Build Predicate evr-low

(evr-low [count: COUNT])

Filters the builds to only the lowest EVR for each package name among the original series.

COUNT if specified must be a positive integer greater than zero, representing the maximum number of lowest-EVR builds to match for each package name. Default behavior is a count of 1 (lowest only).

Build Predicate imported

(imported)

Filters for builds which have no task ID. These builds could be either raw imports or from a content generator.

Build Predicate inherited

(inherited TAG [TAG...])

Filters for builds which are tagged in any of the given TAG or their parents.

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate latest

(latest TAG [TAG...])

Filters for builds which are the latest of their package name in any of the given TAG, following inheritance and honoring package listings and blocks.

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate latest-maven

(latest-maven TAG [TAG...])

Filters for maven builds which are the latest of their GAV (group, artifact, version) in any of the given TAG, following inheritance and honoring package listings and blocks.

This differs from the latest predicate in that multiple copies of the same package may be considered the latest using this method. The uniqueness is by the GAV rather than the package name.

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate name

(name NAME [NAME...])

Filters for builds which have a name matching any of the given NAME patterns.

Build Predicate nvr

(nvr NVR [NVR...])

Filters for builds which have an NVR matching any of the given NVR (name-version-release) patterns.

Build Predicate owner

(owner USER [USER...])

Filters for builds whose owner's name or ID matches any of the given USER. USER may be specified by either name or ID, but not by pattern. USER will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchUser exception being raised.

Build Predicate pkg-allowed

(pkg-allowed TAG [TAG...])

Filters for builds whose package name is allowed in any of the given tags, honoring inheritance.

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate pkg-blocked

(pkg-blocked TAG [TAG...])

Filters for builds whose package name is explicitly blocked in any of the given tags, honoring inheritance.

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate pkg-unlisted

(pkg-unlisted TAG [TAG...])

Filters for builds which have their package name unlisted (neither allowed nor blocked) in any of the given tags, honoring inheritance.

TAG may be specified by either name or ID, but not by pattern. TAG will be validated when the sieve is first run -- this may result in a kojismokydingo.NoSuchTag exception being raised.

Build Predicate release

(release REL [REL...])

Filters for builds which have a release matching any of the given REL patterns.

Build Predicate signed

(signed [SIGKEY...])

Filters for builds which have an RPM archive that has been signed with a key matching any of the given SIGKEY patterns.

If no SIGKEY patterns are supplied, then filters for builds which have an RPM archive that has been signed with any key.

Build Predicate state

(state STATE [STATE...])

Filters for builds which are in one of the given build states. Each STATE may be specified as either a name or a state ID, but each must be a valid koji build state.

Valid states are:

  • 1 BUILDING

  • 2 COMPLETE

  • 3 DELETED

  • 4 FAILED

  • 5 CANCELED

Build Predicate tagged

(tagged [TAG...])

Filters for builds which are tagged with a tag having a name or ID matching any of the given TAG patterns.

If no TAG patterns are specified, then filters for builds which have any tags at all.

Build Predicate type

(type BTYPE [BTYPE...])

Filters for builds which have archives of the given build type. Normal build types are rpm, maven, image, and win. Koji instances may support plugins which extend the available build types beyond these.

Build Predicate version

(version VER [VER...])

Filters for builds which have a version matching any of the given VER patterns.

Add-On Build Predicates

The koji plugin commands filter-builds and list-component-builds, and the standalone command ksd-filter-tags will attempt to load additional user-defined build sieves utilizing Python entry_points. This behavior may be disabled by with the --no-entry-points option.

In order to provide additional tag sieves, a Python package must supply an entry_point to a nullary function which will return a list of Sieve classes, under the group named koji_smoky_dingo_build_sieves

Tag Sieves

To facilitate filtering sequences of koji tag info dicts, there are a number of available sieves provided in the kojismokydingo.sift.tags module.

A sifter instance with these and the core sieves available by default can be created via kojismokydingo.sift.tags.tag_info_sifter()

Tag Predicate arch

(arch [ARCH...])

If no ARCH patterns are specified, matches tags which have any architectures at all.

If ARCH patterns are specified, then only matches tags which have an architecture that matches any of the given patterns.

Tag Predicate build-tag

(build-tag [TARGET...])

If no TARGET is specified, then matches tags which are used as the build tag for any target.

If any TARGET patterns are specified, then matches tags which are used as the build tag for a target with a name matching any of the patterns.

Tag Predicate compare-latest

(compare-latest PACKAGE [OP VERSION])

If OP and VERSION are not specified, matches tags which have any build of the given PACKAGE name as latest.

If OP and VERSION are specified, matches tags which have the a latest build of the given PACKAGE name which compare correctly. If tag doesn't have any build of the given package, it will not match.

OP can be any of the following comparison operators: ==, !=, >, >=, <, <=

VERSION can be in any of the following forms:

  • EPOCH:VERSION

  • EPOCH:VERSION-RELEASE

  • VERSION

  • VERSION-RELEASE

If EPOCH is omitted, it is presumed to be 0. If RELEASE is omitted, it is presumed to be equivalent.

Tag Predicate dest-tag

(dest-tag [TARGET...])

If no TARGET is specified, then matches tags which are used as the destination tag for any target.

If any TARGET patterns are specified, then matches tags which are used as the destination tag for a target with a name matching any of the patterns.

Tag Predicate exact-arch

(exact-arch [ARCH...])

If no ARCH names are specified, matches only tags which have no architectures.

If ARCH names are specified, they must be specified as symbols. Only matches tags which have the exact same set of architectures.

Tag Predicate group

(group GROUP [GROUP...])

Matches tags which have any of the given install groups configured. Honors inheritance.

Tag Predicate group-pkg

(group-pkg GROUP PKG [PKG...] [require_all: False])

Matches tags which have the given install group, which also contains any of the given PKG names.

If require_all is set to True then all of the given PKG names must be present in order for a tag to match.

Tag Predicate has-ancestor

(has-ancestor [TAG...])
(inherits-from [TAG...])

If no TAG patterns are specified, matches tags which have any parents.

If TAG patterns are specified, matches tags which have a parent at any depth matching any of the given patterns.

Tag Predicate has-child

(has-child [TAG...])
(parent-of [TAG...])

If no TAG patterns are specified, matches tags which are the direct parent to any other tag.

If TAG patterns are specified, matches tags which are the direct parent to any tag matching any of the given patterns.

Tag Predicate has-descendant

(has-descendant [TAG...])
(inherited-by [TAG...])

If no TAG patterns are specified, matches tags which are inherited by any other tag.

If TAG patterns are specified, matches tags which are inherited by any tag matching any of the patterns, at any depth.

Tag Predicate has-parent

(has-parent [TAG...])
(child-of [TAG...])

If no TAG patterns are specified, matches tags which have any parents.

If TAG patterns are specified, matchs tags which have any direct parent matching any of the given patterns.

Tag Predicate locked

(locked)

Matches tags which have been locked.

Tag Predicate name

(name NAME [NAME...])

Matches tags which have a name that matches any of the given NAME patterns.

Tag Predicate permission

(permission [PERM...])

If no PERM is specified, then matches tags which have any permission set.

If any PERM patters are specified, then matches tags which have any of the listed permissions set.

Tag Predicate pkg-allowed

(pkg-allowed PKG [PKG...])

Matches tags which have a package listing with any of the given PKG contained therein and not blocked, honoring inheritance.

Tag Predicate pkg-blocked

(pkg-blocked PKG [PKG...])

Matches tags which have a package listing with any of the given PKG contained therein and blocked, honoring inheritance.

Tag Predicate pkg-unlisted

(pkg-unlisted PKG [PKG...])

Matches tags which have no package listing (neither allowed nor blocked) for any of the given PKG names. Honors inheritance.

Add-On Tag Predicates

The koji plugin command filter-tags and the standalone command ksd-filter-tags will attempt to load additional user-defined tag sieves utilizing Python entry_points. This behavior may be disabled by with the --no-entry-points option.

In order to provide additional tag sieves, a Python package must supply an entry_point to a nullary function which will return a list of Sieve classes, under the group named koji_smoky_dingo_tag_sieves