[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [32866] trunk/blender: fix to allow [ #24009] to be fixed.

Campbell Barton ideasman42 at gmail.com
Thu Nov 4 13:59:03 CET 2010


Revision: 32866
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=32866
Author:   campbellbarton
Date:     2010-11-04 13:59:03 +0100 (Thu, 04 Nov 2010)

Log Message:
-----------
fix to allow [#24009] to be fixed.

WM_operator_poll() could fail in cases WM_operator_name_call() would succeed because calling the operator would setup the context before calling poll.
this would result in python raising an invalid error or menu items being greyed out.

now python can also check with an operator context:
  bpy.ops.object.editmode_toggle.poll('INVOKE_SCREEN') 

Modified Paths:
--------------
    trunk/blender/release/scripts/modules/bpy/ops.py
    trunk/blender/source/blender/editors/interface/interface.c
    trunk/blender/source/blender/editors/interface/interface_regions.c
    trunk/blender/source/blender/python/intern/bpy_operator.c
    trunk/blender/source/blender/windowmanager/WM_api.h
    trunk/blender/source/blender/windowmanager/intern/wm_event_system.c

Modified: trunk/blender/release/scripts/modules/bpy/ops.py
===================================================================
--- trunk/blender/release/scripts/modules/bpy/ops.py	2010-11-04 11:45:30 UTC (rev 32865)
+++ trunk/blender/release/scripts/modules/bpy/ops.py	2010-11-04 12:59:03 UTC (rev 32866)
@@ -115,14 +115,34 @@
     def _get_doc(self):
         return op_as_string(self.idname())
 
+    @staticmethod
+    def _parse_args(args):
+        C_dict = None
+        C_exec = 'EXEC_DEFAULT'
+
+        if len(args) == 0:
+            pass
+        elif len(args) == 1:
+            if type(args[0]) != str:
+                C_dict = args[0]
+            else:
+                C_exec = args[0]
+        elif len(args) == 2:
+            C_exec, C_dict = args
+        else:
+            raise ValueError("1 or 2 args execution context is supported")
+
+        return C_dict, C_exec
+
     __doc__ = property(_get_doc)
 
     def __init__(self, module, func):
         self.module = module
         self.func = func
 
-    def poll(self, context=None):
-        return op_poll(self.idname_py(), context)
+    def poll(self, *args):
+        C_dict, C_exec = __class__._parse_args(args)
+        return op_poll(self.idname_py(), C_dict, C_exec)
 
     def idname(self):
         # submod.foo -> SUBMOD_OT_foo
@@ -135,31 +155,11 @@
     def __call__(self, *args, **kw):
 
         # Get the operator from blender
-        if len(args) > 2:
-            raise ValueError("1 or 2 args execution context is supported")
-
-        C_dict = None
-
         if args:
-
-            C_exec = 'EXEC_DEFAULT'
-
-            if len(args) == 2:
-                C_exec = args[0]
-                C_dict = args[1]
-            else:
-                if type(args[0]) != str:
-                    C_dict = args[0]
-                else:
-                    C_exec = args[0]
-
-            if len(args) == 2:
-                C_dict = args[1]
-
+            C_dict, C_exec = __class__._parse_args(args)
             ret = op_call(self.idname_py(), C_dict, kw, C_exec)
-
         else:
-            ret = op_call(self.idname_py(), C_dict, kw)
+            ret = op_call(self.idname_py(), None, kw)
 
         if 'FINISHED' in ret:
             import bpy

Modified: trunk/blender/source/blender/editors/interface/interface.c
===================================================================
--- trunk/blender/source/blender/editors/interface/interface.c	2010-11-04 11:45:30 UTC (rev 32865)
+++ trunk/blender/source/blender/editors/interface/interface.c	2010-11-04 12:59:03 UTC (rev 32866)
@@ -629,7 +629,7 @@
 			if(but->context)
 				CTX_store_set((bContext*)C, but->context);
 
-			if(ot == NULL || WM_operator_poll((bContext*)C, ot)==0) {
+			if(ot == NULL || WM_operator_poll_context((bContext*)C, ot, but->opcontext)==0) {
 				but->flag |= UI_BUT_DISABLED;
 				but->lock = 1;
 			}

Modified: trunk/blender/source/blender/editors/interface/interface_regions.c
===================================================================
--- trunk/blender/source/blender/editors/interface/interface_regions.c	2010-11-04 11:45:30 UTC (rev 32865)
+++ trunk/blender/source/blender/editors/interface/interface_regions.c	2010-11-04 12:59:03 UTC (rev 32866)
@@ -434,7 +434,7 @@
 		if(but->flag & UI_BUT_DISABLED) {
 			const char *poll_msg;
 			CTX_wm_operator_poll_msg_set(C, NULL);
-			WM_operator_poll(C, but->optype);
+			WM_operator_poll_context(C, but->optype, but->opcontext);
 			poll_msg= CTX_wm_operator_poll_msg_get(C);
 			if(poll_msg) {
 				BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), "Disabled: %s", poll_msg);

Modified: trunk/blender/source/blender/python/intern/bpy_operator.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_operator.c	2010-11-04 11:45:30 UTC (rev 32865)
+++ trunk/blender/source/blender/python/intern/bpy_operator.c	2010-11-04 12:59:03 UTC (rev 32866)
@@ -46,12 +46,15 @@
 	char		*opname;
 	PyObject	*context_dict= NULL; /* optional args */
 	PyObject	*context_dict_back;
+	char		*context_str= NULL;
 	PyObject	*ret;
 
+	int context= WM_OP_EXEC_DEFAULT;
+
 	// XXX Todo, work out a better solution for passing on context, could make a tuple from self and pack the name and Context into it...
 	bContext *C = BPy_GetContext();
 	
-	if (!PyArg_ParseTuple(args, "s|O:_bpy.ops.poll", &opname, &context_dict))
+	if (!PyArg_ParseTuple(args, "s|Os:_bpy.ops.poll", &opname, &context_dict, &context_str))
 		return NULL;
 	
 	ot= WM_operatortype_find(opname, TRUE);
@@ -61,6 +64,15 @@
 		return NULL;
 	}
 
+	if(context_str) {
+		if(RNA_enum_value_from_id(operator_context_items, context_str, &context)==0) {
+			char *enum_str= BPy_enum_as_string(operator_context_items);
+			PyErr_Format(PyExc_TypeError, "Calling operator \"bpy.ops.%s.poll\" error, expected a string enum in (%.200s)", opname, enum_str);
+			MEM_freeN(enum_str);
+			return NULL;
+		}
+	}
+	
 	if(!PyDict_Check(context_dict))
 		context_dict= NULL;
 
@@ -70,7 +82,7 @@
 	Py_XINCREF(context_dict); /* so we done loose it */
 	
 	/* main purpose of thsi function */
-	ret= WM_operator_poll((bContext*)C, ot) ? Py_True : Py_False;
+	ret= WM_operator_poll_context((bContext*)C, ot, context) ? Py_True : Py_False;
 	
 	/* restore with original context dict, probably NULL but need this for nested operator calls */
 	Py_XDECREF(context_dict);
@@ -126,7 +138,7 @@
 	CTX_py_dict_set(C, (void *)context_dict);
 	Py_XINCREF(context_dict); /* so we done loose it */
 
-	if(WM_operator_poll((bContext*)C, ot) == FALSE) {
+	if(WM_operator_poll_context((bContext*)C, ot, context) == FALSE) {
 		const char *msg= CTX_wm_operator_poll_msg_get(C);
 		PyErr_Format( PyExc_SystemError, "Operator bpy.ops.%.200s.poll() %s", opname, msg ? msg : "failed, context is incorrect");
 		CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */

Modified: trunk/blender/source/blender/windowmanager/WM_api.h
===================================================================
--- trunk/blender/source/blender/windowmanager/WM_api.h	2010-11-04 11:45:30 UTC (rev 32865)
+++ trunk/blender/source/blender/windowmanager/WM_api.h	2010-11-04 12:59:03 UTC (rev 32866)
@@ -218,6 +218,7 @@
 
 
 int			WM_operator_poll		(struct bContext *C, struct wmOperatorType *ot);
+int			WM_operator_poll_context(struct bContext *C, struct wmOperatorType *ot, int context);
 int			WM_operator_call		(struct bContext *C, struct wmOperator *op);
 int			WM_operator_repeat		(struct bContext *C, struct wmOperator *op);
 int			WM_operator_name_call	(struct bContext *C, const char *opstring, int context, struct PointerRNA *properties);

Modified: trunk/blender/source/blender/windowmanager/intern/wm_event_system.c
===================================================================
--- trunk/blender/source/blender/windowmanager/intern/wm_event_system.c	2010-11-04 11:45:30 UTC (rev 32865)
+++ trunk/blender/source/blender/windowmanager/intern/wm_event_system.c	2010-11-04 12:59:03 UTC (rev 32866)
@@ -72,6 +72,8 @@
 #include "wm_event_types.h"
 #include "wm_draw.h"
 
+static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only);
+
 /* ************ event management ************** */
 
 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
@@ -379,6 +381,12 @@
 	return 1;
 }
 
+/* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
+int WM_operator_poll_context(bContext *C, wmOperatorType *ot, int context)
+{
+	return wm_operator_call_internal(C, ot, NULL, NULL, context, TRUE);
+}
+
 static void wm_operator_print(wmOperator *op)
 {
 	char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
@@ -603,11 +611,15 @@
 	}
 }
 
-int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports)
+int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports, short poll_only)
 {
 	wmWindowManager *wm= CTX_wm_manager(C);
 	int retval= OPERATOR_PASS_THROUGH;
 
+	/* this is done because complicated setup is done to call this function that is better not duplicated */
+	if(poll_only)
+		return WM_operator_poll(C, ot);
+
 	if(WM_operator_poll(C, ot)) {
 		wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
 		
@@ -693,7 +705,7 @@
  * this is for python to access since its done the operator lookup
  * 
  * invokes operator in context */
-static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
+static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only)
 {
 	wmWindow *window= CTX_wm_window(C);
 	wmEvent *event;
@@ -758,7 +770,7 @@
 						CTX_wm_region_set(C, ar1);
 				}
 				
-				retval= wm_operator_invoke(C, ot, event, properties, reports);
+				retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
 				
 				/* set region back */
 				CTX_wm_region_set(C, ar);
@@ -772,7 +784,7 @@
 				ARegion *ar= CTX_wm_region(C);
 
 				CTX_wm_region_set(C, NULL);
-				retval= wm_operator_invoke(C, ot, event, properties, reports);
+				retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
 				CTX_wm_region_set(C, ar);
 
 				return retval;
@@ -786,7 +798,7 @@
 
 				CTX_wm_region_set(C, NULL);
 				CTX_wm_area_set(C, NULL);
-				retval= wm_operator_invoke(C, ot, event, properties, reports);
+				retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
 				CTX_wm_region_set(C, ar);
 				CTX_wm_area_set(C, area);
 
@@ -794,7 +806,7 @@
 			}
 			case WM_OP_EXEC_DEFAULT:
 			case WM_OP_INVOKE_DEFAULT:
-				return wm_operator_invoke(C, ot, event, properties, reports);
+				return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
 		}
 	}
 	
@@ -807,7 +819,7 @@
 {
 	wmOperatorType *ot= WM_operatortype_find(opstring, 0);
 	if(ot)
-		return wm_operator_call_internal(C, ot, context, properties, NULL);
+		return wm_operator_call_internal(C, ot, properties, NULL, context, FALSE);
 
 	return 0;
 }
@@ -839,7 +851,7 @@
 		printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
 #endif
 
-	retval= wm_operator_call_internal(C, ot, context, properties, reports);

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list