absolute path to a file
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:
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))
>> 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).
>> 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:
> 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
> 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
> 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.
>> 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
> 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:
> 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:
> 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
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.
> Cameron Simpson <cs at cskk.id.au>