Coverage for /home/runner/work/tket/tket/pytket/pytket/config/pytket_config.py: 93%

57 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-14 11:30 +0000

1# Copyright Quantinuum 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15import json 

16import os 

17from abc import ABC, abstractmethod 

18from dataclasses import asdict, dataclass 

19from pathlib import Path 

20from typing import Any, ClassVar, TypeVar 

21 

22 

23def get_config_file_path() -> Path: 

24 """Get a path to the config file on this machine.""" 

25 config_dir: Path 

26 xdg_conifg_dir = os.environ.get("XDG_CONFIG_HOME") 

27 if xdg_conifg_dir is None: 27 ↛ 28line 27 didn't jump to line 28 because the condition on line 27 was never true

28 config_dir = Path.home() / ".config" 

29 else: 

30 config_dir = Path(xdg_conifg_dir) 

31 

32 return config_dir / "pytket" / "config.json" 

33 

34 

35class PytketConfig: 

36 """PytketConfig represents a loaded config file for 

37 pytket and extension packages.""" 

38 

39 extensions: dict[str, Any] 

40 

41 def __init__( 

42 self, 

43 extensions: dict[str, Any] | None = None, 

44 ) -> None: 

45 """Construct a PytketConfig object with inital config parameter values. 

46 

47 :param extensions: Dictionary holding parameter values for extension packages, 

48 defaults to None 

49 :type extensions: Optional[Dict[str, Any]], optional 

50 """ 

51 

52 self.extensions = {} if extensions is None else extensions 

53 

54 @classmethod 

55 def default(cls) -> "PytketConfig": 

56 """Construct a default PytketConfig""" 

57 return PytketConfig() 

58 

59 @classmethod 

60 def read_file(cls, config_file_path: Path) -> "PytketConfig": 

61 """Construct a PytketConfig from reading a file with a given Path.""" 

62 with config_file_path.open("r", encoding="utf-8") as config_file: 

63 config = json.load(config_file) 

64 return PytketConfig( 

65 config.get("extensions", dict()), 

66 ) 

67 

68 def write_file(self, config_file_path: Path) -> None: 

69 """Write a PytketConfig to a file with a given Path.""" 

70 config_file_path.parent.mkdir(parents=True, exist_ok=True) 

71 with config_file_path.open("w", encoding="utf-8") as config_file: 

72 config = { 

73 "extensions": self.extensions, 

74 } 

75 json.dump(config, config_file, indent=2) 

76 

77 

78def load_config_file() -> PytketConfig: 

79 """Load config from default file path.""" 

80 return PytketConfig.read_file(get_config_file_path()) 

81 

82 

83def write_config_file(config: PytketConfig) -> None: 

84 """Write config to default file path.""" 

85 config.write_file(get_config_file_path()) 

86 

87 

88T = TypeVar("T", bound="PytketExtConfig") 

89 

90 

91@dataclass 

92class PytketExtConfig(ABC): 

93 """Abstract base class for pytket extension config classes.""" 

94 

95 ext_dict_key: ClassVar[str] = "" 

96 

97 @classmethod 

98 @abstractmethod 

99 def from_extension_dict(cls: type[T], ext_dict: dict[str, Any]) -> T: 

100 """Abstract method to build PytketExtConfig from dictionary serialized form.""" 

101 ... 

102 

103 def to_dict(self) -> dict[str, Any]: 

104 """Serialize to dictionary.""" 

105 return asdict(self) 

106 

107 @classmethod 

108 def from_pytketconfig(cls: type[T], p_config: PytketConfig) -> T: 

109 """Build from PytketConfig instance.""" 

110 if cls.ext_dict_key in p_config.extensions: 

111 return cls.from_extension_dict(p_config.extensions[cls.ext_dict_key]) 

112 return cls.from_extension_dict({}) 

113 

114 @classmethod 

115 def from_default_config_file(cls: type[T]) -> T: 

116 """Load from default config file.""" 

117 return cls.from_pytketconfig(load_config_file()) 

118 

119 def update_pytket_config(self, pytket_config: PytketConfig) -> None: 

120 """Update a PytketConfig instance from this extension config.""" 

121 pytket_config.extensions.update({self.ext_dict_key: self.to_dict()}) 

122 

123 def update_default_config_file(self) -> None: 

124 """Update default config file with current parameters 

125 in this extension config.""" 

126 config = load_config_file() 

127 self.update_pytket_config(config) 

128 write_config_file(config)