3

Let's say I have file named test.m containing

test[arg1_] := ( Print[arg1];)

What is the best technique for calling test like test["value"] while preventing test from being added to the $ContextPath?

The following almost works except you must know function values ahead of time

Block[{$ContextPath,test}, Needs["test`"];test["test"]];

My question is: How do I generalize the call above to work with any number of functions without the user needing to input definitions manually?

William
  • 7,595
  • 2
  • 22
  • 70
  • @Nasser although I agree generally speaking everything should be in package, let's pretend the example of above is about calling functions that exist in a notebook. Now surely you are not recommending me wrap the notebook functions with Package statements simply to call a notebooks function from another notebook. At the end of the day I believe such functionality could be useful and to me it really seems like a solution is to implausible. – William Aug 03 '13 at 23:12

1 Answers1

7

As Nasser notes in a comment every Symbol has a context. You should be aware that Symbols are created during parsing. See: Local variables in Module leak into the Global context.

Alright, now that we worked out what you want here is the simplified answer:

We can perform a similar operation to BeginPackage with Block:

Block[{$ContextPath = {"runPrv`", "System`"}, $Context = "runPrv`"}, . . .]

We can combine this with Leonid's method from Is it possible to use Begin and End inside a Manipulate? to keep contexts from being fully resolved until we are ready for evaluation inside the Block. (Note that Global` Symbols are still created, as discussed above, but they will not be defined.) I believe "runPrv`" may be left out of $ContextPath in our application so long as we don't change the $Context from "runPrv`" within the Block itself. Finally we have:

SetAttributes[runPrivate, HoldAllComplete];

runPrivate[code_] :=
  With[{body = MakeBoxes @ code},
    Block[{$ContextPath = {"System`"}, $Context = "runPrv`"},
      ToExpression @ body]]

Now:

runPrivate[
  Get["test.m"];
  a = 5;
  test[a]
]

5

Global Symbols a and test remain undefined:

?a
?test

Global`a

Global`test

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • +1 The 2nd example is great and helps clarify. This relates strongly to the original question. How would I modify this following code so a isn't defined. Block[{$ContextPath = {"test\", "System`"}, $Context = "test`"}, a = 1;]I see in your response here http://mathematica.stackexchange.com/questions/24347/how-can-i-make-private-functions-inside-packages/24350#24350 you only mention 2 approaches for avoiding to define declarations int the$Context. Is there a technique that avoids pre-pending the context test\ and in addition it doesn't require you run Clear[function1] first. – William Aug 05 '13 at 00:13
  • @Liam You are working against the normal evaluation order, and there may be other approaches depending on what you want, such as a Cell with a unique Context, but that specific case may best be handled as Leonid showed here, e.g.: Block[{$ContextPath = {"test`", "System`"}, $Context = "test`", code = MakeBoxes[a = 1]}, ToExpression[code]] – Mr.Wizard Aug 05 '13 at 00:23
  • @Liam I put this example in the question. I think perhaps we finally have what you want? – Mr.Wizard Aug 05 '13 at 00:36
  • Yes. Now this isn't exactly production quality, but I'm going to assume there isn't really a better way to suppress the message like z::shdw: Symbol z appears in multiple contexts {test\,Global`}; definitions in context test` may shadow or be shadowed by other definitions. >>except withQuiet[Block[{$ContextPath = {"test`", "System`"}, $Context = "test`", code = MakeBoxes[p = 1]}, ToExpression[code]], General::shdw]` or is there another way? – William Aug 05 '13 at 00:40
  • @Liam I was busy updating my answer. Could you please give me an example using the current runPrivate code that produces this shadowing when run in a fresh session? – Mr.Wizard Aug 05 '13 at 00:49
  • They example above throws the message in M8 if I don't have Quiet. For example Block[{$ContextPath = {"test\", "System`"}, $Context = "test`", code = MakeBoxes[p = 1]}, ToExpression[code]]` – William Aug 05 '13 at 00:52
  • @Liam That's strange, I'm not getting it in version 7. Does it help to use With, e.g.: With[{body = MakeBoxes[p = 1]}, Block[{$ContextPath = {"test`", "System`"}, $Context = "test`"}, ToExpression[body]]] ? – Mr.Wizard Aug 05 '13 at 00:54
  • No. But if I run the code twice the $Context I believe has been defined and I get no message. – William Aug 05 '13 at 00:57
  • @Liam for reference does it happen if we leave "test`" out of the $ContextPath, e.g.: With[{body = MakeBoxes[p = 1]}, Block[{$ContextPath = {"System`"}, $Context = "test`"}, ToExpression[body]]] – Mr.Wizard Aug 05 '13 at 01:02
  • Leaving of `test`` works without any message – William Aug 05 '13 at 01:03
  • After some testing of the code I believe there should be a Remove["runPrv\*"]at the end. Otherwise potential variable conflicts can occur. For exampleAttributesdefined withProtected` cause errors. – William Aug 05 '13 at 02:23
  • @Liam Could you give me an example of that problem? – Mr.Wizard Aug 05 '13 at 05:20
  • The example you have posted illustrates the problem. Basically run the code in your example then type in `runPriv`` and try auto completing Ctrl + K. Any variables defined will bleed into the next time you run the code. – William Aug 05 '13 at 05:28
  • example http://pastebin.com/1nsB0qad While we are modifying the code, I am going to ask a quick question. Is there any way to explicitly force something to be written to Global\`` inside private context maybe someway to defineGlobal`` as another context/variable in your code? – William Aug 05 '13 at 05:41
  • @Liam Ah, I see. I was assuming you had only one private context and you wanted to keep what you had loaded. For example, you could run runPrivate[Get["test.m"];] and then later run runPrivate[test["thing"]] and the call would work. You could modify the code to use a context of your choosing, or as already proposed clear the context when done. You could also have runPrivate create random context when run, which wouldn't help memory use but would prevent collisions and should work even if you Locked something within a prior context. If you need help with any of these let me know. – Mr.Wizard Aug 05 '13 at 06:12
  • What about my second question about Global\``? Is there a way to get this to work.runPrv [Global`a=1]` as in a way to force global variables? – William Aug 05 '13 at 14:39
  • For documentation purposes. The following code clears attributes and contexts http://pastebin.com/M2b5DUqK after running the function each time. The following is supposed to use random contexts http://pastebin.com/2ZTpkYeF but I would guess there is better way to achieve similar functionality. In addition it often gives Remove::remal : Symbol Removed[RContext] already removed. – William Aug 05 '13 at 16:08
  • Again posting for documentation purposes. Ultimately the code here http://pastebin.com/R6JzhBsU uses InternalInheritedBlock to prevent users form modifying System\*` values – William Aug 16 '13 at 03:30
  • @Liam Looking at your last comment it seems you're trying not merely run some code in a separate context but create some kind of sandbox area where arbitrary code can be executed safely. Is that correct? – Mr.Wizard Aug 16 '13 at 09:40
  • Yes that is correct, but just to clarify I am not looking for a secure sand boxing environment in the sense that they can't mess with the system. It's not like I'm planning to run someone else's code. I didn't say that explicitly, but that has been the evolving intent. It would probably be a good idea to add that keyword to the question for anyone that might be searching for such a thing. – William Aug 16 '13 at 15:02
  • @Liam You also might consider a new question specifically describing that situation; I think a parallel kernel or the like might be a workable solution. This question never got much attention unfortunately. – Mr.Wizard Aug 16 '13 at 15:06