Skip to content

Anatomy: expand user and vars in root paths#1884

Open
antirotor wants to merge 10 commits into
developfrom
enhancement/YN-0773_expanded-roots
Open

Anatomy: expand user and vars in root paths#1884
antirotor wants to merge 10 commits into
developfrom
enhancement/YN-0773_expanded-roots

Conversation

@antirotor

@antirotor antirotor commented Jun 8, 2026

Copy link
Copy Markdown
Member

Changelog Description

Expand variables and user ~ in root paths.

Roots like ~/foo or $HOME/foo or {HOME}/foo should all resolve correctly. Note that this feature needs to be used with care as dynamic roots can break a lot of things if not used properly.

Additional info

This is alternative to #1882

Testing notes:

  1. Publishing to roots with ~ should work as expected
  2. Loading should work too

@ynbot ynbot added size/S type: enhancement Improvement of existing functionality or minor addition labels Jun 8, 2026
@antirotor antirotor linked an issue Jun 9, 2026 that may be closed by this pull request
@antirotor antirotor self-assigned this Jun 9, 2026
@antirotor antirotor added the sponsored This is directly sponsored by a client or community member label Jun 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends anatomy root handling so root definitions containing ~, $VAR/${VAR}, or {VAR} can be resolved correctly, and so root-template detection can match paths even when only one side (stored root vs input path) is expanded.

Changes:

  • Expand ~ and environment variables when computing the current-platform root value.
  • Enhance find_root_template_from_path to compare both original and expanded forms of both stored roots and input paths.
  • Add an isolated unit-test module covering root matching and remapping scenarios, including env-var and ~ expansion cases.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 5 comments.

File Description
client/ayon_core/pipeline/anatomy/roots.py Adds expanduser/expandvars to root value resolution and expands matching logic in find_root_template_from_path.
tests/client/ayon_core/pipeline/anatomy/test_root_item.py Adds unit tests for root resolution, matching, and remapping with expanded/unexpanded inputs.
tests/client/ayon_core/pipeline/anatomy/__init__.py Adds package marker for the new test module location.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread client/ayon_core/pipeline/anatomy/roots.py
Comment thread client/ayon_core/pipeline/anatomy/roots.py Outdated
Comment thread tests/client/ayon_core/pipeline/anatomy/test_root_item.py Outdated
Comment thread tests/client/ayon_core/pipeline/anatomy/test_root_item.py Outdated
Comment thread tests/client/ayon_core/pipeline/anatomy/test_root_item.py Outdated
@iLLiCiTiT

iLLiCiTiT commented Jun 23, 2026

Copy link
Copy Markdown
Member

What is the use-case of having ~ or env in anatomy roots? Allowing it might block us on implementing server side handling of paths.

@antirotor

Copy link
Copy Markdown
Member Author

What is the use-case of having ~ or env in anatomy roots? Allowing it might block us on implementing server side handling of paths.

The use case is client request to be able to use gdrive folder that is per user mounted in their respective home dirs.

@iLLiCiTiT

iLLiCiTiT commented Jun 23, 2026

Copy link
Copy Markdown
Member

The use case is client request to be able to use gdrive folder that is per user mounted in their respective home dirs.

I'd say that this is what should be used.
image

Why I'm against: This will open can of worms. Allowing to do this "easily" looks like you can safely do it, but there is a ton of caviads related to this approach that might make us look bad.

I might be wrong, summoning @BigRoy for opinion.

@antirotor

Copy link
Copy Markdown
Member Author

'd say that this is what should be used.

This is really not practical for whole studio setup. You would have to do it for every site.

@BigRoy

BigRoy commented Jun 24, 2026

Copy link
Copy Markdown
Member

Why I'm against: This will open can of worms. Allowing to do this "easily" looks like you can safely do it, but there is a ton of caviads related to this approach that might make us look bad.

I agree with this. The tricky thing is that these expanded root paths and their 'context' are stored in a variety of places, e.g. in publishes, suddenly we're passing the context as root[work] with the user location instead of the actual root of the current platform. Or even making a rootless path for something from another user's machine will not succeed (not sure if relevant to anything.)

But regarding using "site root overrides":

This is really not practical for whole studio setup. You would have to do it for every site.

