Tcl Source Code

Artifact [30f647d467]
Login

Artifact 30f647d46731841bdb5a932f0ebc05b865725435:

Attachment "tip283.patch" to ticket [1577282fff] added by msofer 2006-10-26 08:13:12.
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	26 Oct 2006 01:10:28 -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.203
diff -u -r1.203 tclBasic.c
--- generic/tclBasic.c	23 Oct 2006 23:04:12 -0000	1.203
+++ generic/tclBasic.c	26 Oct 2006 01:10:31 -0000
@@ -3294,13 +3294,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/tclNamesp.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclNamesp.c,v
retrieving revision 1.105
diff -u -r1.105 tclNamesp.c
--- generic/tclNamesp.c	23 Oct 2006 22:49:25 -0000	1.105
+++ generic/tclNamesp.c	26 Oct 2006 01:10:34 -0000
@@ -5015,7 +5015,7 @@
 	char *name;
 	Tcl_DictSearch search;
 	Tcl_Obj *listObj;
-	int done, len, allocatedMapFlag = 0;
+	int done, len;
 	/*
 	 * Defaults
 	 */
@@ -5045,9 +5045,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) {
@@ -5056,25 +5053,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) {
@@ -5083,17 +5074,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) {
@@ -5101,55 +5085,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);
@@ -5337,7 +5288,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.
@@ -5345,9 +5296,6 @@
 
 		    if (Tcl_DictObjFirst(interp, objv[1], &search,
 			    &subcmdObj, &listObj, &done) != TCL_OK) {
-			if (allocatedMapFlag) {
-			    Tcl_DecrRefCount(mapObj);
-			}
 			return TCL_ERROR;
 		    }
 		    if (done) {
@@ -5356,17 +5304,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) {
@@ -5374,39 +5315,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:
@@ -6275,6 +6188,7 @@
 	tempObjv = (Tcl_Obj **) ckalloc(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_EVAL_NOREWRITE);
 	Tcl_DecrRefCount(prefixObj);
@@ -6310,6 +6224,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);
@@ -6337,9 +6252,6 @@
 			"\n    while parsing result of ensemble unknown subcommand handler");
 		return TCL_ERROR;
 	    }
-	    if (prefixObjc > 0) {
-		goto runResultingSubcommand;
-	    }
 
 	    /*
 	     * Restore the interp's call data, which may have been wiped out
@@ -6348,7 +6260,11 @@
 
 	    iPtr->callObjc = objc;
 	    iPtr->callObjv = objv;
-	    
+	    	    
+	    if (prefixObjc > 0) {
+		goto runResultingSubcommand;
+	    }
+
 	    /*
 	     * Namespace alive & empty result => reparse.
 	     */
@@ -6674,12 +6590,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);
@@ -6740,11 +6651,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.59
diff -u -r1.59 namespace.test
--- tests/namespace.test	23 Oct 2006 22:49:26 -0000	1.59
+++ tests/namespace.test	26 Oct 2006 01:10:36 -0000
@@ -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
@@ -1816,6 +1856,35 @@
     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}} {}}
+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 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}} {}}
 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} {
@@ -1889,6 +2004,45 @@
     while parsing result of ensemble unknown subcommand handler
     invoked from within
 "foo bar"}}
+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-48.1 {ensembles and namespace import: unknown handler} {
     namespace eval foo {
@@ -1926,6 +2080,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
@@ -2568,7 +2758,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}