Coverage for install/scipp/_binding.py: 30%

57 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-04-28 01:28 +0000

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

2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 

3# @author Jan-Lukas Wynen 

4 

5import types 

6 

7from ._scipp import core 

8 

9_dict_likes = [ 

10 core.Dataset, 

11 core.Coords, 

12 core.Masks, 

13 core._BinsMeta, 

14 core._BinsCoords, 

15 core._BinsMasks, 

16 core._BinsAttrs, 

17] 

18 

19 

20def _get(self, key, default=None): 

21 """ 

22 Return the value for key if key is in present, else default. 

23 """ 

24 try: 

25 return self[key] 

26 except KeyError: 

27 return default 

28 

29 

30def bind_get(): 

31 for cls in _dict_likes: 

32 method = _convert_to_method(name='get', func=_get, abbreviate_doc=False) 

33 method.__doc__ = ( 

34 "Get the value associated with the " "provided key or the default value." 

35 ) 

36 cls.get = method 

37 

38 

39def _expect_dimensionless_or_unitless(x): 

40 if x.unit is not None and x.unit != core.units.dimensionless: 

41 raise core.UnitError(f'Expected unit dimensionless or no unit, got {x.unit}.') 

42 

43 

44def _expect_no_variance(x): 

45 if x.variance is not None: 

46 raise core.VariancesError('Expected input without variances.') 

47 

48 

49def _int_dunder(self) -> int: 

50 _expect_dimensionless_or_unitless(self) 

51 _expect_no_variance(self) 

52 return int(self.value) 

53 

54 

55def _float_dunder(self) -> float: 

56 _expect_dimensionless_or_unitless(self) 

57 _expect_no_variance(self) 

58 return float(self.value) 

59 

60 

61def bind_conversion_to_builtin(cls): 

62 cls.__int__ = _convert_to_method(name='__int__', func=_int_dunder) 

63 cls.__float__ = _convert_to_method(name='__float__', func=_float_dunder) 

64 

65 

66class _NoDefaultType: 

67 def __repr__(self): 

68 return 'NotSpecified' 

69 

70 

71_NoDefault = _NoDefaultType() 

72 

73 

74def _pop(self, key, default=_NoDefault): 

75 """ 

76 Remove and return an element. 

77 

78 If key is not found, default is returned if given, otherwise KeyError is raised. 

79 """ 

80 if key not in self and default is not _NoDefault: 

81 return default 

82 return self._pop(key) 

83 

84 

85def bind_pop(): 

86 for cls in _dict_likes: 

87 bind_function_as_method(cls=cls, name='pop', func=_pop, abbreviate_doc=False) 

88 

89 

90def bind_functions_as_methods(cls, namespace, func_names): 

91 for func_name, func in ((n, namespace[n]) for n in func_names): 

92 bind_function_as_method(cls=cls, name=func_name, func=func) 

93 

94 

95def bind_function_as_method(*, cls, name, func, abbreviate_doc=True): 

96 setattr( 

97 cls, 

98 name, 

99 _convert_to_method(name=name, func=func, abbreviate_doc=abbreviate_doc), 

100 ) 

101 

102 

103def _convert_to_method(*, name, func, abbreviate_doc=True): 

104 method = types.FunctionType( 

105 func.__code__, func.__globals__, name, func.__defaults__, func.__closure__ 

106 ) 

107 method.__kwdefaults__ = func.__kwdefaults__ 

108 method.__annotations__ = func.__annotations__ 

109 if func.__doc__ is not None: 

110 # Extract the summary from the docstring. 

111 # This relies on check W293 in flake8 to avoid implementing a more 

112 # sophisticate / expensive parser that running during import of scipp. 

113 # Line feeds are replaced because they mess with the 

114 # reST parser of autosummary. 

115 if abbreviate_doc: 

116 method.__doc__ = ( 

117 func.__doc__.split('\n\n', 1)[0].replace('\n', ' ') 

118 + f'\n\n:seealso: Details in :py:meth:`scipp.{name}`' 

119 ) 

120 else: 

121 method.__doc__ = func.__doc__ 

122 if hasattr(func, '__wrapped__'): 

123 method.__wrapped__ = func.__wrapped__ 

124 return method