lint¶
Recipe Linter
Writing additional checks¶
Lint checks are defined in bioconda_utils.lint.checks
as
subclasses of LintCheck
. It might be easiest to have a look at that
module and the already existing checks and go from there.
Briefly, each class becomes a check by:
The class name becomes the name of the check in the documentation and when skipping lints. The convention is to use lower case separated by underscore.
The docstring is used to describe the check on the command line, on Github when the check fails and in the documentation.
The first line is considered a title or one line summary used where a brief description is needed. The remainder is a long desription and should include brief help on how to fix the detected issue.
The class property
severity
defaults toERROR
but can be set toINFO
orWARNING
for informative checks that should not cause linting to fail.The class property
requires
may contain a list of other check classes that are required to have passed before this check is executed. Use this to avoid duplicate errors presented, or to ensure that asumptions made by your check are met by the recipe.Each class is instantiated once per linting run. Do slow preparation work in the constructor. E.g. the
recipe_is_blacklisted
check loads and parses the blacklist here.As each recipe is linted, your check will get called on three functions:
check_recipe
,check_deps
andcheck_source
.The first is simply passed the
Recipe
object, which you can use to inspect the recipe in question. The latter two are for convenience and performance. Thecheck_deps
call receives a mapping of all dependencies mentioned in the recipe to a list of locations in which the dependency occurred. E.g.:{'setuptools': ['requirements/run', 'outputs/0/requirements/run']}
The
check_sources
is called for each source listed in the recipe, whethersource:
is a dict or a source, eliminating the need to repeat handling of these two cases in each check. The valuesource
passed is a dictionary of the source, the valuesection
a path to the source section.When a check encounters an issue, it should use
self.message()
to record the issue. The message is commonly modified with a path in the meta data to point to the part of themeta.yaml
that lead to the error. This will be used to position annotations on github.If a check never calls
self.message()
, it is assumed to have passed (=no errors generated).You can also provide an alternate filename (
fname
) and/or specify the line number directly (line
).
Module Autodocs¶
Environment Variables
- LINT_SKIP¶
If set, will be parsed instead of the most recent commit. To skip checks for specific recipes, add strings of the following form:
[lint skip <check_name> for <recipe_name>]
Build tool usage |
|
Completeness |
|
Deprecated packages and syntax |
|
Use of |
|
Policy compliance |
|
Repository checks |
|
Syntax checks |
Functions
Loads and returns the available lint checks |
Classes
|
Base class for lint checks |
|
Meta class for lint checks |
|
Message issued by LintChecks |
|
Lint executor |
|
Severities for lint checks |
|
The recipe was not understood by conda-build |
|
The recipe meta.yaml contains a duplicate key |
|
The recipe has an empty meta.yaml!? |
|
The recipe could not be rendered by Jinja2 |
|
An unexpected exception was raised during linting |
|
The recipe is missing a build section. |
|
The recipe is missing a meta.yaml file |
|
The recipe is missing name and/or version |
|
Something went wrong inside the linter |
|
The recipe failed to parse due to selector lines |
Documentation
- class bioconda_utils.lint.Severity(value)[source]¶
Severities for lint checks
- INFO = 10¶
Checks of this severity are purely informational
- WARNING = 20¶
Checks of this severity indicate possible problems
- ERROR = 30¶
Checks of this severity must be fixed and will fail a recipe.
- class bioconda_utils.lint.LintMessage(recipe: Recipe, check: LintCheck, severity: Severity = Severity.ERROR, title: str = '', body: str = '', start_line: int = 0, end_line: int = 0, fname: str = 'meta.yaml', canfix: bool = False)[source]¶
Message issued by LintChecks
Create new instance of LintMessage(recipe, check, severity, title, body, start_line, end_line, fname, canfix)
- property recipe¶
The recipe this message refers to
- property check¶
The check issuing the message
- property severity¶
The severity of the message
- property title¶
Message title to be presented to user
- property body¶
Message body to be presented to user
- property start_line¶
Line at which problem begins
- property end_line¶
Line at which problem ends
- property fname¶
Name of file in which error was found
- property canfix¶
Whether the problem can be auto fixed
- class bioconda_utils.lint.LintCheckMeta(name: str, bases: Tuple[type, ...], namespace: Dict[str, Any], **kwargs)[source]¶
Meta class for lint checks
Handles registry
Creates LintCheck classes
- class bioconda_utils.lint.LintCheck(_linter)[source]¶
Base class for lint checks
- messages: List[LintMessage]¶
Messages collected running tests
- run(recipe, fix=False)[source]¶
Run the check on a recipe. Called by Linter
- Parameters:
- Return type:
- check_recipe(recipe)[source]¶
Execute check on recipe
Override this method in subclasses, using
self.message()
to issueLintMessage
as failures are encountered.
- check_deps(deps)[source]¶
Execute check on recipe dependencies
Example format for deps:
{ 'setuptools': ['requirements/run', 'outputs/0/requirements/run/1'], 'compiler_cxx': ['requirements/build/0'] }
You can use the values in the list directly as
section
parameter toself.message()
.
- message(section=None, fname=None, line=None, data=None)[source]¶
Add a message to the lint results
Also calls
fix
if we are supposed to be fixing.- Parameters:
section (
Optional
[str
]) – If specified, a lint location within the recipe meta.yaml pointing to this section/subsection will be added to the messagefname (
Optional
[str
]) – If specified, the message will apply to this file, rather than the recipe meta.yamlline (
Optional
[int
]) – If specified, sets the line number for the message directlydata (
Optional
[Any
]) – Data to be passed tofix
. If check can fix, set this to something other than None.
- Return type:
- classmethod make_message(recipe, section=None, fname=None, line=None, canfix=False)[source]¶
Create a LintMessage
- Parameters:
section (
Optional
[str
]) – If specified, a lint location within the recipe meta.yaml pointing to this section/subsection will be added to the messagefname (
Optional
[str
]) – If specified, the message will apply to this file, rather than the recipe meta.yamlline – If specified, sets the line number for the message directly
- Return type:
- class bioconda_utils.lint.linter_failure(_linter)[source]¶
An unexpected exception was raised during linting
Please file an issue at the bioconda-utils repo
- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.duplicate_key_in_meta_yaml(_linter)[source]¶
The recipe meta.yaml contains a duplicate key
This is invalid YAML, as it’s unclear what the structure should become. Please merge the two offending sections
- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.missing_version_or_name(_linter)[source]¶
The recipe is missing name and/or version
Please make sure the recipe has at least:
package: name: package_name version: 0.12.34
- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.empty_meta_yaml(_linter)[source]¶
The recipe has an empty meta.yaml!?
Please check if you forgot to commit its contents.
- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.missing_build(_linter)[source]¶
The recipe is missing a build section.
Please add:
build: number: 0
- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.unknown_selector(_linter)[source]¶
The recipe failed to parse due to selector lines
Please request help from @bioconda/core.
- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.missing_meta_yaml(_linter)[source]¶
The recipe is missing a meta.yaml file
Most commonly, this is because the file was accidentally named
meta.yml
. If so, rename it tometa.yaml
.- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.conda_render_failure(_linter)[source]¶
The recipe was not understood by conda-build
Please request help from @bioconda/core.
- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.jinja_render_failure(_linter)[source]¶
The recipe could not be rendered by Jinja2
Check if you are missing quotes or curly braces in Jinja2 template expressions. (The parts with
{{ something }}
or{% set var="value" %}
).- messages: List[LintMessage]¶
Messages collected running tests
- class bioconda_utils.lint.unknown_check(_linter)[source]¶
Something went wrong inside the linter
Please request help from @bioconda/core
- messages: List[LintMessage]¶
Messages collected running tests
- bioconda_utils.lint.recipe_error_to_lint_check = {<class 'bioconda_utils.recipe.DuplicateKey'>: <class 'bioconda_utils.lint.duplicate_key_in_meta_yaml'>, <class 'bioconda_utils.recipe.MissingKey'>: <class 'bioconda_utils.lint.missing_version_or_name'>, <class 'bioconda_utils.recipe.EmptyRecipe'>: <class 'bioconda_utils.lint.empty_meta_yaml'>, <class 'bioconda_utils.recipe.MissingBuild'>: <class 'bioconda_utils.lint.missing_build'>, <class 'bioconda_utils.recipe.HasSelector'>: <class 'bioconda_utils.lint.unknown_selector'>, <class 'bioconda_utils.recipe.MissingMetaYaml'>: <class 'bioconda_utils.lint.missing_meta_yaml'>, <class 'bioconda_utils.recipe.CondaRenderFailure'>: <class 'bioconda_utils.lint.conda_render_failure'>, <class 'bioconda_utils.recipe.RenderFailure'>: <class 'bioconda_utils.lint.jinja_render_failure'>}¶
Maps
_recipe.RecipeError
toLintCheck
- class bioconda_utils.lint.Linter(config, recipe_folder, exclude=None, nocatch=False)[source]¶
Lint executor
- Parameters:
config (
Dict
) – Configuration dict as provided byutils.load_config()
.recipe_folder (
str
) – Folder which recipes are located.exclude (
Optional
[List
[str
]]) – List of function names inregistry
to skip globally. When running on CI, this will be merged with anything else detected from the commit message or LINT_SKIP environment variable using the special string “[skip lint <function name> for <recipe name>]”. While those other mechanisms define skipping on a recipe-specific basis, this argument can be used to skip tests for all recipes. Use sparingly.nocatch (
bool
) – Don’t catch exceptions in lint checks and turn them into linter_error lint messages. Used by tests.
- load_skips()[source]¶
Parses lint skips
If
LINT_SKIP
or the most recent commit contains[ lint skip <check_name> for <recipe_name> ]
, that particular check will be skipped.
- lint(recipe_names, fix=False)[source]¶
Run linter on multiple recipes
Lint messages are collected in the linter. They can be retrieved with
get_messages
and the list cleared withclear_messages
.