This is also agree with. Which makes me think what technically differs between a site root override and this? (Except for the fact that the expanded path is set somewhere?)
The USD resolver would now also need to trigger the expandvars (assuming USD can't load directly from a path set as ~/something?


@iLLiCiTiT from a pipeline/technical perspective code-wise. How much does site specific root overrides really differ from expanding the vars?

I think this will need thorough testing, cross-platform with multiple users/home directories - and possibly even with the USD resolver resolving to a published product that uses ~?

@iLLiCiTiT

iLLiCiTiT commented Jun 25, 2026

Copy link
Copy Markdown
Member

How much does site specific root overrides really differ from expanding the vars?

At the end the important bit is that it can be resolved server side, service side, or in USD resolver.

assuming USD can't load directly from a path set as ~/something

Also ${HOME}/something or $HOME/something and %HOME%/something on windows because of expandvars and {HOME}/something because of format_map(os.environ).

@antirotor

Copy link
Copy Markdown
Member Author

While I agree that this might be dangerous and might break things - there are many more similar things around pipeline. My point is that nothig is forcing you to do it. It just gives you a possibility. If this is documented properly, it is up to the studio to go that way or not.

Linux allows you to type sudo rm -rf / and it has its own uses, but noone force you to write it.

@antirotor

Copy link
Copy Markdown
Member Author

%HOME%/something

this won't work as it is not expanded by python

@iLLiCiTiT

iLLiCiTiT commented Jun 25, 2026

Copy link
Copy Markdown
Member

this won't work as it is not expanded by python

It is. I just chose wrong env key which is not set by windows. %USERPROFILE%/something woult be expanded.

The thing is that with this is looks like you can safely use it, which is not true, you'll block yourself from some features, and it might not work all the time, and it will look like our fault.

)
except KeyError:
self.value = (
os.path.expandvars(

@iLLiCiTiT iLLiCiTiT Jun 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we gona do it, I'm against using expandvars, only {ENV} should be possible.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the reason? My argument is that using paths with $VAR inside is pretty standard.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is standard, but then you have to support it at every place which is using roots, including USD resolver, it is hard to find out if it is missing, and what exact variable it is (it does not have strictly defined end the the variable name). $MY_custom_var is valid env key, but how you find out if it should be $MY or $MY_custom...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the behavior is clearly defined. VAR = "foo":

lorem $VAR: lorem foo
lorem $VARipsum: lorem
lorem $VAR ipsum: lorem foo ipsum
lorem %VAR%: lorem foo
lorem %VAR%ipsum: lorem fooipsum

os.path.expandvars() is taking care of that in python and the resolver needs to implement {VAR} format anyway so adding $VAR and/or %VAR% is trivial at that point. But I don't really mind. @BigRoy ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't easily find out if it is missing (without validating it for each platform) and there are limitations, e.g. you can't use $VARipsum (which is missing in your example). I'm 100% against it. I know it is "known", but it is not easily detectable and easy to validate.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is there:

lorem $VARipsum: lorem

But it also supports curly bracket format that is standard too. So if you want this to work, you can:

lorem ${VAR}ipsum: lorem fooipsum

and all that is supported by os.path.expandvars()

@iLLiCiTiT iLLiCiTiT Jul 3, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unresolved expanding and you have no idea it is unresolved

print(os.path.expandvars("$NOTEXISTING"))
>>> $NOTEXISTING

This does require to be more specific, something that should be filled is not

import os
os.environ["TEST"]="value"
print(os.path.expandvars("$TEST_something"))
>>> $TEST_something

Supporting this will only bring issues. At the end it does what the python formatting does, but with less control and more headaches for us.

@ynbot ynbot moved this to Review In Progress in PR reviewing Jun 25, 2026
# Expand variables in the stored root path so we can compare it
# against an already-expanded input path (and vice-versa).
expanded_root_path = self._clean_root(
os.path.expandvars(os.path.expanduser(root_path))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Root paths should be already expanded in cleaned_data.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I get it - it is not expanded there, only for current platform. so are you suggesting that the expansion logic should be moved to ... probably _clean_path()?

# is already expanded while the caller passed an unexpanded path, or
# to normalise both sides consistently.
expanded_mod_path = self._clean_path(
os.path.expandvars(os.path.expanduser(path))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not allow to pass in not expanded path.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why? shouldn't one place take care of that intead reimplementing the logic in all places where you want to use this feature before calling find_root_template_from_path()?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you call find_root_template_from_path you should have full path, not ~/some/file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is what you get now:

ar = AnatomyRoot(
            parent=...,
            root_raw_data={"linux": "/home/user/projects"},
            name="work",
)

success, result = item.find_root_template_from_path("~/projects/shot/file.ma")

you'll get result == "{root[work]}/shot/file.ma"

it handles matching unexpanded paths to expanded roots in various forms (~, $VAR, etc.) and only if it cannot match, it will success == False.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And where you'd get the path "~/projects/shot/file.ma" from?

@antirotor

Copy link
Copy Markdown
Member Author

btw this is draft PR for USD Resolver - ynput/ayon-usd-resolver#80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S sponsored This is directly sponsored by a client or community member type: enhancement Improvement of existing functionality or minor addition

Projects

Status: Review In Progress

Development

Successfully merging this pull request may close these issues.

YN-0773: make sure variables are expanded in roots

5 participants