Improve status code comparison (#13006)

Fixes bug caused by override of BaseEnum.__new__ which broke comparison of status code values
This commit is contained in:
Oliver authored and GitHub committed 2026-10-13 20:45:44 +11:00
1 parent 7e89e93252
commit 34ea373d88
2 files changed
+62 -7

No files matched your search

+27 -7
View File
@@ -9,6 +9,18 @@ from typing import Optional
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
def _new_int(cls, value):
"""Construct a new int-based enum member with the provided value as its int payload.
Non-integer values (which are not valid status codes, and are ignored by
StatusCode._is_element) fall back to a zero payload.
"""
if isinstance(value, int):
return int.__new__(cls, value)
return int.__new__(cls)
class BaseEnum(enum.IntEnum): class BaseEnum(enum.IntEnum):
"""An `Enum` capable of having its members have docstrings. """An `Enum` capable of having its members have docstrings.
@@ -16,8 +28,13 @@ class BaseEnum(enum.IntEnum):
""" """
def __new__(cls, *args): def __new__(cls, *args):
"""Assign values on creation.""" """Assign values on creation.
obj = object.__new__(cls)
Note: The underlying int payload must be set (via int.__new__) to match the value,
otherwise any operation which uses the raw int (e.g. '!=', '<', bool(), json.dumps)
would see 0 for every member.
"""
obj = _new_int(cls, args[0])
obj._value_ = args[0] obj._value_ = args[0]
return obj return obj
@@ -51,10 +68,13 @@ class BaseEnum(enum.IntEnum):
return hash(self.value) return hash(self.value)
def __ne__(self, obj): def __ne__(self, obj):
"""Override inequality operator to allow comparison with int.""" """Override inequality operator to remain consistent with __eq__."""
if type(self) is type(obj): result = self.__eq__(obj)
return super().__ne__(obj)
return self.value != obj if result is NotImplemented:
return result
return not result
class StatusCode(BaseEnum): class StatusCode(BaseEnum):
@@ -72,7 +92,7 @@ class StatusCode(BaseEnum):
def __new__(cls, *args): def __new__(cls, *args):
"""Define object out of args.""" """Define object out of args."""
obj = int.__new__(cls) obj = _new_int(cls, args[0])
obj._value_ = args[0] obj._value_ = args[0]
# Normal item definition # Normal item definition
@@ -123,6 +123,41 @@ class GeneralStateTest(InvenTreeTestCase):
# label # label
self.assertEqual(GeneralStatus.label(10), 'Pending') self.assertEqual(GeneralStatus.label(10), 'Pending')
def test_int_payload(self):
"""The underlying int payload of each member matches its value.
Regression test: members were previously constructed with a zero int payload,
so any operation using the raw int ('!=' between members of the same class,
ordering, bool(), arithmetic, json serialization) was silently wrong.
"""
import json
from build.status_codes import BuildStatus
for cls in [GeneralStatus, BuildStatus]:
for member in cls.values():
self.assertEqual(int.__repr__(member), str(member.value))
pending, complete = GeneralStatus.PENDING, GeneralStatus.COMPLETE
# Comparison between members of the same class
self.assertNotEqual(pending, complete)
self.assertEqual(pending, GeneralStatus.PENDING)
self.assertNotEqual(BuildStatus.PENDING, BuildStatus.COMPLETE)
self.assertLess(pending, complete)
self.assertEqual(sorted([complete, pending]), [pending, complete])
# Comparison against raw integers
self.assertEqual(pending, 10)
self.assertNotEqual(pending, 20)
self.assertEqual(10, pending)
self.assertNotEqual(20, pending)
self.assertTrue(bool(pending))
self.assertEqual(pending + 1, 11)
self.assertEqual(f'{complete:d}', '30')
self.assertEqual(json.dumps({'status': complete}), '{"status": 30}')
def test_color(self): def test_color(self):
"""Test that the color enum validation works.""" """Test that the color enum validation works."""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e: