Coverage for klayout_pex/util/multiple_choice.py: 70%

27 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-17 17:24 +0000

1# 

2# -------------------------------------------------------------------------------- 

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

4# Johannes Kepler University, Institute for Integrated Circuits. 

5# 

6# This file is part of KPEX  

7# (see https://github.com/martinjankoehler/klayout-pex). 

8# 

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

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

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

12# (at your option) any later version. 

13# 

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

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

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

17# GNU General Public License for more details. 

18# 

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

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

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

22# -------------------------------------------------------------------------------- 

23# 

24from __future__ import annotations 

25from typing import * 

26 

27 

28class MultipleChoicePattern: 

29 def __init__(self, pattern: str): 

30 """ 

31 Multiple Choice pattern, allows blacklisting and whitelisting. 

32 For example, given a list of dielectric, let the user decide which of them to include or exclude. 

33 Allowed patterns: 

34 - all (default): complete list of choices included 

35 - none: no choices included at all 

36 - +dielname: include choice named 'dielname' 

37 - -dielname: exclude choice named 'dielname' 

38 Examples: 

39 - all,-nild5,-nild6 

40 - include all dielectrics except nild5 and nild6 

41 - none,+nild5,+capild 

42 - include only dielectrics named nild5 and capild 

43 """ 

44 self.pattern = pattern 

45 

46 components = pattern.split(sep=',') 

47 components = [c.lower().strip() for c in components] 

48 self.has_all = 'all' in components 

49 self.has_none = 'none' in components 

50 self.included = [c[1:] for c in components if c.startswith('+')] 

51 self.excluded = [c[1:] for c in components if c.startswith('-')] 

52 if self.has_none and self.has_all: 

53 raise ValueError("Multiple choice pattern can't have both subpatterns all and none") 

54 if self.has_none and len(self.excluded) >= 1: 

55 raise ValueError("Multiple choice pattern based on none can only have inclusive (+) subpatterns") 

56 if self.has_all and len(self.included) >= 1: 

57 raise ValueError("Multiple choice pattern based on all can only have exclusive (-) subpatterns") 

58 

59 def filter(self, choices: List[str]) -> List[str]: 

60 if self.has_all: 

61 return [c for c in choices if c not in self.excluded] 

62 return [c for c in choices if c in self.included] 

63 

64 def is_included(self, choice: str) -> bool: 

65 if self.has_none: 

66 return choice in self.included 

67 if self.has_all: 

68 return choice not in self.excluded 

69 return False