Tcl Source Code

Artifact [8c169837c7]
Login

Artifact 8c169837c7df3656ae8692df21759db9f826536f:

Attachment "tip283-1.patch" to ticket [1577282fff] added by msofer 2006-11-02 03:40:28.
Index: doc/namespace.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/namespace.n,v
retrieving revision 1.21
diff -u -r1.21 namespace.n
--- doc/namespace.n	26 Aug 2006 13:00:38 -0000	1.21
+++ doc/namespace.n	1 Nov 2006 20:26:32 -0000
@@ -679,7 +679,8 @@
 the result of the command.  Note that it is legal to make the target
 of an ensemble rewrite be another (or even the same) ensemble
 command.  The ensemble command will not be visible through the use of
-the \fBuplevel\fR or \fBinfo level\fR commands.
+the \fBuplevel\fR or \fBinfo level\fR commands, with the exception of
+\fBinfo level 0\fR.
 .SS "ENSEMBLE OPTIONS"
 .PP
 The following options, supported by the \fBnamespace ensemble
@@ -687,13 +688,16 @@
 an ensemble command behaves:
 .TP
 \fB\-map\fR
-When non-empty, this option supplies a dictionary that provides a
-mapping from subcommand names to a list of prefix words to substitute
-in place of the ensemble command and subcommand words (in a manner
-similar to an alias created with \fBinterp alias\fR; the words are not
-reparsed after substitution).  When this option is empty, the mapping
-will be from the local name of the subcommand to its fully-qualified
-name.  Note that when this option is non-empty and the
+When non-empty, this option supplies a dictionary that provides a mapping
+from subcommand names to a list of prefix words to substitute in place of
+the ensemble command and subcommand words (in a manner similar to an alias
+created with interp alias; the words are not reparsed after
+substitution). The resulting command is invoked with the original arguments
+in the namespace of the ensemble. 
+When this option is empty, the mapping
+will be from the local name of the subcommand to its alias, resolved from
+the ensemble's namespace.
+Note that when this option is non-empty and the
 \fB\-subcommands\fR option is empty, the ensemble subcommand names
 will be exactly those words that have mappings in the dictionary.
 .TP
@@ -771,9 +775,9 @@
 .PP
 When the result is a non-empty list, the words of that list are used
 to replace the ensemble command and subcommand, just as if they had
-been looked up in the \fB\-map\fR. It is up to the unknown handler to
-supply all namespace qualifiers if the implementing subcommand is not
-in the namespace of the caller of the ensemble command. Also note that
+been looked up in the \fB\-map\fR. The implementing subcommand is looked up
+in the ensemble's namespace using normal command location rules.
+Also note that
 when ensemble commands are chained (e.g. if you make one of the
 commands that implement an ensemble subcommand into an ensemble, in a
 manner similar to the text widget's tag and mark subcommands) then the
Index: generic/tclBasic.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclBasic.c,v
retrieving revision 1.209
diff -u -r1.209 tclBasic.c
--- generic/tclBasic.c	31 Oct 2006 20:19:44 -0000	1.209
+++ generic/tclBasic.c	1 Nov 2006 20:26:34 -0000
@@ -3292,13 +3292,12 @@
 	varFramePtr = iPtr->rootFramePtr;
 	savedVarFramePtr = iPtr->varFramePtr;
 	iPtr->varFramePtr = varFramePtr; 
+    } else if (iPtr->lookupNsPtr) {
+	savedNsPtr = varFramePtr->nsPtr;
+	varFramePtr->nsPtr = iPtr->lookupNsPtr;
     } else if (flags & TCL_EVAL_INVOKE) {
 	savedNsPtr = varFramePtr->nsPtr;
-	if (iPtr->lookupNsPtr) {
-	    varFramePtr->nsPtr = iPtr->lookupNsPtr;
-	} else {
-	    varFramePtr->nsPtr = iPtr->globalNsPtr;
-	}
+	varFramePtr->nsPtr = iPtr->globalNsPtr;
     }
 
     /*
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.290
diff -u -r1.290 tclInt.h
--- generic/tclInt.h	31 Oct 2006 20:19:45 -0000	1.290
+++ generic/tclInt.h	1 Nov 2006 20:26:37 -0000
@@ -1370,8 +1370,8 @@
 				 * or NULL if no active traces. */
     int returnCode;		/* [return -code] parameter */
     CallFrame *rootFramePtr;    /* Global frame pointer for this interpreter */
