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

213 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-25 22:22 +0000

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

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

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

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

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

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

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

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

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

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

14# Copyright 2017-2025 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:: See :ref:`high-level help <COMMON/CallByRef>` for explanations and usage examples. 

35""" 

36from decimal import Decimal 

37from sys import version_info # needed for versions before Python 3.11 

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

39 

40try: 

41 from pyTooling.Decorators import export 

42 from pyTooling.MetaClasses import ExtendedType 

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

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

45 

46 try: 

47 from Decorators import export 

48 from MetaClasses import ExtendedType 

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

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

51 raise ex 

52 

53 

54T = TypeVar("T") 

55 

56 

57@export 

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

59 """ 

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

61 

62 .. seealso:: 

63 

64 * :class:`CallByRefBoolParam` |br| 

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

66 * :class:`CallByRefIntParam` |br| 

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

68 """ 

69 

70 Value: T #: internal value 

71 

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

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

74 

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

76 """ 

77 self.Value = value 

78 

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

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

81 

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

83 :returns: Itself. 

84 """ 

85 self.Value = other 

86 return self 

87 

88 # binary operators - comparison 

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

90 """ 

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

92 

93 :param other: Parameter to compare against. 

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

95 """ 

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

97 return self.Value == other.Value 

98 else: 

99 return self.Value == other 

100 

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

102 """ 

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

104 

105 :param other: Parameter to compare against. 

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

107 """ 

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

109 return self.Value != other.Value 

110 else: 

111 return self.Value != other 

112 

113 # Type conversion operators 

114 def __repr__(self) -> str: 

115 """ 

116 Returns the wrapped object's string representation. 

117 

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

119 """ 

120 return repr(self.Value) 

121 

122 def __str__(self) -> str: 

123 """ 

124 Returns the wrapped object's string equivalent. 

125 

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

127 """ 

128 return str(self.Value) 

129 

130 

131@export 

132class CallByRefBoolParam(CallByRefParam): 

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

134 

135 # Binary operators - comparison 

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

137 """ 

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

139 

140 :param other: Parameter to compare against. 

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

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

143 """ 

144 if isinstance(other, bool): 

145 return self.Value == other 

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

147 return self.Value == other.Value 

148 else: 

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

150 if version_info >= (3, 11): # pragma: no cover 

151 ex.add_note(f"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 if version_info >= (3, 11): # pragma: no cover 

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

170 raise ex 

171 

172 # Type conversion operators 

173 def __bool__(self) -> bool: 

174 """ 

175 Type conversion to :class:`bool`. 

176 

177 :returns: The wrapped value. 

178 """ 

179 return self.Value 

180 

181 def __int__(self) -> int: 

182 """ 

183 Type conversion to :class:`int`. 

184 

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

186 """ 

187 return int(self.Value) 

188 

189 

190@export 

191class CallByRefIntParam(CallByRefParam): 

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

193 

194 # Unary operators 

195 def __neg__(self) -> int: 

196 """Negate: -self.""" 

197 return -self.Value 

198 

199 def __pos__(self) -> int: 

200 """Positive: +self.""" 

201 return +self.Value 

202 

203 def __invert__(self) -> int: 

204 """Invert: ~self.""" 

205 return ~self.Value 

206 

207 # Binary operators - logical 

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

209 """And: self & other.""" 

210 if isinstance(other, int): 

211 return self.Value & other 

212 else: 

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

214 if version_info >= (3, 11): # pragma: no cover 

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

216 raise ex 

217 

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

219 """Or: self | other.""" 

220 if isinstance(other, int): 

221 return self.Value | other 

222 else: 

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

224 if version_info >= (3, 11): # pragma: no cover 

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

226 raise ex 

227 

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

229 """Xor: self ^ other.""" 

230 if isinstance(other, int): 

231 return self.Value ^ other 

232 else: 

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

234 if version_info >= (3, 11): # pragma: no cover 

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

236 raise ex 

237 

238 # Binary inplace operators 

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

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

241 if isinstance(other, int): 

242 self.Value &= other 

243 return self 

244 else: 

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

246 if version_info >= (3, 11): # pragma: no cover 

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

248 raise ex 

249 

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

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

252 if isinstance(other, int): 

253 self.Value |= other 

254 return self 

255 else: 

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

257 if version_info >= (3, 11): # pragma: no cover 

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

259 raise ex 

260 

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

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

263 if isinstance(other, int): 

264 self.Value ^= other 

265 return self 

266 else: 

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

268 if version_info >= (3, 11): # pragma: no cover 

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

270 raise ex 

271 

272 # Binary operators - arithmetic 

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

274 """Addition: self + other.""" 

275 if isinstance(other, int): 

276 return self.Value + other 

277 else: 

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

279 if version_info >= (3, 11): # pragma: no cover 

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

281 raise ex 

282 

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

284 """Subtraction: self - other.""" 

285 if isinstance(other, int): 

286 return self.Value - other 

287 else: 

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

289 if version_info >= (3, 11): # pragma: no cover 

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

291 raise ex 

292 

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

294 """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 if version_info >= (3, 11): # pragma: no cover 

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

