absolute path to a file

Cameron Simpson cs at cskk.id.au
Sun Aug 18 19:31:05 EDT 2019


Paul, I can see we must train you in the interleaved response style :-)

On 18Aug2019 17:29, Paul St George <email at paulstgeorge.com> wrote:
>On 18/08/2019 02:03, Cameron Simpson wrote:
>1: Is image01.tif a real existing file when you ran this code?
>
>Yes. image01.tif is real, existing and apparent.

But in what directory? What is its _actual_ full path as you expect it 
to be?

>>>> print(obj.name,'uses',n.image.name,'saved at',n.image.filepath, 
>'which is at', realpath(n.image.filepath))
>gives:
>Plane uses image01.tif saved at //image01.tif which is at /image01.tif
>
>(Chris and Peter lead me to believe that Blender has a special kind of 
>relative path. The double slashes replace the path to the blend file’s 
>directory.) realpath has done something but I know not what.

I expect that realpath has normalised the string '//image01.tif' into 
the normal UNIX path '/image01.tif'. Because realpath works with UNIX 
paths, '//image01.tif' is not the path to an existing file from its 
point of view, because that would be in / ('//' is equivalent to '/' in 
UNIX, because multiple slashes coalesce).

>2a:
>What is your current working directory when you run this code?
>
>Well, there at least two answers to that question. Within Blender's 
>Python Console:
>>>> os.getcwd()
>‘/‘

Did you run the other code in Blender's Console?

The thing to bear in mind is that on many GUI desktops a GUI 
application's working directory may be '/'. Specificly, _not_ your home 
directory. This is because they're invoked (usually) by the desktop 
manager programme. And also because it sidesteps awkward situations like 
having the working directory in some removable medium such as a thumb 
drive, which might get removed.

So the Blender CWD is _not_ your home directory or whatever directory 
your terminal prompt may be in.

And therefore things that depend on the current directory will behave 
counterintuitively. In your case, by getting '/' from os.getcwd().

So: if the image01.tif file is _not_ in '/' (which I expect to be the 
case) then Python's realpath will be doing a lexical operation because 
as far as it is concerned the pathname ('//image01.tif') is not the path 
of an existing file. And its getcwd() isn't what you expect, thus the 
result not being what you want.

>But I am *guessing* the real location of the CWD is
>/Applications/Blender/blender.app/Contents/Resources/2.79/scripts

This isn't a meaningful statement to me. The CWD is the working 
directory of the python interpreter running your code. If that is the 
Blender Console and the consale says the cwd is '/', then that's what it 
is.

In particular, if you invoke some Python script as /path/to/script.py, 
your cwd does not magicly become '/path/to'.

The cwd is a per process ephemeral thing, which is why the shell "cd" 
command works: it switches the working directory for the shell, and that 
context is inherited by child processes (your commands).

>I tried using realpath, bpy.path.abspath, and os.path.abspath on this 
>forward slash but nothing changed.

Because the leading slash makes it an absolute path by UNIX rules. The 
cwd never gets a look in; it is irrelevant.

>For amusement, I also tried print(os.path.join(os.getcwd(), os.getcwd()))

os.path.join is pretty lexical; it largely exists to avoid wiring in an 
OS-dependent path separator into your code (eg Windows' '\\' versus UNIX 
'/' or OS9 ':'). But it notices an absolute path in the sequence and 
discards earlier values, so the second getcwd is kept; not sure why this 
is useful behaviour. But putting an absolute path on the right hand end 
of a join isn't meaningful either, so the result is at best amusing 
anyway.

>2b:
>If the underlying OS realpath fails, the Python library function might 
>fall back on os.path.join(os.getcwd(), filename).
>
>It should, shouldn’t it. But it doesn’t.

Well, I over simplified. If your path doesn't stat() (== "resolve 
directly to a filesystem object", on which the OS realpath relies), 
Python's realpath seems to be doing a _purely lexical_ path 
normalisation, like os.path.normpath.

So, what have you passed in? '//image01.tif'. That is an absolute path.  
The cwd is not relevant, realpath runs with it as is. 'image01.tif' is 
not in '/', so it doesn't stat(), so we just normalise the path to 
'/image01.tif'.

Alternative values:

'image01.tif' (your n.image.filepath[2:], to skip the Blender specific 
'//' relative pathname notation). That is a relative path. Can we stat 
it? That will happen in the Python interpreter's cwd, which apparently 
is Blender's cwd, which is '/'. And there's no '/image01.tif', so it 
doesn't stat. Back to lexcial work:

  normpath(os.path.join(os.getcwd(),filename))

i.e.:

  normpath(os.path.joinpath('/','image01.tif'))

which becomes '/image01.tif'.

>Anyway, wouldn’t this be an absolute path via the location of the CWD?

That's not a meaningful term to me.

An absolute path begins with a slash and doesn't depend on the cwd to 
locate the filesystem object.

A relative path does not begin with a slash, and uses the cwd as the 
starting point to locate the filesystem object.

I suspect that you need to resolve two issues:

1: What is the working directory in which image01.tif actually resides?  

It looks like that is _not_ the getcwd() which Blender gets, because it 
is a GUI app whose cwd is not your work area. That will cause _all_ 
relative paths to resolve incorrectly from your pointof view.

2: Are you getting a path for image01.tif from Blender using its special 
relative path notation, the leading '//'?

Which is alluded to here:

  https://docs.blender.org/manual/en/latest/files/blend/save_load.html

If you're working with such a path, you want to transform this into a 
path to the directory from this the "Blender relative" path was 
obtained, such as the directory used in some file chooser dialogue box. 

So you might want to write an unBlenderiser (untested example):

  from os.path import isabs, join as joinpath

  def unblenderise(filename, context_dir=None):
    # transmute Blender "relative" path
    if filename.startswith('//'):
      filename = filename[2:]
    if not isabs(filename) and context_dir is not None:
      # attach the filename to `context_dir` if supplied
      filename = joinpath(context_dir, filename)
    return filename

The idea here is to get back a meaningful UNIX path from a Blender path.  
It first strips a leading '//'. Next, _if_ the filename is relative 
_and_ you supplied the optional context_dir (eg the directory used for a 
file brwoser dialogue box), then it prepends that context directory.

This _does not_ call abspath or realpath or anything, it just returns a 
filename which can be used with them.

The idea is that if you know where image01.tif lives, you could supply 
that as the context directory.

3: We could grip it by the husk.

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



More information about the Python-list mailing list