8
$\begingroup$

I am trying to create a documentated application inside FileNameJoin[{$UserBaseDirectory, "Applications"}]

I obviously dont fully understand the concept of the package-system, because I get troubles with loading in the packages using Needs. The structure of my "TestApplication" is simple. Mainfolder "TestApplication" contains a package "TestPackage.m" wich looks like this:

BeginPackage["TestApplication`TestPackage`"]

TestFunction::usage = "TestFunction[x] gives x^2"

Begin["`Private`"]

TestFunction[x_] := x^2

End[]

EndPackage[]

To load the definitions I added a "init.m" file inside a folder "Kernel" and it contains the following line:

 Get["TestApplication`TestPackage`"]

If I now call:

 Needs["TestApplication`]

Mathematica gives me a warning:

Needs::nocont: Context TestApplication` was not created when Needs was evaluated. >>

This is very anoying, because I want to create a documentation for my TestPackage with many examples and since the input cells of a reference page have the option CellContext -> CellGroup every CellGroup has to call Needs and the user would see this message everytime he wants to evaluate an example.

To avoid this message I thought of just creating that context "TestApplication`" so that Mathematica stops complaining about that. This is what I did: I added another Package named "TestApplication.m" into the folder "TestApplication" with the content:

BeginPackage["TestApplication`"]
EndPackage[]

And I extended the init.m file to

Get["TestApplication`TestPackage`"]
Get["TestApplication`TestApplication`"]

for loading in this new empty (root-)package of my application.

Well if I now call

Needs["TestApplication"]
TestFunction[3]

inside a fresh notebook, everything seems to be fine.

No Needs::nocont-Message is produced anymore, BUT the problem starts if i call Needs inside my reference page. Here it just dont work anymore. It does not load the definition of TestApplication``TestPackage``TestFunction!

To test this i made a small testnotebook for you:

<< TestApplication`

TestFunction[3]

CreateDocument[
Cell[CellGroupData[{Cell[
 BoxData[RowBox[{"Needs", "[", "\"\<TestApplication`\>\"", 
    "]"}]], "Input"],
Cell[BoxData[RowBox[{"TestFunction", "[", "3", "]"}]],   "Input"],Cell[BoxData["$ContextPath"], "Input"]}]],

StyleDefinitions -> 
FrontEnd`FileName[{"Wolfram"}, "Reference.nb", 
CharacterEncoding -> "UTF-8"]]

It will open a minimal "reference page" wich calles the function "TestFunction". Its important, that the application has been loaded previously with Get[TestApplication]to reproduce the problem.

I tried to analyze the problem by looking at $ContextPath inside of the reference notebook. If the application "TestApplication" has not been loaded before from a global notebook, $ContextPath contains all the subcontexts of "TestApplication" after calling Needs.

In:

 $ContextPath

Out:

 {"TestApplication`", "TestApplication`TestPackage`",  "Notebook$$154$436091`", "System`"}

This way it works fine. But if "TestApplication" allready has been loaded before from an empty notebook (fresh kernel) with a global context (not the reference nb) than the $ContextPath only contains the toplevelcontext TestApplication. (after calling Needs from inside the reference page)

In:

 $ContextPath

Out:

 {"TestApplication`", "Notebook$$154$436091`", "System`"}

This seems to be the problem, why `TestApplicationTestPackageTestfunction is not loaded this time.

Does someone know why this happens or how I can do this better?

EDIT 1:

I meanwhile found a solution but it might be dirty. if someone knows a more convenient way to do this, any answer is still much appreciated!

I replaced the content of the init.m file by:

BeginPackage["TestApplication`", {"TestApplication`TestPackage`"}]
EndPackage[]

And I deleted the file "TestApplication.m", since it was not neccessary anymore. With this if Needs["TestApplication``"] is called, the empty context TestApplication is created and this itself calls Needs["TestApplication``TestPackage``"]. But i still have no idea why my first attempt did not worked. Remember that this problem only appears in the special case where a local CellContext is used, e.g. in reference pages.

EDIT 2:

