From: Siu Kwan Lam <1929845+sklam@users.noreply.github.com>
Origin: https://github.com/numba/numba/pull/8545
Date: Tue, 29 Nov 2022 19:06:11 -0600
Subject: Make PYVERSION check more consistent

---
 numba/core/bytecode.py    | 12 +++++++----
 numba/core/byteflow.py    | 52 +++++++++++++++++++++++++++++++----------------
 numba/core/interpreter.py | 22 +++++++++++++-------
 3 files changed, 58 insertions(+), 28 deletions(-)

diff --git a/numba/core/bytecode.py b/numba/core/bytecode.py
index 5fe02ef..022c944 100644
--- a/numba/core/bytecode.py
+++ b/numba/core/bytecode.py
@@ -90,12 +90,15 @@ class ByteCodeInst(object):
         # https://bugs.python.org/issue27129
         # https://github.com/python/cpython/pull/25069
         assert self.is_jump
-        if PYVERSION >= (3, 11):
+        if PYVERSION == (3, 11):
             if self.opcode in (dis.opmap[k]
                                for k in ("JUMP_BACKWARD",
                                          "POP_JUMP_BACKWARD_IF_TRUE",
                                          "POP_JUMP_BACKWARD_IF_FALSE")):
                 return self.offset - (self.arg - 1) * 2
+        elif PYVERSION > (3, 11):
+            raise NotImplementedError(PYVERSION)
+
         if PYVERSION >= (3, 10):
             if self.opcode in JREL_OPS:
                 return self.next + self.arg * 2
@@ -338,11 +341,12 @@ class ByteCodePy311(_ByteCode):
             return ent
 
 
-if PYVERSION >= (3, 11):
+if PYVERSION == (3, 11):
     ByteCode = ByteCodePy311
-else:
+elif PYVERSION < (3, 11):
     ByteCode = _ByteCode
