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

240 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-21 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 typing import Any, Generic, TypeVar, Optional as Nullable 

38 

39try: 

40 from pyTooling.Decorators import export 

41 from pyTooling.MetaClasses import ExtendedType 

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

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

44 

45 try: 

46 from Decorators import export 

47 from MetaClasses import ExtendedType 

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

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

50 raise ex 

51 

52 

53T = TypeVar("T") 

54 

55 

56@export 

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

58 """ 

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

60 

61 .. seealso:: 

62 

63 * :class:`CallByRefBoolParam` |br| 

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

65 * :class:`CallByRefIntParam` |br| 

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

67 """ 

68 

69 Value: T #: internal value 

70 

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

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

73 

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

75 """ 

76 self.Value = value 

77 

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

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

80 

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

82 :returns: Itself. 

83 """ 

84 self.Value = other 

85 return self 

86 

87 # binary operators - comparison 

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

89 """ 

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

91 

92 :param other: Parameter to compare against. 

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

94 """ 

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

96 return self.Value == other.Value 

97 else: 

98 return self.Value == other 

99 

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

101 """ 

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

103 

104 :param other: Parameter to compare against. 

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

106 """ 

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

108 return self.Value != other.Value 

109 else: 

110 return self.Value != other 

111 

112 # Type conversion operators 

113 def __repr__(self) -> str: 

114 """ 

115 Returns the wrapped object's string representation. 

116 

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

118 """ 

119 return repr(self.Value) 

120 

121 def __str__(self) -> str: 

122 """ 

123 Returns the wrapped object's string equivalent. 

124 

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

126 """ 

127 return str(self.Value) 

128 

129 

130@export 

131class CallByRefBoolParam(CallByRefParam): 

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

133 

134 # Binary operators - comparison 

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

136 """ 

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

138 

139 :param other: Parameter to compare against. 

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

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

142 """ 

143 if isinstance(other, bool): 

144 return self.Value == other 

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

146 return self.Value == other.Value 

147 else: 

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

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

150 raise ex 

151 

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

153 """ 

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

155 

156 :param other: Parameter to compare against. 

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

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

159 """ 

160 if isinstance(other, bool): 

161 return self.Value != other 

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

163 return self.Value != other.Value 

164 else: 

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

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

167 raise ex 

168 

169 # Type conversion operators 

170 def __bool__(self) -> bool: 

171 """ 

172 Type conversion to :class:`bool`. 

173 

174 :returns: The wrapped value. 

175 """ 

176 return self.Value 

177 

178 def __int__(self) -> int: 

179 """ 

180 Type conversion to :class:`int`. 

181 

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

183 """ 

184 return int(self.Value) 

185 

186 

187@export 

188class CallByRefIntParam(CallByRefParam): 

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

190 

191 # Unary operators 

192 def __neg__(self) -> int: 

193 """Negate: -self.""" 

194 return -self.Value 

195 

196 def __pos__(self) -> int: 

197 """Positive: +self.""" 

198 return +self.Value 

199 

200 def __invert__(self) -> int: 

201 """Invert: ~self.""" 

202 return ~self.Value 

203 

204 # Binary operators - logical 

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

206 """And: self & other.""" 

207 if isinstance(other, int): 

208 return self.Value & other 

209 else: 

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

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

212 raise ex 

213 

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

215 """Or: self | other.""" 

216 if isinstance(other, int): 

217 return self.Value | other 

218 else: 

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

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

221 raise ex 

222 

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

224 """Xor: self ^ other.""" 

225 if isinstance(other, int): 

226 return self.Value ^ other 

227 else: 

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

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

230 raise ex 

231 

232 # Binary inplace operators 

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

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

235 if isinstance(other, int): 

236 self.Value &= other 

237 return self 

238 else: 

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

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

241 raise ex 

242 

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

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

245 if isinstance(other, int): 

246 self.Value |= other 

247 return self 

248 else: 

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

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

251 raise ex 

252 

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

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

255 if isinstance(other, int): 

256 self.Value ^= other 

257 return self 

258 else: 

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

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

