How to determine whether a node_tree is a material or a node group? I'd like to know whether a given node_tree is either a material or node group as dynamically as possible. The following function works somehow but I don't want to specify the any additional argument for a fundamental reason:
def my_function(node_tree):
    #Incomplete example:
    #if node_tree is from material:
        print(&quot;It is a node_tree of a material&quot;)
    #elif node_tree is node_groups:
        print(&quot;It is a node_tree of a node_groups&quot;)

# Use case 1:
mat = C.object.active_material
my_function(mat.node_tree) # Returns a node_tree

# Use case 2:
group = D.node_groups[&quot;My Groups&quot;] # Returns a node_tree
my_function(groups)

node_tree.id_data returns a node_tree in all my tests so I guess you can use something like isinstance() in conjunction with id_data? Is there any way to test the actual type like isinstance(node_tree, bpy.types.???) ?
How it represents itself.
In as much as it aint pretty have had to rely on this before, may be for same can't remember.  Noticed in console that the representation  &quot;knows&quot;. (echoing in the console uses repr(ob) printing uses str(ob))
&gt;&gt;&gt; D.materials['Material'].node_tree
bpy.data.materials['Material'].node_tree

&gt;&gt;&gt; D.node_groups['Foo']
bpy.data.node_groups['Foo']

as a string is also of little use
&gt;&gt;&gt; print(D.materials['Material'].node_tree)
&lt;bpy_struct, ShaderNodeTree(&quot;Shader Nodetree&quot;) at 0x7f6bd0a86e08&gt;

whereas looking at types et al kept coming up as same.  Hence can use this in your method
&gt;&gt;&gt; def from_material(nodetree):
...     return repr(nodetree).startswith(&quot;bpy.data.materials&quot;)
...     
&gt;&gt;&gt; from_material(D.materials['Material'].node_tree)
True

&gt;&gt;&gt; from_material(D.node_groups['Foo'])
False

alternatively endswith(&quot;node_tree&quot;) is possibly a quicker option.
Extra,

Nice solution. But so how can I get that material into a variable in
this function?

Once we know it's a material, can slice off &quot;.node_tree&quot; and evaluate it.
&gt;&gt;&gt; repr(D.materials['Material'].node_tree)[:-10]
&quot;bpy.data.materials['Material']&quot;

&gt;&gt;&gt; eval(repr(D.materials['Material'].node_tree)[:-10])
bpy.data.materials['Material']

Similarly could slice &quot;bpy.data.materials['&quot; from the start and &quot;'].node_tree&quot; from the end to just keep the name.
&gt;&gt;&gt; matname = repr(D.materials['Material'].node_tree)[20:-12]
&gt;&gt;&gt; matname
'Material'

&gt;&gt;&gt; D.materials.get(matname)
bpy.data.materials['Material']