Plugins
Plugins are the basic unit for rules application. Plugins defines actions which can be either verbs or conditionals.
conditionals filter the execution of verbs in a rule. If all the conditionals of a rule returns
true
, the verbs are executed. Conditionals are identified because they start with the prefixif
.verbs execute actions against the files defined in the special
files
property of each rule. They act like asserters.
inclusion
Plain content inclusion management.
includeLines
Check that the files include all lines passed as argument.
If the files don’t include all lines specified as argument, it will raise a checking error. Newlines are ignored, so they should not be specified.
Example
{
rules: [
files: [".gitignore"],
includeLines: ["venv*/", "/dist/"]
]
}
New in version 0.1.0.
ifIncludeLines
Conditional to exclude rule only if some files include a set of lines.
If one file don’t include all lines passed as parameter, the rule will be ignored.
Accepts an object mapping files to lines that must be included in order to execute the rule.
Example
If the license defined in the LICENSE file is BSD-3, tool.poetry.license
must correspont:
{
rules: [
files: ["pyproject.toml"],
ifIncludeLines: {
LICENSE: ["BSD 3-Clause License"],
},
JMESPathsMatch: [
["tool.poetry.license", "BSD-3-License"],
]
]
}
New in version 0.1.0.
excludeContent
Check that the files do not include certain content.
The specified partial contents can match multiple lines and line ending characters.
Example
Don’t allow code blocks in RST documentation files:
Bash is not a POSIX compliant shell.
Pygments’ JSON5 lexer is not implemented yet.
{
rules: [
files: ["docs/**/*.rst"],
excludeContent: [
".. code-block:: ",
".. code-block:: bash",
".. code-block:: json5",
],
]
}
New in version 0.3.0.
existence
Check existence of files.
ifFilesExist
Check if a set of files and/or directories exists.
Accepts an array of paths. If a path ends with /
character it is
considered a directory.
Examples
If the directory src/ exists, a pyproject.toml file must exist also:
{
rules: [
files: ["pyproject.toml"],
ifFilesExist: ["src/"],
]
}
If the file .pre-commit-hooks.yaml exists, must be declared as an array:
{
rules: [
files: [".pre-commit-hooks.yaml"],
ifFilesExist: [".pre-commit-hooks.yaml"],
JMESPathsMatch: [["type(@)", "array"]]
]
}
New in version 0.4.0.
jmespath
JMES paths manipulation against files.
The actions of this plugin operates against object-serialized versions of files, so only files that can be serialized can be targetted (see Objects serialization).
You can use in expressions all JMESPath builtin functions plus a set of convenient functions defined by the plugin internally:
- regex_match(pattern: str, string: str[, flags: int=0]) bool
Match a regular expression against a string using the Python’s built-in
re.match()
function.New in version 0.1.0.
Changed in version 0.5.0: Allow to pass
flags
optional argument as an integer.
- regex_matchall(pattern: str, strings: list[str]) bool
Match a regular expression against a set of strings defined in an array using the Python’s built-in
re.match()
function.New in version 0.1.0.
Deprecated since version 0.4.0.
- regex_search(pattern: str, string: str[, flags: int=0]) list[str]
Search using a regular expression against a string using the Python’s built-in
re.search()
function. Returns all found groups in an array or an array with the full match as the unique item if no groups are defined. If no results are found, returns an empty array.New in version 0.1.0.
Changed in version 0.5.0: Allow to pass
flags
optional argument as an integer.
- op(source: type, operation: str, target: type) bool
Applies the operator operator between the two values using the operators for two values defined in
op
. The next operators are available:<=
:operator.le()
==
:operator.eq()
!=
:operator.ne()
>=
:operator.ge()
is
:operator.is_()
is_not
:operator.is_not()
is-not
:operator.is_not()
is not
:operator.is_not()
isNot
:operator.is_not()
and
:operator.and_()
or
:operator.or_()
**
:operator.pow()
count_of
:operator.countOf()
count of
:operator.countOf()
count-of
:operator.countOf()
countOf
:operator.countOf()
index_of
:operator.indexOf()
index of
:operator.indexOf()
index-of
:operator.indexOf()
indexOf
:operator.indexOf()
If
source
andtarget
are both of type array and the operator is one of the next ones, the arrays are converted toset
before applying the operator:<=
:operator.le()
>=
:operator.ge()
and
:operator.and_()
or
:operator.or_()
New in version 0.1.0.
Changed in version 0.4.0: Convert to
set
before applying operators if both arguments are arrays.
- shlex_split(cmd_str: str) list
Split a string using the Python’s built-in
shlex.split()
function.New in version 0.4.0.
- shlex_join(cmd_list: list[str]) str
Join a list of strings using the Python’s built-in
shlex.join()
function.New in version 0.4.0.
- round(number: float[, precision: int]) float
Round a number to a given precision using the function
round()
.New in version 0.5.0.
- range([start: float, ]stop: float[, step: float]) list
Return an array of numbers from
start
tostop
with a step ofstep
casting the result of the constructorrange
to an array.New in version 0.5.0.
- capitalize(string: str) str
Capitalize the first letter of a string using
str.capitalize()
.New in version 0.5.0.
- casefold(string: str) str
Return a casefolded copy of a string using
str.casefold()
.New in version 0.5.0.
- center(string: str, width: int[, fillchar: str]) str
Return centered in a string of length
width
usingstr.center()
.New in version 0.5.0.
- count(value: str | list, sub: any[, start: int[, end: int]]) int
Return the number of occurrences of
sub
invalue
usingstr.count()
. Ifstart
andend
are given, return the number of occurrences betweenstart
andend
. .New in version 0.5.0.
- find(string: str | list, sub: any[, start: int[, end: int]]) int
Return the lowest index in
value
where subvaluesub
is found. Ifstart
andend
are given, return the number of occurrences betweenstart
andend
. If not found,-1
is returned. Ifvalue
is a string it uses internally the Python’s built-in functionstr.find()
orstr.index()
ifvalue
is an array.New in version 0.5.0.
- format(schema: str, *args: any) str
Return a string formatted using the Python’s built-in
format()
function. The variableschema
only accepts numeric indexes delimited by braces{}
for positional arguments in*args
.New in version 0.5.0.
- isalnum(string: str) bool
Return True if all characters in
string
are alphanumeric usingstr.isalnum()
.New in version 0.5.0.
- isalpha(string: str) bool
Return True if all characters in
string
are alphabetic usingstr.isalpha()
.New in version 0.5.0.
- isascii(string: str) bool
Return True if all characters in
string
are ASCII usingstr.isascii()
.New in version 0.5.0.
- isdecimal(string: str) bool
Return True if all characters in
string
are decimal usingstr.isdecimal()
.New in version 0.5.0.
- isdigit(string: str) bool
Return True if all characters in
string
are digits usingstr.isdigit()
.New in version 0.5.0.
- isidentifier(string: str) bool
Return True if all characters in
string
are identifiers if the string is a valid identifier according to the Python language definition usingstr.isidentifier()
.New in version 0.5.0.
- islower(string: str) bool
Return True if all characters in
string
are lowercase usingstr.islower()
.New in version 0.5.0.
- isnumeric(string: str) bool
Return True if all characters in
string
are numeric usingstr.isnumeric()
.New in version 0.5.0.
- isprintable(string: str) bool
Return True if all characters in
string
are printable usingstr.isprintable()
.New in version 0.5.0.
- isspace(string: str) bool
Return True if all characters in
string
are whitespace usingstr.isspace()
.New in version 0.5.0.
- istitle(string: str) bool
Return True if all characters in
string
are titlecased usingstr.istitle()
.New in version 0.5.0.
- isupper(string: str) bool
Return True if all characters in
string
are uppercase usingstr.isupper()
.New in version 0.5.0.
- ljust(string: str, width: int[, fillchar: str]) str
Return a left-justified version of the string using
str.ljust()
.New in version 0.5.0.
- lower(string: str) str
Return a lowercased version of the string using
str.lower()
.New in version 0.5.0.
- lstrip(string: str[, chars: str]) str
Return a left-stripped version of the string using
str.lstrip()
.New in version 0.5.0.
- partition(string: str, sep: str) list[str]
Return an array of 3 items containing the part before the separator, the separator itself, and the part after the separator.
New in version 0.5.0.
- removeprefix(string: str, prefix: str) str
Return a string with the given prefix removed using
str.removeprefix()
.New in version 0.5.0.
- removesuffix(string: str, suffix: str) str
Return a string with the given suffix removed using
str.removesuffix()
.New in version 0.5.0.
- rfind(string: str | list, sub: any[, start: int[, end: int]]) int
Return the highest index in
value
where subvaluesub
is found. Ifstart
andend
are given, return the number of occurrences betweenstart
andend
. If not found,-1
is returned. Ifvalue
is a string it uses internally the Python’s built-in functionstr.find()
orstr.index()
ifvalue
is an array.New in version 0.5.0.
- rjust(string: str, width: int[, fillchar: str]) str
Return a right-justified version of the string using
str.rjust()
.New in version 0.5.0.
- rpartition(string: str, sep: str) list[str]
Return an array of 3 items containing the part after the separator, the separator itself, and the part before the separator splitting the string at the last occurrence of
sep
.New in version 0.5.0.
- rsplit(string: str[, sep: str[, maxsplit: int]]) list[str]
Return a list of the words in the string, using
sep
as the delimiter string as returned from the methodstr.rsplit()
. Except for splitting from the right,rsplit()
behaves likesplit()
.New in version 0.5.0.
- rstrip(string: str[, chars: str]) str
Return a right-stripped version of the string using
str.rstrip()
.New in version 0.5.0.
- split(string: str[, sep: str[, maxsplit: int]]) list[str]
Return a list of the words in the string, using
sep
as the delimiter string as returned from the methodstr.split()
. Ifsep
is not given, it defaults toNone
, meaning that any whitespace string is a separator.New in version 0.5.0.
- splitlines(string: str[, keepends: bool]) list[str]
Return a list of the lines in the string, breaking at line boundaries using the method
str.splitlines()
.New in version 0.5.0.
- strip(string: str[, chars: str]) str
Return a stripped version of the string using
str.strip()
.New in version 0.5.0.
- swapcase(string: str) str
Return a swapped-case version of the string using
str.swapcase()
.New in version 0.5.0.
- title(string: str) str
Return a titlecased version of the string using
str.title()
.New in version 0.5.0.
- upper(string: str) str
Return an uppercased version of the string using
str.upper()
.New in version 0.5.0.
- zfill(string: str, width: int) str
Return a zero-padded version of the string using
str.zfill()
.New in version 0.5.0.
- enumerate(string: str | list | dict) list[list[int, str]]
Return an array of arrays containing the index and value of each item in the iterable. If the iterable is an object, the value is converted before using
to_items()
.New in version 0.5.0.
- to_items(string: dict) list[list[str, any]]
Convert an object to an array of arrays containing the key and value of each item.
New in version 0.5.0.
- from_items(items: list[list[str, any]]) dict
Convert an array of arrays containing the key and value of each item to an object.
New in version 0.5.0.
JMESPathsMatch
Compares a set of JMESPath expressions against results.
Object-serializes each file in the files
property of the rule
and executes each expression given in the first item of the
tuples passed as value. If a result don’t match, report an error.
Example
The .editorconfig file must have the next content:
root = true
[*]
end_of_line = lf
charset = utf-8
indent_style = space
trim_trailing_whitespace = true
{
rules: [
{
files: [".editorconfig"],
JMESPathsMatch: [
['"".root', true],
['"*".end_of_line', "lf"],
['"*".indent_style', "space"],
['"*".charset', "utf-8"],
['"*".trim_trailing_whitespace', true],
],
}
]
}
New in version 0.1.0.
crossJMESPathsMatch
JMESPaths matching between multiple files.
Accepts an array of arrays. Each one of these arrays must have the syntax:
[
"filesJMESPathExpression", // expression to query each file in `files` property of the rule
["otherFile.ext", "JMESPathExpression"]..., // optionally other files
"finalJMESPathExpression", // an array with results of previous expressions as input
expectedValue, // value to compare with the result of final JMESPath expression
]
The executed steps are:
For each object-serialized file in
files
property of the rule.Execute
"filesJMESPathExpression"
and append the result to a temporal array.For each pair of
["otherFile.ext", "JMESPathExpression"]
, execute"JMESPathExpression"
against the object-serialized version of"otherFile.ext"
and append each result to the temporal array.Execute
"finalJMESPathExpression"
against the temporal array.Compare the final result with
expectedValue
and raise error if not match.
Tip
Other file paths can be URLs if you want to match against online sources.
Example
The release
field of a Sphinx configuration defined in a file
docs/conf.py must be the same that the version of the project metadata
defined in th file pyproject.toml, field tool.poetry.version
:
{
rules: [
{
files: ["pyproject.toml"],
crossJMESPathsMatch: [
[
"tool.poetry.metadata",
["docs/conf.py", "release"],
"op([0], '==', [1])",
true,
],
],
hint: "Versions of documentation and metadata must be the same"
}
]
}
Note that you can pass whatever number of other files, even 0 and just apply
files
and final
expressions to each file in files
property of
the rule. For example, the next configuration would not raise errors:
{
rules: [
{
files: ["foo.json"],
crossJMESPathsMatch: [
["bar", "[0].baz", 7],
]
}
]
}
{"bar": {"baz": 7}}
New in version 0.4.0.
ifJMESPathsMatch
Compares a set of JMESPath expressions against results.
JSON-serializes each file in the ifJMESPathsMatch
property
of the rule and executes each expression given in the first item of the
tuples passed as value for each file. If a result don’t match,
skips the rule.
Example
If inline-quotes
config of flake8 is defined to use double quotes,
Black must be configured as the formatting tool in pyproject.toml
:
{
rules: [
{
files: ["pyproject.toml"],
ifJMESPathsMatch: {
"pyproject.toml": [
["tool.flakeheaven.inline_quotes", "double"],
],
},
JMESPathsMatch: [
["contains(keys(@), 'tool')", true],
["contains(keys(tool), 'black')", true],
}
}
]
}
New in version 0.1.0.