Module uim.model.base
Expand source code
# -*- coding: utf-8 -*-
# Copyright © 2021 Wacom Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import hashlib
import uuid
from abc import ABC, abstractmethod
from enum import Enum
from typing import List, Any
# Global configuration
node_registration_debug: bool = False
class InkModelException(Exception):
pass
class IdentifiableMethod(Enum):
"""Different ID encoding methods """
UUID = 0
MD5 = 1
class Identifier(ABC):
"""
Internal UIM 128bit Identifier (UimID)
--------------------------------------
UimID is a 128-bit number used internally by the implementations.
It can be parsed from strings in the following formats:
Simple Hexadecimal String Representation (S-Form)
-------------------------------------------------
The representation of a UimID value is the hex-decimal string representation of the 128-bit number,
assuming that it's encoded using Big Endian byte ordering.
For example: fa70390871c84d91b83c9b56549043ca
Hyphenated Hexadecimal String Representation (H-Form)
-----------------------------------------------------
This representation of a UimID value has the following codec: <part1>-<part2>-<part3>-<part4>-<part5>
Assuming that the UimID 128-bit value is encoded using Big Endian byte ordering, it is split into 5 groups of
bytes and each group is formatted as hexadecimal number.
---------------------------------------------------------------------------
| | Part 1 | Part 2 | Part 3 | Part 4 | Part 5 |
---------------------------------------------------------------------------
| Bytes | 0, 1, 2, 3 | 4, 5 | 6,7 | 8, 9 | 10, 11, 12, 13, 14, 15 |
---------------------------------------------------------------------------
-----------------------------------------------------------------------------
| S | 32 digits: 00000000000000000000000000000000 |
| | E.g., fa70390871c84d91b83c9b56549043ca |
-----------------------------------------------------------------------------
| H | 32 digits separated by hyphens: 00000000-0000-0000-0000-000000000000 |
| | E.g., fa703908-71c8-4d91-b83c-9b56549043ca |
-----------------------------------------------------------------------------
The identifier's string representation varies based on the domain:
- For MD5 hashes - "N" form
- For Ink Model Internals (Strokes, Nodes, Group Nodes etc.) - "D" form
"""
SEPARATOR: str = "\n"
def __init__(self, identifier: uuid.UUID, method: IdentifiableMethod = IdentifiableMethod.MD5):
self.__identifier: uuid.UUID = identifier
self.__method: IdentifiableMethod = method
if identifier is not None and type(identifier) is not uuid.UUID:
raise TypeError('Identifier must be of type UUID. [Type: {}]'.format(type(identifier)))
@property
def id(self) -> uuid.UUID:
"""Identifier of object. (`UUID`, read-only)"""
if self.__identifier is None:
self.__identifier = self.__generate_id__()
return self.__identifier
@property
def id_h_form(self) -> str:
"""
Hyphenated Hexadecimal String Representation (H-Form) (`str`, read-only)
Examples
--------
32 digits separated by hyphens: 00000000-0000-0000-0000-000000000000, e.g., fa703908-71c8-4d91-b83c-9b56549043ca
"""
return Identifier.uimid_to_h_form(self.id)
@property
def id_s_form(self):
"""
Simple Hexadecimal String Representation (S-Form). (`str`, read-only)
Examples
--------
32 digits: 00000000000000000000000000000000, e.g., fa70390871c84d91b83c9b56549043ca
"""
return Identifier.uimid_to_s_form(self.id)
@abstractmethod
def __generate_id__(self) -> uuid.UUID:
pass
@classmethod
def str_to_uimid(cls, uuid_str: str) -> uuid.UUID:
"""
Convert from string to UimID (UUID).
Parameters
----------
uuid_str: `str`
UimID as s-form
Returns
-------
uuid: `UUID`
UUID from string
Raises
------
FormatException
If UUID string is not valid.
"""
try:
return uuid.UUID(uuid_str)
except ValueError as e:
raise InkModelException(f'{uuid_str} is not a valid UUID. [Exception:= {str(e)}]')
@classmethod
def uimid_to_h_form(cls, uimid: uuid.UUID) -> str:
"""
Convert Uim-ID in h-form.
Parameters
----------
uimid: `UUID`
UUID
Returns
-------
h_form: str
h-form of UimID
Raises
------
FormatException
If UUID string is not valid.
"""
if uimid is None:
return ''
uuid_str: str = uimid.hex
return f'{uuid_str[:4]}-{uuid_str[4:6]}-{uuid_str[6:8]}-{uuid_str[8:10]}-{uuid_str[10:]}'
@classmethod
def uimid_to_s_form(cls, uimid: uuid.UUID) -> str:
"""
Convert Uim-ID in s-form.
Parameters
----------
uimid: `UUID`
UUID
Returns
-------
s_form: `str`
s-form of UimID
"""
return uimid.hex
@classmethod
def from_bytes(cls, from_bytes: bytes) -> uuid.UUID:
"""
Convert bytes array to UUID.
Parameters
----------
from_bytes: `bytes`
Byte array encoding the UUID
Returns
-------
uuid: `UUID`
Valid UUID
Raises
------
InkModelException
Bytes cannot be converted to UUID
"""
try:
return uuid.UUID(bytes_le=from_bytes)
except ValueError as e:
raise InkModelException(f'Bytes: {from_bytes} cannot be converted to UUID. [Exception:= {str(e)}]')
class HashIdentifier(Identifier):
"""
MD5-hash based Unique Identifier Generation Algorithm
-----------------------------------------------------
The described algorithm allows generation of unique identifiers based on a tag (encoded as string value) and
a collection of components.
The supported component types are:
- Integer number
- Floating-point number
- String
List of properties defined as key-value pairs; the key and value of a pair are considered to be of type string.
The described method takes a tag value and a list of components as arguments and generates a unique MD5 hash as
an 8-byte array.
Parameters
----------
identifier: `UUID`
Identifier
"""
def __init__(self, identifier: uuid.UUID = None):
super().__init__(identifier=identifier, method=IdentifiableMethod.MD5)
@abstractmethod
def __tokenize__(self) -> List[Any]:
"""
Generates a list of tokens which are identify the object. The subclass defines the order of tokens.
Returns
-------
tokens: `List[Any]`
List of tokenized items
"""
pass
def __generate_id__(self) -> uuid.UUID:
token: list = self.__tokenize__()
message: str = ''
for t in token:
if t is None:
message += ''
elif isinstance(t, float):
message += '{:.4f}'.format(t)
elif isinstance(t, int):
message += str(t)
elif isinstance(t, Enum):
message += str(t.name)
elif isinstance(t, uuid.UUID):
message += Identifier.uimid_to_s_form(t)
elif isinstance(t, list):
for val in sorted(t, key=lambda value: value[0]):
message += val[0]
message += Identifier.SEPARATOR
message += val[1]
message += Identifier.SEPARATOR
else:
message += str(t)
message += Identifier.SEPARATOR
md5_hash: str = hashlib.md5(message.encode(encoding='UTF-8', errors='strict')).hexdigest()
return uuid.UUID(md5_hash)
def __invalidate__(self):
"""Invalidate the hashed id if an internal value has changed."""
self.__id = None
class UUIDIdentifier(Identifier):
"""
UUID Identifier.
Parameters
----------
identifier: `UUID`
Identifier
"""
def __init__(self, identifier: uuid.UUID):
super().__init__(identifier=identifier, method=IdentifiableMethod.UUID)
@staticmethod
def id_generator() -> uuid.UUID:
"""
UUID generator function.
Returns
-------
random: UUID
Random generated UUID
"""
return uuid.uuid4()
def __generate_id__(self) -> uuid.UUID:
return UUIDIdentifier.id_generator()
def __repr__(self):
return f'<UUIDIdentifiable : [s-form:={self.id_s_form}, h-form:={self.id_h_form}]>'
Classes
class HashIdentifier (identifier: uuid.UUID = None)
-
MD5-hash based Unique Identifier Generation Algorithm
The described algorithm allows generation of unique identifiers based on a tag (encoded as string value) and a collection of components.
The supported component types are: - Integer number - Floating-point number - String
List of properties defined as key-value pairs; the key and value of a pair are considered to be of type string. The described method takes a tag value and a list of components as arguments and generates a unique MD5 hash as an 8-byte array.
Parameters
identifier
:UUID
- Identifier
Expand source code
class HashIdentifier(Identifier): """ MD5-hash based Unique Identifier Generation Algorithm ----------------------------------------------------- The described algorithm allows generation of unique identifiers based on a tag (encoded as string value) and a collection of components. The supported component types are: - Integer number - Floating-point number - String List of properties defined as key-value pairs; the key and value of a pair are considered to be of type string. The described method takes a tag value and a list of components as arguments and generates a unique MD5 hash as an 8-byte array. Parameters ---------- identifier: `UUID` Identifier """ def __init__(self, identifier: uuid.UUID = None): super().__init__(identifier=identifier, method=IdentifiableMethod.MD5) @abstractmethod def __tokenize__(self) -> List[Any]: """ Generates a list of tokens which are identify the object. The subclass defines the order of tokens. Returns ------- tokens: `List[Any]` List of tokenized items """ pass def __generate_id__(self) -> uuid.UUID: token: list = self.__tokenize__() message: str = '' for t in token: if t is None: message += '' elif isinstance(t, float): message += '{:.4f}'.format(t) elif isinstance(t, int): message += str(t) elif isinstance(t, Enum): message += str(t.name) elif isinstance(t, uuid.UUID): message += Identifier.uimid_to_s_form(t) elif isinstance(t, list): for val in sorted(t, key=lambda value: value[0]): message += val[0] message += Identifier.SEPARATOR message += val[1] message += Identifier.SEPARATOR else: message += str(t) message += Identifier.SEPARATOR md5_hash: str = hashlib.md5(message.encode(encoding='UTF-8', errors='strict')).hexdigest() return uuid.UUID(md5_hash) def __invalidate__(self): """Invalidate the hashed id if an internal value has changed.""" self.__id = None
Ancestors
- Identifier
- abc.ABC
Subclasses
- PathPointProperties
- Environment
- InkInputProvider
- InputContext
- InputDevice
- SensorChannel
- SensorChannelsContext
- SensorContext
Class variables
var SEPARATOR : str
Inherited members
class IdentifiableMethod (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
Different ID encoding methods
Expand source code
class IdentifiableMethod(Enum): """Different ID encoding methods """ UUID = 0 MD5 = 1
Ancestors
- enum.Enum
Class variables
var MD5
var UUID
class Identifier (identifier: uuid.UUID, method: IdentifiableMethod = IdentifiableMethod.MD5)
-
Internal UIM 128bit Identifier (UimID)
UimID is a 128-bit number used internally by the implementations.
It can be parsed from strings in the following formats:
Simple Hexadecimal String Representation (S-Form)
The representation of a UimID value is the hex-decimal string representation of the 128-bit number, assuming that it's encoded using Big Endian byte ordering.
For example: fa70390871c84d91b83c9b56549043ca
Hyphenated Hexadecimal String Representation (H-Form)
This representation of a UimID value has the following codec:
- - - - Assuming that the UimID 128-bit value is encoded using Big Endian byte ordering, it is split into 5 groups of bytes and each group is formatted as hexadecimal number.
| | Part 1 | Part 2 | Part 3 | Part 4 | Part 5 |
| Bytes | 0, 1, 2, 3 | 4, 5 | 6,7 | 8, 9 | 10, 11, 12, 13, 14, 15 |
| S | 32 digits: 00000000000000000000000000000000 | | | E.g., fa70390871c84d91b83c9b56549043ca |
| H | 32 digits separated by hyphens: 00000000-0000-0000-0000-000000000000 | | | E.g., fa703908-71c8-4d91-b83c-9b56549043ca |
The identifier's string representation varies based on the domain: - For MD5 hashes - "N" form - For Ink Model Internals (Strokes, Nodes, Group Nodes etc.) - "D" form
Expand source code
class Identifier(ABC): """ Internal UIM 128bit Identifier (UimID) -------------------------------------- UimID is a 128-bit number used internally by the implementations. It can be parsed from strings in the following formats: Simple Hexadecimal String Representation (S-Form) ------------------------------------------------- The representation of a UimID value is the hex-decimal string representation of the 128-bit number, assuming that it's encoded using Big Endian byte ordering. For example: fa70390871c84d91b83c9b56549043ca Hyphenated Hexadecimal String Representation (H-Form) ----------------------------------------------------- This representation of a UimID value has the following codec: <part1>-<part2>-<part3>-<part4>-<part5> Assuming that the UimID 128-bit value is encoded using Big Endian byte ordering, it is split into 5 groups of bytes and each group is formatted as hexadecimal number. --------------------------------------------------------------------------- | | Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | --------------------------------------------------------------------------- | Bytes | 0, 1, 2, 3 | 4, 5 | 6,7 | 8, 9 | 10, 11, 12, 13, 14, 15 | --------------------------------------------------------------------------- ----------------------------------------------------------------------------- | S | 32 digits: 00000000000000000000000000000000 | | | E.g., fa70390871c84d91b83c9b56549043ca | ----------------------------------------------------------------------------- | H | 32 digits separated by hyphens: 00000000-0000-0000-0000-000000000000 | | | E.g., fa703908-71c8-4d91-b83c-9b56549043ca | ----------------------------------------------------------------------------- The identifier's string representation varies based on the domain: - For MD5 hashes - "N" form - For Ink Model Internals (Strokes, Nodes, Group Nodes etc.) - "D" form """ SEPARATOR: str = "\n" def __init__(self, identifier: uuid.UUID, method: IdentifiableMethod = IdentifiableMethod.MD5): self.__identifier: uuid.UUID = identifier self.__method: IdentifiableMethod = method if identifier is not None and type(identifier) is not uuid.UUID: raise TypeError('Identifier must be of type UUID. [Type: {}]'.format(type(identifier))) @property def id(self) -> uuid.UUID: """Identifier of object. (`UUID`, read-only)""" if self.__identifier is None: self.__identifier = self.__generate_id__() return self.__identifier @property def id_h_form(self) -> str: """ Hyphenated Hexadecimal String Representation (H-Form) (`str`, read-only) Examples -------- 32 digits separated by hyphens: 00000000-0000-0000-0000-000000000000, e.g., fa703908-71c8-4d91-b83c-9b56549043ca """ return Identifier.uimid_to_h_form(self.id) @property def id_s_form(self): """ Simple Hexadecimal String Representation (S-Form). (`str`, read-only) Examples -------- 32 digits: 00000000000000000000000000000000, e.g., fa70390871c84d91b83c9b56549043ca """ return Identifier.uimid_to_s_form(self.id) @abstractmethod def __generate_id__(self) -> uuid.UUID: pass @classmethod def str_to_uimid(cls, uuid_str: str) -> uuid.UUID: """ Convert from string to UimID (UUID). Parameters ---------- uuid_str: `str` UimID as s-form Returns ------- uuid: `UUID` UUID from string Raises ------ FormatException If UUID string is not valid. """ try: return uuid.UUID(uuid_str) except ValueError as e: raise InkModelException(f'{uuid_str} is not a valid UUID. [Exception:= {str(e)}]') @classmethod def uimid_to_h_form(cls, uimid: uuid.UUID) -> str: """ Convert Uim-ID in h-form. Parameters ---------- uimid: `UUID` UUID Returns ------- h_form: str h-form of UimID Raises ------ FormatException If UUID string is not valid. """ if uimid is None: return '' uuid_str: str = uimid.hex return f'{uuid_str[:4]}-{uuid_str[4:6]}-{uuid_str[6:8]}-{uuid_str[8:10]}-{uuid_str[10:]}' @classmethod def uimid_to_s_form(cls, uimid: uuid.UUID) -> str: """ Convert Uim-ID in s-form. Parameters ---------- uimid: `UUID` UUID Returns ------- s_form: `str` s-form of UimID """ return uimid.hex @classmethod def from_bytes(cls, from_bytes: bytes) -> uuid.UUID: """ Convert bytes array to UUID. Parameters ---------- from_bytes: `bytes` Byte array encoding the UUID Returns ------- uuid: `UUID` Valid UUID Raises ------ InkModelException Bytes cannot be converted to UUID """ try: return uuid.UUID(bytes_le=from_bytes) except ValueError as e: raise InkModelException(f'Bytes: {from_bytes} cannot be converted to UUID. [Exception:= {str(e)}]')
Ancestors
- abc.ABC
Subclasses
Class variables
var SEPARATOR : str
Static methods
def from_bytes(from_bytes: bytes) ‑> uuid.UUID
-
Convert bytes array to UUID.
Parameters
from_bytes
:bytes
- Byte array encoding the UUID
Returns
uuid
:UUID
- Valid UUID
Raises
InkModelException
- Bytes cannot be converted to UUID
Expand source code
@classmethod def from_bytes(cls, from_bytes: bytes) -> uuid.UUID: """ Convert bytes array to UUID. Parameters ---------- from_bytes: `bytes` Byte array encoding the UUID Returns ------- uuid: `UUID` Valid UUID Raises ------ InkModelException Bytes cannot be converted to UUID """ try: return uuid.UUID(bytes_le=from_bytes) except ValueError as e: raise InkModelException(f'Bytes: {from_bytes} cannot be converted to UUID. [Exception:= {str(e)}]')
def str_to_uimid(uuid_str: str) ‑> uuid.UUID
-
Convert from string to UimID (UUID).
Parameters
uuid_str
:str
- UimID as s-form
Returns
uuid
:UUID
- UUID from string
Raises
FormatException
- If UUID string is not valid.
Expand source code
@classmethod def str_to_uimid(cls, uuid_str: str) -> uuid.UUID: """ Convert from string to UimID (UUID). Parameters ---------- uuid_str: `str` UimID as s-form Returns ------- uuid: `UUID` UUID from string Raises ------ FormatException If UUID string is not valid. """ try: return uuid.UUID(uuid_str) except ValueError as e: raise InkModelException(f'{uuid_str} is not a valid UUID. [Exception:= {str(e)}]')
def uimid_to_h_form(uimid: uuid.UUID) ‑> str
-
Convert Uim-ID in h-form.
Parameters
uimid
:UUID
- UUID
Returns
h_form
:str
- h-form of UimID
Raises
FormatException
- If UUID string is not valid.
Expand source code
@classmethod def uimid_to_h_form(cls, uimid: uuid.UUID) -> str: """ Convert Uim-ID in h-form. Parameters ---------- uimid: `UUID` UUID Returns ------- h_form: str h-form of UimID Raises ------ FormatException If UUID string is not valid. """ if uimid is None: return '' uuid_str: str = uimid.hex return f'{uuid_str[:4]}-{uuid_str[4:6]}-{uuid_str[6:8]}-{uuid_str[8:10]}-{uuid_str[10:]}'
def uimid_to_s_form(uimid: uuid.UUID) ‑> str
-
Convert Uim-ID in s-form.
Parameters
uimid
:UUID
- UUID
Returns
s_form
:str
- s-form of UimID
Expand source code
@classmethod def uimid_to_s_form(cls, uimid: uuid.UUID) -> str: """ Convert Uim-ID in s-form. Parameters ---------- uimid: `UUID` UUID Returns ------- s_form: `str` s-form of UimID """ return uimid.hex
Instance variables
var id : uuid.UUID
-
Identifier of object. (
UUID
, read-only)Expand source code
@property def id(self) -> uuid.UUID: """Identifier of object. (`UUID`, read-only)""" if self.__identifier is None: self.__identifier = self.__generate_id__() return self.__identifier
var id_h_form : str
-
Hyphenated Hexadecimal String Representation (H-Form) (
str
, read-only)Examples
32 digits separated by hyphens: 00000000-0000-0000-0000-000000000000, e.g., fa703908-71c8-4d91-b83c-9b56549043ca
Expand source code
@property def id_h_form(self) -> str: """ Hyphenated Hexadecimal String Representation (H-Form) (`str`, read-only) Examples -------- 32 digits separated by hyphens: 00000000-0000-0000-0000-000000000000, e.g., fa703908-71c8-4d91-b83c-9b56549043ca """ return Identifier.uimid_to_h_form(self.id)
var id_s_form
-
Simple Hexadecimal String Representation (S-Form). (
str
, read-only)Examples
32 digits: 00000000000000000000000000000000, e.g., fa70390871c84d91b83c9b56549043ca
Expand source code
@property def id_s_form(self): """ Simple Hexadecimal String Representation (S-Form). (`str`, read-only) Examples -------- 32 digits: 00000000000000000000000000000000, e.g., fa70390871c84d91b83c9b56549043ca """ return Identifier.uimid_to_s_form(self.id)
class InkModelException (*args, **kwargs)
-
Common base class for all non-exit exceptions.
Expand source code
class InkModelException(Exception): pass
Ancestors
- builtins.Exception
- builtins.BaseException
class UUIDIdentifier (identifier: uuid.UUID)
-
UUID Identifier.
Parameters
identifier
:UUID
- Identifier
Expand source code
class UUIDIdentifier(Identifier): """ UUID Identifier. Parameters ---------- identifier: `UUID` Identifier """ def __init__(self, identifier: uuid.UUID): super().__init__(identifier=identifier, method=IdentifiableMethod.UUID) @staticmethod def id_generator() -> uuid.UUID: """ UUID generator function. Returns ------- random: UUID Random generated UUID """ return uuid.uuid4() def __generate_id__(self) -> uuid.UUID: return UUIDIdentifier.id_generator() def __repr__(self): return f'<UUIDIdentifiable : [s-form:={self.id_s_form}, h-form:={self.id_h_form}]>'
Ancestors
- Identifier
- abc.ABC
Subclasses
Class variables
var SEPARATOR : str
Static methods
def id_generator() ‑> uuid.UUID
-
UUID generator function.
Returns
random
:UUID
- Random generated UUID
Expand source code
@staticmethod def id_generator() -> uuid.UUID: """ UUID generator function. Returns ------- random: UUID Random generated UUID """ return uuid.uuid4()
Inherited members