Convenience facilities for objects.
Latest release 20260912:
- New NoAttrs leaf mixin to allow classes to freely use super().getattr and to issue a more informative AttributeError.
- Refreshable: new refresh_related1() method for related objects to be refreshed but not recursively.
- Refreshable: use self.refresh_lock as a mutex if present.
- Refreshable: pass the related() and related1() items through not_none to accomodate these meethods returning some Nones, which is often expedient.
- Refreshable.{refresh,_refresh}: drop the optional resource parameter, turned out to be unused.
Short summary:
-
as_dict: Return a dictionary with keys mapping to the values of the attributes ofo. -
copy: Convenient function to shallow copy an object with simple modifications. -
flavour: Return constants indicating the ``flavour'' of an object: *T_MAP: DictType, DictionaryType, objects with an keys or keys attribute. * `T_SEQ`: TupleType, ListType, objects with an iter attribute. * `T_SCALAR`: Anything else. -
NoAttrs: A stub class whose__getattr__raisesAttributeError. This exists to be put at the end of the subclasses for concrete nonmixin classes which have multiple__getattr__methods in their MRO, so that they can always callsuper().__getattr__. -
O: TheOclass is now obsolete, please subclasstypes.SimpleNamespaceor use a dataclass. -
O_attritems: Generator yielding(attr,value)for relevant attributes ofo. -
O_attrs: Yield attribute names fromowhich are pertinent toO_str. -
O_merge: Merge key:value pairs from a mapping into an object. -
O_str: Return astrrepresentation of the objecto. -
obj_as_dict: OBSOLETE version of obj_as_dict, suggestion: use cs.obj.as_dict. -
Proxy: An extremely simple proxy object that passes all unmatched attribute accesses to the proxied object. -
public_subclasses: Return a set of the subclasses ofclswhich have public names. -
Refreshable: A mixin for refreshable objects. The object must provide a_refresh()method, returningTrueif the object was updated and false otherwise. -
Sentinel: A simple class for named sentinels whosestr()is just the name and whose==usesis. -
singleton: Obtain an object forkeyviaregistry(a mapping ofkey=>object). Return(is_new,object). -
SingletonMixin: A mixin turning a subclass into a singleton factory. -
TrackedClassMixin: A mixin to track all instances of a particular class.
Functions
O_attritems(o)
Generator yielding (attr,value) for relevant attributes of o.
O_attrs(o)
Yield attribute names from o which are pertinent to O_str.
Note: this calls getattr(o,attr) to inspect it in order to
prune callables.
O_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.
O_str(o, no_recurse=False, seen=None)
Return a str representation of the object o.
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.
as_dict(o, selector=None)
Return a dictionary with keys mapping to the values of the attributes of o.
Parameters:
o: the object to mapselector: the optional selection criterion
If selector is omitted or None, select "public" attributes,
those not commencing with an underscore.
If selector is a str, select attributes starting with selector.
Otherwise presume selector is callable
and select attributes attr where selector(attr) is true.
copy(obj, **kw)
Convenient function to shallow copy an object with simple modifications.
Performs a shallow copy of self using copy.copy.
Treat all keyword arguments as (attribute,value) 2-tuples and
replace those attributes with the supplied values.
flavour(obj)
Return constants indicating the ``flavour'' of an object:
T_MAP: DictType, DictionaryType, objects with an keys or keys attribute.T_SEQ: TupleType, ListType, objects with an iter attribute.T_SCALAR: Anything else.
obj_as_dict(o, **kw)
OBSOLETE version of obj_as_dict, suggestion: use cs.obj.as_dict
OBSOLETE convesion of an object to a dict. Please us cs.obj.as_dict.
public_subclasses(cls, extras=())
Return a set of the subclasses of cls which have public names.
singleton(registry, key, factory, fargs, fkwargs)
Obtain an object for key via registry (a mapping of key=>object).
Return (is_new,object).
If the key exists in the registry, return the associated object.
Otherwise create a new object by calling factory(*fargs,**fkwargs)
and store it as key in the registry.
The registry may be any mapping of keys to objects
but will usually be a weakref.WeakValueDictionary
in order that object references expire as normal,
allowing garbage collection.
Note: this function is not thread safe. Multithreaded users should hold a mutex.
See the SingletonMixin class for a simple mixin to create
singleton classes,
which does provide thread safe operations.
Classes
class NoAttrs
A stub class whose __getattr__ raises AttributeError.
This exists to be put at the end of the subclasses for concrete
nonmixin classes which have multiple __getattr__ methods
in their MRO, so that they can always call super().__getattr__.
This exists because object has no __getattr__, necessitating
this ugly shuffle in mixin __getattr__ methods:
def __getattr__(self, attr):
... return the mixin's special attr ...
try:
gsa = super().__getattr__
except AttributeError:
raise AttributeError(f'{self.__class__.__name__}.{attr}')
else:
return gsa(attr)
With NoAttrs at the end of the MRO every mixin's __geattr__ can be:
def __getattr__(self, attr):
... return the mixin's special attr ...
return super().__getattr__(attr)
A concrete class is then defined as:
class C(AMixin,BMixin,NoAttrs):
Because of Python's MRO construction rules, a subclass like:
class D(C,DMixin,NoAttrs):
still places NoAttrs at the end of the MRO, producing correct behaviour.
NoAttrs.__getattr__ issues a warning if super().__getattr__ actually exists.
#e## NoAttrs.dict
Read-only proxy of a mapping.
#e## NoAttrs.firstlineno
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
NoAttrs.getattr(self, attr)
Just raise AttributeError with a nice mesage.
#e## NoAttrs.static_attributes
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
class O(types.SimpleNamespace)
The O class is now obsolete, please subclass types.SimpleNamespace
or use a dataclass.
#e## O.firstlineno
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
#e## O.static_attributes
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
#e## O.callers
Build an unordered collection of unique elements.
class Proxy
An 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.
#e## Proxy.dict
Read-only proxy of a mapping.
#e## Proxy.firstlineno
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
#e## Proxy.static_attributes
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
class Refreshable
A mixin for refreshable objects.
The object must provide a _refresh() method, returning
True if the object was updated and false otherwise.
This mixin provides a .refresh() method which implements the refresh logic
and various related refresh_* methods/properties.
#e## Refreshable.REFRESH_LIFESPAN
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
#e## Refreshable.REFRESH_RATELIMIT
Convert a string or number to a floating-point number, if possible.
#e## Refreshable.dict
Read-only proxy of a mapping.
#e## Refreshable.firstlineno
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
#e## Refreshable.static_attributes
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
Refreshable.refresh(self, *, data=None, force=False, lifespan: float | None = None, map=<class 'map'>, ratelimit: float | None = None, recurse=False, seen=None, **_refresh_kw) -> bool
Refresh this object; if it is stale attempt to refresh via self._refresh().
Return True if the object was updated, False otherwise.
Note that this returns False if the object was considered
still current; False is not an error.
The refresh policy is: if force or (the object's information
is stale and the rate limit does not preclude a refresh),
call self._refresh().
This is measured by self.refresh_needed().
State about the refresh poll times is kept in
self.refresh_last_poll and self.refresh_last_update.
Keyword parameters:
data: optional object providing the refresh data, passed toself._refresh()as thedatakeyword argument; if notNonethen_refreshis always called and should apply it instead of making whatever API call or other action it would perform passed to `self._refresh()force: optional flag, defaultFalse; if true attempt a refresh regardless of staleness or the rate limitlifespan: how many seconds before updated information is considered no stale, default fromself.refresh_lifespan, ortype(self).REFRESH_RATELIMITmap: optionalmap()-like function to call refresh on the related objects; this hook exists to allow for example APIs to pass in a curriedpmap()call to refresh some objects in parallel; the default map function ismap()ratelimit: how many seconds should elapsed before attempting a refresh, default fromself.refresh_ratelimitortype(self).REFRESH_RATELIMITrecurse: optional flag, defaultFalse; if true then refresh the objects fromself.refresh_related1()nonrecursively and recursively refresh the objects fromself.refresh_related()seen: optional set of keys to prevent unbound recursion
Other keyword parameters are passed to self._refresh() if it is called.
Note the two related-object methods, refresh_related()
and refresh_related1(). These exist to allow a recursive
refresh to refresh "descendant" objects recursively and
other related "adjacent" or "superior" objects nonrecursively.
Supposing you're refreshing a section topic in a news site;
you might recursively refresh all the articles in the topic
and the authors of the articles. However you likely also
want to refresh other objects related to the topic, perhaps
the table of topics and other things; recursing into those
objects might refresh everything on the entire site.
Accordingly the descendant objects would be enumerated by
refresh_related() and the adjacent/superior objects by
refresh_related1().
Note that because a recurse=True call descends a potentially
arbitrary tree of related objects, if map is supplied as,
for example, pmap, that should be a curried call with a
shared Semaphore to control the concurrency, such as
partial(pmap,concurrent=Semaphore(4)).
It is probably a mistake to call this with non-None data
and recurse true. It will be accepts and the data applied
to self and the recursion done without data.
(Now we raise ValueError for this.)
Refreshable.refresh_key(self)
Return the key value for the seen set used in a recursive refresh.
This default returns id(self); classes like Entity
would use a characteristic value like self.name.
Refreshable.refresh_needed(self, *, lifespan: float | None = None, now: float | None = None) -> bool
Test whether self is considered stale.
Parameters:
lifespan: how many seconds before updated information is considered no stale, default fromself.refresh_lifespan, ortype(self).REFRESH_LIFESPANnow: optional reference time, default fromtime.time()
Refreshable.refresh_related(self) -> Iterable
Return the related objects which should also be refreshed in recursive refreshes.
Refreshable.refresh_related1(self) -> Iterable
Return the related objects which should also be refreshed in recursive refreshes, but nonrecursively.
Refreshable.refreshed(self, **refresh_kw)
A variant on refresh() which returns self to support chaining.
This isn't the default because that's not Pythonic.
class Sentinel
A simple class for named sentinels whose str() is just the name
and whose == uses is.
Example:
>>> from cs.obj import Sentinel
>>> MISSING = Sentinel("MISSING")
>>> print(MISSING)
MISSING
>>> other = Sentinel("other")
>>> MISSING == other
False
>>> MISSING == MISSING
True
#e## Sentinel.firstlineno
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
#e## Sentinel.hash
The type of the None singleton.
#e## Sentinel.slots
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
#e## Sentinel.static_attributes
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
class SingletonMixin
A mixin turning a subclass into a singleton factory.
Note: this mixin overrides object.__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 = y
Note: we probe self.__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
...
#e## SingletonMixin.dict
Read-only proxy of a mapping.
#e## SingletonMixin.firstlineno
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
SingletonMixin.hash(self)
default hash and equality methods
#e## SingletonMixin.static_attributes
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
SingletonMixin.singleton_also_by(also_key, key)
Obtain a singleton by a secondary key.
Return the instance or None.
Parameters:
also_key: the name of the secondary key indexkey: the key for the index
class TrackedClassMixin
A 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 its init method. Note that class_to_track is
typically the class name itself, not type(self) which would
track the specific subclass. At some relevant point one can call:
self.tcm_dump(class_to_track[, file])
class_to_track needs a tcm_get_state method 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.
#e## TrackedClassMixin.dict
Read-only proxy of a mapping.
#e## TrackedClassMixin.firstlineno
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.int(). For floating-point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
int('0b100', base=0) 4
#e## TrackedClassMixin.static_attributes
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
TrackedClassMixin.tcm_all_state(klass)
Generator yielding tracking information
for objects of type klass
in the form (o,state)
where o if a tracked object
and state is the object's get_tcm_state method result.
TrackedClassMixin.tcm_dump(klass, f=None)
Dump the tracking information for klass to the file f
(default sys.stderr).
Release Log
Release 20260912:
- New NoAttrs leaf mixin to allow classes to freely use super().getattr and to issue a more informative AttributeError.
- Refreshable: new refresh_related1() method for related objects to be refreshed but not recursively.
- Refreshable: use self.refresh_lock as a mutex if present.
- Refreshable: pass the related() and related1() items through not_none to accomodate these meethods returning some Nones, which is often expedient.
- Refreshable.{refresh,_refresh}: drop the optional resource parameter, turned out to be unused.
Release 20260610:
- Refreshable._refresh: this method must now accept an optional
data=parameter which will be used to update the entity instead of consulting the original source. - Refrehable.refresh: new map parameter to specify a function instead of map for the recursion, such as pmap().
- Refreshable.refreshed: change the meaning of
.refreshd()to be self after a.refresh(), supporting chaining.
Release 20260526: Refreshable mixin class embodying refresh logic.
Release 20250306:
- Remove cs.py3 dependency, this module has not worked in python2 for quite a while.
- copy: drop use of positional parameters.
Release 20250103: public_subclasses: new optional extras= parameter for additional classes to scan, now returns a set.
Release 20241009: public_subclasses: catch TypeError from "type.subclasses", which is just a function.
Release 20241005: New public_subclasses(cls) returning all subclasses with public names.
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.
Release 20220530: SingletonMixin: add default hash and eq methods 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 invoke new, 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 default init implicitly through new mechanics.
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.rst
Release 20150110: cleaned out some old junk, readied metadata for PyPI
Metadata
Release files for cs-obj 20260912
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cs_obj-20260912.tar.gz | 18.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cs_obj-20260912-py2.py3-none-any.whl | Python 2, Python 3 | none | any | Details |
Total release size: 35.3 kB
Release files / cs_obj-20260912.tar.gz
| Download URL | cs_obj-20260912.tar.gz |
|---|---|
| Size | 18.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0be591d762b5985006502f4b63118656275dd938865f7acc03b3f4599049f92b
|
|
BLAKE2b-256 checksum How to use checksums |
fd4fcdc34a6f097aa9dabfb805e1a193bf68b34f718844823a08f7e4c1b71627
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.1
|
Release files / cs_obj-20260912-py2.py3-none-any.whl
| Download URL | cs_obj-20260912-py2.py3-none-any.whl |
|---|---|
| Size | 16.9 kB |
| Tags | Python 2 Python 3 |
|
SHA-256 checksum How to use checksums |
93adcd10ff957eed5a798c76c78e3dc6706d8fe2fc0360e0355496644e0f05ef
|
|
BLAKE2b-256 checksum How to use checksums |
2a0c240419e51d01eb0d6bf474bfd51596f39d23ee728f57abb51388a7ad9be3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.1
|