1from __future__ import annotations
2
3import warnings
4
5from pandas.compat._optional import import_optional_dependency
6from pandas.util._exceptions import find_stack_level
7
8from pandas.util.version import Version
9
10# https://github.com/pandas-dev/pandas/issues/65408
11# Avoid bad numexpr binaries producing incorrect results
12# https://github.com/pydata/numexpr/issues/557
13# https://github.com/pydata/numexpr/issues/566
14BLOCKED_NUMEXPR_VERSIONS = frozenset({"2.14.1"})
15
16ne = import_optional_dependency("numexpr", errors="warn")
17
18# the installed version if pandas refuses to use it, otherwise None
19NUMEXPR_BLOCKED_VERSION: str | None = None
20
21if ne is not None and Version(ne.__version__).base_version in BLOCKED_NUMEXPR_VERSIONS:
22 NUMEXPR_BLOCKED_VERSION = ne.__version__
23 ne = None
24
25NUMEXPR_INSTALLED = ne is not None
26
27_warned_blocked = False
28
29
30def warn_numexpr_blocked() -> None:
31 """
32 Warn at most once that the installed numexpr is unusable.
33
34 Called where numexpr would otherwise have been used, so that users who never
35 reach such a code path are not warned (GH#66956).
36 """
37 global _warned_blocked
38
39 if NUMEXPR_BLOCKED_VERSION is None or _warned_blocked:
40 return
41
42 warnings.warn(
43 f"numexpr version '{NUMEXPR_BLOCKED_VERSION}' can silently return incorrect "
44 "results and will not be used by pandas. Install numexpr 2.14.2 or "
45 "newer to re-enable numexpr acceleration.",
46 UserWarning,
47 stacklevel=find_stack_level(),
48 )
49 # set after warning, so that a caller suppressing warnings does not use up
50 # the one-shot
51 _warned_blocked = True
52
53
54__all__ = [
55 "BLOCKED_NUMEXPR_VERSIONS",
56 "NUMEXPR_BLOCKED_VERSION",
57 "NUMEXPR_INSTALLED",
58 "warn_numexpr_blocked",
59]