24
$\begingroup$

I have a package that I have placed in the $BaseDirectory\Applications directory so that Needs can find it. I have a logo in the directory that I will return when a function is called. However, when I call NotebookDirectory in the package I get the directory of the notebook (this seems like expected behavior). I've not been able to find a corresponding PackageDirectory function.

How do I get the directory of a package so I can load a resource (in this case a .jpg logo) to return to the calling notebook?

Update to show package and $InputFileName returns empty string

Update to show proper use of $InputFileName in package

The package saved as an .m file in $BaseDirectory\Applications is:

BeginPackage["MyTest`"];
packageDirectory::usage="Gets the directory of the this package.";
packageLogo::usage="Gets the logo of this package.";
testfoo::usage="does this work"
Begin["`Private`"];
(* $Input only has a value as the package is being loaded so capture that
        value with `Set` in order to use it with `SetDelayed` later. *)
thisIsThePackageDirectory=$InputFileName//DirectoryName;
packageDirectory[]:=thisIsThePackageDirectory
packageLogo[]:=Import[FileNameJoin[{thisIsThePackageDirectory,"logo.jpg"}],"JPEG"] 
testfoo[]:="some text"
End[];
EndPackage[];

The logo.jpg file is in the same directory as the .m package.

The notebook commands are as follows.

Needs["MyTest`"]
Names["MyTest`*"]
(* {"packageDirectory", "packageLogo", "testfoo"} *)
packageLogo[]
(* Logo image returned *)
packageDirectory[] // InputForm
(* "C:\\ProgramData\\Mathematica\\Applications\\MyTest.m" *)
testfoo[]
(* some text *)

Many thanks to @Szabolcs and @Ymareth.

$\endgroup$

1 Answer 1

25
$\begingroup$

FindFile will tell you which file will be loaded when you ask for a particular package with the backtick syntax.

DirectoryName will return the directory that contains a file.

Example:

In[1]:= FindFile["Combinatorica`"]

Out[1]= "/Applications/Mathematica 10.1.app/Contents/AddOns/Packages/Combinatorica/Kernel/init.m"

Update: This solution is probably better: in your package file, use $InputFileName to determine the location of the package file and save it into a variable that is private to the package. The use this variable with the function that shows the logo.

$\endgroup$
6
  • 1
    $\begingroup$ $InputFileName returns an empty string. $\endgroup$
    – Edmund
    Commented Apr 11, 2015 at 12:00
  • 2
    $\begingroup$ I also think FindFile is a bit expensive to use. The system already knows where the package file is. It has loaded it after all. Why do another scan of the $Path directory hierarchies to "find" it and the strip the file name from it. $\endgroup$
    – Edmund
    Commented Apr 11, 2015 at 12:55
  • 4
    $\begingroup$ $InputFileName only has a value while the kernel is loading a package. So the package itself can do something with that filename while its being loaded. $\endgroup$
    – Ymareth
    Commented Apr 11, 2015 at 14:17
  • $\begingroup$ @Ymareth Ah! It is the difference between Set and SetDelayed in my package definitions. I understand now. I had to capture the value of $InputFileName//DirectoryName into a variable using Set. This executes when the package loads. Then I can use this variable anywhere in the SetDelayed defintions. However, I was using $InputFileName in the SetDelayed definitions and these don't run until alter the package as been loaded. Therefore, I was getting the empty string. $\endgroup$
    – Edmund
    Commented Apr 11, 2015 at 14:30
  • 1
    $\begingroup$ @Edmund That is what I meant by "use $InputFileName to determine the location of the package file and save it into a variable that is private to the package". $\endgroup$
    – Szabolcs
    Commented Apr 11, 2015 at 14:34

Your Answer

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

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