Skip to main content

Tagged information entities, built on TagSets for representation and typically an SQLTags for storage. I use these to persist and mediate knowledge, including my interactions with web sites, APIs, and third party databases.

Latest release 20260912: First PyPI release: Entity, Entities, ScanData.

Short summary:

  • Entities: A mixin to support classes which use a .tagsets:BaseTagSets attribute to store their data.

  • Entity: A base class for classes which have a .tags:TagSet attribute and a .tags_db:BaseTagSets containing other Tagsets.

  • ScanData: A class to manage data obtained about SiteEntity instances, for example from an API or scanning a web page.

Classes

class Entities

A mixin to support classes which use a .tagsets:BaseTagSets attribute to store their data.

Subclasses may define the following class attributes:

  • EntityClass: a subclass of Entity which represents data entities; the default is Entity which should be enough if there is no .tYPE_ZONE
  • TYPE_ZONE: the type zone identifying entities in the larger BaseTagSets data; if this is not supplied it is obtained from EntityClass.TYPE_ZONE, if defined

A typical use subclasses cs.sqltags.UsesSQLTags, a subclass of this which uses an SQLTags as the storage backend.

If there is a .TYPE_ZONE, the meaning of the type zone, subname and key are as described for the ZonedTypes class.

Entities.EntityClass

A base class for classes which have a .tags:TagSet attribute and a .tags_db:BaseTagSets containing other Tagsets.

Usually these are considered part of a "zone" - a group of entities in a particular applicaiton domain.

The subclass may itself define its .tags instance attribute or rely on the default cached property .tags, which will return self.tags_db[self.tags_entity_key]. (self.tags_entity_key is self.tags.name by default.)

Note that this mixin brings its own __new__ method which can choose a subclass based on the subclass' .TYPE_SUBNAME attribute. See the __new__ docstring.

This also provides some behaviour based around updating entities based on some kind of API call; the direct values from the API call land on attributes named {zone}.{key} - the .type_zone_update(mapping) provides a convenient call for this.

Entity instances are designed as representing entities in some "zone", a set of entities in some domain or organised grouping; typical examples include entities describes by some API like MusicBrainzNG or objects presented by some website. As such, they subclass ZonedTypes, which expects the entity's .name to be of the form zone.subname.key; the zone partitions entities off into their own domain, the subname is in effect the entity's type within that domain and the key is the entity id within that type.

On this basis, entities updated with data from the zone, for example from an API call or by scraping a web page, normally update tag keys named zone.field where the field is the top level field from the data.

The ZonedTypes.__getattr__(attr) method looks first for a direct tag named attr but falls back to a tag named zone.attr. This allows entities to be tagged with the data from an API, but to be overridden by the direct tag if the API data are considered incorrect or unsuitable.

The ScanData.apply() method follows this principle, applying the scanned data to tags named zone.field.

We relate entities using attributes named field_id, which may be a single key for another entity or a list of keys.

Various derived attributes are also provided, see the __getattr__ docstring for details:

  • field_ent: the related Entity named zone.field.key where key comes from the .field_id attribute
  • field_ents: multiple related Entity named zone.field.key where key comes from the .field_id attribute

Entities.TagsetsClass

The type of the None singleton.

Entities.__class_getitem__(index)

An Entities subclass may be indexed with a string.

If there is no cls.TYPE_ZONE the string is treated either as:

  • if the string ha no dots, a TYPE_ZONE value - the Entities instance for that zone is returned
  • if the string has dots, as an Entity.name and looked up with cls.by_entity_id(index).

If there is a cls.TYPE_ZONE, such as with a SiteMap, the string is treated as a ZonedTypes.type_zone_key and looked up as by indexing that zone's Entities instance.

Example using TheTVDBAPI, which has a TYPE_ZONE:

# fetch the TV series entity with id 1234
# there is a TheTVDBAPI.TYPE_ZONE
series = TheTVDBAPI['series.1234']

# fetch an arbtrary Entity
# the value of `TheTVDBAPI.TYPE_ZONE` is "tvdb"
series = Entities['tvdb.series.1234']

Example using SiteMap, the base class for site maps, and which has no .TYPE_ZONE:

smh_map = SiteMap['smh']
smh_topic = SiteMap['smh.topic.technology']
smh_article = SiteMap['smh']['article.abcd']

