Package: cs.obj
Convenience facilities for objects.Latest release 20220918:SingletonMixin: change example to probe self__dict__ instead of hasattr, faster and less fragile.New Sentinel class for named sentinel objects, equal only to their own instance.Functionas_dict(o, selector=None)Return a dictionary with keys mapping to the values of the attributes ofo.Parameters:o: the object to mapselector: the optional selection criterionIfselectoris omitted orNone, select "public" attributes,
those not commencing with an underscore.Ifselectoris astr, select attributes starting withselector.Otherwise presumeselectoris callable
and select attributesattrwhereselector(attr)is true.Functioncopy(obj, *a, **kw)Convenient function to shallow copy an object with simple modifications.Performs a shallow copy ofselfusing copy.copy.Treat all positional parameters as attribute names, and
replace those attributes with shallow copies of the original
attribute.Treat all keyword arguments as (attribute,value) tuples and
replace those attributes with the supplied values.Functionflavour(obj)Return constants indicating the ``flavour'' of an object:T_MAP: DictType, DictionaryType, objects with ankeysor keys attribute.T_SEQ: TupleType, ListType, objects with aniterattribute.T_SCALAR: Anything else.ClassO(types.SimpleNamespace)TheOclass is now obsolete, please subclasstypes.SimpleNamespaceor use a dataclass.FunctionO_attritems(o)Generator yielding(attr,value)for relevant attributes ofo.FunctionO_attrs(o)Yield attribute names fromowhich are pertinent toO_str.Note: this callsgetattr(o,attr)to inspect it in order to
prune callables.FunctionO_merge(o, _conflict=None, _overwrite=False, **kw)Merge key:value pairs from a mapping into an object.Ignore keys that do not start with a letter.
New attributes or attributes whose values compare equal are
merged in. Unequal values are passed to:_conflict(o, attr, old_value, new_value)to resolve the conflict. If _conflict is omitted or None
then the new value overwrites the old if _overwrite is true.FunctionO_str(o, no_recurse=False, seen=None)Return astrrepresentation of the objecto.Parameters:o: the object to describe.no_recurse: if true, do not recurse into the object's structure.
Default:False.seen: a set of previously sighted objects
to prevent recursion loops.Functionobj_as_dict(*args, **kwargs)OBSOLETE convesion of an object to adict. Please uscs.obj.as_dict.ClassProxyAn extremely simple proxy object
that passes all unmatched attribute accesses to the proxied object.Note that setattr and delattr work directly on the proxy, not the proxied object.ClassSentinelA simple class for named sentinels whosestr()is just the name
and whose==usesis.Example:>>> from cs.obj import Sentinel
>>> MISSING = Sentinel("MISSING")
>>> print(MISSING)
MISSING
>>> other = Sentinel("other")
>>> MISSING == other
False
>>> MISSING == MISSING
TrueFunctionsingleton(registry, key, factory, fargs, fkwargs)Obtain an object forkeyviaregistry(a mapping ofkey=>object).
Return(is_new,object).If thekeyexists in the registry, return the associated object.
Otherwise create a new object by callingfactory(*fargs,**fkwargs)and store it askeyin theregistry.Theregistrymay be any mapping ofkeys to objects
but will usually be aweakref.WeakValueDictionaryin order that object references expire as normal,
allowing garbage collection.Note: this functionis notthread safe.
Multithreaded users should hold a mutex.See theSingletonMixinclass for a simple mixin to create
singleton classes,
which does provide thread safe operations.ClassSingletonMixinA mixin turning a subclass into a singleton factory.Note: this mixin overridesobject.__new__and may not play well with other classes which override__new__.Warning: because of the mechanics of__new__,
the instance's__init__method will always be called
after__new__,
even when a preexisting object is returned.
Therefore that method should be sensible
even for an already initialised
and probably subsequently modified object.My suggested approach is to access some attribute,
and preemptively return if it already exists.
Example:def __init__(self, x, y):
    if 'x' in self.__dict__:
        return
    self.x = x
    self.y = yNote: we probeself.__dict__above to accomodate classes
with a__getattr__method.Note: each class registry has a lock,
which ensures that reuse of an object
in multiple threads will call the__init__method
in a thread safe serialised fashion.Implementation requirements:
a subclass should:provide a method_singleton_key(*args,**kwargs)returning a key for use in the single registry,
computed from the positional and keyword arguments
supplied on instance creation
i.e. those which__init__would normally receive.
This should have the same signature as__init__but usingclsinstead ofself.provide a normal__init__method
which can be safely called again
after some earlier initialisation.This class is thread safe for the registry operations.Example:class Pool(SingletonMixin):

    @classmethod
    def _singleton_key(cls, foo, bah=3):
        return foo, bah

    def __init__(self, foo, bah=3):
        if hasattr(self, 'foo'):
            return
       ... normal __init__ stuff here ...
       self.foo = foo
       ...MethodSingletonMixin.__new__(cls, *a, **kw):
Prepare a new instance ofclsif required.
Return the instance.This creates the class registry if missing,
prepares a key fromcls._singleton_key,
then returns the entry from the registry is present,
or creates a new entry if not.
Note: if the key isNonea new entry is always created
and not recorded in the registry.ClassTrackedClassMixinA mixin to track all instances of a particular class.This is aimed at checking the global state of objects of a
particular type, particularly states like counters. The
tracking is attached to the class itself.The class to be tracked includes this mixin as a superclass and calls:TrackedClassMixin.__init__(class_to_track)from itsinitmethod. Note thatclass_to_trackis
typically the class name itself, nottype(self)which would
track the specific subclass. At some relevant point one can call:self.tcm_dump(class_to_track[, file])class_to_trackneeds atcm_get_statemethod to return the
salient information, such as this from cs.resources.MultiOpenMixin:def tcm_get_state(self):
    return {'opened': self.opened, 'opens': self._opens}See cs.resources.MultiOpenMixin for example use.Release LogRelease 20220918:SingletonMixin: change example to probe self__dict__ instead of hasattr, faster and less fragile.New Sentinel class for named sentinel objects, equal only to their own instance.Release 20220530:
SingletonMixin: add defaulthashandeqmethods to support dict and set membership.Release 20210717:
SingletonMixin: if cls._singleton_key returns None we always make a new instance and do not register it.Release 20210306:
SingletonMixin: make singleton_also_by() a public method.Release 20210131:
SingletonMixin: new _singleton_also_indexmap method to return a mapping of secondary keys to values to secondary lookup, _singleton_also_index() to update these indices, _singleton_also_by to look up a secondary index.Release 20210122:
SingletonMixin: new _singleton_instances() method returning a list of the current instances.Release 20201227:
SingletonMixin: correctly invokenew, a surprisingly fiddly task to get right.Release 20201021:@OBSOLETE(obj_as_dict), recommend "as_dict()".[BREAKING] change as_dict() to accept a single optional selector instead of various mutually exclusive keywords.Release 20200716:
SingletonMixin: no longer require special _singleton_init method, reuse defaultinitimplicitly throughnewmechanics.Release 20200517:
Documentation improvements.Release 20200318:Replace obsolete O class with a new subclass of SimpleNamespace which issues a warning.New singleton() generic factory function and SingletonMixin mixin class for making singleton classes.Release 20190103:New mixin class TrackedClassMixin to track all instances of a particular class.Documentation updates.Release 20170904:
Minor cleanups.Release 20160828:Use "install_requires" instead of "requires" in DISTINFO.Minor tweaks.Release 20150118:
move long_description into cs/README-obj.rstRelease 20150110:
cleaned out some old junk, readied metadata for PyPI