301 raise ex 

302 

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

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

305 if isinstance(other, int): 

306 return self.Value // other 

307 else: 

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

309 if version_info >= (3, 11): # pragma: no cover 

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

311 raise ex 

312 

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

314 """Multiplication: self * other.""" 

315 if isinstance(other, int): 

316 return self.Value * other 

317 else: 

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

319 if version_info >= (3, 11): # pragma: no cover 

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

321 raise ex 

322 

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

324 """Modulo: self % other.""" 

325 if isinstance(other, int): 

326 return self.Value % other 

327 else: 

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

329 if version_info >= (3, 11): # pragma: no cover 

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

331 raise ex 

332 

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

334 """Power: self ** other.""" 

335 if isinstance(other, int): 

336 return self.Value ** other 

337 else: 

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

339 if version_info >= (3, 11): # pragma: no cover 

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

341 raise ex 

342 

343 # Binary inplace operators - arithmetic 

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

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

346 if isinstance(other, int): 

347 self.Value += other 

348 return self 

349 else: 

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

351 if version_info >= (3, 11): # pragma: no cover 

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

353 raise ex 

354 

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

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

357 if isinstance(other, int): 

358 self.Value -= other 

359 return self 

360 else: 

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

362 if version_info >= (3, 11): # pragma: no cover 

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

364 raise ex 

365 

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

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

368 if isinstance(other, int): 

369 self.Value /= other 

370 return self 

371 else: 

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

373 if version_info >= (3, 11): # pragma: no cover 

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

375 raise ex 

376 

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

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

379 if isinstance(other, int): 

380 self.Value //= other 

381 return self 

382 else: 

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

384 if version_info >= (3, 11): # pragma: no cover 

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

386 raise ex 

387 

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

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

390 if isinstance(other, int): 

391 self.Value *= other 

392 return self 

393 else: 

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

395 if version_info >= (3, 11): # pragma: no cover 

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

397 raise ex 

398 

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

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

401 if isinstance(other, int): 

402 self.Value %= other 

403 return self 

404 else: 

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

406 if version_info >= (3, 11): # pragma: no cover 

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

408 raise ex 

409 

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

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

412 if isinstance(other, int): 

413 self.Value **= other 

414 return self 

415 else: 

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

417 if version_info >= (3, 11): # pragma: no cover 

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

419 raise ex 

420 

421 # Binary operators - comparison 

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

423 """ 

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

425 

426 :param other: Parameter to compare against. 

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

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

429 """ 

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

431 return self.Value == other 

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

433 return self.Value == other.Value 

434 else: 

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

436 if version_info >= (3, 11): # pragma: no cover 

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

438 raise ex 

439 

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

441 """ 

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

443 

444 :param other: Parameter to compare against. 

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

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

447 """ 

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

449 return self.Value != other 

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

451 return self.Value != other.Value 

452 else: 

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

454 if version_info >= (3, 11): # pragma: no cover 

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

456 raise ex 

457 

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

459 """ 

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

461 

462 :param other: Parameter to compare against. 

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

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

465 """ 

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

467 return self.Value < other 

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

469 return self.Value < other.Value 

470 else: 

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

472 if version_info >= (3, 11): # pragma: no cover 

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

474 raise ex 

475 

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

477 """ 

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

479 

480 :param other: Parameter to compare against. 

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

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

483 """ 

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

485 return self.Value <= other 

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

487 return self.Value <= other.Value 

488 else: 

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

490 if version_info >= (3, 11): # pragma: no cover 

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

492 raise ex 

493 

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

495 """ 

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

497 

498 :param other: Parameter to compare against. 

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

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

501 """ 

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

503 return self.Value > other 

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

505 return self.Value > other.Value 

506 else: 

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

508 if version_info >= (3, 11): # pragma: no cover 

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

510 raise ex 

511 

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

513 """ 

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

515 

516 :param other: Parameter to compare against. 

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

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

519 """ 

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

521 return self.Value >= other 

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

523 return self.Value >= other.Value 

524 else: 

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

526 if version_info >= (3, 11): # pragma: no cover 

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

528 raise ex 

529 

530 # Type conversion operators 

531 def __bool__(self) -> bool: 

532 """ 

533 Type conversion to :class:`bool`. 

534 

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

536 """ 

537 return bool(self.Value) 

538 

539 def __int__(self) -> int: 

540 """ 

541 Type conversion to :class:`int`. 

542 

543 :returns: The wrapped value.""" 

544 return self.Value 

545 

546 def __float__(self): 

547 """ 

548 Type conversion to :class:`float`. 

549 

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

551 """ 

552 return float(self.Value)