Coverage for pyTooling / CallByRef / __init__.py: 90%

240 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-08 23:46 +0000

1# ==================================================================================================================== # 

2# _____ _ _ ____ _ _ ____ ____ __ # 

3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___|__ _| | | __ ) _ _| _ \ ___ / _| # 

4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | / _` | | | _ \| | | | |_) / _ \ |_ # 

5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |__| (_| | | | |_) | |_| | _ < __/ _| # 

6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____\__,_|_|_|____/ \__, |_| \_\___|_| # 

7# |_| |___/ |___/ |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2017-2026 Patrick Lehmann - Bötzingen, Germany # 

15# # 

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

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

18# You may obtain a copy of the License at # 

19# # 

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

21# # 

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

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

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

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

26# limitations under the License. # 

27# # 

28# SPDX-License-Identifier: Apache-2.0 # 

29# ==================================================================================================================== # 

30# 

31""" 

32Auxiliary classes to implement call-by-reference. 

33 

34.. hint:: 

35 

36 See :ref:`high-level help <COMMON/CallByRef>` for explanations and usage examples. 

37""" 

38from decimal import Decimal 

39from typing import Any, Generic, TypeVar, Optional as Nullable 

40 

41try: 

42 from pyTooling.Decorators import export 

43 from pyTooling.MetaClasses import ExtendedType 

44except (ImportError, ModuleNotFoundError): # pragma: no cover 

45 print("[pyTooling.CallByRef] Could not import from 'pyTooling.*'!") 

46 

47 try: 

48 from Decorators import export 

49 from MetaClasses import ExtendedType 

50 except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover 

51 print("[pyTooling.CallByRef] Could not import directly!") 

52 raise ex 

53 

54 

55T = TypeVar("T") 

56 

57 

58@export 

59class CallByRefParam(Generic[T], metaclass=ExtendedType, slots=True): 

60 """ 

61 Implements a *call-by-reference* parameter. 

62 

63 .. seealso:: 

64 

65 * :class:`CallByRefBoolParam` |br| 

66 |rarr| A special *call-by-reference* implementation for boolean reference types. 

67 * :class:`CallByRefIntParam` |br| 

68 |rarr| A special *call-by-reference* implementation for integer reference types. 

69 """ 

70 

71 Value: T #: internal value 

72 

73 def __init__(self, value: Nullable[T] = None) -> None: 

74 """Constructs a *call-by-reference* object for any type. 

75 

76 :param value: The value to be set as an initial value. 

77 """ 

78 self.Value = value 

79 

80 def __ilshift__(self, other: T) -> 'CallByRefParam[T]': # Starting with Python 3.11+, use typing.Self as return type 

81 """Assigns a value to the *call-by-reference* object. 

82 

83 :param other: The value to be assigned to this *call-by-reference* object. 

84 :returns: Itself. 

85 """ 

86 self.Value = other 

87 return self 

88 

89 # binary operators - comparison 

90 def __eq__(self, other: Any) -> bool: 

91 """ 

92 Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for equality. 

93 

94 :param other: Parameter to compare against. 

95 :returns: ``True``, if both values are equal. 

96 """ 

97 if isinstance(other, CallByRefParam): 97 ↛ 98line 97 didn't jump to line 98 because the condition on line 97 was never true

98 return self.Value == other.Value 

99 else: 

100 return self.Value == other 

101 

102 def __ne__(self, other) -> bool: 

103 """ 

104 Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for inequality. 

105 

106 :param other: Parameter to compare against. 

107 :returns: ``True``, if both values are unequal. 

108 """ 

109 if isinstance(other, CallByRefParam): 109 ↛ 110line 109 didn't jump to line 110 because the condition on line 109 was never true

110 return self.Value != other.Value 

111 else: 

112 return self.Value != other 

113 

114 # Type conversion operators 

115 def __repr__(self) -> str: 

116 """ 

117 Returns the wrapped object's string representation. 

118 

119 :returns: The string representation of the wrapped value. 

120 """ 

121 return repr(self.Value) 

122 

123 def __str__(self) -> str: 

124 """ 

125 Returns the wrapped object's string equivalent. 

126 

127 :returns: The string equivalent of the wrapped value. 

128 """ 

129 return str(self.Value) 

130 

131 

132@export 

133class CallByRefBoolParam(CallByRefParam): 

134 """A special *call-by-reference* implementation for boolean reference types.""" 

135 

136 # Binary operators - comparison 

137 def __eq__(self, other: Any) -> bool: 

138 """ 

139 Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for equality. 

140 

141 :param other: Parameter to compare against. 

142 :returns: ``True``, if both values are equal. 

143 :raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`. 

