how do you handle a variable in a deleted namespace?

Viewed 86

Doing some exploration involving creating and deleting namespaces, I ran into this curiosity:

% namespace eval foo {variable x 1}
% namespace upvar foo x x
% set x
1
% namespace delete foo
% set x
can't read "x": no such variable
% set x 2
can't set "x": upvar refers to variable in deleted namespace
%

After this, x seems to be untouchable. It can't be read or unset because it doesn't exist, but it can't be set because it's in another namespace. How can x be recovered from this state?

What I'm trying to accomplish is importing a set of commands and variables from another namespace, and being able to remove that namespace and have the imported commands and variables go away. namespace delete seems to do the right thing with regards to imported commands, but there is no equivalent for imported variables, and as far as I can tell there isn't even any way to tell if a particular variable is imported (via namespace upvar) from somewhere else - namespace which tells you the current namespace, and namespace origin doesn't exist for variables.

2 Answers

(not an answer)

Looks like a bug to me => https://core.tcl-lang.org/tcl/tktnew

You can't unset the variable either. It seems like the only thing you can do is upvar it to a different variable.

% namespace eval foo {variable x 1}
% namespace upvar foo x x
% namespace delete foo
% set x
can't read "x": no such variable
% set x 1
can't set "x": upvar refers to variable in deleted namespace
% unset x
can't unset "x": no such variable
% set tmp ""
% upvar 0 tmp x
% unset x

What I'm trying to accomplish is importing a set of commands and variables from another namespace, and being able to remove that namespace and have the imported commands and variables go away.

As your posting is more about this buggy behaviour of the upvar mechanism (and the lacking of Tcl's introspection), your overall motivation comes only as a sidenote. Maybe you can realize sth. along the lines of the following, as an alternative (to avoid upvar)?

% namespace eval foo {variable x 1; proc bar {} {;}}
% apply {{} {variable x; puts $x; bar; namespace delete [namespace current]; set x "a"; puts $x} ::foo}
1
a
% apply {{} {variable x; puts $x; bar; namespace delete [namespace current]; set x "a"; puts $x} ::foo}
namespace "::foo" not found
% namespace eval foo {variable x 2; proc bar {} { puts BAR}}
% apply {{} {variable x; puts $x; bar; namespace delete [namespace current]; set x "a"; puts $x} ::foo}
2
BAR
a
  • You trade a namespace environment for a proc environment as an "import" target, and variable is not affected by the zombie links of upvar.
  • You can swap the context namespace, or re-create a deleted one as you wish.
  • You don't need two mechanisms (namespace import and namespace upvar), just one.
  • Flipping the context namespace will incur the loss of the lambdaExpr's intrep (incl. bytecode), so maybe better keep recreating the namespace ::foo.
Related