Skip to content

Commit dc5fbc2

Browse files
committed
Enable pyright hook
1 parent 9984f42 commit dc5fbc2

File tree

7 files changed

+92
-61
lines changed

7 files changed

+92
-61
lines changed

.pre-commit-config.yaml

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -178,31 +178,31 @@ repos:
178178
test/local-content/.*|
179179
plugins/.*
180180
)$
181-
# - repo: https://github.com/RobertCraigie/pyright-python
182-
# rev: v1.1.389
183-
# hooks:
184-
# - id: pyright
185-
# additional_dependencies:
186-
# - nodejs-wheel-binaries
187-
# - ansible-compat>=24.8.0
188-
# - black>=22.10.0
189-
# - cryptography>=39.0.1
190-
# - filelock>=3.12.2
191-
# - importlib_metadata
192-
# - jinja2
193-
# - license-expression >= 30.3.0
194-
# - pip>=22.3.1
195-
# - pytest-mock
196-
# - pytest>=7.2.2
197-
# - rich>=13.2.0
198-
# - ruamel-yaml-clib>=0.2.8
199-
# - ruamel-yaml>=0.18.6
200-
# - subprocess-tee
201-
# - types-PyYAML
202-
# - types-jsonschema>=4.20.0.0
203-
# - types-setuptools
204-
# - wcmatch
205-
# - yamllint
181+
- repo: https://github.com/RobertCraigie/pyright-python
182+
rev: v1.1.389
183+
hooks:
184+
- id: pyright
185+
additional_dependencies:
186+
- nodejs-wheel-binaries
187+
- ansible-compat>=24.8.0
188+
- black>=22.10.0
189+
- cryptography>=39.0.1
190+
- filelock>=3.12.2
191+
- importlib_metadata
192+
- jinja2
193+
- license-expression >= 30.3.0
194+
- pip>=22.3.1
195+
- pytest-mock
196+
- pytest>=7.2.2
197+
- rich>=13.2.0
198+
- ruamel-yaml-clib>=0.2.8
199+
- ruamel-yaml>=0.18.6
200+
- subprocess-tee
201+
- types-PyYAML
202+
- types-jsonschema>=4.20.0.0
203+
- types-setuptools
204+
- wcmatch
205+
- yamllint
206206
- repo: https://github.com/pycqa/pylint
207207
rev: v3.3.1
208208
hooks:

.vscode/settings.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
"**/*.txt",
1818
"**/*.md"
1919
],
20-
"python.analysis.exclude": [
21-
"build"
22-
],
2320
"python.terminal.activateEnvironment": true,
2421
"python.testing.pytestEnabled": true,
2522
"python.testing.unittestEnabled": false,

pyproject.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,16 @@ output-format = "colorized"
153153
score = "n"
154154

155155
[tool.pyright]
156-
exclude = ["venv", ".cache"]
156+
exclude = [
157+
".cache",
158+
".config",
159+
".tox",
160+
"ansible_collections",
161+
"build",
162+
"dist",
163+
"site",
164+
"venv"
165+
]
157166
include = ["src"]
158167
mode = "standard"
159168
# https://github.com/microsoft/pyright/blob/main/docs/configuration.md#sample-pyprojecttoml-file
@@ -339,6 +348,7 @@ exclude = [
339348
]
340349
ignore_names = [
341350
"_ANSIBLE_ARGS",
351+
"__line__",
342352
"__rich_console__",
343353
"fixture_*",
344354
"pytest_addoption",

src/ansiblelint/utils.py

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@
3939
import ruamel.yaml.parser
4040
import yaml
4141
from ansible.errors import AnsibleError, AnsibleParserError
42+
from ansible.module_utils._text import to_bytes
4243
from ansible.module_utils.parsing.convert_bool import boolean
4344
from ansible.parsing.dataloader import DataLoader
4445
from ansible.parsing.mod_args import ModuleArgsParser
4546
from ansible.parsing.plugin_docs import read_docstring
4647
from ansible.parsing.splitter import split_args
48+
from ansible.parsing.vault import PromptVaultSecret
4749
from ansible.parsing.yaml.constructor import AnsibleConstructor, AnsibleMapping
4850
from ansible.parsing.yaml.loader import AnsibleLoader
4951
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleSequence
@@ -56,7 +58,9 @@
5658
from ansible.template import Templar
5759
from ansible.utils.collection_loader import AnsibleCollectionConfig
5860
from yaml.composer import Composer
61+
from yaml.parser import ParserError
5962
from yaml.representer import RepresenterError
63+
from yaml.scanner import ScannerError
6064

6165
from ansiblelint._internal.rules import (
6266
AnsibleParserErrorRule,
@@ -95,8 +99,11 @@
9599
def parse_yaml_from_file(filepath: str) -> AnsibleBaseYAMLObject:
96100
"""Extract a decrypted YAML object from file."""
97101
dataloader = DataLoader()
98-
if hasattr(dataloader, "set_vault_password"):
99-
dataloader.set_vault_password(DEFAULT_VAULT_PASSWORD)
102+
if hasattr(dataloader, "set_vault_secrets"):
103+
dataloader.set_vault_secrets(
104+
[("default", PromptVaultSecret(_bytes=to_bytes(DEFAULT_VAULT_PASSWORD)))]
105+
)
106+
100107
return dataloader.load_from_file(filepath)
101108

102109

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

259270

260271
def template(
@@ -911,7 +922,7 @@ def task_in_list(
911922
"""Get action tasks from block structures."""
912923

913924
def each_entry(data: AnsibleBaseYAMLObject, position: str) -> Iterator[Task]:
914-
if not data:
925+
if not data or not isinstance(data, Iterable):
915926
return
916927
for entry_index, entry in enumerate(data):
917928
if not entry:
@@ -951,29 +962,35 @@ def each_entry(data: AnsibleBaseYAMLObject, position: str) -> Iterator[Task]:
951962
yield from each_entry(data, position)
952963

953964

954-
def add_action_type(actions: AnsibleBaseYAMLObject, action_type: str) -> list[Any]:
965+
def add_action_type(
966+
actions: AnsibleBaseYAMLObject, action_type: str
967+
) -> AnsibleSequence:
955968
"""Add action markers to task objects."""
956-
results = []
957-
for action in actions:
958-
# ignore empty task
959-
if not action:
960-
continue
961-
action["__ansible_action_type__"] = BLOCK_NAME_TO_ACTION_TYPE_MAP[action_type]
962-
results.append(action)
969+
results = AnsibleSequence()
970+
if isinstance(actions, Iterable):
971+
for action in actions:
972+
# ignore empty task
973+
if not action:
974+
continue
975+
action["__ansible_action_type__"] = BLOCK_NAME_TO_ACTION_TYPE_MAP[
976+
action_type
977+
]
978+
results.append(action)
963979
return results
964980

965981

966982
@cache
967983
def parse_yaml_linenumbers(
968984
lintable: Lintable,
969-
) -> AnsibleBaseYAMLObject:
985+
) -> AnsibleBaseYAMLObject | None:
970986
"""Parse yaml as ansible.utils.parse_yaml but with linenumbers.
971987
972988
The line numbers are stored in each node's LINE_NUMBER_KEY key.
973989
"""
974-
result = []
990+
result = AnsibleSequence()
975991

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

1003+
# signature of AnsibleConstructor.construct_mapping
9861004
def construct_mapping(
987-
node: AnsibleBaseYAMLObject,
988-
*,
989-
deep: bool = False,
1005+
node: yaml.MappingNode,
1006+
deep: bool = False, # noqa: FBT002
9901007
) -> AnsibleMapping:
1008+
# pyright: ignore[reportArgumentType]
9911009
mapping = AnsibleConstructor.construct_mapping(loader, node, deep=deep)
992-
if hasattr(node, "__line__"):
993-
mapping[LINE_NUMBER_KEY] = node.__line__
1010+
if hasattr(node, LINE_NUMBER_KEY):
1011+
mapping[LINE_NUMBER_KEY] = getattr(node, LINE_NUMBER_KEY)
9941012
else:
9951013
mapping[LINE_NUMBER_KEY] = mapping._line_number # noqa: SLF001
9961014
mapping[FILENAME_KEY] = lintable.path
@@ -1001,7 +1019,9 @@ def construct_mapping(
10011019
if "vault_password" in inspect.getfullargspec(AnsibleLoader.__init__).args:
10021020
kwargs["vault_password"] = DEFAULT_VAULT_PASSWORD
10031021
loader = AnsibleLoader(lintable.content, **kwargs)
1022+
# redefine Composer.compose_node
10041023
loader.compose_node = compose_node
1024+
# redefine AnsibleConstructor.construct_mapping
10051025
loader.construct_mapping = construct_mapping
10061026
# while Ansible only accepts single documents, we also need to load
10071027
# multi-documents, as we attempt to load any YAML file, not only
@@ -1012,8 +1032,8 @@ def construct_mapping(
10121032
break
10131033
result.append(data)
10141034
except (
1015-
yaml.parser.ParserError,
1016-
yaml.scanner.ScannerError,
1035+
ParserError,
1036+
ScannerError,
10171037
yaml.constructor.ConstructorError,
10181038
ruamel.yaml.parser.ParserError,
10191039
) as exc:

test/rules/fixtures/raw_task.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class RawTaskRule(AnsibleLintRule):
1515
"""Test rule that inspects the raw task."""
1616

1717
id = "raw-task"
18-
shortdesc = "Test rule that inspects the raw task"
18+
_shortdesc = "Test rule that inspects the raw task"
1919
tags = ["fake", "dummy", "test3"]
2020
needs_raw_task = True
2121

test/test_schemas.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ def test_request_timeouterror_handling(
4747
) -> None:
4848
"""Test that schema refresh can handle time out errors."""
4949
error_msg = "Simulating handshake operation time out."
50-
mock_request.urlopen.side_effect = urllib.error.URLError(
51-
TimeoutError(error_msg)
52-
) # pyright: reportAttributeAccessIssue=false
50+
mock_request.urlopen.side_effect = (
51+
urllib.error.URLError( # pyright: ignore[reportAttributeAccessIssue]
52+
TimeoutError(error_msg)
53+
)
54+
)
5355
with caplog.at_level(logging.DEBUG):
5456
assert refresh_schemas(min_age_seconds=0) == 0
5557
mock_request.urlopen.assert_called()

test/test_utils.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from typing import TYPE_CHECKING, Any
2929

3030
import pytest
31+
from ansible.parsing.yaml.constructor import AnsibleMapping, AnsibleSequence
3132
from ansible.utils.sentinel import Sentinel
3233
from ansible_compat.runtime import Runtime
3334

@@ -45,7 +46,6 @@
4546
from _pytest.capture import CaptureFixture
4647
from _pytest.logging import LogCaptureFixture
4748
from _pytest.monkeypatch import MonkeyPatch
48-
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject
4949

5050
from ansiblelint.rules import RulesCollection
5151

@@ -221,7 +221,7 @@ def test_extract_from_list() -> None:
221221
"test_none": None,
222222
"test_string": "foo",
223223
}
224-
blocks = [block]
224+
blocks = AnsibleSequence([block])
225225

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

235235
def test_extract_from_list_recursive() -> None:
236236
"""Check that tasks get extracted from blocks if present."""
237-
block = {
238-
"block": [{"block": [{"name": "hello", "command": "whoami"}]}],
239-
}
240-
blocks: AnsibleBaseYAMLObject = [block]
237+
block = AnsibleMapping(
238+
{
239+
"block": [{"block": [{"name": "hello", "command": "whoami"}]}],
240+
}
241+
)
242+
blocks = AnsibleSequence([block])
241243

242244
test_list = utils.extract_from_list(blocks, ["block"])
243245
assert list(block["block"]) == test_list

0 commit comments

Comments
 (0)