-
+else:
+    raise NotImplementedError(PYVERSION)
 
 class FunctionIdentity(serialize.ReduceMixin):
     """
diff --git a/numba/core/byteflow.py b/numba/core/byteflow.py
index e16d4e0..02db01b 100644
--- a/numba/core/byteflow.py
+++ b/numba/core/byteflow.py
@@ -430,11 +430,13 @@ class TraceRunner(object):
             if inst.arg & 1:
                 state.push(state.make_null())
             state.push(res)
-    else:
+    elif PYVERSION < (3, 11):
         def op_LOAD_GLOBAL(self, state, inst):
             res = state.make_temp()
             state.append(inst, res=res)
             state.push(res)
+    else:
+        raise NotImplementedError(PYVERSION)
 
     def op_COPY_FREE_VARS(self, state, inst):
         state.append(inst)
@@ -782,7 +784,7 @@ class TraceRunner(object):
         state.append(inst, value=val, res=res)
         state.push(res)
 
-    if PYVERSION >= (3, 11):
+    if PYVERSION == (3, 11):
         def op_RAISE_VARARGS(self, state, inst):
             if inst.arg == 0:
                 exc = None
@@ -801,7 +803,7 @@ class TraceRunner(object):
             else:
                 state.terminate()
 
-    else:
+    elif PYVERSION < (3, 11):
         def op_RAISE_VARARGS(self, state, inst):
             in_exc_block = any([
                 state.get_top_block("EXCEPT") is not None,
@@ -820,6 +822,8 @@ class TraceRunner(object):
                 raise ValueError("Multiple argument raise is not supported.")
             state.append(inst, exc=exc)
             state.terminate()
+    else:
+        raise NotImplementedError(PYVERSION)
 
     def op_BEGIN_FINALLY(self, state, inst):
         temps = []
@@ -959,11 +963,11 @@ class TraceRunner(object):
             'FINALLY', state, next=inst.next, end=inst.get_jump_target(),
         )
 
-    if PYVERSION >= (3, 11):
+    if PYVERSION == (3, 11):
         def op_POP_EXCEPT(self, state, inst):
             state.pop()
 
-    else:
+    elif PYVERSION <= (3, 11):
         def op_POP_EXCEPT(self, state, inst):
             blk = state.pop_block()
             if blk['kind'] not in {BlockKind('EXCEPT'), BlockKind('FINALLY')}:
@@ -976,6 +980,8 @@ class TraceRunner(object):
             state.pop()
             # Forces a new block
             state.fork(pc=inst.next)
+    else:
+        raise NotImplementedError(PYVERSION)
 
     def op_POP_BLOCK(self, state, inst):
         blk = state.pop_block()
@@ -1330,12 +1336,14 @@ class TraceRunner(object):
     op_BINARY_XOR = _binaryop
 
     def op_MAKE_FUNCTION(self, state, inst, MAKE_CLOSURE=False):
-        if PYVERSION >= (3, 11):
+        if PYVERSION == (3, 11):
             # https://github.com/python/cpython/commit/2f180ce
             # name set via co_qualname
             name = None
-        else:
+        elif PYVERSION < (3, 11):
             name = state.pop()
+        else:
+            raise NotImplementedError(PYVERSION)
         code = state.pop()
         closure = annotations = kwdefaults = defaults = None
         if PYVERSION < (3, 6):
@@ -1410,7 +1418,7 @@ class TraceRunner(object):
         state.fork(pc=inst.next)
         state.fork(pc=inst.get_jump_target())
 
-    if PYVERSION >= (3, 11):
+    if PYVERSION == (3, 11):
         def op_RERAISE(self, state, inst):
             # This isn't handled, but the state is set up anyway
             exc = state.pop()
@@ -1422,18 +1430,19 @@ class TraceRunner(object):
                 self._adjust_except_stack(state)
             else:
                 state.terminate()
-    else:
+    elif PYVERSION < (3, 11):
         def op_RERAISE(self, state, inst):
             # This isn't handled, but the state is set up anyway
             exc = state.pop()
             state.append(inst, exc=exc)
             state.terminate()
- 
+    else:
+        raise NotImplementedError(PYVERSION)
 
     # NOTE: Please see notes in `interpreter.py` surrounding the implementation
     # of LOAD_METHOD and CALL_METHOD.
 
-    if PYVERSION >= (3, 11):
+    if PYVERSION == (3, 11):
         def op_LOAD_METHOD(self, state, inst):
             item = state.pop()
             extra = state.make_null()
@@ -1441,16 +1450,18 @@ class TraceRunner(object):
             res = state.make_temp()
             state.append(inst, item=item, res=res)
             state.push(res)
-    else:
+    elif PYVERSION < (3, 11):
         def op_LOAD_METHOD(self, state, inst):
             self.op_LOAD_ATTR(state, inst)
+    else:
+        raise NotImplementedError(PYVERSION)
 
     def op_CALL_METHOD(self, state, inst):
         self.op_CALL_FUNCTION(state, inst)
 
 
 @total_ordering
-class State(object):
+class _State(object):
     """State of the trace
     """
     def __init__(self, bytecode, pc, nstack, blockstack, nullvals=()):
@@ -1704,7 +1715,7 @@ class State(object):
                 stack.append(self.make_temp())
         # Handle changes on the blockstack
         blockstack = list(self._blockstack)
-        if PYVERSION >= (3, 11):
+        if PYVERSION == (3, 11):
             # pop expired block in destination pc
             while blockstack:
                 top = blockstack[-1]
@@ -1713,6 +1724,10 @@ class State(object):
                     blockstack.pop()
                 else:
                     break
+        elif PYVERSION < (3, 11):
+            pass # intentionally bypass
+        else:
+            raise NotImplementedError(PYVERSION)
 
         if extra_block:
             blockstack.append(extra_block)
@@ -1757,7 +1772,7 @@ class State(object):
                 for edge in self._outedges}
 
 
-class StatePy311(State):
+class StatePy311(_State):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self._kw_names = None
@@ -1788,9 +1803,12 @@ class StatePy311(State):
         return self.make_temp(prefix="null$")
 
 
-if PYVERSION >= (3, 11):
+if PYVERSION == (3, 11):
     State = StatePy311
-
+elif PYVERSION < (3, 11):
+    State = _State
+else:
+    raise NotImplementedError(PYVERSION)
 
 Edge = namedtuple("Edge", ["pc", "stack", "blockstack", "npush"])
 
diff --git a/numba/core/interpreter.py b/numba/core/interpreter.py
index 9b7cbc2..5bc7bbf 100644
--- a/numba/core/interpreter.py
+++ b/numba/core/interpreter.py
@@ -1520,7 +1520,7 @@ class Interpreter(object):
         self.dfainfo = self.dfa.infos[self.current_block_offset]
         self.assigner = Assigner()
         # Check out-of-scope syntactic-block
-        if PYVERSION >= (3, 11):
+        if PYVERSION == (3, 11):
             while self.syntax_blocks:
                 if offset >= self.syntax_blocks[-1].exit:
                     synblk = self.syntax_blocks.pop()
@@ -1533,12 +1533,14 @@ class Interpreter(object):
             if newtryblk is not None:
                 if newtryblk is not tryblk:
                     self._insert_try_block_begin()
-        else:
+        elif PYVERSION < (3, 11):
             while self.syntax_blocks:
                 if offset >= self.syntax_blocks[-1].exit:
                     self.syntax_blocks.pop()
                 else:
                     break
+        else:
+            raise NotImplementedError(PYVERSION)
 
     def _end_current_block(self):
         # Handle try block
@@ -1751,6 +1753,8 @@ class Interpreter(object):
                     if inst.offset >= top.exit:
                         self.current_block.append(ir.PopBlock(loc=self.loc))
                         self.syntax_blocks.pop()
+        elif PYVERSION > (3, 11):
+            raise NotImplementedError(PYVERSION)
 
         fname = "op_%s" % inst.opname.replace('+', '_')
         try:
@@ -2187,40 +2191,44 @@ class Interpreter(object):
             value = self.get_global_value(name)
             gl = ir.Global(name, value, loc=self.loc)
             self.store(gl, res)
-    else:
+    elif PYVERSION < (3, 11):
         def op_LOAD_GLOBAL(self, inst, res):
             name = self.code_names[inst.arg]
             value = self.get_global_value(name)
             gl = ir.Global(name, value, loc=self.loc)
             self.store(gl, res)
+    else:
+        raise NotImplementedError(PYVERSION)
 
     def op_COPY_FREE_VARS(self, inst):
         pass
 
-    if PYVERSION < (3, 11):
+    if PYVERSION == (3, 11):
         def op_LOAD_DEREF(self, inst, res):
             n_cellvars = len(self.code_cellvars)
             if inst.arg < n_cellvars:
                 name = self.code_cellvars[inst.arg]
                 gl = self.get(name)
             else:
-                idx = inst.arg - n_cellvars
+                idx = inst.arg - len(self.code_locals)
                 name = self.code_freevars[idx]
                 value = self.get_closure_value(idx)
                 gl = ir.FreeVar(idx, name, value, loc=self.loc)
             self.store(gl, res)
-    else:
+    elif PYVERSION < (3, 11):
         def op_LOAD_DEREF(self, inst, res):
             n_cellvars = len(self.code_cellvars)
             if inst.arg < n_cellvars:
                 name = self.code_cellvars[inst.arg]
                 gl = self.get(name)
             else:
-                idx = inst.arg - len(self.code_locals)
+                idx = inst.arg - n_cellvars
                 name = self.code_freevars[idx]
                 value = self.get_closure_value(idx)
                 gl = ir.FreeVar(idx, name, value, loc=self.loc)
             self.store(gl, res)
+    else:
+        raise NotImplementedError(PYVERSION)
 
     def op_STORE_DEREF(self, inst, value):
         n_cellvars = len(self.code_cellvars)
