Skip to content

Type fix class#454

Open
matmair wants to merge 13 commits intomasterfrom
type-fix-class
Open

Type fix class#454
matmair wants to merge 13 commits intomasterfrom
type-fix-class

Conversation

@matmair
Copy link
Owner

@matmair matmair commented Oct 28, 2025

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed class registration for plugin mixins to correctly reflect actual subclass types
    • Improved error message formatting consistency
  • Improvements

    • Enhanced email priority parameter to accept both Priority objects and integers
    • Strengthened type system with more flexible parameter validation across multiple functions
    • Improved notification data type handling

@coderabbitai
Copy link

coderabbitai bot commented Oct 28, 2025

Walkthrough

This PR updates type annotations for Python compatibility, replaces bare __class__ references with self.__class__ throughout plugin mixins for proper subclass resolution, widens multiple function signatures to accept broader types (e.g., int or Priority), adds type-checking directives, and updates error message formatting conventions.

Changes

Cohort / File(s) Summary
Configuration
pyproject.toml
Removes unresolved-reference ignore rule from type checker config, tightening type analysis.
Type Annotation Compatibility
src/backend/InvenTree/InvenTree/helpers.py, src/backend/InvenTree/InvenTree/permissions.py, src/backend/InvenTree/common/settings.py
Updates type hints from list[str] to List[str], expands parameter types using Union (e.g., Union[dict, bool, None]), imports added for compatibility.
Signature Widening
src/backend/InvenTree/InvenTree/helpers_email.py, src/backend/InvenTree/common/models.py
Functions now accept Union[Priority, int] instead of Priority alone for priority parameters; default values and control flow unchanged.
Type Hints in Notification Data
src/backend/InvenTree/common/notifications.py
NotificationBody.name and NotificationBody.message fields updated to Union[str, StrOrPromise]; validation logic adjusted for obj_ref_value checks.
Type-Checking Directives
src/backend/InvenTree/InvenTree/config.py, src/backend/InvenTree/plugin/builtin/labels/inventree_label.py, src/backend/InvenTree/report/models.py, src/backend/InvenTree/common/tests.py
Inline type: ignore comments added to satisfy type checker without runtime changes.
Error Message Formatting
src/backend/InvenTree/InvenTree/models.py, src/backend/InvenTree/order/models.py, src/backend/InvenTree/common/serializers.py
ValidationError and other error messages converted to named-format style; class references updated in error context.
Mixin Class Registration
src/backend/InvenTree/plugin/base/action/mixins.py, src/backend/InvenTree/plugin/base/barcodes/mixins.py, src/backend/InvenTree/plugin/base/event/mixins.py, src/backend/InvenTree/plugin/base/icons/mixins.py, src/backend/InvenTree/plugin/base/integration/APICallMixin.py, src/backend/InvenTree/plugin/base/integration/AppMixin.py, src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py, src/backend/InvenTree/plugin/base/integration/DataExport.py, src/backend/InvenTree/plugin/base/integration/MachineMixin.py, src/backend/InvenTree/plugin/base/integration/NavigationMixin.py, src/backend/InvenTree/plugin/base/integration/NotificationMixin.py, src/backend/InvenTree/plugin/base/integration/ReportMixin.py, src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py, src/backend/InvenTree/plugin/base/integration/SettingsMixin.py, src/backend/InvenTree/plugin/base/integration/TransitionMixin.py, src/backend/InvenTree/plugin/base/integration/UrlsMixin.py, src/backend/InvenTree/plugin/base/integration/ValidationMixin.py, src/backend/InvenTree/plugin/base/label/mixins.py, src/backend/InvenTree/plugin/base/locate/mixins.py, src/backend/InvenTree/plugin/base/mail/mixins.py, src/backend/InvenTree/plugin/broken/bad_actor.py
Replaces __class__ with self.__class__ in add_mixin() calls throughout mixin __init__ methods; ensures registration uses the actual runtime subclass rather than the mixin base class.
Functional Logic Update
src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py
PrintingOptionsSerializer now retrieves template and items_to_print from view context instead of using placeholders, enabling dynamic printer option generation.
Log Error API Change
src/backend/InvenTree/report/models.py
log_report_error() signature updated to require path as first parameter; internal log_error() call updated to pass path.
Test Utility Refactoring
src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py
Introduces class attribute test_event = 'test.event' and replaces hard-coded string references in test calls.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Areas requiring extra attention:

  • Mixin class registration changes (src/backend/InvenTree/plugin/base/*/mixins.py, src/backend/InvenTree/plugin/broken/bad_actor.py): Verify that changing __class__ to self.__class__ across ~20 files produces consistent behavior and correctly registers subclasses in all contexts.
  • PrintingOptionsSerializer logic (src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py): Confirm that retrieving template and items_to_print from the view context works correctly across all request scenarios and doesn't introduce regressions in printer option generation.
  • log_report_error signature change (src/backend/InvenTree/report/models.py): Verify all call sites have been updated to provide the new required path parameter.
  • Type union expansions (src/backend/InvenTree/InvenTree/helpers_email.py, src/backend/InvenTree/common/models.py): Confirm that broadening priority parameters to accept Union[Priority, int] doesn't introduce unintended type coercion or runtime errors.

Poem

🐰 Through classes hopping, references we're swapping,
From bare __class__ to self we now pop,
Type hints align as we cross the time line,
Mixins now register with subclass divine,
A hop towards clarity, errors now shine! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title "Type fix class" is related to the changeset, which does contain numerous type-related changes including type annotations, type ignore comments, and systematic updates to class references using self.__class__ instead of __class__ in plugin mixins. However, the title is vague and generic—the word "class" is ambiguous and could refer to class definitions, type classes, mixin classes, or class references—and it doesn't clearly convey the primary focus of the changes. A teammate scanning the commit history would struggle to understand whether the changes relate to type annotations, class registration, or something else entirely.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch type-fix-class

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
src/backend/InvenTree/common/models.py (1)

2747-2747: Consider adding validation for integer priority values.

The type widening to Union[Priority, int] allows arbitrary integers to be passed as the priority. While the str(prio) conversion at line 2774 handles both types safely, there's no validation to ensure integer values correspond to valid Priority enum values (0-5). This could result in invalid or unexpected priority headers in email messages.

Consider adding validation:

 def issue_mail(
     subject: str,
     body: str,
     from_email: str,
     recipients: Union[str, list],
     fail_silently: bool = False,
     html_message=None,
     prio: Union[Priority, int] = Priority.NORMAL,
     headers: Optional[dict] = None,
 ):
     """Send an email with the specified subject and body, to the specified recipients list.
 
     Mostly used by tasks.
     """
+    # Validate integer priority values
+    if isinstance(prio, int):
+        if prio not in Priority.values:
+            raise ValueError(f"Invalid priority value: {prio}. Must be one of {list(Priority.values)}")
+        
     connection = get_connection(fail_silently=fail_silently)

Alternatively, document the expected integer range in the docstring if arbitrary values are intentionally permitted.

src/backend/InvenTree/common/notifications.py (1)

130-134: Consider using explicit None check for consistency.

Line 132 uses and obj_ref_value (truthy check) while line 220 uses obj_ref_value is not None (explicit None check). This creates a subtle inconsistency—if obj_ref_value were 0, the recent notification check would be skipped but the notification would still be logged.

While Django primary keys rarely start at 0, using an explicit None check would be more consistent and type-correct.

Apply this diff for consistency:

     if (
         check_recent
-        and obj_ref_value
+        and obj_ref_value is not None
         and common.models.NotificationEntry.check_recent(category, obj_ref_value, delta)
     ):
src/backend/InvenTree/InvenTree/helpers.py (1)

465-465: Use a specific type ignore code.

The non-specific # type: ignore comment suppresses all type-checking errors on this line. Use a specific error code (e.g., # type: ignore[arg-type]) to make it clear what's being suppressed and prevent hiding other legitimate issues.

-        wrapper = FileWrapper(io.StringIO(data))  # type: ignore
+        wrapper = FileWrapper(io.StringIO(data))  # type: ignore[arg-type]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2b531b and f35a454.

📒 Files selected for processing (37)
  • pyproject.toml (0 hunks)
  • src/backend/InvenTree/InvenTree/config.py (1 hunks)
  • src/backend/InvenTree/InvenTree/helpers.py (5 hunks)
  • src/backend/InvenTree/InvenTree/helpers_email.py (1 hunks)
  • src/backend/InvenTree/InvenTree/models.py (2 hunks)
  • src/backend/InvenTree/InvenTree/permissions.py (2 hunks)
  • src/backend/InvenTree/common/models.py (1 hunks)
  • src/backend/InvenTree/common/notifications.py (5 hunks)
  • src/backend/InvenTree/common/serializers.py (1 hunks)
  • src/backend/InvenTree/common/settings.py (2 hunks)
  • src/backend/InvenTree/common/tests.py (1 hunks)
  • src/backend/InvenTree/order/models.py (1 hunks)
  • src/backend/InvenTree/plugin/base/action/mixins.py (1 hunks)
  • src/backend/InvenTree/plugin/base/barcodes/mixins.py (2 hunks)
  • src/backend/InvenTree/plugin/base/event/mixins.py (1 hunks)
  • src/backend/InvenTree/plugin/base/icons/mixins.py (2 hunks)
  • src/backend/InvenTree/plugin/base/integration/APICallMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/AppMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/DataExport.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/MachineMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/NavigationMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/NotificationMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/ReportMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/SettingsMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/TransitionMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/UrlsMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/integration/ValidationMixin.py (1 hunks)
  • src/backend/InvenTree/plugin/base/label/mixins.py (1 hunks)
  • src/backend/InvenTree/plugin/base/locate/mixins.py (1 hunks)
  • src/backend/InvenTree/plugin/base/mail/mixins.py (1 hunks)
  • src/backend/InvenTree/plugin/broken/bad_actor.py (1 hunks)
  • src/backend/InvenTree/plugin/builtin/labels/inventree_label.py (1 hunks)
  • src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py (1 hunks)
  • src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py (2 hunks)
  • src/backend/InvenTree/report/models.py (2 hunks)
💤 Files with no reviewable changes (1)
  • pyproject.toml
🧰 Additional context used
🧬 Code graph analysis (28)
src/backend/InvenTree/plugin/base/integration/MachineMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/integration/UrlsMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/locate/mixins.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/broken/bad_actor.py (1)
src/backend/InvenTree/plugin/plugin.py (1)
  • add_mixin (229-234)
src/backend/InvenTree/plugin/base/integration/NotificationMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/integration/DataExport.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/common/tests.py (1)
src/backend/InvenTree/common/settings.py (1)
  • set_global_warning (61-108)
src/backend/InvenTree/InvenTree/helpers_email.py (1)
src/backend/InvenTree/common/models.py (1)
  • Priority (2512-2520)
src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/common/notifications.py (1)
src/backend/InvenTree/common/models.py (2)
  • check_recent (1623-1629)
  • NotificationEntry (1602-1636)
src/backend/InvenTree/plugin/base/icons/mixins.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/integration/NavigationMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/label/mixins.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/action/mixins.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/barcodes/mixins.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/event/mixins.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/integration/APICallMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py (3)
src/backend/InvenTree/InvenTree/models.py (1)
  • get_items (793-801)
src/backend/InvenTree/part/models.py (1)
  • get_items (217-219)
src/backend/InvenTree/stock/models.py (1)
  • get_items (346-348)
src/backend/InvenTree/plugin/base/integration/ValidationMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/mail/mixins.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py (3)
src/backend/InvenTree/plugin/base/mail/mail.py (2)
  • process_mail_out (14-41)
  • process_mail_in (44-77)
src/backend/InvenTree/plugin/samples/mail/mail_sample.py (2)
  • process_mail_out (20-28)
  • process_mail_in (30-38)
src/backend/InvenTree/common/models.py (1)
  • set_setting (681-774)
src/backend/InvenTree/plugin/base/integration/ReportMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/InvenTree/helpers.py (1)
src/backend/InvenTree/InvenTree/models.py (1)
  • path (992-1000)
src/backend/InvenTree/report/models.py (2)
src/backend/InvenTree/common/settings.py (1)
  • get_global_setting (29-41)
src/backend/InvenTree/InvenTree/exceptions.py (1)
  • log_error (21-95)
src/backend/InvenTree/plugin/base/integration/AppMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/integration/TransitionMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (1)
  • PluginMixinEnum (55-82)
src/backend/InvenTree/plugin/base/integration/SettingsMixin.py (1)
src/backend/InvenTree/plugin/plugin.py (2)
  • add_mixin (229-234)
  • PluginMixinEnum (55-82)
🪛 Ruff (0.14.2)
src/backend/InvenTree/InvenTree/helpers.py

12-12: Unused noqa directive (non-enabled: UP035)

Remove unused noqa directive

(RUF100)


161-161: Unused noqa directive (non-enabled: UP006)

Remove unused noqa directive

(RUF100)

src/backend/InvenTree/report/models.py

53-53: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Tests - DB [PostgreSQL]
  • GitHub Check: Tests - DB [MySQL]
  • GitHub Check: Tests - DB [SQLite] + Coverage 3.9
  • GitHub Check: Style [Typecheck]
  • GitHub Check: Tests - inventree-python
  • GitHub Check: Style [Typecheck]
  • GitHub Check: Tests - DB [PostgreSQL]
  • GitHub Check: Tests - DB [MySQL]
  • GitHub Check: Tests - DB [SQLite] + Coverage 3.9
  • GitHub Check: Tests - inventree-python
  • GitHub Check: Tests - API Schema Documentation
  • GitHub Check: matmair.InvenTree (Job Python39)
  • GitHub Check: Tests - API Schema Documentation
  • GitHub Check: Build - Web UI
  • GitHub Check: Build - Web UI
🔇 Additional comments (41)
src/backend/InvenTree/common/tests.py (1)

546-546: LGTM! Appropriate type ignore for negative test case.

The type ignore comment correctly silences the type checker for this intentional test of error handling when None is passed as the key parameter.

src/backend/InvenTree/common/settings.py (2)

5-5: LGTM! Import updated to support widened type annotation.

The import change from Optional to Union correctly supports the widened function signature for set_global_warning.


61-61: LGTM! Type annotation correctly widened to match actual usage.

The signature change from Optional[dict] to Union[dict, bool, None] accurately reflects the function's implementation and usage patterns, where bool values are explicitly supported (line 96 defaults None to True).

src/backend/InvenTree/InvenTree/helpers_email.py (1)

70-70: Type widening matches the downstream function signature.

The change to Union[Priority, int] is consistent with the corresponding update to issue_mail in common/models.py (line 2747). Since send_email passes prio directly to issue_mail (line 110), this alignment is appropriate.

However, the same validation concern mentioned in common/models.py applies here: consider validating integer values to ensure they correspond to valid Priority enum values, or clearly document the acceptable range if arbitrary integers are permitted.

src/backend/InvenTree/common/notifications.py (4)

5-5: LGTM! Type imports correctly added.

The Union and StrOrPromise imports are appropriate for the enhanced type annotations used in the NotificationBody dataclass fields.

Also applies to: 13-13


45-48: LGTM! Type annotations correctly reflect Django translation usage.

The Union[str, StrOrPromise] annotations accurately represent that these fields can accept both plain strings and Django's lazy translation strings (from gettext_lazy / _() function), as evidenced by the usage in lines 58-84.


113-113: LGTM! Type annotation improves clarity.

The Optional[int] annotation correctly reflects that obj_ref_value can be None (when no object is provided) or an integer (Django model primary key).


220-221: LGTM! Explicit None check prevents type errors.

The obj_ref_value is not None check correctly prevents passing None to NotificationEntry.notify() which expects an integer uid parameter. The explicit None check also properly handles the edge case where obj_ref_value could be 0.

src/backend/InvenTree/InvenTree/helpers.py (2)

29-29: LGTM!

The StrOrPromise import is appropriate for Django projects, enabling proper type annotations for functions that accept both regular strings and lazy translation objects.


578-578: LGTM!

The type annotation correctly handles both regular strings and Django lazy translation objects, which is essential since error messages use _() for translation (e.g., line 592).

src/backend/InvenTree/InvenTree/permissions.py (1)

4-4: LGTM: Import addition supports new type annotations.

The addition of Union to the typing imports is necessary for the broadened type annotations used in the map_scope function signature below.

src/backend/InvenTree/InvenTree/config.py (1)

484-485: Line numbers incorrect; type: ignore is appropriate for known cryptography typing limitation.

The line numbers in the original comment are wrong (actual lines are 478-479, not 484-485). More importantly, the # type: ignore comments here address a documented and known limitation in cryptography's type stubs for serialization.Encoding and serialization.PrivateFormat enums—not a hidden problem in the code itself.

Your cryptography version (44.0.3) is current, and this typing gap persists even in recent releases. The workaround is well-documented across cryptography issues and type-checking communities. The code pattern matches cryptography's official documentation exactly, and the private_bytes() call is correct.

While using a more specific code like # type: ignore[arg-type] would be a minor best-practice improvement, the current approach is acceptable and requires no changes.

src/backend/InvenTree/plugin/base/integration/TransitionMixin.py (1)

26-31: LGTM! Correct use of self.__class__ for runtime class resolution.

The change from bare __class__ to self.__class__ ensures the actual subclass is registered at runtime, which is essential for proper mixin inheritance.

src/backend/InvenTree/plugin/base/integration/APICallMixin.py (1)

73-77: LGTM! Proper mixin registration with runtime class.

Using self.__class__ correctly registers the actual plugin subclass rather than the mixin base class.

src/backend/InvenTree/plugin/base/integration/ReportMixin.py (1)

22-25: LGTM! Consistent with mixin registration pattern.

The change ensures correct subclass resolution when the mixin is inherited.

src/backend/InvenTree/plugin/broken/bad_actor.py (1)

11-14: LGTM! Correct mixin registration.

The use of self.__class__ ensures the BadActorPlugin class is properly registered.

src/backend/InvenTree/plugin/base/locate/mixins.py (1)

32-35: LGTM! Proper runtime class registration.

Using self.__class__ correctly identifies the concrete plugin class implementing the LocateMixin.

src/backend/InvenTree/plugin/base/integration/MachineMixin.py (1)

30-33: LGTM! Consistent mixin registration pattern.

The change to self.__class__ aligns with the pattern used across all plugin mixins for proper subclass resolution.

src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py (1)

25-25: LGTM! Correct runtime class registration.

Using self.__class__ ensures the actual plugin subclass (not the base mixin class) is registered, enabling proper runtime type resolution.

src/backend/InvenTree/plugin/base/event/mixins.py (1)

37-37: LGTM! Proper subclass resolution.

This change ensures the mixin registry receives the concrete plugin class rather than the base EventMixin class.

src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py (2)

16-17: Good refactoring for maintainability.

Extracting the repeated string literal to a class attribute improves maintainability and follows the DRY principle.


28-29: No issues found. Type mismatch is intentional design for the sample plugin.

The verification confirms that while a type mismatch exists (strings passed instead of email objects), this is intentional design:

  • The process_mail_out() function includes logic to wrap non-list inputs in a list, demonstrating flexibility
  • The sample plugin implementation intentionally has no type hints and accepts any input type
  • The tests verify the plugin infrastructure and event logging mechanism, not actual mail processing
  • This is appropriate for a sample/test plugin demonstrating how to implement the mail mixin

The type hints on the standalone functions correctly document the expected interface for production usage. No changes are required.

src/backend/InvenTree/plugin/base/icons/mixins.py (2)

23-23: LGTM! Ensures correct mixin registration.

Using self.__class__ registers the actual plugin subclass for proper runtime type tracking.


31-35: Improved error reporting with runtime class name.

The error message now references self.__class__ instead of __class__, ensuring the actual plugin class name is reported at runtime rather than the base mixin class name.

src/backend/InvenTree/plugin/base/action/mixins.py (1)

19-19: LGTM! Consistent with mixin registration pattern.

This change aligns with the broader refactor to register actual plugin subclasses rather than base mixin classes.

src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py (1)

58-58: LGTM! Proper runtime class registration.

The change ensures the plugin registry tracks the concrete plugin class for scheduled task management.

src/backend/InvenTree/plugin/base/mail/mixins.py (1)

39-39: LGTM! Correct subclass resolution for mail mixin.

Using self.__class__ ensures the actual mail plugin subclass is registered for processing.

src/backend/InvenTree/plugin/base/barcodes/mixins.py (2)

40-40: LGTM! Correct barcode mixin registration.

Using self.__class__ ensures the concrete barcode plugin class is properly registered with the plugin system.


110-110: LGTM! Proper supplier barcode mixin registration.

This change mirrors the pattern in the parent BarcodeMixin, ensuring the actual supplier barcode plugin class is registered.

src/backend/InvenTree/plugin/base/integration/ValidationMixin.py (1)

50-50: LGTM! Proper subclass resolution for mixin registration.

Using self.__class__ correctly resolves the runtime subclass rather than the mixin class itself, ensuring proper mixin registration in the plugin system.

src/backend/InvenTree/report/models.py (2)

49-57: LGTM! Function signature now correctly aligns with callee requirements.

The log_report_error function signature and implementation now properly match the expected signature of InvenTree.exceptions.log_error, which requires path as the first positional argument.


649-649: Remove the type: ignore comment—it's unnecessary given identical usage on line 448 in the same file.

Django's ContentFile signature is ContentFile(content, name=None), accepting both bytes and text for content. The call at line 643 (ContentFile(data, report_name)) follows an identical pattern to line 448 (ContentFile(report, report_name)) in the same file, which requires no type suppression. The type: ignore is inconsistent with the codebase convention and does not address a legitimate type-checker issue.

Likely an incorrect or invalid review comment.

src/backend/InvenTree/InvenTree/models.py (1)

911-914: Same verification needed for ValidationError format.

This location uses the same named-parameter format change as Line 577-580. The same verification applies here.

src/backend/InvenTree/plugin/builtin/labels/inventree_label.py (1)

85-85: Verify the need for type: ignore suppression.

Similar to other files in this PR, the type: ignore comment suppresses type-checking for ContentFile. Ensure this addresses a type-checker limitation rather than an actual type incompatibility.

src/backend/InvenTree/plugin/base/integration/UrlsMixin.py (1)

25-25: LGTM! Proper subclass resolution for mixin registration.

Consistent with the broader pattern in this PR, using self.__class__ ensures the concrete subclass is registered rather than the mixin base class.

src/backend/InvenTree/plugin/base/label/mixins.py (1)

39-39: LGTM! Proper subclass resolution for mixin registration.

This change aligns with the consistent pattern across all mixin files in this PR, ensuring proper runtime subclass registration.

src/backend/InvenTree/plugin/base/integration/NavigationMixin.py (1)

25-25: LGTM! Proper subclass resolution for mixin registration.

Consistent with the broader refactoring pattern, using self.__class__ ensures the actual runtime subclass is registered.

src/backend/InvenTree/plugin/base/integration/DataExport.py (1)

33-33: LGTM! Proper subclass resolution for mixin registration.

This completes the consistent pattern of using self.__class__ across all mixin registration calls in this PR, ensuring proper runtime subclass identification.

src/backend/InvenTree/plugin/base/integration/SettingsMixin.py (1)

41-41: LGTM: Proper subclass resolution.

The change from __class__ to self.__class__ ensures that the mixin is registered for the actual concrete plugin class at runtime, rather than the base SettingsMixin class. This is the correct approach for mixin registration in a plugin inheritance hierarchy.

src/backend/InvenTree/plugin/base/integration/AppMixin.py (1)

30-30: LGTM: Consistent with the refactor pattern.

The change to self.__class__ correctly registers the concrete plugin subclass rather than the base AppMixin. This aligns with the broader refactor across all plugin mixins.

src/backend/InvenTree/plugin/base/integration/NotificationMixin.py (1)

33-33: LGTM: Correct subclass resolution.

The change to self.__class__ ensures the notification mixin is properly registered for the concrete plugin subclass. This is consistent with the changes across all other plugin mixins in this PR.

import re
from decimal import Decimal, InvalidOperation
from typing import Optional, TypeVar, Union
from typing import List, Optional, TypeVar, Union # noqa: UP035
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directive and consider using built-in list type.

The # noqa: UP035 directive is unused according to static analysis, and the codebase appears to use lowercase list type hints elsewhere (e.g., models.py line 991 uses def path(self) -> list:). If the project supports Python 3.9+, prefer list[str] over List[str] for consistency.

Apply this diff if Python 3.9+ is supported:

-from typing import List, Optional, TypeVar, Union  # noqa: UP035
+from typing import Optional, TypeVar, Union

Then update line 161 accordingly (see separate comment).

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.14.2)

12-12: Unused noqa directive (non-enabled: UP035)

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In src/backend/InvenTree/InvenTree/helpers.py around line 12, remove the unused
"# noqa: UP035" and stop importing List from typing; instead rely on Python 3.9+
built-in generic types—change the import to only include Optional, TypeVar,
Union (keep others if actually used) and update the type annotation at line 161
from List[str] (or typing.List) to the built-in list[str]. Ensure no other
references to typing.List remain in this file.



def constructPathString(path: list[str], max_chars: int = 250) -> str:
def constructPathString(path: List[str], max_chars: int = 250) -> str: # noqa: UP006
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directive and use built-in list type.

The # noqa: UP006 directive is unused according to static analysis. For consistency with the codebase (see models.py line 991), use the built-in list[str] annotation if Python 3.9+ is supported.

Apply this diff if Python 3.9+ is supported:

-def constructPathString(path: List[str], max_chars: int = 250) -> str:  # noqa: UP006
+def constructPathString(path: list[str], max_chars: int = 250) -> str:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def constructPathString(path: List[str], max_chars: int = 250) -> str: # noqa: UP006
def constructPathString(path: list[str], max_chars: int = 250) -> str:
🧰 Tools
🪛 Ruff (0.14.2)

161-161: Unused noqa directive (non-enabled: UP006)

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In src/backend/InvenTree/InvenTree/helpers.py around line 161, remove the unused
"# noqa: UP006" and change the type annotation from typing.List[str] to the
built-in list[str] (i.e., def constructPathString(path: list[str], max_chars:
int = 250) -> str:); also remove the now-unused List import from typing if it is
no longer referenced elsewhere in the file.

Comment on lines 577 to 580
raise ValidationError(
'Object %s of type %s no longer exists', str(self), str(self.__class__)
'Object {o} of type {t} no longer exists',
params={'o': str(self), 't': str(self.__class__)},
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify Django ValidationError supports named-parameter format.

The error message format has been changed from printf-style ('%s') to named-parameter format ('{o}') with a params dictionary. Please confirm that django.core.exceptions.ValidationError supports this format with the params keyword argument in the version of Django being used.


🌐 Web query:

Does Django ValidationError support named-parameter format with params keyword argument?

💡 Result:

Yes. ValidationError accepts a params keyword (a mapping) and message strings should use named placeholders like %(name)s (not positional %s). See the Django “Raising ValidationError” guidance and examples and the code that passes params into ValidationError. [1][2]


Update placeholder syntax from curly braces to percent-style for Django ValidationError.

Django ValidationError expects named placeholders like %(name)s, not curly braces {name}. The code must be updated to:

raise ValidationError(
    'Object %(o)s of type %(t)s no longer exists',
    params={'o': str(self), 't': str(self.__class__)},
)

The current format with {o} and {t} will not be properly interpolated by Django's ValidationError.

🧰 Tools
🪛 Ruff (0.14.2)

577-580: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


577-580: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In src/backend/InvenTree/InvenTree/models.py around lines 577 to 580, the
ValidationError message uses Python f-string style curly placeholders {o} and
{t} which Django's ValidationError won't interpolate; change the message to use
percent-style named placeholders like %(o)s and %(t)s and keep the params dict
as-is so Django can substitute the values correctly.

Comment on lines 44 to 51
def map_scope(
roles: Optional[list[str]] = None,
roles: Union[list[str], dict[str, str], None] = None,
only_read=False,
read_name=DEFAULT_READ,
map_read: Optional[list[str]] = None,
map_read: Union[list[str], tuple, None] = None,
map_read_name=DEFAULT_READ,
override_all_actions: Optional[str] = None,
) -> dict:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify dict iteration behavior and update docstring.

The roles parameter type has been broadened to accept Union[list[str], dict[str, str], None]. When a dict is passed, line 71 will iterate over the dict, yielding only the keys (not values). If the intent is to use dict keys as table identifiers, this works, but the dict values would be ignored.

Additionally, the docstring on line 55 still references Optional[list[str]] and should be updated to reflect the new accepted types.

Please verify:

  1. Is ignoring dict values (using only keys) the intended behavior when a dict is passed?
  2. Update the docstring to document the new type signature accurately.

🏁 Script executed:

#!/bin/bash
# Search for call sites of map_scope to understand how it's invoked with different argument types
rg -n -C3 'map_scope\(' --type=py

Length of output: 8010


I need to examine the complete function body and trace the variables being passed. Let me gather more information:


🏁 Script executed:

#!/bin/bash
# Get the complete map_scope function body (lines 44-100 to be safe)
sed -n '44,100p' src/backend/InvenTree/InvenTree/permissions.py

Length of output: 2257


🏁 Script executed:

#!/bin/bash
# Find context around line 140 to see what 'calc' is
sed -n '125,145p' src/backend/InvenTree/InvenTree/permissions.py

Length of output: 929


🏁 Script executed:

#!/bin/bash
# Find context around line 425 to see what '_roles' is
sed -n '410,435p' src/backend/InvenTree/InvenTree/permissions.py

Length of output: 1037


🏁 Script executed:

#!/bin/bash
# Find where _roles is defined
rg -n '_roles' --type=py -B2 -A2 src/backend/InvenTree/InvenTree/permissions.py

Length of output: 987


🏁 Script executed:

#!/bin/bash
# Search for all assignments to variables that could be dict[str, str]
rg -n 'map_scope\(roles=' --type=py -B5 src/backend/InvenTree/InvenTree/permissions.py | head -40

Length of output: 242


🏁 Script executed:

#!/bin/bash
# Get the imports section to see where _roles comes from
sed -n '1,30p' src/backend/InvenTree/InvenTree/permissions.py

Length of output: 669


🏁 Script executed:

#!/bin/bash
# Search for _roles definition in the users.ruleset module
find . -path ./node_modules -prune -o -type f -name '*.py' -exec grep -l '_roles' {} \; 2>/dev/null | head -10

Length of output: 346


🏁 Script executed:

#!/bin/bash
# Check the users/oauth2_scopes.py file for _roles definition
grep -n '_roles' src/backend/InvenTree/users/oauth2_scopes.py -B2 -A5

Length of output: 471


🏁 Script executed:

#!/bin/bash
# Get the full context of _roles definition
cat src/backend/InvenTree/users/oauth2_scopes.py | head -100

Length of output: 1303


Update the docstring to reflect the new roles parameter type.

The roles parameter now accepts Union[list[str], dict[str, str], None], but the docstring on line 55 still documents it as Optional[list[str]].

When a dict is passed (as with _roles at line 425), the iteration at line 71 correctly uses only the dict keys—the role/table identifiers needed by get_granular_scope(). The dict values serve only as descriptions and are not needed for scope generation, making this intentional behavior.

Update the docstring to document both accepted types:

  • list[str] - list of role/table identifiers
  • dict[str, str] - mapping of role/table identifiers to descriptions (only keys are used)
🤖 Prompt for AI Agents
In src/backend/InvenTree/InvenTree/permissions.py around lines 44 to 51, the
docstring for the map_scope function still documents roles as
Optional[list[str]] even though the function accepts Union[list[str], dict[str,
str], None]; update the docstring to list both accepted types and their
meanings: list[str] for a list of role/table identifiers and dict[str, str] for
a mapping of role/table identifiers to descriptions (only the dict keys are used
by the function), and ensure the docstring notes None is allowed and that only
keys are used when a dict is provided.

only_read=False,
read_name=DEFAULT_READ,
map_read: Optional[list[str]] = None,
map_read: Union[list[str], tuple, None] = None,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update docstring to reflect tuple support.

The map_read parameter now accepts tuples in addition to lists, which is compatible with the usage on line 75 (method in map_read). However, the docstring on line 58 still references Optional[list[str]] and should be updated to document that tuples are now also accepted.

🤖 Prompt for AI Agents
In src/backend/InvenTree/InvenTree/permissions.py around lines 48 to 58 (and
referenced at line 75), the docstring currently documents map_read as
Optional[list[str]] but the parameter signature and usage accept tuples as well;
update the docstring to state that map_read may be a list or tuple of strings
(e.g., Optional[Union[list[str], tuple[str, ...]]]) and adjust any wording to
reflect tuple support so the documented type matches the function signature and
the "method in map_read" usage.

Comment on lines +531 to +533
raise NotImplementedError(
f'get_status_class() not implemented for {cls.__class__}'
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Incorrect class reference in error message.

In a classmethod, cls.__class__ returns the metaclass type, not the class itself. This will produce an error message like "get_status_class() not implemented for <class 'type'>" instead of showing the actual class name.

Apply this fix:

         raise NotImplementedError(
-            f'get_status_class() not implemented for {cls.__class__}'
+            f'get_status_class() not implemented for {cls.__name__}'
         )
🤖 Prompt for AI Agents
In src/backend/InvenTree/order/models.py around lines 531 to 533, the
NotImplementedError message uses cls.__class__ which resolves to the metaclass
(type) in a classmethod; replace that with the actual class reference (e.g. use
cls or cls.__name__) so the error reports the real class name — update the
f-string to include either f'{cls}' or f'{cls.__name__}' instead of
f'{cls.__class__}'.

Comment on lines +125 to +127
view = kwargs['context']['view']
template = view.get_object()
items_to_print = view.get_items()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify error handling and None handling for view methods.

The code directly accesses kwargs['context']['view'] and calls methods on it without error handling. Additionally, view.get_items() can return None according to the base implementation in InvenTree/models.py, which is then passed to driver.get_printers() on line 136.

Consider the following concerns:

  • Missing error handling: If the view context key doesn't exist or the view doesn't have these methods, this will raise KeyError or AttributeError.
  • None handling: The base get_items() implementation returns None, which may cause issues if driver.get_printers() doesn't handle it properly.

Run the following script to verify how drivers handle None items in get_printers():

Additionally, consider adding type annotations for the new variables (view, template, items_to_print) since this is a "type fix" PR.


🏁 Script executed:

#!/bin/bash
# Verify if label printer drivers handle None items_to_print parameter

# Find all label printer driver implementations
ast-grep --pattern $'class $_ ($$$LabelPrinterBaseDriver$$$) {
  $$$
  def get_printers($$$) {
    $$$
  }
  $$$
}'

# Search for get_printers method implementations and check None handling
rg -nP -A 10 'def get_printers\s*\(' --type=py -g '**/machine/**'

Length of output: 1149



---

🏁 Script executed:

```shell
#!/bin/bash
# Verify the full implementation of get_printers and check for None handling
rg -nP -A 25 'def get_printers\s*\(' src/backend/InvenTree/machine/machine_types/label_printer.py

# Check if get_items() actually returns None or if there's a guard
rg -nP -B 3 -A 5 'def get_items\s*\(' --type=py -g '**/models.py' | head -50

Length of output: 2440



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for get_printers in label printer module with correct path
rg -nP -A 30 'def get_printers\s*\(' git/src/backend/InvenTree/machine/machine_types/label_printer.py

# Check the PrintingOptionsSerializer context to see if view is guaranteed
rg -nP -B 5 -A 15 'class PrintingOptionsSerializer' git/src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py

Length of output: 213



---

🏁 Script executed:

```shell
#!/bin/bash
# Find the actual file paths
fd -t f 'label_printer\.py' 
fd -t f 'inventree_machine\.py'

# Also try to find them with broader search
fd -t f 'machine_types' -type d

Length of output: 296



---

🏁 Script executed:

```shell
#!/bin/bash
# Get full get_printers implementation
cat src/backend/InvenTree/machine/machine_types/label_printer.py | head -150

# Get PrintingOptionsSerializer and surrounding context
rg -nP -B 10 -A 40 'class PrintingOptionsSerializer' src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py

# Also check for error handling around lines 125-127
rg -nP -B 5 -A 10 "kwargs\['context'\]\['view'\]" src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py

Length of output: 8899


Add error handling for view context access and method calls.

The code directly accesses kwargs['context']['view'] and calls methods without validation. This will raise KeyError if the context keys are missing or AttributeError if the view lacks get_object() or get_items() methods.

Lines 125-127 need defensive checks:

  • Verify kwargs['context'] and kwargs['context']['view'] exist
  • Verify the view has get_object() and get_items() methods before calling them
  • Handle gracefully if view structure is unexpected

Consider wrapping this block in a try/except or adding explicit checks before accessing these attributes.

🤖 Prompt for AI Agents
In src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py around lines
125 to 127, the code directly accesses kwargs['context']['view'] and calls
view.get_object() and view.get_items() without validation which can raise
KeyError or AttributeError; update the block to first check that 'context'
exists in kwargs and that 'view' exists in kwargs['context'], then verify the
view object has callable get_object and get_items attributes before invoking
them, and if any check fails handle it gracefully (e.g., log a warning and set
template/items_to_print to safe defaults or return early); optionally wrap the
access in a try/except catching KeyError/AttributeError to provide a clear error
log and fallback behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant