Coverage for lintro / parsers / __init__.py: 60%

15 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Parser modules for Lintro tools.""" 

2 

3from __future__ import annotations 

4 

5from importlib import import_module 

6from typing import TYPE_CHECKING 

7 

8from .base_issue import BaseIssue 

9from .streaming import ( 

10 StreamingParser, 

11 collect_streaming_results, 

12 stream_json_array_fallback, 

13 stream_json_lines, 

14 stream_text_lines, 

15) 

16 

17if TYPE_CHECKING: 

18 # Type checking imports 

19 from lintro.parsers import ( 

20 actionlint, 

21 bandit, 

22 black, 

23 hadolint, 

24 markdownlint, 

25 mypy, 

26 pydoclint, 

27 pytest, 

28 ruff, 

29 semgrep, 

30 yamllint, 

31 ) 

32 

33__all__ = [ 

34 "BaseIssue", 

35 "StreamingParser", 

36 "actionlint", 

37 "bandit", 

38 "black", 

39 "collect_streaming_results", 

40 "hadolint", 

41 "markdownlint", 

42 "mypy", 

43 "pydoclint", 

44 "pytest", 

45 "ruff", 

46 "semgrep", 

47 "stream_json_array_fallback", 

48 "stream_json_lines", 

49 "stream_text_lines", 

50 "yamllint", 

51] 

52 

53# Lazy-load parser submodules to avoid circular imports 

54_SUBMODULES = { 

55 "actionlint", 

56 "bandit", 

57 "black", 

58 "hadolint", 

59 "markdownlint", 

60 "mypy", 

61 "pydoclint", 

62 "pytest", 

63 "ruff", 

64 "semgrep", 

65 "yamllint", 

66} 

67 

68 

69def __getattr__(name: str) -> object: 

70 """Lazy-load parser submodules to avoid circular import issues. 

71 

72 This function is called when an attribute is accessed that doesn't exist 

73 in the module. It allows accessing parser submodules without eagerly 

74 importing them all at package initialization time. 

75 

76 Args: 

77 name: The name of the attribute being accessed. 

78 

79 Returns: 

80 The imported submodule. 

81 

82 Raises: 

83 AttributeError: If the requested name is not a known submodule. 

84 """ 

85 if name in _SUBMODULES: 

86 # Safe: name validated against _SUBMODULES whitelist (internal modules only) 

87 module = import_module(f".{name}", __package__) # nosemgrep: non-literal-import 

88 # Cache the module in this module's namespace for future access 

89 globals()[name] = module 

90 return module 

91 raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 

92 

93 

94def __dir__() -> list[str]: 

95 """Return list of available attributes for this module. 

96 

97 Returns: 

98 List of submodule names and other module attributes. 

99 """ 

100 return list(__all__)