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

241 statements  

« prev     ^ index     » next       coverage.py v7.11.1, created at 2025-11-07 22:21 +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 ex.add_note("Supported types for second operand: bool, CallByRefBoolParam") 

151 raise ex 

152 

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

154 """ 

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

156 

157 :param other: Parameter to compare against. 

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

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

160 """ 

161 if isinstance(other, bool): 

162 return self.Value != other 

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

164 return self.Value != other.Value 

165 else: 

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

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

168 raise ex 

169 

170 # Type conversion operators 

171 def __bool__(self) -> bool: 

172 """ 

173 Type conversion to :class:`bool`. 

174 

175 :returns: The wrapped value. 

176 """ 

177 return self.Value 

178 

179 def __int__(self) -> int: 

180 """ 

181 Type conversion to :class:`int`. 

182 

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

184 """ 

185 return int(self.Value) 

186 

187 

188@export 

189class CallByRefIntParam(CallByRefParam): 

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

191 

192 # Unary operators 

193 def __neg__(self) -> int: 

194 """Negate: -self.""" 

195 return -self.Value 

196 

197 def __pos__(self) -> int: 

198 """Positive: +self.""" 

199 return +self.Value 

200 

201 def __invert__(self) -> int: 

202 """Invert: ~self.""" 

203 return ~self.Value 

204 

205 # Binary operators - logical 

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

207 """And: self & other.""" 

208 if isinstance(other, int): 

209 return self.Value & other 

210 else: 

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

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

213 raise ex 

214 

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

216 """Or: self | other.""" 

217 if isinstance(other, int): 

218 return self.Value | other 

219 else: 

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

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

222 raise ex 

223 

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

225 """Xor: self ^ other.""" 

226 if isinstance(other, int): 

227 return self.Value ^ other 

228 else: 

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

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

231 raise ex 

232 

233 # Binary inplace operators 

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

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

236 if isinstance(other, int): 

237 self.Value &= other 

238 return self 

239 else: 

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

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

242 raise ex 

243 

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

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

246 if isinstance(other, int): 

247 self.Value |= other 

248 return self 

249 else: 

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

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

252 raise ex 

253 

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

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

256 if isinstance(other, int): 

257 self.Value ^= other 

258 return self 

259 else: 

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

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

262 raise ex 

263 

264 # Binary operators - arithmetic 

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

266 """Addition: self + other.""" 

267 if isinstance(other, int): 

268 return self.Value + other 

269 else: 

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

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

272 raise ex 

273 

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

275 """Subtraction: self - other.""" 

276 if isinstance(other, int): 

277 return self.Value - other 

278 else: 

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

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

281 raise ex 

282 

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

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

290 raise ex 

291 

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

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

294 if isinstance(other, int): 

295 return self.Value // other 

296 else: 

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

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

299 raise ex 

300 

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

302 """Multiplication: self * other.""" 

303 if isinstance(other, int): 

304 return self.Value * other 

305 else: 

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

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

308 raise ex 

309 

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

311 """Modulo: self % other.""" 

312 if isinstance(other, int): 

313 return self.Value % other 

314 else: 

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

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

317 raise ex 

318 

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

320 """Power: self ** other.""" 

321 if isinstance(other, int): 

322 return self.Value ** other 

323 else: 

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

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

326 raise ex 

327 

328 # Binary inplace operators - arithmetic 

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

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

331 if isinstance(other, int): 

332 self.Value += other 

333 return self 

334 else: 

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

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

337 raise ex 

338 

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

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

341 if isinstance(other, int): 

342 self.Value -= other 

343 return self 

344 else: 

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

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

347 raise ex 

348 

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

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

351 if isinstance(other, int): 

352 self.Value /= other 

353 return self 

354 else: 

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

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

357 raise ex 

358 

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

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

361 if isinstance(other, int): 

362 self.Value //= other 

363 return self 

364 else: 

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

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

367 raise ex 

368 

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

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

371 if isinstance(other, int): 

372 self.Value *= other 

373 return self 

374 else: 

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

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

377 raise ex 

378 

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

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

381 if isinstance(other, int): 

382 self.Value %= other 

383 return self 

384 else: 

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

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

387 raise ex 

388 

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

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

391 if isinstance(other, int): 

392 self.Value **= other 

393 return self 

394 else: 

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

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

397 raise ex 

398 

399 # Binary operators - comparison 

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

401 """ 

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

403 

404 :param other: Parameter to compare against. 

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

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

407 """ 

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

409 return self.Value == other 

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

411 return self.Value == other.Value 

412 else: 

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

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

415 raise ex 

416 

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

418 """ 

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

420 

421 :param other: Parameter to compare against. 

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

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

424 """ 

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

426 return self.Value != other 

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

428 return self.Value != other.Value 

429 else: 

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

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

432 raise ex 

433 

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

435 """ 

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

437 

438 :param other: Parameter to compare against. 

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

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

441 """ 

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

443 return self.Value < other 

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

445 return self.Value < other.Value 

446 else: 

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

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

449 raise ex 

450 

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

452 """ 

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

454 

455 :param other: Parameter to compare against. 

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

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

458 """ 

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

460 return self.Value <= other 

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

462 return self.Value <= other.Value 

463 else: 

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

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

466 raise ex 

467 

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

469 """ 

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

471 

472 :param other: Parameter to compare against. 

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

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

475 """ 

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

477 return self.Value > other 

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

479 return self.Value > other.Value 

480 else: 

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

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

483 raise ex 

484 

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

486 """ 

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

488 

489 :param other: Parameter to compare against. 

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

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

492 """ 

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

494 return self.Value >= other 

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

496 return self.Value >= other.Value 

497 else: 

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

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

500 raise ex 

501 

502 # Type conversion operators 

503 def __bool__(self) -> bool: 

504 """ 

505 Type conversion to :class:`bool`. 

506 

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

508 """ 

509 return bool(self.Value) 

510 

511 def __int__(self) -> int: 

512 """ 

513 Type conversion to :class:`int`. 

514 

515 :returns: The wrapped value.""" 

516 return self.Value 

517 

518 def __float__(self): 

519 """ 

520 Type conversion to :class:`float`. 

521 

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

523 """ 

524 return float(self.Value)