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 <em...@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 <c...@cskk.id.au>


--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to