Entities.__dict__

Read-only proxy of a mapping.

Entities.__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

Entities.__getitem__(self, index: str | tuple[str, str | int] | tuple[str, str, str | int]) -> cs.tagged.Entity

self.__getitem__(index) calls self.entity(index).

Entities.__init_subclass__(**kw)

Inititialise a subclass by defining .TYPE_ZNE if already present.

Entities.__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.

Entities.as_zone(self, zone=None)

Push this Entities instance as the default mapping for zone, whose default is self.__class__.TYPE_ZONE. Yields the zone, or None if there is no

Entities.by_entity_id(entity_id: str) -> cs.tagged.Entity

Return the Entity instance corresponding to entity_id from the full tb Raise ValueError if entity_id cannot be parsed by ZonedTypes.type_parts_of. Raise KeyError if there is no Entities instance for the zone and we cannot make a default instance.

Entities.by_type_zone

Mapping class that references values weakly.

Entries in the dictionary will be discarded when no strong reference to the value exists anymore

Entities.class_by_type_zone

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Entities.default(zone: str | None = None) -> 'Entities'

Return the default Entities instance for zone. If zone is not defined it is taken from cls.TYPE_ZONE. Raise KeyError for an unregistered zone. Raise TypeError if there is no registered default and the class for zone cannot be instantiated with entcls().

Entities.entity(self, index: str | tuple[str, str | int] | tuple[str, str, str | int], zone=None) -> cs.tagged.Entity

Fetch the Entity instance for the supplied index. This underlies the __getitem__ method.

The meaning of the type zone, subname and key are as described for the ZonedTypes class.

The index may take the following forms:

  • str: a string which will be split into subname and key for use in self.TYPE_ZONE
  • (subname,key): a 2-tuple of the type subname and key in self.TYPE_ZONE the subname make also be a subclass of self.EntityClass
  • (zone,subname,key): a 3-tuple of the type zone, subname and key The subname may also be a class (normally a subclass of Entity, usually a subclass of type(self).EntityClass); in this case the subname will be taken from type(self).TYPE_SUBNAME attribute. The key may also be an int or a uuid.UUID, in which case it will be used as str(key).

Examples:

# the Entity subclass Artist, and the Entities
# subclass MBDB which hold MusicbrainzNG information
from cs.cdrip import Artist, MBDB
mbdb = MBDB()

# Various indices obtaining the record for Jon Cleary,
# whose key is 'mbdb.artist.a417f0e5-2c14-445a-9a07-5a7ad2bdeafa'

# the subname.key as a single string
artist = mbdb['artist.a417f0e5-2c14-445a-9a07-5a7ad2bdeafa']

# the subname and key in a 2-tuple
artist = mbdb['artist', 'a417f0e5-2c14-445a-9a07-5a7ad2bdeafa']

# the record but not from the default MBDB zonne
artist = mbdb['mbdb2', 'artist', 'a417f0e5-2c14-445a-9a07-5a7ad2bdeafa']

# the preferred way to obtain it, using the entity type
artist = mbdb[Artist, 'a417f0e5-2c14-445a-9a07-5a7ad2bdeafa']

# or if you're working with UUIDs
artist_uuid = UUID('a417f0e5-2c14-445a-9a07-5a7ad2bdeafa')
artist = mbdb[Artist, artist_uuid]

Entities.find(self, *criteria, **crit_kw) -> list[cs.tagged.Entity]

Find entities in the database.

This calls self.tagsets.find() and returns the associated Entity instances.

Entities.keys(self, subname=None)

Return the keys from self.tagsets as (subname,type_key) 2-tuples suitable as indices of self. If subname is not None, restrict the keys to those with that subname.

Entities.set_as_zone(self, zone: str, if_unset=False)

Set this Entities instance as the one handling entities in zone.

Entities.zone_entity(self, zone: str) -> 'Entity'

Return the Entity entity associated with a per-type-zone key. For example, self.zone_entity('tvdb') would return the entity for tvdb.tvdb_id where tvdb_id comes from self['id.tvdb'].

class Entity(cs.tagset.ZonedTypes, cs.obj.Refreshable, cs.deco.Promotable, cs.lex.FormatableMixin, cs.obj.NoAttrs)

