Coverage for /usr/lib/python3/dist-packages/gpiozero/compat.py: 21%

103 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-02-10 12:38 +0000

1# vim: set fileencoding=utf-8: 

2# 

3# GPIO Zero: a library for controlling the Raspberry Pi's GPIO pins 

4# 

5# Copyright (c) 2016-2021 Dave Jones <dave@waveform.org.uk> 

6# Copyright (c) 2019 Ben Nuttall <ben@bennuttall.com> 

7# Copyright (c) 2018 Rick Ansell <rick@nbinvincible.org.uk> 

8# Copyright (c) 2016 Andrew Scheller <github@loowis.durge.org> 

9# 

10# SPDX-License-Identifier: BSD-3-Clause 

11 

12from __future__ import ( 

13 unicode_literals, 

14 absolute_import, 

15 print_function, 

16 division, 

17 ) 

18str = type('') 

19 

20import math 

21import cmath 

22import weakref 

23import operator 

24import functools 

25 

26# Handles pre 3.3 versions of Python without collections.abc 

27try: 

28 from collections.abc import Mapping 

29except ImportError: 

30 from collections import Mapping 

31 

32# Back-ported from python 3.5; see 

33# github.com/PythonCHB/close_pep/blob/master/is_close.py for original 

34# implementation 

35def isclose(a, b, rel_tol=1e-9, abs_tol=0.0): 

36 if rel_tol < 0.0 or abs_tol < 0.0: 

37 raise ValueError('error tolerances must be non-negative') 

38 if a == b: # fast-path for exact equality 

39 return True 

40 if cmath.isinf(a) or cmath.isinf(b): 

41 return False 

42 diff = abs(b - a) 

43 return ( 

44 (diff <= abs(rel_tol * b)) or 

45 (diff <= abs(rel_tol * a)) or 

46 (diff <= abs_tol) 

47 ) 

48 

49 

50# Backported from py3.4 

51def mean(data): 

52 if iter(data) is data: 

53 data = list(data) 

54 n = len(data) 

55 if not n: 

56 raise ValueError('cannot calculate mean of empty data') 

57 return sum(data) / n 

58 

59 

60# Backported from py3.4 

61def median(data): 

62 data = sorted(data) 

63 n = len(data) 

64 if not n: 

65 raise ValueError('cannot calculate median of empty data') 

66 elif n % 2: 

67 return data[n // 2] 

68 else: 

69 i = n // 2 

70 return (data[i - 1] + data[i]) / 2 

71 

72 

73# Backported from py3.4 

74def mean(data): 

75 if iter(data) is data: 

76 data = list(data) 

77 n = len(data) 

78 if n < 1: 

79 raise ValueError('mean requires at least one data point') 

80 return sum(data) / n 

81 

82 

83# Backported from py3.3 

84def log2(x): 

85 return math.log(x, 2) 

86 

87 

88# Copied from the MIT-licensed https://github.com/slezica/python-frozendict 

89class frozendict(Mapping): 

90 def __init__(self, *args, **kwargs): 

91 self.__dict = dict(*args, **kwargs) 

92 self.__hash = None 

93 

94 def __getitem__(self, key): 

95 return self.__dict[key] 

96 

97 def copy(self, **add_or_replace): 

98 return frozendict(self, **add_or_replace) 

99 

100 def __iter__(self): 

101 return iter(self.__dict) 

102 

103 def __len__(self): 

104 return len(self.__dict) 

105 

106 def __repr__(self): 

107 return '<frozendict %s>' % repr(self.__dict) 

108 

109 def __hash__(self): 

110 if self.__hash is None: 

111 hashes = map(hash, self.items()) 

112 self.__hash = functools.reduce(operator.xor, hashes, 0) 

113 return self.__hash 

114 

115 

116# Backported from py3.4 

117class WeakMethod(weakref.ref): 

118 """ 

119 A custom `weakref.ref` subclass which simulates a weak reference to 

120 a bound method, working around the lifetime problem of bound methods. 

121 """ 

122 

123 __slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__" 

124 

125 def __new__(cls, meth, callback=None): 

126 try: 

127 obj = meth.__self__ 

128 func = meth.__func__ 

129 except AttributeError: 

130 raise TypeError("argument should be a bound method, not {0}" 

131 .format(type(meth))) 

132 def _cb(arg): 

133 # The self-weakref trick is needed to avoid creating a reference 

134 # cycle. 

135 self = self_wr() 

136 if self._alive: 

137 self._alive = False 

138 if callback is not None: 

139 callback(self) 

140 self = weakref.ref.__new__(cls, obj, _cb) 

141 self._func_ref = weakref.ref(func, _cb) 

142 self._meth_type = type(meth) 

143 self._alive = True 

144 self_wr = weakref.ref(self) 

145 return self 

146 

147 def __call__(self): 

148 obj = super(WeakMethod, self).__call__() 

149 func = self._func_ref() 

150 if obj is None or func is None: 

151 return None 

152 return self._meth_type(func, obj) 

153 

154 def __eq__(self, other): 

155 if isinstance(other, WeakMethod): 

156 if not self._alive or not other._alive: 

157 return self is other 

158 return weakref.ref.__eq__(self, other) and self._func_ref == other._func_ref 

159 return False 

160 

161 def __ne__(self, other): 

162 if isinstance(other, WeakMethod): 

163 if not self._alive or not other._alive: 

164 return self is not other 

165 return weakref.ref.__ne__(self, other) or self._func_ref != other._func_ref 

166 return True 

167 

168 __hash__ = weakref.ref.__hash__