261 raise ex 

262 

263 # Binary operators - arithmetic 

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

265 """Addition: self + other.""" 

266 if isinstance(other, int): 

267 return self.Value + other 

268 else: 

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

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

271 raise ex 

272 

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

274 """Subtraction: 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 ex.add_note(f"Supported types for second operand: int") 

280 raise ex 

281 

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

283 """Division: self / other.""" 

284 if isinstance(other, int): 

285 return self.Value / other 

286 else: 

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

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

289 raise ex 

290 

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

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

293 if isinstance(other, int): 

294 return self.Value // other 

295 else: 

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

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

298 raise ex 

299 

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

301 """Multiplication: self * other.""" 

302 if isinstance(other, int): 

303 return self.Value * other 

304 else: 

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

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

307 raise ex 

308 

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

310 """Modulo: self % other.""" 

311 if isinstance(other, int): 

312 return self.Value % other 

313 else: 

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

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

316 raise ex 

317 

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

319 """Power: self ** other.""" 

320 if isinstance(other, int): 

321 return self.Value ** other 

322 else: 

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

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

325 raise ex 

326 

327 # Binary inplace operators - arithmetic 

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

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

330 if isinstance(other, int): 

331 self.Value += other 

332 return self 

333 else: 

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

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

336 raise ex 

337 

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

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

340 if isinstance(other, int): 

341 self.Value -= other 

342 return self 

343 else: 

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

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

346 raise ex 

347 

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

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

350 if isinstance(other, int): 

351 self.Value /= other 

352 return self 

353 else: 

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

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

356 raise ex 

357 

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

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

360 if isinstance(other, int): 

361 self.Value //= other 

362 return self 

363 else: 

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

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

366 raise ex 

367 

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

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

370 if isinstance(other, int): 

371 self.Value *= other 

372 return self 

373 else: 

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

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

376 raise ex 

377 

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

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

380 if isinstance(other, int): 

381 self.Value %= other 

382 return self 

383 else: 

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

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

386 raise ex 

387 

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

389 r"""Power: 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 ex.add_note(f"Supported types for second operand: int") 

396 raise ex 

397 

398 # Binary operators - comparison 

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

400 """ 

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

402 

403 :param other: Parameter to compare against. 

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

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

406 """ 

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

408 return self.Value == other 

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

410 return self.Value == other.Value 

411 else: 

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

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

414 raise ex 

415 

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

417 """ 

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

419 

420 :param other: Parameter to compare against. 

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

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

423 """ 

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

425 return self.Value != other 

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

427 return self.Value != other.Value 

428 else: 

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

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

431 raise ex 

432 

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

434 """ 

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

436 

437 :param other: Parameter to compare against. 

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

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

440 """ 

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

442 return self.Value < other 

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

444 return self.Value < other.Value 

445 else: 

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

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

448 raise ex 

449 

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

451 """ 

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

453 

454 :param other: Parameter to compare against. 

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

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

457 """ 

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

459 return self.Value <= other 

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

461 return self.Value <= other.Value 

462 else: 

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

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

465 raise ex 

466 

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

468 """ 

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

470 

471 :param other: Parameter to compare against. 

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

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

474 """ 

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

476 return self.Value > other 

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

478 return self.Value > other.Value 

479 else: 

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

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

482 raise ex 

483 

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

485 """ 

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

487 

488 :param other: Parameter to compare against. 

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

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

491 """ 

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

493 return self.Value >= other 

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

495 return self.Value >= other.Value 

496 else: 

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

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

499 raise ex 

500 

501 # Type conversion operators 

502 def __bool__(self) -> bool: 

503 """ 

504 Type conversion to :class:`bool`. 

505 

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

507 """ 

508 return bool(self.Value) 

509 

510 def __int__(self) -> int: 

511 """ 

512 Type conversion to :class:`int`. 

513 

514 :returns: The wrapped value.""" 

515 return self.Value 

516 

517 def __float__(self): 

518 """ 

519 Type conversion to :class:`float`. 

520 

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

522 """ 

523 return float(self.Value)