Coverage for klayout_pex/env.py: 95%

61 statements  

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

1#! /usr/bin/env python3 

2# 

3# -------------------------------------------------------------------------------- 

4# SPDX-FileCopyrightText: 2024-2025 Martin Jan Köhler and Harald Pretl 

5# Johannes Kepler University, Institute for Integrated Circuits. 

6# 

7# This file is part of KPEX  

8# (see https://github.com/iic-jku/klayout-pex). 

9# 

10# This program is free software: you can redistribute it and/or modify 

11# it under the terms of the GNU General Public License as published by 

12# the Free Software Foundation, either version 3 of the License, or 

13# (at your option) any later version. 

14# 

15# This program is distributed in the hope that it will be useful, 

16# but WITHOUT ANY WARRANTY; without even the implied warranty of 

17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

18# GNU General Public License for more details. 

19# 

20# You should have received a copy of the GNU General Public License 

21# along with this program. If not, see <http://www.gnu.org/licenses/>. 

22# SPDX-License-Identifier: GPL-3.0-or-later 

23# -------------------------------------------------------------------------------- 

24# 

25 

26from __future__ import annotations 

27 

28from enum import StrEnum 

29import os 

30from typing import * 

31 

32from .pdk_config import PDK 

33from .log import warning 

34 

35 

36class EnvVar(StrEnum): 

37 FASTCAP_EXE = 'KPEX_FASTCAP_EXE' 

38 FASTERCAP_EXE = 'KPEX_FASTERCAP_EXE' 

39 KLAYOUT_EXE = 'KPEX_KLAYOUT_EXE' 

40 MAGIC_EXE = 'KPEX_MAGIC_EXE' 

41 PDK_ROOT = 'PDK_ROOT' 

42 PDK = 'PDK' 

43 

44 @property 

45 def default_value(self) -> Optional[str]: 

46 match self: 

47 case EnvVar.FASTCAP_EXE: return 'fastcap' 

48 case EnvVar.FASTERCAP_EXE: return 'FasterCap' 

49 case EnvVar.KLAYOUT_EXE: 

50 return 'klayout_app' if os.name == 'nt' \ 

51 else 'klayout' 

52 case EnvVar.MAGIC_EXE: return 'magic' 

53 case EnvVar.PDK_ROOT: return None 

54 case EnvVar.PDK: return None 

55 case _: raise NotImplementedError(f"Unexpected env var '{self.name}'") 

56 

57 @classmethod 

58 def help_epilog_table(cls) -> str: 

59 return f""" 

60| Variable | Description | 

61| ------------------ | ----------------------------------------------------------------------------- | 

62| KPEX_FASTCAP_EXE | Path to FastCap2 Executable. Defaults to '{cls.FASTCAP_EXE.default_value}' | 

63| KPEX_FASTERCAP_EXE | Path to FasterCap Executable. Defaults to '{cls.FASTERCAP_EXE.default_value}' | 

64| KPEX_KLAYOUT_EXE | Path to KLayout Executable. Defaults to '{cls.KLAYOUT_EXE.default_value}' | 

65| KPEX_MAGIC_EXE | Path to MAGIC Executable. Defaults to '{cls.MAGIC_EXE.default_value}' | 

66| PDK_ROOT | Optional (required for default magicrc), e.g. $HOME/.volare | 

67| PDK | Optional (required for default magicrc), (e.g. sky130A) | 

68""" 

69 

70 

71class Env: 

72 def __init__(self, env_dict: Dict[str, Optional[str]]): 

73 self._data = env_dict 

74 

75 @classmethod 

76 def from_os_environ(cls) -> Env: 

77 d = {} 

78 for env_var in EnvVar: 

79 value = os.environ.get(env_var.value, None) 

80 if value is None: 

81 value = env_var.default_value 

82 d[env_var] = value 

83 return Env(d) 

84 

85 @property 

86 def default_magicrc_path(self) -> Optional[str]: 

87 PDK_ROOT = self[EnvVar.PDK_ROOT] 

88 PDK = self[EnvVar.PDK] 

89 default_magicrc_path = \ 

90 None if PDK_ROOT is None or PDK is None \ 

91 else os.path.abspath(f"{PDK_ROOT}/{PDK}/libs.tech/magic/{PDK}.magicrc") 

92 return default_magicrc_path 

93 

94 @property 

95 def default_pdk(self) -> Optional[PDK]: 

96 pdk_str = self[EnvVar.PDK] 

97 if pdk_str is None: 

98 return None 

99 try: 

100 return PDK.from_string(pdk_str) 

101 except ValueError: 

102 warning(f"Ignoring invalid PDK specified in environment variable {EnvVar.PDK.value}: '{pdk_str}'") 

103 return None 

104 

105 def __getitem__(self, env_var: EnvVar) -> Optional[str]: 

106 return self._data[env_var] 

107 

108 def __contains__(self, env_var): 

109 return env_var in self._data 

110 

111 def __repr__(self): 

112 return f"Env({self._data})" 

113