[Tutor] Best way to get root project directory in module search path

Cameron Simpson cs at cskk.id.au
Sun Aug 27 19:03:43 EDT 2017


On 27Aug2017 14:27, boB Stepp <robertvstepp at gmail.com> wrote:
>On Sun, Aug 27, 2017 at 2:13 AM, Cameron Simpson <cs at cskk.id.au> wrote:
>> On 26Aug2017 21:27, boB Stepp <robertvstepp at gmail.com> wrote:
>>> import sys
>>> sys.path.append('..')
[...]
>> The trouble with this specific approach is that '..' relies on your
>> _working_ directory being above ScriptMenuSystem i.e. the directory you
>> shell's in; '..' is not related to the path to the test.py file. What yu
>> want for this is the actual path to the code i.e. __file__.  Eg:
>>
>>  sys.path.append(dirname(dirname(__file__)))
>
>I tried to use this (on Python 2.4/2.6 -- one server has 2.4, the
>other 2.6) and got the following:
>
>Traceback (most recent call last):
>    File "... /test.py", line 9, in <module>
>        sys.path.append(dirname(dirname(__file__)))
>NameError:  name 'dirname' is not defined

from os.path import dirname

>Is this a version issue or do I have the syntax wrong?

Just a missing import. You'll find dirname in the Index section of the Python 
docs. Handy for locating something just mentioned.

>Anyway, I tried something a bit different that implemented this:
>
>pgm_dir = os.path.dirname(os.path.abspath(__file__))
>pgm_root = pgm_dir.replace('/ScriptMenuSystem', '')
>sys.path.append(pgm_root)

Calling os.path.dirname a second time does what your:

  pgm_dir.replace('/ScriptMenuSystem', '')

does, but reliably. Supposing you have the misfortune to have 
'/ScriptMenuSystem' higher up in the full path also? Supposing your package 
changes its name in the future, or is installed with another name? Fragility.

>And this gave me the results I wanted.  Funny that 'dirname' works
>with os.path but not with sys.path.

No no. "dirname" comes from the "os.path" module. That is all.

>BTW, I have not used os.walk yet, but would that be better than my bit
>of string manipulation above?  If yes, an example of walking up and
>getting the directory exactly one level up from __file__ would be
>appreciated.

Well:

  from os.path import abspath, dirname, join
  code_dir = abspath(join(dirname(__file), '..'))

or:

  from os.path import abspath, dirname, join
  code_dir = abspath(join(dirname(__file), '../..'))

depending how high you want to go.

Given that os.walk walks _down_ the tree from some directory, how do you think 
it would help you?

>> but that requires your module to know its own location within your library
>> i.e.  that it is .../dev_scripts/ScriptMenuSystem/test.py, and therefore
>> that you want .../dev_scripts. Fragile. (And __file__ doesn't work in some
>> more arcane ways of distributing modules, such as in zip files but let's not
>> go there, not least because I've never done that myself).
>
>I have been using the same project structure for some years now, so in
>that sense it is well-established and not fragile.

It is more that embedded specific hardwired knowledge in the code is an 
inherently fragile situation, if only because it must be remembered when naming 
changes occur. And they do. I have a quite large package I've been working on 
for a decade and am very seriously contemplating changing its name in the next 
month or so. Fortunately the installed base is small.

>I don't anticipate
>it changing.  There is actually no installation in a traditional
>sense.  Once I develop something worth using, it gets copied (by me as
>root) from my user area to an accessible location to those users that
>requested access.  I had been doing this copying to each user's area,
>but that has grown unwieldy as my couple of users have grown into
>around 15.

For simple packages/modules an install _is_ just a copy. So you're performing 
the install step.

>So I am moving to put everything in a centrally accessible
>location

Very sound. But to use it your users should have that location as part of their 
python path, or the code needs to be invoked by some wrapper which can insert 
the needed path. Both these things live _outside_ the package code.

>and will probably adopt Steve's suggestion to establish a
>group with permissions to use these programs.

Note that this is normally only needed for code that is privileged i.e. runs 
with permissions different to those of the user.

>So I think this idea (Or some variation thereof.) is what I need to
>do.  Or the good experts here show me I'm engaged in foolishness!

I think you're better off arranging your users' environments to include the 
shared library area, or providing a wrapper shell script which does that and 
then invokes the real code.

Eg:

  #!/bin/sh
  PYTHONPATH=/path/to/your/shared/lib:$PYTHONPATH
  export PYTHONPATH
  python -m your.module.name ${1+"$@"}

Such a script has the advantage that the $PYTHONPATH change applies only to the 
python running your module, not to your users' wider environment.

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Tutor mailing list