My first solution works not right for me because i want to be able to specify a file, wich Needs loads, when a context is needed. For example if you use encoded files Test-encoded for the packages inside of TestApplication, its not enough to use `Needs["TestApplicationTestPackage"], since the context is not written in clear text in the encoded file.

I also found a simple work around to do this:

My init.m now looks like this:

Off[Needs::nocont]
SetDirectory[FileNameJoin[{$UserBaseDirectory,"Applications",  "TestPackage"}]];
Needs["TestApplication`TestPackage`", "TestPackage-encoded"]
Needs["TestPackage`TestPackage2`", "TestPackage2-encoded"]
PrependTo[$ContextPath, "TestPackage`"]
ResetDirectory[];
(* On[Needs::nocont] *)

But i am still not happy with this solution. Because i have to turn out the Message and i am not able to turn it on again at the end of init.m, without throwing the message.

EDIT 3:

Adding a file TestApplication.m with the content:

BeginPackage["TestApplication`",{"TestApplication`TestPackage`"}];
EndPackage[];

as Ymareth suggested, is just another form of my own first answer (EDIT1) wich is not very satisfying because you can not specify where the Packages come from.

I want to have something like this:

BeginPackage["TestApplication`"];
    Needs["TestApplication`TestPackage`", "fileOfTestPackage"]
EndPackage[];

But it does not work.

The documentation of BeginPackage says:

BeginPackage["context``",{"need_1``","need_1``",...}]
calls Needs on the need_i. 

So how can i call these Needs manually with specifying the packagefiles?