144 """ 

145 if isinstance(other, bool): 

146 return self.Value == other 

147 elif isinstance(other, CallByRefBoolParam): 147 ↛ 148line 147 didn't jump to line 148 because the condition on line 147 was never true

148 return self.Value == other.Value 

149 else: 

150 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.") 

151 ex.add_note("Supported types for second operand: bool, CallByRefBoolParam") 

152 raise ex 

153 

154 def __ne__(self, other) -> bool: 

155 """ 

156 Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for inequality. 

157 

158 :param other: Parameter to compare against. 

159 :returns: ``True``, if both values are unequal. 

160 :raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`. 

161 """ 

162 if isinstance(other, bool): 

163 return self.Value != other 

164 elif isinstance(other, CallByRefBoolParam): 164 ↛ 165line 164 didn't jump to line 165 because the condition on line 164 was never true

165 return self.Value != other.Value 

166 else: 

167 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.") 

168 ex.add_note(f"Supported types for second operand: bool, CallByRefBoolParam") 

169 raise ex 

170 

171 # Type conversion operators 

172 def __bool__(self) -> bool: 

173 """ 

174 Type conversion to :class:`bool`. 

175 

176 :returns: The wrapped value. 

177 """ 

178 return self.Value 

179 

180 def __int__(self) -> int: 

181 """ 

182 Type conversion to :class:`int`. 

183 

184 :returns: The integer representation of the wrapped boolean value. 

185 """ 

186 return int(self.Value) 

187 

188 

189@export 

190class CallByRefIntParam(CallByRefParam): 

191 """A special *call-by-reference* implementation for integer reference types.""" 

192 

193 # Unary operators 

194 def __neg__(self) -> int: 

195 """Negate: -self.""" 

196 return -self.Value 

197 

198 def __pos__(self) -> int: 

199 """Positive: +self.""" 

200 return +self.Value 

201 

202 def __invert__(self) -> int: 

203 """Invert: ~self.""" 

204 return ~self.Value 

205 

206 # Binary operators - logical 

207 def __and__(self, other: Any) -> int: 

208 """And: self & other.""" 

209 if isinstance(other, int): 

210 return self.Value & other 

211 else: 

212 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by and operator.") 

213 ex.add_note(f"Supported types for second operand: int") 

214 raise ex 

215 

216 def __or__(self, other: Any) -> int: 

217 """Or: self | other.""" 

218 if isinstance(other, int): 

219 return self.Value | other 

220 else: 

221 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by or operator.") 

222 ex.add_note(f"Supported types for second operand: int") 

223 raise ex 

224 

225 def __xor__(self, other: Any) -> int: 

226 """Xor: self ^ other.""" 

227 if isinstance(other, int): 

228 return self.Value ^ other 

229 else: 

230 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

231 ex.add_note(f"Supported types for second operand: int") 

232 raise ex 

233 

234 # Binary inplace operators 

235 def __iand__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type 

236 """Inplace and: self &= other.""" 

237 if isinstance(other, int): 

238 self.Value &= other 

239 return self 

240 else: 

241 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by &= operator.") 

242 ex.add_note(f"Supported types for second operand: int") 

243 raise ex 

244 

245 def __ior__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type 

246 r"""Inplace or: self \|= other.""" 

247 if isinstance(other, int): 

248 self.Value |= other 

249 return self 

250 else: 

251 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by |= operator.") 

252 ex.add_note(f"Supported types for second operand: int") 

253 raise ex 

254 

255 def __ixor__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type 

256 r"""Inplace or: self \|= other.""" 

257 if isinstance(other, int): 

258 self.Value ^= other 

259 return self 

260 else: 

261 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ^= operator.") 

262 ex.add_note(f"Supported types for second operand: int") 

263 raise ex 

264 

265 # Binary operators - arithmetic 

266 def __add__(self, other: Any) -> int: 

267 """Addition: self + other.""" 

268 if isinstance(other, int): 

269 return self.Value + other 

270 else: 

271 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by + operator.") 

272 ex.add_note(f"Supported types for second operand: int") 

273 raise ex 

274 

275 def __sub__(self, other: Any) -> int: 

276 """Subtraction: self - other.""" 

277 if isinstance(other, int): 

278 return self.Value - other 

279 else: 

280 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by - operator.") 

281 ex.add_note(f"Supported types for second operand: int") 

282 raise ex 

283 

284 def __truediv__(self, other: Any) -> int: 

285 """Division: self / other.""" 

286 if isinstance(other, int): 

287 return self.Value / other 

288 else: 

289 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by / operator.") 

290 ex.add_note(f"Supported types for second operand: int") 

291 raise ex 

292 

293 def __floordiv__(self, other: Any) -> int: 

294 """Floor division: self // other.""" 

295 if isinstance(other, int): 

296 return self.Value // other 

297 else: 

298 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by // operator.") 

299 ex.add_note(f"Supported types for second operand: int") 

300 raise ex 

301 

302 def __mul__(self, other: Any) -> int: 

303 """Multiplication: self * other.""" 

304 if isinstance(other, int): 

305 return self.Value * other 

306 else: 

307 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by * operator.") 

308 ex.add_note(f"Supported types for second operand: int") 

309 raise ex 

310 

311 def __mod__(self, other: Any) -> int: 

312 """Modulo: self % other.""" 

313 if isinstance(other, int): 

314 return self.Value % other 

315 else: 

316 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by % operator.") 

317 ex.add_note(f"Supported types for second operand: int") 

318 raise ex 

319 

320 def __pow__(self, other: Any) -> int: 

321 """Power: self ** other.""" 

322 if isinstance(other, int): 

323 return self.Value ** other 

324 else: 

325 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ** operator.") 

326 ex.add_note(f"Supported types for second operand: int") 

327 raise ex 

328 

329 # Binary inplace operators - arithmetic 

330 def __iadd__(self, other: Any) -> 'CallByRefIntParam': 

331 """Addition: self += other.""" 

332 if isinstance(other, int): 

333 self.Value += other 

334 return self 

335 else: 

336 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

337 ex.add_note(f"Supported types for second operand: int") 

338 raise ex 

339 

340 def __isub__(self, other: Any) -> 'CallByRefIntParam': 

341 """Subtraction: self -= other.""" 

342 if isinstance(other, int): 

343 self.Value -= other 

344 return self 

345 else: 

346 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

347 ex.add_note(f"Supported types for second operand: int") 

348 raise ex 

349 

350 def __idiv__(self, other: Any) -> 'CallByRefIntParam': 

351 """Division: self /= other.""" 

352 if isinstance(other, int): 

353 self.Value /= other 

354 return self 

355 else: 

356 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

357 ex.add_note(f"Supported types for second operand: int") 

358 raise ex 

359 

360 def __ifloordiv__(self, other: Any) -> 'CallByRefIntParam': 

361 """Floor division: self // other.""" 

362 if isinstance(other, int): 

363 self.Value //= other 

364 return self 

365 else: 

366 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

367 ex.add_note(f"Supported types for second operand: int") 

368 raise ex 

369 

370 def __imul__(self, other: Any) -> 'CallByRefIntParam': 

371 r"""Multiplication: self \*= other.""" 

372 if isinstance(other, int): 

373 self.Value *= other 

374 return self 

375 else: 

376 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

377 ex.add_note(f"Supported types for second operand: int") 

378 raise ex 

379 

380 def __imod__(self, other: Any) -> 'CallByRefIntParam': 

381 """Modulo: self %= other.""" 

382 if isinstance(other, int): 

383 self.Value %= other 

384 return self 

385 else: 

386 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

387 ex.add_note(f"Supported types for second operand: int") 

388 raise ex 

389 

390 def __ipow__(self, other: Any) -> 'CallByRefIntParam': 

391 r"""Power: self \*\*= other.""" 

392 if isinstance(other, int): 

393 self.Value **= other 

394 return self 

395 else: 

396 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.") 

397 ex.add_note(f"Supported types for second operand: int") 

398 raise ex 

399 

400 # Binary operators - comparison 

401 def __eq__(self, other: Any) -> bool: 

402 """ 

403 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for equality. 

404 

405 :param other: Parameter to compare against. 

406 :returns: ``True``, if both values are equal. 

407 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`. 

