diff --git a/python/core/__init__.py b/python/core/__init__.py
index 5c347b0cc80..07123520f39 100644
--- a/python/core/__init__.py
+++ b/python/core/__init__.py
@@ -211,6 +211,25 @@ class edit(object):
             return False
 
 
+class ReadWriteContextEnterCategory():
+    def __init__(self, context, category_name, details=None):
+        self.context = context
+        self.category_name = category_name
+        self.details = details
+        self.popper = None
+
+    def __enter__(self):
+        self.popper = self.context._enterCategory(self.category_name, self.details)
+        return self.context
+
+    def __exit__(self, ex_type, ex_value, traceback):
+        del self.popper
+        return True
+
+
+QgsReadWriteContext.enterCategory = ReadWriteContextEnterCategory
+
+
 class QgsTaskWrapper(QgsTask):
 
     def __init__(self, description, flags, function, on_finished, *args, **kwargs):
diff --git a/python/core/qgsreadwritecontext.sip.in b/python/core/qgsreadwritecontext.sip.in
index d427d5c1f28..d33b16ba38f 100644
--- a/python/core/qgsreadwritecontext.sip.in
+++ b/python/core/qgsreadwritecontext.sip.in
@@ -71,6 +71,25 @@ Append a message to the context
 .. versionadded:: 3.2
 %End
 
+QgsReadWriteContextCategoryPopper enterCategory( const QString &category, const QString &details = QString() ) /PyName=_enterCategory/;
+%Docstring
+Push a category to the stack
+
+.. note::
+
+   The return value should always be used so category can be automatically left.
+
+.. note::
+
+   It is not aimed at being used in Python. Instead use the context manager.
+.. code-block:: python
+
+       context = QgsReadWriteContext()
+       with QgsReadWriteContext.enterCategory(context, category, details):
+         # do something
+
+.. versionadded:: 3.2
+%End
 
     QList<QgsReadWriteContext::ReadWriteMessage> takeMessages();
 %Docstring
@@ -83,6 +102,27 @@ Return the stored messages and remove them
 };
 
 
+class QgsReadWriteContextCategoryPopper
+{
+%Docstring
+QgsReadWriteContextCategoryPopper allows entering a context category
+and takes care of leaving this category on deletion of the class.
+This would happen when it gets out of scope.
+
+.. versionadded:: 3.2
+%End
+
+%TypeHeaderCode
+#include "qgsreadwritecontext.h"
+%End
+  public:
+    QgsReadWriteContextCategoryPopper( QgsReadWriteContext &context );
+%Docstring
+Creates a popper
+%End
+    ~QgsReadWriteContextCategoryPopper();
+};
+
 /************************************************************************
  * This file has been generated automatically from                      *
  *                                                                      *
diff --git a/scripts/sipify.pl b/scripts/sipify.pl
index aef2e192d3f..6ff1e029337 100755
--- a/scripts/sipify.pl
+++ b/scripts/sipify.pl
@@ -847,6 +847,8 @@ while ($LINE_IDX < $LINE_COUNT){
         $LINE =~ s/^(\s*template\s*<)(?:class|typename) (\w+>)(.*)$/$1$2$3/;
         $LINE =~ s/\s*\boverride\b//;
         $LINE =~ s/\s*\bextern \b//;
+        $LINE =~ s/\s*\bMAYBE_UNUSED \b//;
+        $LINE =~ s/\s*\bNODISCARD \b//;   
         $LINE =~ s/^(\s*)?(const )?(virtual |static )?inline /$1$2$3/;
         $LINE =~ s/\bconstexpr\b/const/;
         $LINE =~ s/\bnullptr\b/0/g;
diff --git a/src/core/qgsreadwritecontext.h b/src/core/qgsreadwritecontext.h
index e16b157d560..e97a51d6ef6 100644
--- a/src/core/qgsreadwritecontext.h
+++ b/src/core/qgsreadwritecontext.h
@@ -80,16 +80,18 @@ class CORE_EXPORT QgsReadWriteContext
      */
     void pushMessage( const QString &message, Qgis::MessageLevel level = Qgis::Warning );
 
-#ifndef SIP_RUN
-
     /**
      * Push a category to the stack
      * \note The return value should always be used so category can be automatically left.
-     * \note Not available in the Python bindings.
+     * \note It is not aimed at being used in Python. Instead use the context manager.
+     * \code{.py}
+     *   context = QgsReadWriteContext()
+     *   with QgsReadWriteContext.enterCategory(context, category, details):
+     *     # do something
+     * \endcode
      * \since QGIS 3.2
      */
-    MAYBE_UNUSED NODISCARD QgsReadWriteContextCategoryPopper enterCategory( const QString &category, const QString &details = QString() );
-#endif
+    MAYBE_UNUSED NODISCARD QgsReadWriteContextCategoryPopper enterCategory( const QString &category, const QString &details = QString() ) SIP_PYNAME( _enterCategory );
 
     /**
      * Return the stored messages and remove them
@@ -109,17 +111,21 @@ class CORE_EXPORT QgsReadWriteContext
     friend class QgsReadWriteContextCategoryPopper;
 };
 
-#ifndef SIP_RUN
-///@cond PRIVATE
-class QgsReadWriteContextCategoryPopper
+
+/**
+ * QgsReadWriteContextCategoryPopper allows entering a context category
+ * and takes care of leaving this category on deletion of the class.
+ * This would happen when it gets out of scope.
+ * \since 3.2
+ */
+class CORE_EXPORT QgsReadWriteContextCategoryPopper
 {
   public:
+    //! Creates a popper
     QgsReadWriteContextCategoryPopper( QgsReadWriteContext &context ) : mContext( context ) {}
     ~QgsReadWriteContextCategoryPopper() {mContext.leaveCategory();}
   private:
     QgsReadWriteContext mContext;
 };
-///@endcond PRIVATE
-#endif
 
 #endif // QGSREADWRITECONTEXT_H