Skip to content

Commit

Permalink
Enable pyright hook
Browse files Browse the repository at this point in the history
  • Loading branch information
ssbarnea committed Nov 26, 2024
1 parent 9984f42 commit 104b360
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 61 deletions.
51 changes: 26 additions & 25 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ repos:
rev: 24.10.0
hooks:
- id: black
language_version: "3.11"
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
Expand Down Expand Up @@ -178,31 +179,31 @@ repos:
test/local-content/.*|
plugins/.*
)$
# - repo: https://github.com/RobertCraigie/pyright-python
# rev: v1.1.389
# hooks:
# - id: pyright
# additional_dependencies:
# - nodejs-wheel-binaries
# - ansible-compat>=24.8.0
# - black>=22.10.0
# - cryptography>=39.0.1
# - filelock>=3.12.2
# - importlib_metadata
# - jinja2
# - license-expression >= 30.3.0
# - pip>=22.3.1
# - pytest-mock
# - pytest>=7.2.2
# - rich>=13.2.0
# - ruamel-yaml-clib>=0.2.8
# - ruamel-yaml>=0.18.6
# - subprocess-tee
# - types-PyYAML
# - types-jsonschema>=4.20.0.0
# - types-setuptools
# - wcmatch
# - yamllint
- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.389
hooks:
- id: pyright
additional_dependencies:
- nodejs-wheel-binaries
- ansible-compat>=24.8.0
- black>=22.10.0
- cryptography>=39.0.1
- filelock>=3.12.2
- importlib_metadata
- jinja2
- license-expression >= 30.3.0
- pip>=22.3.1
- pytest-mock
- pytest>=7.2.2
- rich>=13.2.0
- ruamel-yaml-clib>=0.2.8
- ruamel-yaml>=0.18.6
- subprocess-tee
- types-PyYAML
- types-jsonschema>=4.20.0.0
- types-setuptools
- wcmatch
- yamllint
- repo: https://github.com/pycqa/pylint
rev: v3.3.1
hooks:
Expand Down
3 changes: 0 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
"**/*.txt",
"**/*.md"
],
"python.analysis.exclude": [
"build"
],
"python.terminal.activateEnvironment": true,
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
Expand Down
12 changes: 11 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,16 @@ output-format = "colorized"
score = "n"

[tool.pyright]
exclude = ["venv", ".cache"]
exclude = [
".cache",
".config",
".tox",
"ansible_collections",
"build",
"dist",
"site",
"venv"
]
include = ["src"]
mode = "standard"
# https://github.com/microsoft/pyright/blob/main/docs/configuration.md#sample-pyprojecttoml-file
Expand Down Expand Up @@ -339,6 +348,7 @@ exclude = [
]
ignore_names = [
"_ANSIBLE_ARGS",
"__line__",
"__rich_console__",
"fixture_*",
"pytest_addoption",
Expand Down
64 changes: 42 additions & 22 deletions src/ansiblelint/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@
import ruamel.yaml.parser
import yaml
from ansible.errors import AnsibleError, AnsibleParserError
from ansible.module_utils._text import to_bytes
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.parsing.dataloader import DataLoader
from ansible.parsing.mod_args import ModuleArgsParser
from ansible.parsing.plugin_docs import read_docstring
from ansible.parsing.splitter import split_args
from ansible.parsing.vault import PromptVaultSecret
from ansible.parsing.yaml.constructor import AnsibleConstructor, AnsibleMapping
from ansible.parsing.yaml.loader import AnsibleLoader
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleSequence
Expand All @@ -56,7 +58,9 @@
from ansible.template import Templar
from ansible.utils.collection_loader import AnsibleCollectionConfig
from yaml.composer import Composer
from yaml.parser import ParserError
from yaml.representer import RepresenterError
from yaml.scanner import ScannerError

from ansiblelint._internal.rules import (
AnsibleParserErrorRule,
Expand Down Expand Up @@ -95,8 +99,11 @@
def parse_yaml_from_file(filepath: str) -> AnsibleBaseYAMLObject:
"""Extract a decrypted YAML object from file."""
dataloader = DataLoader()
if hasattr(dataloader, "set_vault_password"):
dataloader.set_vault_password(DEFAULT_VAULT_PASSWORD)
if hasattr(dataloader, "set_vault_secrets"):
dataloader.set_vault_secrets(
[("default", PromptVaultSecret(_bytes=to_bytes(DEFAULT_VAULT_PASSWORD)))]
)

return dataloader.load_from_file(filepath)


Expand Down Expand Up @@ -254,7 +261,11 @@ def set_collections_basedir(basedir: Path) -> None:
"""Set the playbook directory as playbook_paths for the collection loader."""
# Ansible expects only absolute paths inside `playbook_paths` and will
# produce weird errors if we use a relative one.
AnsibleCollectionConfig.playbook_paths = str(basedir.resolve())
# https://github.com/psf/black/issues/4519
# fmt: off
AnsibleCollectionConfig.playbook_paths = ( # pyright: ignore[reportAttributeAccessIssue]
str(basedir.resolve()))
# fmt: on


def template(
Expand Down Expand Up @@ -911,7 +922,7 @@ def task_in_list(
"""Get action tasks from block structures."""

def each_entry(data: AnsibleBaseYAMLObject, position: str) -> Iterator[Task]:
if not data:
if not data or not isinstance(data, Iterable):
return
for entry_index, entry in enumerate(data):
if not entry:
Expand Down Expand Up @@ -951,29 +962,35 @@ def each_entry(data: AnsibleBaseYAMLObject, position: str) -> Iterator[Task]:
yield from each_entry(data, position)


def add_action_type(actions: AnsibleBaseYAMLObject, action_type: str) -> list[Any]:
def add_action_type(
actions: AnsibleBaseYAMLObject, action_type: str
) -> AnsibleSequence:
"""Add action markers to task objects."""
results = []
for action in actions:
# ignore empty task
if not action:
continue
action["__ansible_action_type__"] = BLOCK_NAME_TO_ACTION_TYPE_MAP[action_type]
results.append(action)
results = AnsibleSequence()
if isinstance(actions, Iterable):
for action in actions:
# ignore empty task
if not action:
continue

Check warning on line 974 in src/ansiblelint/utils.py

View check run for this annotation

Codecov / codecov/patch

src/ansiblelint/utils.py#L974

Added line #L974 was not covered by tests
action["__ansible_action_type__"] = BLOCK_NAME_TO_ACTION_TYPE_MAP[
action_type
]
results.append(action)
return results


@cache
def parse_yaml_linenumbers(
lintable: Lintable,
) -> AnsibleBaseYAMLObject:
) -> AnsibleBaseYAMLObject | None:
"""Parse yaml as ansible.utils.parse_yaml but with linenumbers.
The line numbers are stored in each node's LINE_NUMBER_KEY key.
"""
result = []
result = AnsibleSequence()

def compose_node(parent: yaml.nodes.Node, index: int) -> yaml.nodes.Node:
# signature of Composer.compose_node
def compose_node(parent: yaml.nodes.Node | None, index: int) -> yaml.nodes.Node:
# the line number where the previous token has ended (plus empty lines)
line = loader.line
node = Composer.compose_node(loader, parent, index)
Expand All @@ -983,14 +1000,15 @@ def compose_node(parent: yaml.nodes.Node, index: int) -> yaml.nodes.Node:
node.__line__ = line + 1 # type: ignore[attr-defined]
return node

# signature of AnsibleConstructor.construct_mapping
def construct_mapping(
node: AnsibleBaseYAMLObject,
*,
deep: bool = False,
node: yaml.MappingNode,
deep: bool = False, # noqa: FBT002
) -> AnsibleMapping:
# pyright: ignore[reportArgumentType]
mapping = AnsibleConstructor.construct_mapping(loader, node, deep=deep)
if hasattr(node, "__line__"):
mapping[LINE_NUMBER_KEY] = node.__line__
if hasattr(node, LINE_NUMBER_KEY):
mapping[LINE_NUMBER_KEY] = getattr(node, LINE_NUMBER_KEY)

Check warning on line 1011 in src/ansiblelint/utils.py

View check run for this annotation

Codecov / codecov/patch

src/ansiblelint/utils.py#L1011

Added line #L1011 was not covered by tests
else:
mapping[LINE_NUMBER_KEY] = mapping._line_number # noqa: SLF001
mapping[FILENAME_KEY] = lintable.path
Expand All @@ -1001,7 +1019,9 @@ def construct_mapping(
if "vault_password" in inspect.getfullargspec(AnsibleLoader.__init__).args:
kwargs["vault_password"] = DEFAULT_VAULT_PASSWORD
loader = AnsibleLoader(lintable.content, **kwargs)
# redefine Composer.compose_node
loader.compose_node = compose_node
# redefine AnsibleConstructor.construct_mapping
loader.construct_mapping = construct_mapping
# while Ansible only accepts single documents, we also need to load
# multi-documents, as we attempt to load any YAML file, not only
Expand All @@ -1012,8 +1032,8 @@ def construct_mapping(
break
result.append(data)
except (
yaml.parser.ParserError,
yaml.scanner.ScannerError,
ParserError,
ScannerError,
yaml.constructor.ConstructorError,
ruamel.yaml.parser.ParserError,
) as exc:
Expand Down
2 changes: 1 addition & 1 deletion test/rules/fixtures/raw_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class RawTaskRule(AnsibleLintRule):
"""Test rule that inspects the raw task."""

id = "raw-task"
shortdesc = "Test rule that inspects the raw task"
_shortdesc = "Test rule that inspects the raw task"
tags = ["fake", "dummy", "test3"]
needs_raw_task = True

Expand Down
8 changes: 5 additions & 3 deletions test/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ def test_request_timeouterror_handling(
) -> None:
"""Test that schema refresh can handle time out errors."""
error_msg = "Simulating handshake operation time out."
mock_request.urlopen.side_effect = urllib.error.URLError(
TimeoutError(error_msg)
) # pyright: reportAttributeAccessIssue=false
mock_request.urlopen.side_effect = (
urllib.error.URLError( # pyright: ignore[reportAttributeAccessIssue]
TimeoutError(error_msg)
)
)
with caplog.at_level(logging.DEBUG):
assert refresh_schemas(min_age_seconds=0) == 0
mock_request.urlopen.assert_called()
Expand Down
14 changes: 8 additions & 6 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from typing import TYPE_CHECKING, Any

import pytest
from ansible.parsing.yaml.constructor import AnsibleMapping, AnsibleSequence
from ansible.utils.sentinel import Sentinel
from ansible_compat.runtime import Runtime

Expand All @@ -45,7 +46,6 @@
from _pytest.capture import CaptureFixture
from _pytest.logging import LogCaptureFixture
from _pytest.monkeypatch import MonkeyPatch
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject

from ansiblelint.rules import RulesCollection

Expand Down Expand Up @@ -221,7 +221,7 @@ def test_extract_from_list() -> None:
"test_none": None,
"test_string": "foo",
}
blocks = [block]
blocks = AnsibleSequence([block])

test_list = utils.extract_from_list(blocks, ["block"])
test_none = utils.extract_from_list(blocks, ["test_none"])
Expand All @@ -234,10 +234,12 @@ def test_extract_from_list() -> None:

def test_extract_from_list_recursive() -> None:
"""Check that tasks get extracted from blocks if present."""
block = {
"block": [{"block": [{"name": "hello", "command": "whoami"}]}],
}
blocks: AnsibleBaseYAMLObject = [block]
block = AnsibleMapping(
{
"block": [{"block": [{"name": "hello", "command": "whoami"}]}],
}
)
blocks = AnsibleSequence([block])

test_list = utils.extract_from_list(blocks, ["block"])
assert list(block["block"]) == test_list
Expand Down

0 comments on commit 104b360

Please sign in to comment.