A base class for classes which have a .tags:TagSet attribute and a .tags_db:BaseTagSets containing other Tagsets.

Usually these are considered part of a "zone" - a group of entities in a particular applicaiton domain.

The subclass may itself define its .tags instance attribute or rely on the default cached property .tags, which will return self.tags_db[self.tags_entity_key]. (self.tags_entity_key is self.tags.name by default.)

Note that this mixin brings its own __new__ method which can choose a subclass based on the subclass' .TYPE_SUBNAME attribute. See the __new__ docstring.

This also provides some behaviour based around updating entities based on some kind of API call; the direct values from the API call land on attributes named {zone}.{key} - the .type_zone_update(mapping) provides a convenient call for this.

Entity instances are designed as representing entities in some "zone", a set of entities in some domain or organised grouping; typical examples include entities describes by some API like MusicBrainzNG or objects presented by some website. As such, they subclass ZonedTypes, which expects the entity's .name to be of the form zone.subname.key; the zone partitions entities off into their own domain, the subname is in effect the entity's type within that domain and the key is the entity id within that type.

On this basis, entities updated with data from the zone, for example from an API call or by scraping a web page, normally update tag keys named zone.field where the field is the top level field from the data.

The ZonedTypes.__getattr__(attr) method looks first for a direct tag named attr but falls back to a tag named zone.attr. This allows entities to be tagged with the data from an API, but to be overridden by the direct tag if the API data are considered incorrect or unsuitable.

The ScanData.apply() method follows this principle, applying the scanned data to tags named zone.field.

We relate entities using attributes named field_id, which may be a single key for another entity or a list of keys.

Various derived attributes are also provided, see the __getattr__ docstring for details:

  • field_ent: the related Entity named zone.field.key where key comes from the .field_id attribute
  • field_ents: multiple related Entity named zone.field.key where key comes from the .field_id attribute

Entity.__annotations__

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Entity.__delitem__(self, tag_name: str)

Remove an entry from self.tags.

Entity.__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

Entity.__getattr__(self, attr)

Try ZonedTypes.__getattr__ (which lokks up [attr] then [f'{zone}.{attr}']) then fall back to suffix based synthetic attributes where an attribute ending in _suffix is implemented by the suffix_suffix(attr) method if it exists.

The following synthetic attibutes are implemented:

  • attr0_or_none: return .attr0 or None if that does not exist
  • subtype_ent: the entity with name type_zone.subtype.id or None where id comes from the .attr_id value; see the suffix_ent method.
  • subtype_ents: the entities with name type_zone.subtype.id or None where each id comes from the .attr_id values; see the suffix_ents method.

Entity.__getitem__(self, tag_name: str)

Index self.tags.

Entity.__setitem__(self, tag_name, value, *, verbose=False)

Set a tag value.

Entity.__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.

Entity.as_dict(self)

Proxy .as_dict() to self.tags.

Entity.entity

The .entity attribute space, whose attributes map to entities which are UsesTags instances from the appropriate Entities instances according to their zone.

Example:

  tags = TagSet({'id.playon':'recording.1234567'})
  playon_recording = tags.entity.playon

Entity.entity_

The .entity_ attribute space, whose attributes map to entities which are UsesTags instances from the appropriate Entities instances according to their zone. Unlike .entity, a missing id. tag returns None instead of raising AttributeError.

Example:

  tags = TagSet({'id.playon':'recording.1234567'})
  playon_recording = tags.entity.playon

Entity.format_attributes

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Entity.format_kwargs(self)

A format_kwargs method to support cs.lex.FormatableMixin.

Entity.get(self, tag_name: str, default=None)

Call .tags.get(tag_name).

Entity.items(self)

The tags items.

Entity.prefix_in(self, attr) -> collections.abc.Sequence[typing.Self]

