Coverage for src/check_datapackage/cli.py: 96%

23 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-09 08:15 +0000

1"""Functions for the exposed CLI.""" 

2 

3from typing import Annotated, Any, Optional 

4 

5from cyclopts import Parameter 

6from seedcase_soil import ( 

7 Address, 

8 parse_source, 

9 pretty_print, 

10 read_properties, 

11 run_without_tracebacks, 

12 setup_cli, 

13) 

14 

15from check_datapackage.check import check 

16from check_datapackage.config import Config 

17from check_datapackage.exclusion import Exclusion 

18from check_datapackage.extensions import Extensions, RequiredCheck 

19 

20CUSTOM_CHECKS_CONFIG_ERROR = ( 

21 "Custom checks cannot be configured in TOML because `check` must be " 

22 "a Python callable. Define CustomCheck extensions in Python instead." 

23) 

24 

25 

26class ExtensionsCli: 

27 """Extensions that can be represented in a TOML config file.""" 

28 

29 def __init__( 

30 self, 

31 required_checks: Optional[list[RequiredCheck]] = None, 

32 custom_checks: Any = None, 

33 ) -> None: 

34 """Create CLI extensions from config-file-supported fields.""" 

35 if custom_checks is not None: 

36 raise ValueError(CUSTOM_CHECKS_CONFIG_ERROR) 

37 self.required_checks = required_checks or [] 

38 

39 

40app = setup_cli( 

41 name="check-datapackage", 

42 help=( 

43 "check-datapackage checks if metadata is compliant with the Data Package" 

44 "standard" 

45 ), 

46 config_name=".cdp.toml", 

47) 

48 

49 

50@app.command(name="check") 

51def check_cmd( 

52 source: str = "datapackage.json", 

53 /, # End of positional-only args 

54 *, # Start of keyword-only params 

55 strict: bool = False, 

56 exclusions: Annotated[list[Exclusion], Parameter(show=False)] = [], 

57 extensions: Annotated[ExtensionsCli, Parameter(show=False)] = ExtensionsCli(), 

58) -> None: 

59 """Check a Data Package's metadata against the Data Package standard. 

60 

61 Outputs a human-readable explanation of any issues found. 

62 

63 Args: 

64 source: The location of a `datapackage.json`, defaults to a file or folder 

65 path. Can also be an `https:` source to a remote `datapackage.json` or a 

66 `github:` / `gh:` pointing to a repo with a `datapackage.json` 

67 in the repo root (in the format `gh:org/repo`, which can also include 

68 reference to a tag or branch, such as `gh:org/repo@main` or 

69 `gh:org/repo@1.0.1`). 

70 strict: If True, check "SHOULD" properties in addition to "MUST" 

71 properties from the Data Package standard. 

72 exclusions: A hidden CLI/config parameter for excluding issues by JSONPath 

73 and/or issue type. 

74 extensions: A hidden CLI/config parameter for adding extra checks. 

75 """ 

76 address: Address = parse_source(source) 

77 properties: dict[str, Any] = read_properties(address) 

78 config = Config( 

79 strict=strict, 

80 exclusions=exclusions, 

81 extensions=Extensions(required_checks=extensions.required_checks), 

82 ) 

83 check(properties, config=config, error=True) 

84 pretty_print("[green]All checks passed![/green]") 

85 

86 

87def main() -> None: 

88 """Create an entry point to run the cli without tracebacks.""" 

89 run_without_tracebacks(app)