408 """ 

409 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool): 

410 return self.Value == other 

411 elif isinstance(other, CallByRefIntParam): 411 ↛ 412line 411 didn't jump to line 412 because the condition on line 411 was never true

412 return self.Value == other.Value 

413 else: 

414 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.") 

415 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam") 

416 raise ex 

417 

418 def __ne__(self, other) -> bool: 

419 """ 

420 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for inequality. 

421 

422 :param other: Parameter to compare against. 

423 :returns: ``True``, if both values are unequal. 

424 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`. 

425 """ 

426 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool): 

427 return self.Value != other 

428 elif isinstance(other, CallByRefIntParam): 428 ↛ 429line 428 didn't jump to line 429 because the condition on line 428 was never true

429 return self.Value != other.Value 

430 else: 

431 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.") 

432 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam") 

433 raise ex 

434 

435 def __lt__(self, other: Any) -> bool: 

436 """ 

437 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than. 

438 

439 :param other: Parameter to compare against. 

440 :returns: ``True``, if the wrapped value is less than the other value. 

441 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`. 

442 """ 

443 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool): 

444 return self.Value < other 

445 elif isinstance(other, CallByRefIntParam): 445 ↛ 446line 445 didn't jump to line 446 because the condition on line 445 was never true

446 return self.Value < other.Value 

447 else: 

448 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.") 

449 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam") 

450 raise ex 

451 

452 def __le__(self, other: Any) -> bool: 

453 """ 

