Coverage for src/somesy/core/core.py: 78%

55 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-11-03 13:31 +0000

1"""Core somesy functions.""" 

2 

3import json 

4import logging 

5from pathlib import Path 

6from typing import Any 

7 

8import tomlkit 

9 

10from .log import VERBOSE 

11 

12logger = logging.getLogger("somesy") 

13 

14INPUT_FILES_ORDERED = [ 

15 ".somesy.toml", 

16 "somesy.toml", 

17 "pyproject.toml", 

18 "package.json", 

19 "Project.toml", 

20 "fpm.toml", 

21 "Cargo.toml", 

22] 

23"""Input files ordered by priority for discovery.""" 

24 

25 

26def write_json(data: Any, path: Path) -> None: 

27 """Write data as UTF-8 JSON with 2-space indentation, unescaped non-ASCII and a trailing newline.""" 

28 with path.open("w", encoding="utf-8", newline="\n") as f: 

29 json.dump(data, f, ensure_ascii=False, indent=2) 

30 f.write("\n") 

31 

32 

33def discover_input(input_file: Path | None = None) -> Path: 

34 """Check given input file path. If not given, find somesy configuration file path from default list. 

35 

36 Args: 

37 input_file: somesy configuration file path. Defaults to None. 

38 

39 Raises: 

40 FileNotFoundError: Raised if no somesy input file found from cli input or the defaults. 

41 

42 Returns: 

43 somesy configuration file path. 

44 

45 """ 

46 if input_file: 

47 if input_file.is_file(): 

48 logger.info(f"Using provided file '{input_file}' as somesy input file.") 

49 return input_file 

50 else: 

51 logger.warning( 

52 f"Passed file '{input_file}' does not exist. Searching for usable somesy input file..." 

53 ) 

54 

55 for filename in INPUT_FILES_ORDERED: 

56 input_file = Path(filename) 

57 if input_file.is_file(): 

58 try: 

59 get_input_content(input_file) 

60 except RuntimeError: 

61 continue 

62 

63 msg = f"Using '{input_file}' as somesy input file." 

64 logger.log(VERBOSE, msg) 

65 return input_file 

66 

67 raise FileNotFoundError("No somesy input file found.") 

68 

69 

70def get_input_content(path: Path, *, no_unwrap: bool = False) -> dict[str, Any]: 

71 """Read contents of a supported somesy input file. 

72 

73 Given a path to a TOML file, this function reads the file and returns its content as a TOMLDocument object. 

74 The function checks if the file is a valid somesy input file by checking its name and content. 

75 

76 Args: 

77 path (Path): path to the input file 

78 no_unwrap (bool): if True, the function returns the TOMLDocument object instead of unwrapping it 

79 

80 Returns: 

81 the content of the input file as a TOMLDocument object 

82 

83 Raises: 

84 ValueError: if the input file is not a valid somesy input file or if the file is not a TOML file. 

85 RuntimeError: if the input file does not contain a somesy input section at expected key 

86 

87 """ 

88 logger.debug(f"Path {path}") 

89 # somesy.toml / .somesy.toml 

90 if path.suffix == ".toml" and "somesy" in path.name: 

91 with open(path, "r", encoding="utf-8") as f: 

92 ret = tomlkit.load(f) 

93 return ret if no_unwrap else ret.unwrap() 

94 

95 # pyproject.toml or fpm.toml 

96 if (path.suffix == ".toml" and "pyproject" in path.name) or path.name in [ 

97 "Project.toml", 

98 "fpm.toml", 

99 ]: 

100 with open(path, "r", encoding="utf-8") as f: 

101 input_content = tomlkit.load(f) 

102 if "tool" in input_content and "somesy" in input_content["tool"]: 

103 return input_content["tool"]["somesy"].unwrap() 

104 else: 

105 raise RuntimeError( 

106 "No tool.somesy section found in pyproject.toml file!" 

107 ) 

108 

109 # Cargo.toml 

110 if path.name == "Cargo.toml": 

111 with open(path, "r", encoding="utf-8") as f: 

112 input_content = tomlkit.load(f) 

113 if ( 

114 "package" in input_content 

115 and "metadata" in input_content["package"] 

116 and "somesy" in input_content["package"]["metadata"] 

117 ): 

118 return input_content["package"]["metadata"]["somesy"].unwrap() 

119 else: 

120 raise RuntimeError( 

121 "No package.somesy section found in Cargo.toml file!" 

122 ) 

123 

124 # package.json 

125 if path.suffix == ".json" and "package" in path.name: 

126 with open(path, "r", encoding="utf-8") as f: 

127 input_content = json.load(f) 

128 if "somesy" in input_content: 

129 return input_content["somesy"] 

130 else: 

131 raise RuntimeError("No somesy section found in package.json file!") 

132 

133 # no match: 

134 raise ValueError("Unsupported input file.")