# How to reset stage memory between tests

**URL:** <https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570>\
**Category:** USD\
**Tags:** python\
**Created:** [May 31, 2024, 6:37am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570 "2024-05-31T06:37:37Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![pawel-fin](https://avatars.discourse-cdn.com/v4/letter/p/51bf81/32.png) [@pawel-fin](https://forum.aousd.org/u/pawel-fin)\
**Post date:** [May 31, 2024, 6:37am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/1 "2024-05-31T06:37:37Z")

</div>

Hello,  
I’m trying to find a way to reset USD memory cache between tests so each test gets a clean slate. currently, if two tests try to make a stage with the same name then one of them fails.

```auto
from pxr import Usd, UsdGeom, UsdShade, Sdf, UsdUtils

usd_path = "c:/temp/test123213.usd"

#one test can create a temp stage:
stage = Usd.Stage.CreateNew(usd_path)

#then another one may create the same one and fail
stage = Usd.Stage.CreateNew(usd_path)

```

I tried:

```auto
UsdUtils.StageCache.Get().Clear()

```

but it didn’t fix it.

the problem gets very annoying the moment there is a shared fixture that generates a new stage as it has to be set to session level otherwise it fails.

```auto
	Error in 'pxrInternal_v0_22 __pxrReserved__ ::SdfLayer::_CreateNew' at line 494 in file C:\b\w\ca6c508eae419cf8\USD\pxr\usd\sdf\layer.cpp : 'A layer already exists with identifier 'c:/temp/test123213.usd''

```

some obvious hacks are to generate unique names for each test or use session-level fixtures. However, this doesn’t give me any confidence in the tests as I have no idea what else might be shared between them. Ideally, I would like to call reser/clean to start fresh in each test.

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [May 31, 2024, 8:12am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/2 "2024-05-31T08:12:58Z")

</div>

This is due to the fact that the first handle is still in memory in a way.

```python
from pxr import Usd

usd_path = "c:/temp/test123213.usd"

stage = Usd.Stage.CreateNew(usd_path)
del stage
stage = Usd.Stage.CreateNew(usd_path)

```

This works for me. It seems similarly done in [USD tests](https://github.com/PixarAnimationStudios/OpenUSD/blob/2864f3d04f396432f22ec5d6928fc37d34bb4c90/pxr/usd/usd/testenv/testUsdCreateProperties.py#L509-L514).

Note that in Python the variable `stage` is still the old instance, because Python first evaluates the expression on the right hand side `Usd.Stage.CreateNew(usd_path)` and then goes to assigning it as new value to `stage`. Up to that point, the old stage is still in memory. It seems just assigning `stage = None` in-between didn’t fix it, but it’s good to know that your example code may have suffered from that.

I initially thought that this may have worked as well:

```python
stage = Usd.Stage.CreateNew(usd_path)
stage = None
stage = Usd.Stage.CreateNew(usd_path)

```

But it didn’t.

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [May 31, 2024, 5:54pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/3 "2024-05-31T17:54:19Z")

</div>

Alternatively, you could use `stage.CreateInMemory()` as many times as you like, and either `Export()` or `GetRootLayer().Export()` if/when it is required to serialize the root layer contents (or the flattened stage, in the first case) to filesystem/storage.

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [May 31, 2024, 5:55pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/4 "2024-05-31T17:55:27Z")

</div>

ahh… actually you’d need a unique identifier for each, still. Sorry for the noise…

---

<div class="post-metadata">

**Author:** ![pawel-fin](https://avatars.discourse-cdn.com/v4/letter/p/51bf81/32.png) [@pawel-fin](https://forum.aousd.org/u/pawel-fin)\
**Post date:** [June 2, 2024, 11:30pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/5 "2024-06-02T23:30:13Z")

</div>

My example was heavily simplified. The `CreateNew` might be called in totally different files in unrelated tests.  
if I had access to stage variable I could do this:  
`UsdUtils.StageCache.Get().Erase(stage)`  
to remove it from the cache. My problem is that I have access to it.

Let’s imagine a scenario like this:

```auto
def test1():
    usd_path = "c:/temp/test123213.usd"
    stage = Usd.Stage.CreateNew(usd_path)
    assert False
    UsdUtils.StageCache.Get().Erase(stage)

def test2():
    UsdUtils.StageCache.Get().Clear() #this is what I would like to do
    usd_path = "c:/temp/test123213.usd"
    stage = Usd.Stage.CreateNew(usd_path)
    assert True

```

The first test fails leaving a garbage stage in memory which affects the second one.  
What I was after was a way to clear everything blindly before running a test not knowing what stages might have been created.  
Again, this is a simplified example of a real case. According to the docs this should work:  
`UsdUtils.StageCache.Get().Clear()`  
especially that:  
`UsdUtils.StageCache.Get().Erase(stage)`  
does work. Maybe I’m not using it correctly? There must be a way to have a clean slate for each test to make sure they are properly isolated.

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [June 3, 2024, 5:31am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/6 "2024-06-03T05:31:08Z")

</div>

If every test is supposed to be independent, can I ask why you’re even using a UsdStageCache?

---

<div class="post-metadata">

**Author:** ![pawel-fin](https://avatars.discourse-cdn.com/v4/letter/p/51bf81/32.png) [@pawel-fin](https://forum.aousd.org/u/pawel-fin)\
**Post date:** [June 3, 2024, 8:50am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/7 "2024-06-03T08:50:01Z")

</div>

because I do not know how to disable it. is there a way to not use it?

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [June 3, 2024, 10:01am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/8 "2024-06-03T10:01:24Z")

</div>

You could try and find the `Sdf.Layer` with `Sdf.Layer.Find()` and then do the `del stage` trick.

This works fine for example:

```python
from pxr import Usd, Sdf

usd_path = "c:/temp/test123213.usd"

layer = Sdf.Layer.Find(usd_path)
if layer:
    stage = Usd.Stage.Open(layer)
    del stage

stage = Usd.Stage.CreateNew(usd_path)

```

This means that potentially you could do this:

```python
def close_layer_stage(identifier):
    layer = Sdf.Layer.Find(usd_path)
    if layer:
        stage = Usd.Stage.Open(layer)
        del stage

```

However, it’s weird that it forces you to open the stage to do this though.

---

<div class="post-metadata">

**Author:** ![nvmkuruc](https://avatars.discourse-cdn.com/v4/letter/n/76d3ee/32.png) [@nvmkuruc](https://forum.aousd.org/u/nvmkuruc)\
**Post date:** [June 3, 2024, 11:45am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/9 "2024-06-03T11:45:35Z")

</div>

> [@pawel-fin](#):
>
> because I do not know how to disable it. is there a way to not use it?

Reading through this thread, I wondering if there might be some confusion around stage caches and the layer registry.

Stage caches are optional, you must opt into using them. The layer registry is not. It’s a global singleton and all stages share layers in the registry. I’m wondering if the behavior your seeing isn’t related to stage caches, but rather the shared layer registry.

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [June 3, 2024, 2:56pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/10 "2024-06-03T14:56:33Z")

</div>

> Reading through this thread, I wondering if there might be some confusion around stage caches and the layer registry.

It might very well be - but do you know then what API can be used to explicitly purge a particular layer from the shared layer registry?

Like a `Sdf.Layer.Close(identifier)` or similar?

Or maybe there are reasons that’s impossible and the intended approach to this is to actually reuse the layer if it exists in memory already? Along the lines of:

```python
def create_stage(identifier):
    layer = Sdf.Layer.Find(identifier)
    if layer:
        # open existing layer 
        # todo: potentially you may want to `clear` the layer here too?
        return Usd.Stage.Open(layer)
    else:
        return Usd.Stage.CreateNew(identifier)

```

---

<div class="post-metadata">

**Author:** ![nvmkuruc](https://avatars.discourse-cdn.com/v4/letter/n/76d3ee/32.png) [@nvmkuruc](https://forum.aousd.org/u/nvmkuruc)\
**Post date:** [June 3, 2024, 3:24pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/11 "2024-06-03T15:24:15Z")

</div>

There is no way to manually release a layer from the registry if any object is holding onto it (via a SdfLayerRefPtr).

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [June 3, 2024, 3:43pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/12 "2024-06-03T15:43:04Z")

</div>

> There is no way to manually release a layer from the registry if any object is holding onto it (via a SdfLayerRefPtr).

But if one were to do this:

```python
stage = Usd.Stage.CreateNew(usd_path)
stage = None
stage = Usd.Stage.CreateNew(usd_path)

```

Then technically - what handle to that layer would still exist? Or what ways do I have to then explicitly flush out that layer’s identifier if I’m sure there is not existing reference to it? Is there any way force garbage collect it?

It currently sounds like even if there are _no_ handles to it there’s no API to flush it?

---

<div class="post-metadata">

**Author:** ![nvmkuruc](https://avatars.discourse-cdn.com/v4/letter/n/76d3ee/32.png) [@nvmkuruc](https://forum.aousd.org/u/nvmkuruc)\
**Post date:** [June 3, 2024, 4:02pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/13 "2024-06-03T16:02:01Z")

</div>

The stickiness of the layer registry is something I’ve observed before and don’t have a great answer as to what’s going on.

I know that OpenUSD will both try to release the GIL and allows some objects to be destroyed asynchronously ( [Universal Scene Description: pxr/base/work/utils.h File Reference (openusd.org)](https://openusd.org/dev/api/base_2work_2utils_8h.html#acb70f288568c34e650e334ace9d22d75)). It seems possible that either by design or by accident that one could reacquire the layer in the registry before it’s been fully cleaned up. I have not verified this is the cause. Just observing that there’s some complexity in deleting a stage.

@spiff documents some of the complexities with yielding more control to the user for the layer registry here: [usd: add a UsdSchemaRegistry function for identifying schematics layers by mattyjams · Pull Request #2688 · PixarAnimationStudios/OpenUSD (github.com)](https://github.com/PixarAnimationStudios/OpenUSD/pull/2688#issuecomment-2125455495)

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [June 3, 2024, 11:11pm UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/14 "2024-06-03T23:11:02Z")

</div>

We are somewhat confused 🙂

The code given above:

> [@BigRoyNL](#):
>
> ```auto
> stage = Usd.Stage.CreateNew(usd_path)
> stage = None
> stage = Usd.Stage.CreateNew(usd_path)
> 
> ```

works smashingly and without error for us in any python shell, and more generally, in cpython (which boost::python relies on, and therefore OpenUSD) should always gc the stage upon `stage = None`, and when the stage destructs, the layer will also be destroyed and removed from the layer registry (the file associated with it will remain on the filesystem, if using the ArDefaultResolver, but the next `CreateNew()` will happily overwrite it.

It seems clear, though, that folks are having issues, adn we’d like to help. Could someone provide us with a minimal, complete repro that demonstrates stage/layer lifetime problems?

Thank you!

---

<div class="post-metadata">

**Author:** ![pawel-fin](https://avatars.discourse-cdn.com/v4/letter/p/51bf81/32.png) [@pawel-fin](https://forum.aousd.org/u/pawel-fin)\
**Post date:** [June 4, 2024, 1:55am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/15 "2024-06-04T01:55:23Z")

</div>

I think my first oversimplified example is what created the most confusion. My apology. I also didn’t know there were two different caches, one for stages and one for layers. It seems that my problem is indeed related to the layer cache.

this is a simplified scenario that breaks for me in the second test:

```auto
from pxr import Usd, UsdUtils

class Test_usd():
    def test_test1(self):
        usd_path = "c:/temp/test123213.usd"
        stage = Usd.Stage.CreateNew(usd_path)
        assert False

    def test_test2(self):
        UsdUtils.StageCache.Get().Clear() # this is what I would like to do
        usd_path = "c:/temp/test123213.usd"
        stage = Usd.Stage.CreateNew(usd_path)
        assert True

```

when the first test fails the stage is not released and makes the second test fail too. When the first test passes then the stage seems to be cleaned correctly and the second test executes fine. In my case running pytest in python 3.10 results with this unexpected error:

```auto
self = <scratch.Test_usd object at 0x000001FCBA299690>

    def test_test2(self):
        UsdUtils.StageCache.Get().Clear() # this is what I would like to do
        usd_path = "c:/temp/test123213.usd"
> stage = Usd.Stage.CreateNew(usd_path)
E pxr.Tf.ErrorException: 
E Error in 'pxrInternal_v0_22 __pxrReserved__ ::SdfLayer::_CreateNew' at line 612 in file S:\jenkins\workspace\ECP\ecg-usd-build\ecg-usd-full-python3.10-windows\ecg-usd-build\usd\pxr\usd\sdf\layer.cpp : 'A layer already exists with identifier 'c:/temp/test123213.usd''

scratch.py:14: ErrorException

```

@BigRoyNL trick is neat but why do we need tricks like this in the first place?

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [June 4, 2024, 4:53am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/16 "2024-06-04T04:53:54Z")

</div>

We are wondering if pytest or another layer of your infrastructure might be hanging on to the exceptions raised by your tests? The backtrace associated with it would keep the raising frame alive, and with it all its locals, including the stage.

If that’s so, and you can’t change the policy, then unfortunately it would be on you to reset/del (should be equivalent) the stage before asserting.

---

<div class="post-metadata">

**Author:** ![BigRoyNL](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigroynl/32/413_2.png) [@BigRoyNL](https://forum.aousd.org/u/BigRoyNL)\
**Post date:** [June 4, 2024, 4:57am UTC](https://forum.aousd.org/t/how-to-reset-stage-memory-between-tests/1570/17 "2024-06-04T04:57:38Z")

</div>

You know what. I think I know. When testing this I quickluy tested this inside Maya with Maya USD. However, I believe that internally uses its own usd stage cache and it might be related to that?

However, that may make it extra weird that `del stage` did work?
