Skip to content

Commit

Permalink
6-Add exposing all paths as an opt-in feature in the Cached and File …
Browse files Browse the repository at this point in the history
…Resolver.
  • Loading branch information
LucaScheller committed Mar 24, 2024
1 parent d740dfe commit 75c9fcc
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 37 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ set(AR_FILERESOLVER_USD_PYTHON_MODULE_FULLNAME ${AR_RESOLVER_USD_PYTHON_MODULE_N
set(AR_FILERESOLVER_TARGET_LIB fileResolver)
set(AR_FILERESOLVER_TARGET_PYTHON _${AR_FILERESOLVER_TARGET_LIB})
set(AR_FILERESOLVER_INSTALL_PREFIX ${AR_PROJECT_NAME}/${AR_FILERESOLVER_USD_PLUGIN_NAME})
set(AR_FILERESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS "AR_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS" CACHE STRING "Environment variable that controls if absolute path identifiers should be exposed to mapping.")

# Python Resolver
option(AR_PYTHONRESOLVER_BUILD "Build the PythonResolver" OFF)
if("$ENV{AR_RESOLVER_NAME}" STREQUAL "pythonResolver")
Expand All @@ -52,6 +54,7 @@ set(AR_CACHEDRESOLVER_USD_PYTHON_EXPOSE_MODULE_NAME PythonExpose)
set(AR_CACHEDRESOLVER_TARGET_LIB cachedResolver)
set(AR_CACHEDRESOLVER_TARGET_PYTHON _${AR_CACHEDRESOLVER_TARGET_LIB})
set(AR_CACHEDRESOLVER_INSTALL_PREFIX ${AR_PROJECT_NAME}/${AR_CACHEDRESOLVER_USD_PLUGIN_NAME})
set(AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS "AR_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS" CACHE STRING "Environment variable that controls if absolute path identifiers should be Python exposed.")
set(AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS "AR_EXPOSE_RELATIVE_PATH_IDENTIFIERS" CACHE STRING "Environment variable that controls if relative path identifiers should be Python exposed.")

# Http Resolver
Expand Down
12 changes: 11 additions & 1 deletion docs/src/resolvers/CachedResolver/PythonAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,29 @@ As with our mapping and caching pairs, the result is cached in C++ to enable fas

As identifiers are context independent, the cache is stored on the resolver itself. See below on how to modify and inspect the cache.

We also have the option to expose any identifier, regardless of absolute/relative/search path based formatting to our `PythonExpose.py` -> `ResolverContext.ResolveAndCache` method by setting the `AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeAbsolutePathIdentifierState(True)`. As this then runs all paths through the Python exposed section, make sure that paths are batch added/pre-cached as much as possible to keep the resolve efficient.

```python
from pxr import Ar, Usd
from usdAssetResolver import CachedResolver

# Enable relative identifier modification
cached_resolver = Ar.GetUnderlyingResolver()
# Enable relative identifier modification
cached_resolver.SetExposeRelativePathIdentifierState(True)
print("Resolver is currently exposing relative path identifiers to Python | {}".format(cached_resolver.GetExposeRelativePathIdentifierState()))
# Or set the "AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS" environment variable to 1.
# This can't be done via Python, as it has to happen before the resolver is loaded.
cached_resolver.GetExposeRelativePathIdentifierState() # Get the state of exposing relative path identifiers
cached_resolver.SetExposeRelativePathIdentifierState() # Set the state of exposing relative path identifiers

# Enable absolute identifier resolving
cached_resolver.SetExposeAbsolutePathIdentifierState(True)
print("Resolver is currently exposing absolute path identifiers to Python | {}".format(cached_resolver.GetExposeAbsolutePathIdentifierState()))
# Or set the "AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS" environment variable to 1.
# This can't be done via Python, as it has to happen before the resolver is loaded.
cached_resolver.GetExposeAbsolutePathIdentifierState() # Get the state of exposing absolute path identifiers
cached_resolver.SetExposeAbsolutePathIdentifierState() # Set the state of exposing absolute path identifiers

# We can also inspect and edit our relative identifiers via the following methods on the resolver.
# You'll usually only want to use these outside of the Resolver.CreateRelativePathIdentifier method only for debugging.
# The identifier cache is not context dependent (as identifiers are not).
Expand Down
7 changes: 6 additions & 1 deletion docs/src/resolvers/CachedResolver/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ By default (similar to the FileResolver and USD's default resolver), any absolut
Optionally you can opt-in into also exposing relative identifiers to Python by setting the `AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeRelativePathIdentifierState(True)`. This is a more advanced feature and is therefore disabled by default. See our [production example](example.md) section for more information on how to use this and why it can be useful.
```

All non file path identifiers (anything that doesn't start with "/"/"./"/"../") will forward their request to the `PythonExpose.py` -> `ResolverContext.ResolveAndCache` method.
All non file path identifiers (anything that doesn't start with "/", "./", "../") will forward their request to the `PythonExpose.py` -> `ResolverContext.ResolveAndCache` method.
If you want to customize this resolver, just edit the methods in PythonExpose.py to fit your needs. You can either edit the file directly or move it anywhere where your "PYTHONPATH"/"sys.path" paths look for Python modules.

```admonish tip title="Pro Tip"
Optionally you can opt-in into also exposing absolute identifiers (so all (absolute/relative/identifiers that don't start with "/","./","../") identifiers) to Python by setting the `AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeAbsolutePathIdentifierState(True)`. This enforces all identifiers to run through Python. Use this with care, we recommend only using this for debugging or when having a large dataset of pre-cached mapping pairs easily available.
```

We also recommend checking out our unit tests of the resolver to see how to interact with it. You can find them in the "<Repo Root>/src/CachedResolver/testenv" folder or on [GitHub](https://github.com/LucaScheller/VFX-UsdAssetResolver/blob/main/src/CachedResolver/testenv/testCachedResolver.py).

Here is a full list of features:
Expand All @@ -22,6 +26,7 @@ Here is a full list of features:
- On context creation via the `PythonExpose.py` -> `ResolverContext.Initialize` method. This gets called whenever a context gets created (including the fallback default context). For example Houdini creates the default context if you didn't specify a "Resolver Context Asset Path" in your stage on the active node/in the stage network. If you do specify one, then a new context gets spawned that does the above mentioned mapping pair lookup and then runs the `PythonExpose.py` -> `ResolverContext.Initialize` method.
- On resolve for non file path identifiers (anything that doesn't start with "/"/"./"/"../") via the `PythonExpose.py` -> `ResolverContext.ResolveAndCache` method. Here you are free to only add the active asset path via `ctx.AddCachingPair(asset_path, resolved_asset_path)` or any number of relevant asset paths.
- We optionally also support hooking into relative path identifier creation via Python. This can be enabled by setting the `AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeRelativePathIdentifierState(True)`. We then have access in our `PythonExpose.py` -> `Resolver.CreateRelativePathIdentifier` method. Here we can then return a non file path (anything that doesn't start with "/"/"./"/"../") identifier for our relative path, which then also gets passed to our `PythonExpose.py` -> `ResolverContext.ResolveAndCache` method. This allows us to also redirect relative paths to our liking for example when implementing special pinning/mapping behaviours. For more info check out our [production example](./example.md) section. As with our mapping and caching pairs, the result is cached in C++ to enable faster lookups on consecutive calls. As identifiers are context independent, the cache is stored on the resolver itself. See our [Python API](./PythonAPI.md) section on how to clear the cache.
- We optionally also support exposing alle path identifiers to our `ResolverContext.ResolveAndCache` Python method. This can be enabled by setting the `AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeAbsolutePathIdentifierState(True)`. This then forwards any path to be run through our Python exposed method, regardless of how the identifier is formatted. Use this with care, we recommend only using this for debugging or when having a large dataset of pre-cached mapping pairs easily available.
- In comparison to our [FileResolver](../FileResolver/overview.md) and [PythonResolver](../PythonResolver/overview.md), the mapping/caching pair values need to point to the absolute disk path (instead of using a search path). We chose to make this behavior different, because in the "PythonExpose.py" you can directly customize the "final" on-disk path to your liking.
- The resolver contexts are cached globally, so that DCCs, that try to spawn a new context based on the same mapping file using the [```Resolver.CreateDefaultContextForAsset```](https://openusd.org/dev/api/class_ar_resolver.html), will re-use the same cached resolver context. The resolver context cache key is currently the mapping file path. This may be subject to change, as a hash might be a good alternative, as it could also cover non file based edits via the exposed Python resolver API.
- ```Resolver.CreateContextFromString```/```Resolver.CreateContextFromStrings``` is not implemented due to many DCCs not making use of it yet. As we expose the ability to edit the context at runtime, this is also often not necessary. If needed please create a request by submitting an issue here: [Create New Issue](https://github.com/LucaScheller/VFX-UsdAssetResolver/issues/new)
Expand Down
18 changes: 18 additions & 0 deletions docs/src/resolvers/FileResolver/PythonAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ Tokens can be found in FileResolver.Tokens:
```python
FileResolver.Tokens.mappingPairs
```
## Resolver
We also have the opt-in feature to expose any identifier, regardless of absolute/relative/search path based formatting to be run through our mapped pairs mapping by setting the `AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeAbsolutePathIdentifierState(True)`. This then forwards any path to be run through our mapped pairs mapping, regardless of how the identifier is formatted.

```python
from pxr import Ar, Usd
from usdAssetResolver import FileResolver

file_resolver = Ar.GetUnderlyingResolver()

# Enable absolute identifier resolving
file_resolver.SetExposeAbsolutePathIdentifierState(True)
print("Resolver is currently exposing absolute path identifiers to the mapping pairs | {}".format(file_resolver.GetExposeAbsolutePathIdentifierState()))
# Or set the "AR_FILERESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS" environment variable to 1.
# This can't be done via Python, as it has to happen before the resolver is loaded.
file_resolver.GetExposeAbsolutePathIdentifierState() # Get the state of exposing absolute path identifiers
file_resolver.SetExposeAbsolutePathIdentifierState() # Set the state of exposing absolute path identifiers
```

## Resolver Context
You can manipulate the resolver context (the object that holds the configuration the resolver uses to resolve paths) via Python in the following ways:

Expand Down
6 changes: 6 additions & 0 deletions docs/src/resolvers/FileResolver/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
This resolver is a file system based resolver similar to the default resolver with support for custom mapping pairs.
{{#include ../shared_features.md:resolverSharedFeatures}}
- You can adjust the resolver context content during runtime via exposed Python methods (More info [here](./PythonAPI.md)). Refreshing the stage is also supported, although it might be required to trigger additional reloads in certain DCCs.
- We optionally also support exposing alle path identifiers to our `ResolverContext.ResolveAndCache` Python method. This can be enabled by setting the `AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeAbsolutePathIdentifierState(True)`. This then forwards any path to be run through our mapped pairs mapping, regardless of how the identifier is formatted.

```admonish tip title="Pro Tip"
Optionally you can opt-in into also exposing absolute identifiers (so all (absolute/relative/identifiers that don't start with "/","./","../") identifiers) to our mapping pair mechanism by setting the `AR_FILERESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS` environment variable to `1` or by calling `pxr.Ar.GetUnderlyingResolver().SetExposeAbsolutePathIdentifierState(True)`. This enforces all identifiers to run through our mapped pairs mapping. The mapped result can also be a search path based path, which then uses the search paths to resolve itself. (So mapping from an absolute to search path based path via the mapping pairs is possible.)
```

{{#include ../shared_features.md:resolverEnvConfiguration}}


## Debug Codes
Adding following tokens to the `TF_DEBUG` env variable will log resolver information about resolution/the context respectively.
* `FILERESOLVER_RESOLVER`
Expand Down
5 changes: 4 additions & 1 deletion src/CachedResolver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ target_compile_definitions(${AR_CACHEDRESOLVER_TARGET_LIB}
AR_CACHEDRESOLVER_USD_PLUGIN_NAME=${AR_CACHEDRESOLVER_USD_PLUGIN_NAME}
AR_CACHEDRESOLVER_USD_PYTHON_MODULE_FULLNAME=${AR_CACHEDRESOLVER_USD_PYTHON_MODULE_FULLNAME}
AR_CACHEDRESOLVER_USD_PYTHON_EXPOSE_MODULE_NAME=${AR_CACHEDRESOLVER_USD_PYTHON_EXPOSE_MODULE_NAME}

AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS=${AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS}
AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS=${AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS}
)
# Install
configure_file(plugInfo.json.in plugInfo.json)
Expand Down Expand Up @@ -104,6 +105,8 @@ target_compile_definitions(${AR_CACHEDRESOLVER_TARGET_PYTHON}
AR_CACHEDRESOLVER_USD_PLUGIN_NAME=${AR_CACHEDRESOLVER_USD_PLUGIN_NAME}
AR_CACHEDRESOLVER_USD_PYTHON_MODULE_FULLNAME=${AR_CACHEDRESOLVER_USD_PYTHON_MODULE_FULLNAME}
AR_CACHEDRESOLVER_USD_PYTHON_EXPOSE_MODULE_NAME=${AR_CACHEDRESOLVER_USD_PYTHON_EXPOSE_MODULE_NAME}
AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS=${AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS}
AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS=${AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS}
)
# Install
install (
Expand Down
5 changes: 5 additions & 0 deletions src/CachedResolver/resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "pxr/base/arch/systemInfo.h"
#include "pxr/base/tf/fileUtils.h"
#include "pxr/base/tf/getenv.h"
#include "pxr/base/tf/pathUtils.h"
#include "pxr/base/tf/pyInvoke.h"
#include "pxr/base/tf/staticTokens.h"
Expand Down Expand Up @@ -86,6 +87,7 @@ _ResolveAnchored(
}

CachedResolver::CachedResolver() {
this->SetExposeAbsolutePathIdentifierState(TfGetenvBool(DEFINE_STRING(AR_CACHEDRESOLVER_ENV_EXPOSE_ABSOLUTE_PATH_IDENTIFIERS), false));
this->SetExposeRelativePathIdentifierState(TfGetenvBool(DEFINE_STRING(AR_CACHEDRESOLVER_ENV_EXPOSE_RELATIVE_PATH_IDENTIFIERS), false));
};

Expand Down Expand Up @@ -327,6 +329,9 @@ CachedResolver::_IsContextDependentPath(
const std::string& assetPath) const
{
TF_DEBUG(CACHEDRESOLVER_RESOLVER_CONTEXT).Msg("Resolver::_IsContextDependentPath()\n");
if (this->exposeAbsolutePathIdentifierState){
return true;
}
return _IsNotFilePath(assetPath);
}

Expand Down
11 changes: 9 additions & 2 deletions src/CachedResolver/resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "resolverContext.h"

#include "pxr/pxr.h"
#include "pxr/base/tf/getenv.h"
#include "pxr/usd/ar/resolver.h"

#include <memory>
Expand All @@ -31,6 +30,14 @@ class CachedResolver final : public ArResolver
AR_CACHEDRESOLVER_API
virtual ~CachedResolver();

AR_CACHEDRESOLVER_API
bool GetExposeAbsolutePathIdentifierState(){ return this->exposeAbsolutePathIdentifierState; }
AR_CACHEDRESOLVER_API
void SetExposeAbsolutePathIdentifierState(const bool state){
if (state != this->exposeAbsolutePathIdentifierState){
this->exposeAbsolutePathIdentifierState = state;
}
}
AR_CACHEDRESOLVER_API
bool GetExposeRelativePathIdentifierState(){ return this->exposeRelativePathIdentifierState; }
AR_CACHEDRESOLVER_API
Expand All @@ -40,7 +47,6 @@ class CachedResolver final : public ArResolver
this->ClearCachedRelativePathIdentifierPairs();
}
}

AR_CACHEDRESOLVER_API
void AddCachedRelativePathIdentifierPair(const std::string& sourceStr, const std::string& targetStr);
AR_CACHEDRESOLVER_API
Expand Down Expand Up @@ -93,6 +99,7 @@ class CachedResolver final : public ArResolver
const CachedResolverContext* _GetCurrentContextPtr() const;
CachedResolverContext _fallbackContext;
const std::string emptyString{""};
bool exposeAbsolutePathIdentifierState{false};
bool exposeRelativePathIdentifierState{false};
std::map<std::string, std::string> cachedRelativePathIdentifierPairs;
};
Expand Down
36 changes: 36 additions & 0 deletions src/CachedResolver/testenv/testCachedResolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,42 @@ def test_Resolve(self):
resolved_path = resolver.Resolve(layer_file_path)
self.assertEqual(resolved_path.GetPathString(), layer_file_path)

def test_ResolveAbsoluteIdentifier(self):
with tempfile.TemporaryDirectory() as temp_dir_path:
# Get resolver
resolver = Ar.GetResolver()
cached_resolver = Ar.GetUnderlyingResolver()
# Create files
layer_a_identifier = "layer_a.usd"
layer_a_file_path = os.path.join(temp_dir_path, layer_a_identifier)
Sdf.Layer.CreateAnonymous().Export(layer_a_file_path)
layer_b_identifier = "layer_b.usd"
layer_b_file_path = os.path.join(temp_dir_path, layer_b_identifier)
Sdf.Layer.CreateAnonymous().Export(layer_b_file_path)
# Create context
ctx = CachedResolver.ResolverContext()
ctx.AddCachingPair(layer_a_identifier, layer_a_file_path)
ctx.AddCachingPair(layer_b_identifier, layer_b_file_path)
ctx.AddCachingPair(layer_b_file_path, layer_a_file_path)
# Reset UnitTestHelper
PythonExpose.UnitTestHelper.reset(current_directory_path=temp_dir_path)
with Ar.ResolverContextBinder(ctx):
resolved_path = resolver.Resolve(layer_a_identifier)
self.assertEqual(resolved_path.GetPathString(), layer_a_file_path)
self.assertTrue(os.path.isabs(resolved_path.GetPathString()))
resolved_path = resolver.Resolve(layer_b_identifier)
self.assertEqual(resolved_path.GetPathString(), layer_b_file_path)
self.assertTrue(os.path.isabs(resolved_path.GetPathString()))
# Check that exposed absolute file path resolving works
resolved_path = resolver.Resolve(layer_b_file_path)
self.assertEqual(resolved_path.GetPathString(), layer_b_file_path)
self.assertTrue(os.path.isabs(resolved_path.GetPathString()))
cached_resolver.SetExposeAbsolutePathIdentifierState(True)
resolved_path = resolver.Resolve(layer_b_file_path)
self.assertEqual(resolved_path.GetPathString(), layer_a_file_path)
self.assertTrue(os.path.isabs(resolved_path.GetPathString()))
cached_resolver.SetExposeAbsolutePathIdentifierState(False)

def test_ResolveForNewAsset(self):
resolver = Ar.GetResolver()

Expand Down
Loading

0 comments on commit 75c9fcc

Please sign in to comment.