454 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than-or-equal. 

455 

456 :param other: Parameter to compare against. 

457 :returns: ``True``, if the wrapped value is less than or equal the other value. 

458 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`. 

459 """ 

460 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool): 

461 return self.Value <= other 

462 elif isinstance(other, CallByRefIntParam): 462 ↛ 463line 462 didn't jump to line 463 because the condition on line 462 was never true

463 return self.Value <= other.Value 

464 else: 

465 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.") 

466 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam") 

467 raise ex 

468 

469 def __gt__(self, other: Any) -> bool: 

470 """ 

471 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for geater-than. 

472 

473 :param other: Parameter to compare against. 

474 :returns: ``True``, if the wrapped value is greater than the other value. 

475 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`. 

476 """ 

477 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool): 

478 return self.Value > other 

479 elif isinstance(other, CallByRefIntParam): 479 ↛ 480line 479 didn't jump to line 480 because the condition on line 479 was never true

480 return self.Value > other.Value 

481 else: 

482 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.") 

483 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam") 

484 raise ex 

485 

486 def __ge__(self, other: Any) -> bool: 

487 """ 

488 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for greater-than-or-equal. 

489 

490 :param other: Parameter to compare against. 

491 :returns: ``True``, if the wrapped value is greater than or equal the other value. 

492 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`. 

493 """ 

494 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool): 

495 return self.Value >= other 

496 elif isinstance(other, CallByRefIntParam): 496 ↛ 497line 496 didn't jump to line 497 because the condition on line 496 was never true

497 return self.Value >= other.Value 

498 else: 

499 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.") 

500 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam") 

501 raise ex 

502 

503 # Type conversion operators 

504 def __bool__(self) -> bool: 

505 """ 

506 Type conversion to :class:`bool`. 

507 

508 :returns: The boolean representation of the wrapped integer value. 

509 """ 

510 return bool(self.Value) 

511 

512 def __int__(self) -> int: 

513 """ 

514 Type conversion to :class:`int`. 

515 

516 :returns: The wrapped value.""" 

517 return self.Value 

518 

519 def __float__(self) -> float: 

520 """ 

521 Type conversion to :class:`float`. 

522 

523 :returns: The float representation of the wrapped integer value. 

524 """ 

525 return float(self.Value)