absolute path to a file

Paul St George email at paulstgeorge.com
Mon Aug 19 02:52:24 EDT 2019


On 19/08/2019 01:31, Cameron Simpson wrote:
> 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?

Aha. The Blender file that I am using to test this on has two images 
used as textures. They reside at:
/Users/Lion/Desktop/test8/image01.tif
/Users/Lion/Desktop/images/image02.tif

The Blender file is at /Users/Lion/Desktop/test8/tifftest8.blend

> 
>>>>> 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?

I ran this from Blender's console:
 >>>filename = "/Users/Lion/Desktop/test8/readout.py"
 >>>exec(compile(open(filename).read(), filename, 'exec'))

I ran os.getcwd() from both within /Users/Lion/Desktop/test8/readout.py
and directly within the Blender 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.
Yes! Until Peter Otten's timely intervention, I was trying to do this 
and had managed to join the 
path_to_the_folder_that_contains_the_Blender_file to 
the_name_of_the_image (stripped of its preceding slashes).

Your unblenderise looks much better than my nascent saneblender so thank 
you. I will explore more!

Does it depend on knowing where image01.tif lives and manually supplying 
that? I had been trying to use os.path.dirname() but of course only 
getting '//' so getting back to where I had started.



> 
> 3: We could grip it by the husk.
> 
> Cheers,
> Cameron Simpson <cs at cskk.id.au>





More information about the Python-list mailing list