-    Namespace *lookupNsPtr;	/* Namespace to use ONLY on the next
-				 * TCL_EVAL_INVOKE call to Tcl_EvalObjv */
+    Namespace *lookupNsPtr;	/* Namespace to use for command lookup ONLY on
+				 * the next call to Tcl_EvalObjv */ 
 
     /*
      * Information used by Tcl_AppendResult to keep track of partial results.
Index: generic/tclNamesp.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclNamesp.c,v
retrieving revision 1.114
diff -u -r1.114 tclNamesp.c
--- generic/tclNamesp.c	31 Oct 2006 20:19:45 -0000	1.114
+++ generic/tclNamesp.c	1 Nov 2006 20:26:41 -0000
@@ -5016,7 +5016,7 @@
 	char *name;
 	Tcl_DictSearch search;
 	Tcl_Obj *listObj;
-	int done, len, allocatedMapFlag = 0;
+	int done, len;
 	/*
 	 * Defaults
 	 */
@@ -5046,9 +5046,6 @@
 	for (; objc>1 ; objc-=2,objv+=2 ) {
 	    if (Tcl_GetIndexFromObj(interp, objv[0], createOptions, "option",
 		    0, &index) != TCL_OK) {
-		if (allocatedMapFlag) {
-		    Tcl_DecrRefCount(mapObj);
-		}
 		return TCL_ERROR;
 	    }
 	    switch ((enum EnsCreateOpts) index) {
@@ -5057,25 +5054,19 @@
 		continue;
 	    case CRT_SUBCMDS:
 		if (Tcl_ListObjLength(interp, objv[1], &len) != TCL_OK) {
-		    if (allocatedMapFlag) {
-			Tcl_DecrRefCount(mapObj);
-		    }
 		    return TCL_ERROR;
 		}
 		subcmdObj = (len > 0 ? objv[1] : NULL);
 		continue;
 	    case CRT_MAP: {
-		Tcl_Obj *patchedDict = NULL, *subcmdObj;
-
-		/*
+		Tcl_Obj *subcmdObj;
+		
+                /*
 		 * Verify that the map is sensible.
 		 */
 
 		if (Tcl_DictObjFirst(interp, objv[1], &search,
 			&subcmdObj, &listObj, &done) != TCL_OK) {
-		    if (allocatedMapFlag) {
-			Tcl_DecrRefCount(mapObj);
-		    }
 		    return TCL_ERROR;
 		}
 		if (done) {
@@ -5084,17 +5075,10 @@
 		}
 		do {
 		    Tcl_Obj **listv;
-		    char *cmd;
 
 		    if (Tcl_ListObjGetElements(interp, listObj, &len,
 			    &listv) != TCL_OK) {
 			Tcl_DictObjDone(&search);
-			if (patchedDict) {
-			    Tcl_DecrRefCount(patchedDict);
-			}
-			if (allocatedMapFlag) {
-			    Tcl_DecrRefCount(mapObj);
-			}
 			return TCL_ERROR;
 		    }
 		    if (len < 1) {
@@ -5102,55 +5086,22 @@
 				"ensemble subcommand implementations "
 				"must be non-empty lists", TCL_STATIC);
 			Tcl_DictObjDone(&search);
-			if (patchedDict) {
-			    Tcl_DecrRefCount(patchedDict);
-			}
-			if (allocatedMapFlag) {
-			    Tcl_DecrRefCount(mapObj);
-			}
 			return TCL_ERROR;
 		    }
-		    cmd = TclGetString(listv[0]);
-		    if (!(cmd[0] == ':' && cmd[1] == ':')) {
-			Tcl_Obj *newList = Tcl_NewListObj(len, listv);
-			Tcl_Obj *newCmd = Tcl_NewStringObj(nsPtr->fullName,-1);
-
-			if (nsPtr->parentPtr) {
-			    Tcl_AppendStringsToObj(newCmd, "::", NULL);
-			}
-			Tcl_AppendObjToObj(newCmd, listv[0]);
-			Tcl_ListObjReplace(NULL, newList, 0, 1, 1, &newCmd);
-			if (patchedDict == NULL) {
-			    patchedDict = Tcl_DuplicateObj(objv[1]);
-			}
-			Tcl_DictObjPut(NULL, patchedDict, subcmdObj, newList);
-		    }
 		    Tcl_DictObjNext(&search, &subcmdObj, &listObj, &done);
 		} while (!done);
 
-		if (allocatedMapFlag) {
-		    Tcl_DecrRefCount(mapObj);
-		}
-		mapObj = (patchedDict ? patchedDict : objv[1]);
-		if (patchedDict) {
-		    allocatedMapFlag = 1;
-		}
+		mapObj = objv[1];
 		continue;
 	    }
 	    case CRT_PREFIX:
 		if (Tcl_GetBooleanFromObj(interp, objv[1],
 			&permitPrefix) != TCL_OK) {
-		    if (allocatedMapFlag) {
-			Tcl_DecrRefCount(mapObj);
-		    }
 		    return TCL_ERROR;
 		}
 		continue;
 	    case CRT_UNKNOWN:
 		if (Tcl_ListObjLength(interp, objv[1], &len) != TCL_OK) {
-		    if (allocatedMapFlag) {
-			Tcl_DecrRefCount(mapObj);
-		    }
 		    return TCL_ERROR;
 		}
 		unknownObj = (len > 0 ? objv[1] : NULL);
@@ -5338,7 +5289,7 @@
 		    subcmdObj = (len > 0 ? objv[1] : NULL);
 		    continue;
 		case CONF_MAP: {
-		    Tcl_Obj *patchedDict = NULL, *subcmdObj;
+		    Tcl_Obj *subcmdObj;
 
 		    /*
 		     * Verify that the map is sensible.
@@ -5346,9 +5297,6 @@
 
 		    if (Tcl_DictObjFirst(interp, objv[1], &search,
 			    &subcmdObj, &listObj, &done) != TCL_OK) {
-			if (allocatedMapFlag) {
-			    Tcl_DecrRefCount(mapObj);
-			}
 			return TCL_ERROR;
 		    }
 		    if (done) {
@@ -5357,17 +5305,10 @@
 		    }
 		    do {
 			Tcl_Obj **listv;
-			char *cmd;
 
 			if (Tcl_ListObjGetElements(interp, listObj, &len,
 				&listv) != TCL_OK) {
 			    Tcl_DictObjDone(&search);
-			    if (patchedDict) {
-				Tcl_DecrRefCount(patchedDict);
-			    }
-			    if (allocatedMapFlag) {
-				Tcl_DecrRefCount(mapObj);
-			    }
 			    return TCL_ERROR;
 			}
 			if (len < 1) {
@@ -5375,39 +5316,11 @@
 				    "ensemble subcommand implementations "
 				    "must be non-empty lists", TCL_STATIC);
 			    Tcl_DictObjDone(&search);
-			    if (patchedDict) {
-				Tcl_DecrRefCount(patchedDict);
-			    }
-			    if (allocatedMapFlag) {
-				Tcl_DecrRefCount(mapObj);
-			    }
 			    return TCL_ERROR;
 			}
-			cmd = TclGetString(listv[0]);
-			if (!(cmd[0] == ':' && cmd[1] == ':')) {
-			    Tcl_Obj *newList = Tcl_NewListObj(len, listv);
-			    Tcl_Obj *newCmd =
-				    Tcl_NewStringObj(nsPtr->fullName, -1);
-			    if (nsPtr->parentPtr) {
-				Tcl_AppendStringsToObj(newCmd, "::", NULL);
-			    }
-			    Tcl_AppendObjToObj(newCmd, listv[0]);
-			    Tcl_ListObjReplace(NULL, newList, 0,1, 1,&newCmd);
-			    if (patchedDict == NULL) {
-				patchedDict = Tcl_DuplicateObj(objv[1]);
-			    }
-			    Tcl_DictObjPut(NULL, patchedDict, subcmdObj,
-				    newList);
-			}
 			Tcl_DictObjNext(&search, &subcmdObj, &listObj, &done);
 		    } while (!done);
-		    if (allocatedMapFlag) {
-			Tcl_DecrRefCount(mapObj);
-		    }
-		    mapObj = (patchedDict ? patchedDict : objv[1]);
-		    if (patchedDict) {
-			allocatedMapFlag = 1;
-		    }
+		    mapObj = objv[1];
 		    continue;
 		}
 		case CONF_NAMESPACE:
@@ -6277,6 +6190,7 @@
 		sizeof(Tcl_Obj *) * (objc - 2 + prefixObjc));
 	memcpy(tempObjv, prefixObjv, sizeof(Tcl_Obj *) * prefixObjc);
 	memcpy(tempObjv+prefixObjc, objv+2, sizeof(Tcl_Obj *) * (objc-2));
+	iPtr->lookupNsPtr = ensemblePtr->nsPtr;
 	result = Tcl_EvalObjv(interp, objc-2+prefixObjc, tempObjv,
 		TCL_EVAL_INVOKE);
 	Tcl_DecrRefCount(prefixObj);
@@ -6298,6 +6212,7 @@
      */
 
     if (ensemblePtr->unknownHandler != NULL && reparseCount++ < 1) {
+	Interp *iPtr = (Interp *) interp;
 	int paramc, i;
 	Tcl_Obj **paramv, *unknownCmd, *ensObj;
 
@@ -6311,6 +6226,7 @@
 	Tcl_ListObjGetElements(NULL, unknownCmd, &paramc, &paramv);
 	Tcl_Preserve(ensemblePtr);
 	Tcl_IncrRefCount(unknownCmd);
+	iPtr->lookupNsPtr = ensemblePtr->nsPtr;
 	result = Tcl_EvalObjv(interp, paramc, paramv, 0);
 	if (result == TCL_OK) {
 	    prefixObj = Tcl_GetObjResult(interp);
@@ -6667,12 +6583,7 @@
 	     * the programmer's responsibility (or [::unknown] of course).
 	     */
 
-	    cmdObj = Tcl_NewStringObj(ensemblePtr->nsPtr->fullName, -1);
-	    if (ensemblePtr->nsPtr->parentPtr != NULL) {
-		Tcl_AppendStringsToObj(cmdObj, "::", name, NULL);
-	    } else {
-		Tcl_AppendStringsToObj(cmdObj, name, NULL);
-	    }
+	    cmdObj = Tcl_NewStringObj(name, -1);
 	    cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
 	    Tcl_SetHashValue(hPtr, (ClientData) cmdPrefixObj);
 	    Tcl_IncrRefCount(cmdPrefixObj);
@@ -6733,11 +6644,7 @@
 		    if (isNew) {
 			Tcl_Obj *cmdObj, *cmdPrefixObj;
 
-			TclNewObj(cmdObj);
-			Tcl_AppendStringsToObj(cmdObj,
-				ensemblePtr->nsPtr->fullName,
-				(ensemblePtr->nsPtr->parentPtr ? "::" : ""),
-				nsCmdName, NULL);
+			cmdObj = Tcl_NewStringObj(nsCmdName, -1);
 			cmdPrefixObj = Tcl_NewListObj(1, &cmdObj);
 			Tcl_SetHashValue(hPtr, (ClientData) cmdPrefixObj);
 			Tcl_IncrRefCount(cmdPrefixObj);
Index: tests/namespace.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/namespace.test,v
retrieving revision 1.61
diff -u -r1.61 namespace.test
--- tests/namespace.test	31 Oct 2006 13:46:33 -0000	1.61
+++ tests/namespace.test	1 Nov 2006 20:26:42 -0000
@@ -1529,7 +1529,7 @@
     set result [list [ns x0 z] [ns x1] [ns x2] [ns x3]]
     namespace delete ns
     set result
-} {{1 ::ns::x0::z} 1 2 3}
+} {{1 z} 1 2 3}
 
 test namespace-43.1 {ensembles: dict-driven} {
     namespace eval ns {
@@ -1555,6 +1555,19 @@
     namespace delete ns
     set result
 } {{1 {}} {2 0} {1 .} {1 {. foo}} {2 1} {2 2}}
+test namespace-43.2.a {ensembles: dict-driven} {
+    namespace eval ns {
+	namespace export x*
+	proc x1 {args} {list 1 $args}
+	proc x2 {args} {list 2 [llength $args]}
+	namespace ensemble create -map {
+	    a x1 b x2 c {x1 .} d {x2 .}
+	}
+    }
+    set result [list [ns a] [ns b] [ns c] [ns c foo] [ns d] [ns d foo]]
+    namespace delete ns
+    set result
+} {{1 {}} {2 0} {1 .} {1 {. foo}} {2 1} {2 2}}
 set SETUP {
     namespace eval ns {
 	namespace export a b
@@ -1588,6 +1601,7 @@
 	proc c args {format 3,[llength $args]}
 	proc d args {format 4,[llength $args]}
 	namespace ensemble create -subcommands {b c} -map {c ::ns::d}
+	namespace ensemble create -command ::nsa -subcommands {b c} -map {c d}
     }
 }
 test namespace-43.8 {ensembles: list-and-map-driven} -setup $SETUP -body {
@@ -1599,9 +1613,15 @@
 test namespace-43.10 {ensembles: list-and-map-driven} -setup $SETUP -body {
     ns b foo bar boo spong wibble
 } -cleanup {namespace delete ns} -result 2,5
+test namespace-43.10a {ensembles: list-and-map-driven} -setup $SETUP -body {
+    nsa b foo bar boo spong wibble
+} -cleanup {namespace delete ns} -result 2,5
 test namespace-43.11 {ensembles: list-and-map-driven} -setup $SETUP -body {
     ns c foo bar boo spong wibble
 } -cleanup {namespace delete ns} -result 4,5
+test namespace-43.11.a {ensembles: list-and-map-driven} -setup $SETUP -body {
+    nsa c foo bar boo spong wibble
+} -cleanup {namespace delete ns} -result 4,5
 test namespace-43.12 {ensembles: list-and-map-driven} -setup $SETUP -body {
     ns d foo bar boo spong wibble
 } -cleanup {namespace delete ns} -returnCodes error -result {unknown or ambiguous subcommand "d": must be b, or c}
@@ -1651,7 +1671,7 @@
     foobar foobarcon
 } -cleanup {
     rename foobar {}
-} -returnCodes error -result {invalid command name "::foobarconfigure"}
+} -returnCodes error -result {invalid command name "foobarconfigure"}
 
 test namespace-45.1 {ensemble: introspection} {
     namespace eval ns {
@@ -1672,7 +1692,7 @@
     }
     namespace delete ns
     set result
-} {A ::ns::x}
+} {A x}
 
 test namespace-46.1 {ensemble: modification} {
     namespace eval ns {
@@ -1694,6 +1714,26 @@
     namespace delete ns
     set result
 } {{A ::ns::x} 123 {B ::ns::x} 123 {} 123}
+test namespace-46.1.a {ensemble: modification} {
+    namespace eval ns {
+	namespace export x
+	proc x {} {format 123}
+
+	# Ensemble maps A->x
+	namespace ensemble create -command ns -map {A x}
+	set ::result [list [namespace ensemble configure ns -map] [ns A]]
+
+	# Ensemble maps B->x
+	namespace ensemble configure ns -map {B x}
+	lappend ::result [namespace ensemble configure ns -map] [ns B]
+
+	# Ensemble maps x->x
+	namespace ensemble configure ns -map {}
+	lappend ::result [namespace ensemble configure ns -map] [ns x]
+    }
+    namespace delete ns
+    set result
+} {{A x} 123 {B x} 123 {} 123}
 test namespace-46.2 {ensemble: ensembles really use current export list} {
     namespace eval ns {
 	namespace export x1
@@ -1815,7 +1855,36 @@
     lappend result [catch {ns c d e} msg] $msg
     lappend result [catch {ns Magic foo bar spong wibble} msg] $msg
     list $result [lsort [info commands ::ns::*]] $log [namespace delete ns]
-} {{0 2 0 2 0 2 0 2 1 {unknown or protected subcommand "Magic"}} {::ns::Magic ::ns::a ::ns::b ::ns::c} {{making a} {running ::ns::a b c} {running ::ns::a b c} {making b} {running ::ns::b c d} {making c} {running ::ns::c d e} {unknown Magic - args = foo bar spong wibble}} {}}
+} {{0 2 0 2 0 2 0 2 1 {unknown or protected subcommand "Magic"}} {::ns::Magic ::ns::a ::ns::b ::ns::c} {{making a} {running a b c} {running a b c} {making b} {running b c d} {making c} {running c d e} {unknown Magic - args = foo bar spong wibble}} {}}
+test namespace-47.1.a {ensemble: unknown handler} {
+    set log {}
+    namespace eval ns {
+ 	namespace export {[a-z]*}
+ 	proc Magic {ensemble subcmd args} {
+ 	    global log
+ 	    if {[string match {[a-z]*} $subcmd]} {
+ 		lappend log "making $subcmd"
+ 		proc $subcmd args {
+ 		    global log
+ 		    lappend log "running [info level 0]"
+ 		    llength $args
+ 		}
+ 	    } else {
+ 		lappend log "unknown $subcmd - args = $args"
+ 		return -code error \
+		    "unknown or protected subcommand \"$subcmd\""
+ 	    }
+ 	}
+ 	namespace ensemble create -unknown Magic
+    }
+    set result {}
+    lappend result [catch {ns a b c} msg] $msg
+    lappend result [catch {ns a b c} msg] $msg
+    lappend result [catch {ns b c d} msg] $msg
+    lappend result [catch {ns c d e} msg] $msg
+    lappend result [catch {ns Magic foo bar spong wibble} msg] $msg
+    list $result [lsort [info commands ::ns::*]] $log [namespace delete ns]
+} {{0 2 0 2 0 2 0 2 1 {unknown or protected subcommand "Magic"}} {::ns::Magic ::ns::a ::ns::b ::ns::c} {{making a} {running a b c} {running a b c} {making b} {running b c d} {making c} {running c d e} {unknown Magic - args = foo bar spong wibble}} {}}
 test namespace-47.2 {ensemble: unknown handler} {
     namespace eval ns {
 	namespace export {[a-z]*}
@@ -1834,6 +1903,24 @@
     (ensemble unknown subcommand handler)
     invoked from within
 "ns spong"} {}}
+test namespace-47.2.a {ensemble: unknown handler} {
+    namespace eval ns {
+	namespace export {[a-z]*}
+	proc Magic {ensemble subcmd args} {
+	    error foobar
+	}
+	namespace ensemble create -unknown Magic
+    }
+    list [catch {ns spong} msg] $msg $::errorInfo [namespace delete ns]
+} {1 foobar {foobar
+    while executing
+"error foobar"
+    (procedure "Magic" line 2)
+    invoked from within
+"Magic ::ns spong"
+    (ensemble unknown subcommand handler)
+    invoked from within
+"ns spong"} {}}
 test namespace-47.3 {ensemble: unknown handler} {
     namespace eval ns {
 	variable count 0
@@ -1849,6 +1936,21 @@
     }
     list [catch {ns spong} msg] $msg $ns::count [namespace delete ns]
 } {1 {unknown or ambiguous subcommand "spong": must be a, b, or c} 1 {}}
+test namespace-47.3 {ensemble: unknown handler} {
+    namespace eval ns {
+	variable count 0
+	namespace export {[a-z]*}
+	proc a {} {}
+	proc c {} {}
+	proc Magic {ensemble subcmd args} {
+	    variable count
+	    incr count
+	    proc b {} {}
+	}
+	namespace ensemble create -unknown Magic
+    }
+    list [catch {ns spong} msg] $msg $ns::count [namespace delete ns]
+} {1 {unknown or ambiguous subcommand "spong": must be a, b, or c} 1 {}}
 test namespace-47.4 {ensemble: unknown handler} {
     namespace eval ns {
 	namespace export {[a-z]*}
@@ -1862,6 +1964,19 @@
     result of ensemble unknown subcommand handler: ::ns::Magic ::ns spong
     invoked from within
 "ns spong"} {}}
+test namespace-47.4.a {ensemble: unknown handler} {
+    namespace eval ns {
+	namespace export {[a-z]*}
+	proc Magic {ensemble subcmd args} {
+	    return -code break
+	}
+	namespace ensemble create -unknown Magic
+    }
+    list [catch {ns spong} msg] $msg $::errorInfo [namespace delete ns]
+} {1 {unknown subcommand handler returned bad code: break} {unknown subcommand handler returned bad code: break
+    result of ensemble unknown subcommand handler: Magic ::ns spong
+    invoked from within
+"ns spong"} {}}
 test namespace-47.5 {ensemble: unknown handler} {
     namespace ensemble create -command foo -unknown bar
     proc bar {args} {
@@ -1898,6 +2013,44 @@
     rename foo {}
     set result
 } {::foo|one two three}
+test namespace-47.7.a {ensemble: unknown handler invokes in ensemble's ns} \
+-body {
+    namespace eval ns0 {
+	proc MagicImpl args {
+	    return {Magic called}
+	}
+    }
+    namespace eval ns1 {
+	namespace path ::ns0
+	proc Magic {ensemble subcmd args} {
+	    return [list MagicImpl]
+	}
+	namespace ensemble create -unknown Magic
+    }
+    ns1 foo
+} -cleanup {
+    namespace delete ns0
+    namespace delete ns1
+} -result {Magic called}
+test namespace-47.8.a {ensemble: unknown handler invokes in ensemble's ns} \
+-body {
+    namespace eval ns0 {
+	proc MagicImpl args {
+	    return {Magic called}
+	}
+    }
+    namespace eval ns1 {
+	namespace unknown [list namespace eval ::ns0]
+	proc Magic {ensemble subcmd args} {
+	    return [list MagicImpl]
+	}
+	namespace ensemble create -unknown Magic
+    }
+    ns1 foo
+} -cleanup {
+    namespace delete ns0
+    namespace delete ns1
+} -result {Magic called}
 test namespace-47.8 {ensemble: unknown handler, commands with spaces} {
     namespace ensemble create -command foo -unknown {bar boo}
     proc bar {args} {
@@ -1944,6 +2097,42 @@
     namespace delete foo
     set result
 } {{-map {} -namespace ::foo -prefixes 1 -subcommands x -unknown ::foo::u} XXX 123 ::foo::bar {y 456} YYY 456 ::foo::bar {z 789} ZZZ 789}
+test namespace-48.1.a {ensembles and namespace import: unknown handler} {
+    namespace eval foo {
+	namespace export bar
+	namespace ensemble create -command bar -unknown u -subcomm x
+	proc u {ens args} {
+	    global result
+	    lappend result $ens $args
+	    namespace ensemble config $ens -subcommand {x y}
+	}
+	proc u2 {ens args} {
+	    global result
+	    lappend result $ens $args
+	    namespace ensemble config ::bar -subcommand {x y z}
+	}
+	proc x args {
+	    global result
+	    lappend result XXX $args
+	}
+	proc y args {
+	    global result
+	    lappend result YYY $args
+	}
+	proc z args {
+	    global result
+	    lappend result ZZZ $args
+	}
+    }
+    namespace import -force foo::bar
+    set result [list [namespace ensemble config bar]]
+    bar x 123
+    bar y 456
+    namespace ensemble config bar -unknown ::foo::u2
+    bar z 789
+    namespace delete foo
+    set result
+} {{-map {} -namespace ::foo -prefixes 1 -subcommands x -unknown u} XXX 123 ::foo::bar {y 456} YYY 456 ::foo::bar {z 789} ZZZ 789}
 test namespace-48.2 {ensembles and namespace import: exists} {
     namespace eval foo {
 	namespace ensemble create -command ::foo::bar
@@ -2586,7 +2775,24 @@
     rename unknown.save ::unknown
     namespace eval :: [list namespace unknown $handler]
 } -result SUCCESS
-    
+
+#
+# TIP 283: Tests for interaction of ensembles, paths and namespace unknown
+#
+# Note that some other tests named ".a" are included above in the ensemble
+# section
+
+# -unknown found in path
+# -unknown found by ns-unknown
+# -unknown follows path
+# -unknown follows ns-unknown
+# -map follows path
+# -map follows ns-unknown
+# -subcommands follows path
+# -subcommands follows ns-unknown
+# exports follow path (?)
+# exports follow ns-unknown (?)
+
 # cleanup
 catch {rename cmd1 {}}
 catch {unset l}