$\endgroup$
3
  • $\begingroup$ Can you explain why using Get["TestApplication`TestPackage`"] in init.m and renaming your encoded files to TestPackage.m instead of TestPackage-encoded is not good enough? It seems to not make much sense to use Needs in your TestAppliation`init.m as that will be read only once when loaded with Needs["TestApplication`"] anyway, so there is no gain in using Needs there over Get. I also don't understand why it would be a problem when the context is not human readable within an encoded file, Mathematica will happily load it anyway... $\endgroup$ Apr 10, 2015 at 23:12
  • $\begingroup$ @AlbertRetey Thanks for your interest! Get["TestApplication``TestPackage``"] is not enough because then i get the Needs::nocont Message. This message strangely only appears inside a reference page. Open any reference page or use my example code to reproduce the problem. You are right with renaming the encoded files, but what if I want to give a key for encoding. Than I need to explicitly use Get["TestPackage.m", "key"] and i can not use the syntax BeginPackage["TestApplication``", {"TestApplication``TestPackage``}] By the way how do you quote ``` inside of code? $\endgroup$
    – sacratus
    Apr 10, 2015 at 23:35
  • $\begingroup$ The problem with encoded files is that mathematica dont automatically finds the file, if the context is needed. Try FindFile["context"] on an encoded file. Your suggestion to rename the encoded files to "context.m" cured this problem. $\endgroup$
    – sacratus
    Apr 10, 2015 at 23:41

2 Answers 2

1
+100
$\begingroup$

I'm still not sure whether I understand your problem and what you are trying to achieve but I now found something which is not entirely obvious. I think you try to load a sub-package file which was encoded with a key and put its context on $ContextPath. Here is what I think will do what you want:

First you need a directory TestApplication in one of the directories on $Path, but I think this is what you have already done correct and I will as you did use FileNameJoin[{$UserBaseDirectory,"Applications"}]. Then in that directory you'd place the encoded file TestPackage.m which should contain something like:

BeginPackage["TestApplication`TestPackage`"];
TestFunction::usage = "TestFunction[x] gives x^2"
Begin["`Private`"];
TestFunction[x_]:=x^2;
End[];
EndPackage[];

for my test I did put that content into a file TestPackage.txt in the same directory and then used

With[{fname=FileNameJoin[{
   $UserBaseDirectory,"Applications","TestApplication","TestPackage"
}]},
   Encode[fname<>".txt",fname<>".m","secret"]
]

to encode with the given key. I did then create another file called init.m in a subdirectory Kernel within the TestApplication directory with the following code in it:

Get["TestApplication`TestPackage`","secret"];

BeginPackage["TestApplication`",{"TestApplication`TestPackage`"}];

EndPackage[];

I can then in a fresh kernel call

Needs["TestApplication`"]

and that loads "TestApplication`TestPackage`" and puts that into $ContextPath so that you can now call TestFunction[2].

The only tricky part here was to first load the package content and then after that use the BeginPackage to please Needs which wants init.m create the "TestApplication" context and to also have the subpackage in $ContextPath as well with the second argument. There are several other variants to achieve the same (e.g. by explicitly modifing $ContextPath) but I think this is probably the least tricky one. Of course you could now encode init.m as well so it is not too easy to get ahold of the key you used...

$\endgroup$
1
  • $\begingroup$ Thank you very much, now i tested your solution and is working fine. I knew that I was close and that it could should not be a complicated solution. $\endgroup$
    – sacratus
    Apr 13, 2015 at 16:41
1
$\begingroup$

I think you're confused about context paths, partly because your application only has one package.

I assume you have a package called TestPackage.m located in a folder called TestApplication located within FileNameJoin[{$UserBaseDirectory, "Applications"}]? On my machine this folder would be C:\Users\myusername\AppData\Roaming\Mathematica\Applications\TestApplication.

Given that you can load your package in one of two ways...

Get["TestApplication`TestPackage`"];

or

Needs["TestApplication`TestPackage`"];

Both of these do the same thing 'the first time they are executed'. The difference is Get always loads the package. Whereas Needs checks to see if that context TestApplication`TestPackage` exists already. If it does then it does nothing.

Your call to Needs["TestPackage`"] is looking for a TestPackage.m somewhere in the $Path but there isn't one because its in the folder TestApplication. But you don't need to call Needs at all because your Get has already loaded the package.

Your function called TestFunction should be visible... try ?TestFunction and is really called TestApplication`TestPackage`TestFunction but you're allowed to use the short name because $ContextPath contains TestApplication`TestPackage` - BeginPackage did that for you.

If you want to load at the application level then you'd need to create another package called TestApplication.m along the lines of...

BeginPackage["TestApplication`",{"TestApplication`TestPackage`"}];
EndPackage[];

Put that in the folder TestApplication.

Then you can do Get or Needs["TestApplication`"];

The packages will chain load each other (you can put multiple packages into the list). Your functions will be in contexts of their own packages.

TestApplication`TestPackage`TestFunction NOT TestApplication`TestFunction.
$\endgroup$
5
  • $\begingroup$ You may also need to add the folder TestApplication to your $Path. $\endgroup$
    – Ymareth
    Apr 10, 2015 at 12:24
  • $\begingroup$ Hey thanks for your efforts, but that does not fix my actual problem. I want to be able to load all the Packages from the applicationlevel by typing in Needs["TestApplication"] into a reference Page. Adding the folder TestApplication to $Path is not userfriendly enough. $\endgroup$
    – sacratus
    Apr 10, 2015 at 16:52
  • $\begingroup$ I also think that your answer is reflecting obvious information that i have allready given in my question. I am aware of the context paths, Get and Needs in general, but i have a special problem in combination with calling Needs on a reference Page. Your solution would work if you add an init.m file with the line Get["TestApplication``TestPackage``"]. But it has the problem that you can not specify from where you want to load the packages, for example if they are encoded. $\endgroup$
    – sacratus
    Apr 10, 2015 at 17:25
  • $\begingroup$ Your TestApplication.m only refers to the context TestApplication`. So if you do Get[TestApplication`] which I think translates to Get["TestApplication.m"] then you will have created the context TestApplication`. Your subsequent calls to Needs["TestApplication`"] would go and load the init.m inside the TestApplication folder which would activate the chaining Needs you've put there so loading TestApplication`TestPackage` BUT because TestApplication` is already on the $ContextPath it does nothing. I don't get why you're calling Get["TestApplication`"] explicitly first. $\endgroup$
    – Ymareth
    Apr 11, 2015 at 11:00
  • $\begingroup$ Ah ok, this could be an explanation. I call 'Get["TestApplication"] first to reproduce the problem. Think of an application that you first load and than you open the referencepage of e.g. Testfunction. There you would have to call Needs or Get again. You can test this with Compinatonia, there it works because it has no subcontexts. $\endgroup$
    – sacratus
    Apr 11, 2015 at 11:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.