Resolve in_subtype[_field to the Entity instance of subtype subtype whose field_id attribute contains self.type_key. The default field is self.type_subname.

For example, if self.name is "tvdb.episode.1234" then self.in_season would return a list of all the tvdb.season entities whose episode_id attributes referred to 1234.

Where the

Entity.print(self)

The default print() runs self.printt(). This is intended to be a nice print of important stuff.

Entity.refresh_key(self)

The unique key identifying this object for use in recursive refreshes.

Entity.refresh_last_update

The last time a refresh update time.

Entity.setdefault(self, key, default_value)

Set self[key]=default_value if key is not present.

Entity.suffix_ent(self, attr) -> Optional[Self]

Resolve subtype_ent to self[type_zone.subtype.id] or None if no self[subtype_id]

Entity.suffix_ents(self, attr) -> collections.abc.Sequence[typing.Self]

Resolve subtype_ents to [self[type_zone.*subtype*.id]]or()if noself[*subtype*_id]]`.

Entity.tags

A default .tags property which obtains a TagSet from self.tags_db via using the TagSet name self.tags_entity_key. This is for subclasses which might fetch the .tags on demand.

Subclasses typically set .tags during __init__ and therefore have no need for a .tags_entity_key property.

Entity.tags_entity_key

Our tagged entity key, self.tags.name.

This is only really needed by the .tags cached property; most subclasses of Entity set .tags during __init__. If you have an "on demand" subclass you should override this method to compute the entity key without relying on the (missing) .tags attribute.

Entity.type_zone_update(self, mapping, prefix=None, *, lc_=False)

Update self with mapping, using prefix. The default prefix is self.type_zone`.

Entity.update(self, *update_a, **update_kw)

Update the tags, tupically from a mapping or keyword arguments.

Entity.values(self)

The tags values.

class ScanData

A class to manage data obtained about SiteEntity instances, for example from an API or scanning a web page.

The data for an SiteEntity can be obtained by indexing the ScanData instance with a SiteEntity instance or a (ent_cls,type_key) 2-tuple

ScanData.__dict__

Read-only proxy of a mapping.

ScanData.__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

ScanData.__getitem__(self, ent: Union[tuple, ForwardRef('Entity')])

The data for the supplied ent.

ScanData.__iter__(self)

Iteration yields (Entity,datadict) 2-tuples.

ScanData.__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.

ScanData.apply(self, *refresh_ents)

Apply the scanned data to its entities.

If an entity ent is a member of refresh_ents then call ent.refresh(data=data) on the basis that the data are complete enough to consider the entity refreshed, otherwise call ent.type_zone_update(data).

The purpose of the call to ent.refresh() is to exercise the refresh machinery. On a Refreshable object ent this marks the object as current with the new data; the data are applied with Refreshable._refresh(), the zone specific method, which typically also uses ent.type_zone_update(data).

This follows the tag name design outlined in the Entity docstring, where API/site data are stored with tags named zone.field.

ScanData.conv(self, ent: Union[tuple, ForwardRef('Entity')], mapping, key, conv=None)

Update the data for ent from mapping[key] if present. If conv is not None it should be a callable accepting the value from mapping[key] and returning a converted value to store in the entity data.

ScanData.printt(self, title=None)

Call cs.lex.printt() to print the scanned data.

ScanData.update(self, ent: Union[tuple[type, int | str], ForwardRef('Entity')], **data_kw)

Update the data for ent from data_kw.

Release Log

Release 20260912: First PyPI release: Entity, Entities, ScanData.

Release files for cs-tagged 20260912

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cs-tagged 20260912
File Size Uploaded
cs_tagged-20260912.tar.gz 18.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cs-tagged 20260912
File Interpreter ABI Platform
cs_tagged-20260912-py3-none-any.whl Python 3 none any Details

Total release size: 35.9 kB

Release files / cs_tagged-20260912.tar.gz

Download URL cs_tagged-20260912.tar.gz
Size 18.8 kB
Tags Source
SHA-256 checksum
How to use checksums
06dfa739136815c50c30a70546b1dd768a97af961d7e21ab6bd419634b180b7f
BLAKE2b-256 checksum
How to use checksums
fa2ed382aec7d84fdcbcb111e4ea6a61f227f80987d360c163f1171325d81883
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.1

Release files / cs_tagged-20260912-py3-none-any.whl

Download URL cs_tagged-20260912-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4c8498f5f5e9b0ddcdccb09ee8268105c65a8ff5f27aef94a9ab0a5a96859fce
BLAKE2b-256 checksum
How to use checksums
9718e5676cf9cd44a4f797362286b10455986ec544787bdf062bb9a3939600e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.1

Release history Release notifications | RSS feed

This release

20260912 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page