Skip to content

Instantly share code, notes, and snippets.

@theodox
Created July 13, 2018 02:57
Show Gist options
  • Save theodox/41fe8e138406f41216e41c8a81237af7 to your computer and use it in GitHub Desktop.
Save theodox/41fe8e138406f41216e41c8a81237af7 to your computer and use it in GitHub Desktop.
import maya.cmds as cmds
from functools import wraps
from collections import Iterable
class NamespaceError(RuntimeError):
"""raised when a namespace object does not reflect the reality of the scene"""
pass
class NamespaceNameError(ValueError):
"""raised for impossible namespace names"""
def format_ns(ns, relative=False):
if not relative:
if ns == ':' or ns == '':
return ':'
ns = (':' + ns).replace('::', ':')
valid_name = cmds.namespace(vn=ns)
if ns != valid_name:
raise NamespaceNameError("invalid namespace name '{}'".format(ns))
return ns
class namespace(Iterable):
'''
represents a maya namespace
Basic Usage:
==============
Start with the name of a namespace
example = namespace('example')
example
> namespace(':example')
By default namespaces are ABSOLUTE, which is different from Maya's normal flow. This makes it easier
to manage namespaces without constantly checking global settings. You can use relative namespaces
instead by passing 'relative=True'
relative = namespace('child', relative=True)
You can explicilty create a sub namespace using the `parent` flag
relative_to_other = namespace('child', parent = other_ns)
Or just specify a complete namespace path
namespace("path:to:namespace")
To switch to a namespace:
example.activate()
cmds.polyCube()
> 'example:pCube1'
To switch back to where you where when `activate()` was called:
example.deactivate()
If the namespace does not exist, it errors:
namespace('grjhfg').activate()
# NamespaceError: file namespace.py line 104: No namespace matches name: ':grjhfg'.
Check that a namespace exists with `if`:
ns = namespace('check_first')
if ns:
ns.activate()
To create a new namespace in the scene (this is a null-op if the namespace already exists)
new_ns = namespace('create_me').create()
To destroy a namespace: (By default, namespace contents are moved to root)
example.delete()
In the event of a name clash you can use 'force' to rename any clashing objects
that result from a deletion:
example.delete(force_rename=True)
To destroy the contents along with the namespace:
example.delete(delete_contents = True)
To move the contents of the namespace to another namespace:
example.move( ':other_namespae')
or, the new namespace can be a namespace object:
other = namespace('other')
example.move(other)
To rename a namespace (again, new name cam be a string or namespace object):
example.rename ('new_name')
example.rename (namespace('new_name'))
What's in a namespace:
=======================
the 'contents' property yields eveything in the namespace
example = namespace('example')
for item in example.contents:
print item
#for convenience, iterating the namespace is the same as '.contents'
for item in example:
print item
# the 'namespace' property yields all child namespaces inside this namespace:
for item in example.namespaces:
print item
for child in item.contents:
print ' ', child
# these properties are iterators, so you can use list(), set(), any() on them:
list(namespace('test'))
> Result: [u'|test:pCube1', u'|test:pCube1|test:pCubeShape1', u'test:polyCube1']
Namespaces and cmds
=====================
A namespace acts like a string, so you can pass it to cmds:
like_a_string = namespace('some_name')
print cmds.ls(like_a_string + ":*")
or
root = namespace('root').create()
branch = namespace('branch', parent=root).create()
cmds.namespaceInfo(branch, fullName=True)
> Result: u'root:branch'
As context manager
==================
A namespace can be a context manager -- use this to activate a namespace for some
operations and then deactivate it when done:
with namespace("use_this") as temporyary_namespace:
cmds.polyCube()
cmds.polySphere()
When the context manager exists, the namespace will revert to whatever it was before.
As a decorator
===============
A namespace can also be used to decorate a function:
@namespace('exporter')
def make_some_export_stuff():
....
this is equivalent to
with namespace('exporter'):
make_some_export_stuff()
'''
slots = '_cached', '_name'
def __init__(self, ns, parent=None, relative=False):
# default namespaces to absolute,
# user must explicitly ask for a relative one
if parent:
parent_ns = format_ns(parent, False)
child_ns = format_ns(ns, True)
self._name = parent_ns + ":" + child_ns
else:
self._name = format_ns(ns, relative)
self._cached = None
def create(self):
"""
creates the namespace if it does not already exist.relative
Note that creating a child namespace works only if
the parent namespace already exists. So
namespace ('a:b:c').create()
only works if 'a:b' already exists
"""
if not self:
cmds.namespace(add=self)
return self
def activate(self):
self._cached = cmds.namespaceInfo(cur=True, an=True)
try:
cmds.namespace(set=self)
return self
except RuntimeError as r:
raise NamespaceError(r)
def deactivate(self):
if self._cached:
cmds.namespace(set=self._cached)
return namespace(self._cached)
# context manager support
def __enter__(self):
return self.activate()
def __exit__(self, *exception_args):
self.deactivate()
# don't swallow exceptions
return False
# decorator support
def __call__(self, fn):
'''
context manager decorator
@namespace('somenamespace')
def make_stuff()
# things made here go into 'somenamespace'
'''
@wraps(fn)
def ns_ctx(*args, **kwargs):
with self:
fn(*args, **kwargs)
return ns_ctx
def __iter__(self):
'''
iterating over the namespace object is iterating over the list
of its contents
'''
return self.contents
# if <ns> returns True if the namespace exists
def __nonzero__(self):
return cmds.namespace(ex=self)
# these methods the namespace impoersonate a string
def __add__(self, other):
return self._name + other
def __radd__(self, other):
return other + self._name
def __eq__(self, other):
return self._name == str(other)
def __repr__(self):
return "namespace('" + self + "')"
def __str__(self):
return self._name
def delete(self, delete_contents=False, force_rename=True):
if delete_contents:
cmds.namespace(rm=self, dnc=True)
else:
# if force_rename is false, this will except in the event of a name clash
cmds.namespace(rm=self, mergeNamespaceWithRoot=True, force=force_rename)
def move(self, new_name):
'''
move namespace contents to namespace 'new_name'. new_name can be a string
or a namespace object
'''
target = namespace(str(new_name))
if not target:
raise NamespaceError("namespace '{}' does not exist". format(target))
cmds.namespace(moveNamespace=(self, target))
return target
def rename(self, new_name, relative=False):
'''
rename this namespace to new_name. new_name can be a string or a namespace object
if 'relative' is true the rename is relative to the current namespace
'''
new_name = format_ns(new_name, relative)
if relative:
current = cmds.namespaceInfo(cur=True)
cmds.namespace(rename=(self, new_name))
self._name = format(current + ':' + new_name, relative=False)
else:
parent_ns, _, child_ns = new_name.rpartition(":")
cmds.namespace(rename=(self, child_ns), parent=parent_ns or ":")
self._name = new_name
@property
def contents(self):
"""
iterator that returns all the objects (not namespaces) in this namespace. Includes child namespaces
"""
for item in cmds.ls(self + ":*", long=True):
yield item
for child in self.namespaces:
for grandchild in child.contents:
yield grandchild
@property
def namespaces(self):
"""
iterator that returns any child namespaces under this namespace
"""
kids = [namespace(n, relative=False) for n in cmds.namespaceInfo(self, lon=True) or []]
for k in kids:
yield k
for grandchild in k.namespaces:
yield grandchild
@classmethod
def current(cls):
return cls(cmds.namespaceInfo(cur=True))
@classmethod
def of(cls, maya_object):
prefix, _, maya_object = maya_object.rpartition(":")
return cls(prefix)
@classmethod
def list_namespaces(cls):
return [cls(n) for n in cmds.namespaceInfo(":", lon=True)]
@classmethod
def reset(cls):
cmds.namespace(set=":")
@theodox
Copy link
Author

theodox commented Jul 13, 2018

This version does not work in 2019 but does in 2016. I'll post the fixes later after I am sure I grok the problem

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