1""" 2Numba-specific errors and warnings. 3""" 4 5 6import abc 7import contextlib 8import os 9import sys 10import warnings 11import numba.core.config 12import numpy as np 13from collections import defaultdict 14from numba.core.utils import add_metaclass, reraise, chain_exception 15from functools import wraps 16from abc import abstractmethod 17 18# Filled at the end 19__all__ = [] 20 21 22class NumbaWarning(Warning): 23 """ 24 Base category for all Numba compiler warnings. 25 """ 26 27 def __init__(self, msg, loc=None, highlighting=True, ): 28 self.msg = msg 29 self.loc = loc 30 if highlighting: 31 highlight = termcolor().errmsg 32 else: 33 def highlight(x): 34 return x 35 if loc: 36 super(NumbaWarning, self).__init__( 37 highlight("%s\n%s\n" % (msg, loc.strformat()))) 38 else: 39 super(NumbaWarning, self).__init__(highlight("%s" % (msg,))) 40 41 42class NumbaPerformanceWarning(NumbaWarning): 43 """ 44 Warning category for when an operation might not be 45 as fast as expected. 46 """ 47 48 49class NumbaDeprecationWarning(NumbaWarning): 50 """ 51 Warning category for use of a deprecated feature. 52 """ 53 54 55class NumbaPendingDeprecationWarning(NumbaWarning): 56 """ 57 Warning category for use of a feature that is pending deprecation. 58 """ 59 60 61class NumbaParallelSafetyWarning(NumbaWarning): 62 """ 63 Warning category for when an operation in a prange 64 might not have parallel semantics. 65 """ 66 67 68class NumbaTypeSafetyWarning(NumbaWarning): 69 """ 70 Warning category for unsafe casting operations. 71 """ 72 73 74class NumbaExperimentalFeatureWarning(NumbaWarning): 75 """ 76 Warning category for using an experimental feature. 77 """ 78 79# These are needed in the color formatting of errors setup 80 81 82@add_metaclass(abc.ABCMeta) 83class _ColorScheme(object): 84 85 @abstractmethod 86 def code(self, msg): 87 pass 88 89 @abstractmethod 90 def errmsg(self, msg): 91 pass 92 93 @abstractmethod 94 def filename(self, msg): 95 pass 96 97 @abstractmethod 98 def indicate(self, msg): 99 pass 100 101 @abstractmethod 102 def highlight(self, msg): 103 pass 104 105 @abstractmethod 106 def reset(self, msg): 107 pass 108 109 110class _DummyColorScheme(_ColorScheme): 111 112 def __init__(self, theme=None): 113 pass 114 115 def code(self, msg): 116 pass 117 118 def errmsg(self, msg): 119 pass 120 121 def filename(self, msg): 122 pass 123 124 def indicate(self, msg): 125 pass 126 127 def highlight(self, msg): 128 pass 129 130 def reset(self, msg): 131 pass 132 133 134# holds reference to the instance of the terminal color scheme in use 135_termcolor_inst = None 136 137try: 138 import colorama 139 140 # If the colorama version is < 0.3.9 it can break stdout/stderr in some 141 # situations, as a result if this condition is met colorama is disabled and 142 # the user is warned. Note that early versions did not have a __version__. 143 colorama_version = getattr(colorama, '__version__', '0.0.0') 144 145 if tuple([int(x) for x in colorama_version.split('.')]) < (0, 3, 9): 146 msg = ("Insufficiently recent colorama version found. " 147 "Numba requires colorama >= 0.3.9") 148 # warn the user 149 warnings.warn(msg) 150 # trip the exception to disable color errors 151 raise ImportError 152 153 # If Numba is running in testsuite mode then do not use error message 154 # coloring so CI system output is consistently readable without having 155 # to read between shell escape characters. 156 if os.environ.get('NUMBA_DISABLE_ERROR_MESSAGE_HIGHLIGHTING', None): 157 raise ImportError # just to trigger the exception handler below 158 159except ImportError: 160 161 class NOPColorScheme(_DummyColorScheme): 162 def __init__(self, theme=None): 163 if theme is not None: 164 raise ValueError("specifying a theme has no effect") 165 _DummyColorScheme.__init__(self, theme=theme) 166 167 def code(self, msg): 168 return msg 169 170 def errmsg(self, msg): 171 return msg 172 173 def filename(self, msg): 174 return msg 175 176 def indicate(self, msg): 177 return msg 178 179 def highlight(self, msg): 180 return msg 181 182 def reset(self, msg): 183 return msg 184 185 def termcolor(): 186 global _termcolor_inst 187 if _termcolor_inst is None: 188 _termcolor_inst = NOPColorScheme() 189 return _termcolor_inst 190 191else: 192 193 from colorama import init, reinit, deinit, Fore, Style 194 195 class ColorShell(object): 196 _has_initialized = False 197 198 def __init__(self): 199 init() 200 self._has_initialized = True 201 202 def __enter__(self): 203 if self._has_initialized: 204 reinit() 205 206 def __exit__(self, *exc_detail): 207 Style.RESET_ALL 208 deinit() 209 210 class reset_terminal(object): 211 def __init__(self): 212 self._buf = bytearray(b'') 213 214 def __enter__(self): 215 return self._buf 216 217 def __exit__(self, *exc_detail): 218 self._buf += bytearray(Style.RESET_ALL.encode('utf-8')) 219 220 # define some default themes, if more are added, update the envvars docs! 221 themes = {} 222 223 # No color added, just bold weighting 224 themes['no_color'] = {'code': None, 225 'errmsg': None, 226 'filename': None, 227 'indicate': None, 228 'highlight': None, 229 'reset': None, } 230 231 # suitable for terminals with a dark background 232 themes['dark_bg'] = {'code': Fore.BLUE, 233 'errmsg': Fore.YELLOW, 234 'filename': Fore.WHITE, 235 'indicate': Fore.GREEN, 236 'highlight': Fore.RED, 237 'reset': Style.RESET_ALL, } 238 239 # suitable for terminals with a light background 240 themes['light_bg'] = {'code': Fore.BLUE, 241 'errmsg': Fore.BLACK, 242 'filename': Fore.MAGENTA, 243 'indicate': Fore.BLACK, 244 'highlight': Fore.RED, 245 'reset': Style.RESET_ALL, } 246 247 # suitable for terminals with a blue background 248 themes['blue_bg'] = {'code': Fore.WHITE, 249 'errmsg': Fore.YELLOW, 250 'filename': Fore.MAGENTA, 251 'indicate': Fore.CYAN, 252 'highlight': Fore.RED, 253 'reset': Style.RESET_ALL, } 254 255 # suitable for use in jupyter notebooks 256 themes['jupyter_nb'] = {'code': Fore.BLACK, 257 'errmsg': Fore.BLACK, 258 'filename': Fore.GREEN, 259 'indicate': Fore.CYAN, 260 'highlight': Fore.RED, 261 'reset': Style.RESET_ALL, } 262 263 default_theme = themes['no_color'] 264 265 class HighlightColorScheme(_DummyColorScheme): 266 def __init__(self, theme=default_theme): 267 self._code = theme['code'] 268 self._errmsg = theme['errmsg'] 269 self._filename = theme['filename'] 270 self._indicate = theme['indicate'] 271 self._highlight = theme['highlight'] 272 self._reset = theme['reset'] 273 _DummyColorScheme.__init__(self, theme=theme) 274 275 def _markup(self, msg, color=None, style=Style.BRIGHT): 276 features = '' 277 if color: 278 features += color 279 if style: 280 features += style 281 with ColorShell(): 282 with reset_terminal() as mu: 283 mu += features.encode('utf-8') 284 mu += (msg).encode('utf-8') 285 return mu.decode('utf-8') 286 287 def code(self, msg): 288 return self._markup(msg, self._code) 289 290 def errmsg(self, msg): 291 return self._markup(msg, self._errmsg) 292 293 def filename(self, msg): 294 return self._markup(msg, self._filename) 295 296 def indicate(self, msg): 297 return self._markup(msg, self._indicate) 298 299 def highlight(self, msg): 300 return self._markup(msg, self._highlight) 301 302 def reset(self, msg): 303 return self._markup(msg, self._reset) 304 305 def termcolor(): 306 global _termcolor_inst 307 if _termcolor_inst is None: 308 scheme = themes[numba.core.config.COLOR_SCHEME] 309 _termcolor_inst = HighlightColorScheme(scheme) 310 return _termcolor_inst 311 312feedback_details = """ 313Please report the error message and traceback, along with a minimal reproducer 314at: https://github.com/numba/numba/issues/new 315 316If more help is needed please feel free to speak to the Numba core developers 317directly at: https://gitter.im/numba/numba 318 319Thanks in advance for your help in improving Numba! 320""" 321 322unsupported_error_info = """ 323Unsupported functionality was found in the code Numba was trying to compile. 324 325If this functionality is important to you please file a feature request at: 326https://github.com/numba/numba/issues/new 327""" 328 329interpreter_error_info = """ 330Unsupported Python functionality was found in the code Numba was trying to 331compile. This error could be due to invalid code, does the code work 332without Numba? (To temporarily disable Numba JIT, set the `NUMBA_DISABLE_JIT` 333environment variable to non-zero, and then rerun the code). 334 335If the code is valid and the unsupported functionality is important to you 336please file a feature request at: https://github.com/numba/numba/issues/new 337 338To see Python/NumPy features supported by the latest release of Numba visit: 339https://numba.pydata.org/numba-doc/latest/reference/pysupported.html 340and 341https://numba.pydata.org/numba-doc/latest/reference/numpysupported.html 342""" 343 344constant_inference_info = """ 345Numba could not make a constant out of something that it decided should be 346a constant. This could well be a current limitation in Numba's internals, 347however please first check that your code is valid for compilation, 348particularly with respect to string interpolation (not supported!) and 349the requirement of compile time constants as arguments to exceptions: 350https://numba.pydata.org/numba-doc/latest/reference/pysupported.html?highlight=exceptions#constructs 351 352If the code is valid and the unsupported functionality is important to you 353please file a feature request at: https://github.com/numba/numba/issues/new 354 355If you think your code should work with Numba. %s 356""" % feedback_details 357 358typing_error_info = """ 359This is not usually a problem with Numba itself but instead often caused by 360the use of unsupported features or an issue in resolving types. 361 362To see Python/NumPy features supported by the latest release of Numba visit: 363https://numba.pydata.org/numba-doc/latest/reference/pysupported.html 364and 365https://numba.pydata.org/numba-doc/latest/reference/numpysupported.html 366 367For more information about typing errors and how to debug them visit: 368https://numba.pydata.org/numba-doc/latest/user/troubleshoot.html#my-code-doesn-t-compile 369 370If you think your code should work with Numba, please report the error message 371and traceback, along with a minimal reproducer at: 372https://github.com/numba/numba/issues/new 373""" 374 375reportable_issue_info = """ 376------------------------------------------------------------------------------- 377This should not have happened, a problem has occurred in Numba's internals. 378You are currently using Numba version %s. 379%s 380""" % (numba.__version__, feedback_details) 381 382error_extras = dict() 383error_extras['unsupported_error'] = unsupported_error_info 384error_extras['typing'] = typing_error_info 385error_extras['reportable'] = reportable_issue_info 386error_extras['interpreter'] = interpreter_error_info 387error_extras['constant_inference'] = constant_inference_info 388 389 390def deprecated(arg): 391 """Define a deprecation decorator. 392 An optional string should refer to the new API to be used instead. 393 394 Example: 395 @deprecated 396 def old_func(): ... 397 398 @deprecated('new_func') 399 def old_func(): ...""" 400 401 subst = arg if isinstance(arg, str) else None 402 403 def decorator(func): 404 def wrapper(*args, **kwargs): 405 msg = "Call to deprecated function \"{}\"." 406 if subst: 407 msg += "\n Use \"{}\" instead." 408 warnings.warn(msg.format(func.__name__, subst), 409 category=DeprecationWarning, stacklevel=2) 410 return func(*args, **kwargs) 411 412 return wraps(func)(wrapper) 413 414 if not subst: 415 return decorator(arg) 416 else: 417 return decorator 418 419 420class WarningsFixer(object): 421 """ 422 An object "fixing" warnings of a given category caught during 423 certain phases. The warnings can have their filename and lineno fixed, 424 and they are deduplicated as well. 425 """ 426 427 def __init__(self, category): 428 self._category = category 429 # {(filename, lineno, category) -> messages} 430 self._warnings = defaultdict(set) 431 432 @contextlib.contextmanager 433 def catch_warnings(self, filename=None, lineno=None): 434 """ 435 Store warnings and optionally fix their filename and lineno. 436 """ 437 with warnings.catch_warnings(record=True) as wlist: 438 warnings.simplefilter('always', self._category) 439 yield 440 441 for w in wlist: 442 msg = str(w.message) 443 if issubclass(w.category, self._category): 444 # Store warnings of this category for deduplication 445 filename = filename or w.filename 446 lineno = lineno or w.lineno 447 self._warnings[filename, lineno, w.category].add(msg) 448 else: 449 # Simply emit other warnings again 450 warnings.warn_explicit(msg, w.category, 451 w.filename, w.lineno) 452 453 def flush(self): 454 """ 455 Emit all stored warnings. 456 """ 457 def key(arg): 458 # It is possible through codegen to create entirely identical 459 # warnings, this leads to comparing types when sorting which breaks 460 # on Python 3. Key as str() and if the worse happens then `id` 461 # creates some uniqueness 462 return str(arg) + str(id(arg)) 463 464 for (filename, lineno, category), messages in sorted( 465 self._warnings.items(), key=key): 466 for msg in sorted(messages): 467 warnings.warn_explicit(msg, category, filename, lineno) 468 self._warnings.clear() 469 470 471class NumbaError(Exception): 472 473 def __init__(self, msg, loc=None, highlighting=True): 474 self.msg = msg 475 self.loc = loc 476 if highlighting: 477 highlight = termcolor().errmsg 478 else: 479 def highlight(x): 480 return x 481 482 if loc: 483 new_msg = "%s\n%s\n" % (msg, loc.strformat()) 484 else: 485 new_msg = "%s" % (msg,) 486 super(NumbaError, self).__init__(highlight(new_msg)) 487 488 @property 489 def contexts(self): 490 try: 491 return self._contexts 492 except AttributeError: 493 self._contexts = lst = [] 494 return lst 495 496 def add_context(self, msg): 497 """ 498 Add contextual info. The exception message is expanded with the new 499 contextual information. 500 """ 501 self.contexts.append(msg) 502 f = termcolor().errmsg('{0}\n') + termcolor().filename('During: {1}') 503 newmsg = f.format(self, msg) 504 self.args = (newmsg,) 505 return self 506 507 def patch_message(self, new_message): 508 """ 509 Change the error message to the given new message. 510 """ 511 self.args = (new_message,) + self.args[1:] 512 513 514class UnsupportedError(NumbaError): 515 """ 516 Numba does not have an implementation for this functionality. 517 """ 518 pass 519 520 521class UnsupportedRewriteError(UnsupportedError): 522 """UnsupportedError from rewrite passes 523 """ 524 pass 525 526 527class IRError(NumbaError): 528 """ 529 An error occurred during Numba IR generation. 530 """ 531 pass 532 533 534class RedefinedError(IRError): 535 """ 536 An error occurred during interpretation of IR due to variable redefinition. 537 """ 538 pass 539 540 541class NotDefinedError(IRError): 542 """ 543 An undefined variable is encountered during interpretation of IR. 544 """ 545 546 def __init__(self, name, loc=None): 547 self.name = name 548 msg = "Variable '%s' is not defined." % name 549 super(NotDefinedError, self).__init__(msg, loc=loc) 550 551 552class VerificationError(IRError): 553 """ 554 An error occurred during IR verification. Once Numba's internal 555 representation (IR) is constructed it is then verified to ensure that 556 terminators are both present and in the correct places within the IR. If 557 it is the case that this condition is not met, a VerificationError is 558 raised. 559 """ 560 pass 561 562 563class MacroError(NumbaError): 564 """ 565 An error occurred during macro expansion. 566 """ 567 pass 568 569 570class DeprecationError(NumbaError): 571 """ 572 Functionality is deprecated. 573 """ 574 pass 575 576 577class LoweringError(NumbaError): 578 """ 579 An error occurred during lowering. 580 """ 581 582 def __init__(self, msg, loc=None): 583 super(LoweringError, self).__init__(msg, loc=loc) 584 585 586class UnsupportedParforsError(NumbaError): 587 """ 588 An error ocurred because parfors is not supported on the platform. 589 """ 590 pass 591 592 593class ForbiddenConstruct(LoweringError): 594 """ 595 A forbidden Python construct was encountered (e.g. use of locals()). 596 """ 597 pass 598 599 600class TypingError(NumbaError): 601 """ 602 A type inference failure. 603 """ 604 pass 605 606 607class UntypedAttributeError(TypingError): 608 def __init__(self, value, attr, loc=None): 609 module = getattr(value, 'pymod', None) 610 if module is not None and module == np: 611 # unsupported numpy feature. 612 msg = ("Use of unsupported NumPy function 'numpy.%s' " 613 "or unsupported use of the function.") % attr 614 else: 615 msg = "Unknown attribute '{attr}' of type {type}" 616 msg = msg.format(type=value, attr=attr) 617 super(UntypedAttributeError, self).__init__(msg, loc=loc) 618 619 620class ByteCodeSupportError(NumbaError): 621 """ 622 Failure to extract the bytecode of the user's function. 623 """ 624 625 def __init__(self, msg, loc=None): 626 super(ByteCodeSupportError, self).__init__(msg, loc=loc) 627 628 629class CompilerError(NumbaError): 630 """ 631 Some high-level error in the compiler. 632 """ 633 pass 634 635 636class ConstantInferenceError(NumbaError): 637 """ 638 Failure during constant inference. 639 """ 640 641 def __init__(self, value, loc=None): 642 super(ConstantInferenceError, self).__init__(value, loc=loc) 643 644 645class InternalError(NumbaError): 646 """ 647 For wrapping internal error occured within the compiler 648 """ 649 650 def __init__(self, exception): 651 super(InternalError, self).__init__(str(exception)) 652 self.old_exception = exception 653 654 655class RequireLiteralValue(TypingError): 656 """ 657 For signalling that a function's typing requires a constant value for 658 some of its arguments. 659 """ 660 pass 661 662 663class ForceLiteralArg(NumbaError): 664 """A Pseudo-exception to signal the dispatcher to type an argument literally 665 666 Attributes 667 ---------- 668 requested_args : frozenset[int] 669 requested positions of the arguments. 670 """ 671 def __init__(self, arg_indices, fold_arguments=None, loc=None): 672 """ 673 Parameters 674 ---------- 675 arg_indices : Sequence[int] 676 requested positions of the arguments. 677 fold_arguments: callable 678 A function ``(tuple, dict) -> tuple`` that binds and flattens 679 the ``args`` and ``kwargs``. 680 loc : numba.ir.Loc or None 681 """ 682 super(ForceLiteralArg, self).__init__( 683 "Pseudo-exception to force literal arguments in the dispatcher", 684 loc=loc, 685 ) 686 self.requested_args = frozenset(arg_indices) 687 self.fold_arguments = fold_arguments 688 689 def bind_fold_arguments(self, fold_arguments): 690 """Bind the fold_arguments function 691 """ 692 e = ForceLiteralArg(self.requested_args, fold_arguments, 693 loc=self.loc) 694 return chain_exception(e, self) 695 696 def combine(self, other): 697 """Returns a new instance by or'ing the requested_args. 698 """ 699 if not isinstance(other, ForceLiteralArg): 700 m = '*other* must be a {} but got a {} instead' 701 raise TypeError(m.format(ForceLiteralArg, type(other))) 702 return ForceLiteralArg(self.requested_args | other.requested_args) 703 704 def __or__(self, other): 705 """Same as self.combine(other) 706 """ 707 return self.combine(other) 708 709 710class LiteralTypingError(TypingError): 711 """ 712 Failure in typing a Literal type 713 """ 714 pass 715 716 717def _format_msg(fmt, args, kwargs): 718 return fmt.format(*args, **kwargs) 719 720 721_numba_path = os.path.dirname(__file__) 722loc_info = {} 723 724 725@contextlib.contextmanager 726def new_error_context(fmt_, *args, **kwargs): 727 """ 728 A contextmanager that prepend contextual information to any exception 729 raised within. If the exception type is not an instance of NumbaError, 730 it will be wrapped into a InternalError. The exception class can be 731 changed by providing a "errcls_" keyword argument with the exception 732 constructor. 733 734 The first argument is a message that describes the context. It can be a 735 format string. If there are additional arguments, it will be used as 736 ``fmt_.format(*args, **kwargs)`` to produce the final message string. 737 """ 738 errcls = kwargs.pop('errcls_', InternalError) 739 740 loc = kwargs.get('loc', None) 741 if loc is not None and not loc.filename.startswith(_numba_path): 742 loc_info.update(kwargs) 743 744 try: 745 yield 746 except NumbaError as e: 747 e.add_context(_format_msg(fmt_, args, kwargs)) 748 raise 749 except Exception as e: 750 newerr = errcls(e).add_context(_format_msg(fmt_, args, kwargs)) 751 tb = sys.exc_info()[2] if numba.core.config.FULL_TRACEBACKS else None 752 reraise(type(newerr), newerr, tb) 753 754 755__all__ += [name for (name, value) in globals().items() 756 if not name.startswith('_') and isinstance(value, type) 757 and issubclass(value, (Exception, Warning))] 758