Source code for musif.cache.utils

import builtins
import pickle
import weakref
from pathlib import PurePath, Path
from typing import Any, List, Optional, Tuple

import music21 as m21
import pandas as pd

from musif.cache.cache import MethodCache, ObjectReference, SmartModuleCache


[docs] class FileCacheIntoRAM: """ This class simply stores a dictionary of key-value. In `musif`, it is used to cache the objects (values) coming from the parsing of files (whose names are the keys). It is never written to disk and only kept into RAM. """ def __init__(self, capacity: int): self._capacity = capacity self._items = [] def put(self, key: str, value: Any): if self.full: del self._items[0] self._items.append({"key": key, "value": value}) def get(self, key: str) -> Optional[Any]: for item in self._items: if item["key"] == key: return item["value"] return None def clear(self) -> None: self._items.clear() @property def full(self) -> bool: return len(self._items) == self._capacity
[docs] def iscache(obj1): """ Check if `obj1` is a `SmartModuleCache` or a `MethodCache` object """ if type(obj1) in (MethodCache, SmartModuleCache): return True return False
[docs] def isinstance(obj1, cls): """ Check if obj1 is instance of `cls`. This function grants that `SmartModuleCache` objects are checked against their reference objects. """ if type(obj1) is SmartModuleCache: t = obj1.cache["_type"] return builtins.issubclass(t, cls) else: return builtins.isinstance(obj1, cls)
[docs] def hasattr(obj1, attr): """ Check if `obj1` has `attr`. This function grants that `SmartModuleCache` objects are checked against their cache or reference objects. """ if type(obj1) is SmartModuleCache: ref = obj1.cache["_reference"].reference if ref is not None: return builtins.hasattr(ref, attr) else: return attr in obj1.cache else: return builtins.hasattr(obj1, attr)
[docs] def wrap_module_objects( obj: Any, target_addresses: List[str] = ["music21"], resurrect_reference: Optional[Tuple] = None, parent: Optional[ObjectReference] = None, name: Tuple[str] = ("",), args: Tuple[Optional[Tuple]] = (None,), ): """ Returns the object wrapped with `SmartModuleCache` class if it was defined in one of the `target_addresses` If `obj` is a list or a tuple, e new list/tuple this function works recursively on their objects. If the object is an instance of `weakref.ReferenceType` this function converts the object to a regular reference and then applies the wrapping. """ if isinstance(obj, weakref.ReferenceType): __import__('ipdb').set_trace() return obj() __module = obj.__class__.__module__ for module in target_addresses: if __module.startswith(module): return SmartModuleCache( reference=obj, target_addresses=target_addresses, resurrect_reference=resurrect_reference, parent=parent, name=name, args=args, ) if isinstance(obj, (list, tuple)): ret = [ wrap_module_objects( v, target_addresses, resurrect_reference, parent, (*name, "__getitem__"), (*args, (i,)), ) for i, v in enumerate(obj) ] if isinstance(obj, list): return ret elif isinstance(obj, tuple): return tuple(ret) return obj
[docs] def store_score_df(score, fname): """ Stores `score` into `fname` (a file-like object, a string or a Path object) using dataframes and returns the object saved. The returned object is a dictionary with keys the name of the parts and values dataframes with the following columns: * "Type": A string identifying the type of object. Possible values: ``"Note"``, ``"Rest"``, ``"Measure"``, ``"Time Signature"`` * "Name": A string with the name of the note in Common Western Notation or with the time signature string for time signatures; for measures and rests, the value ``"-"`` is used. * "Value": The midi pitch for notes, -1 for others * "Measure Onset": The beat position of the object in reference to the beginning of the measure, -1 for measures * "Part Onset": The onset position of the object in reference to the beginning of the part * "Duration": The duration of the object, -1 for time signatures * "Tie": If a tie is applied to the note, its type is there (one of ``"start"``, ``"continue"``, ``"stop"``), otherwise ``"-"`` is used """ def append_note(alist, note, offset, type, pitch=None, name=None): onset = offset + note.offset dur = note.duration.quarterLength m_onset = note.offset if pitch is None: pitch = note.pitch.midi if name is None: name = note.nameWithOctave if note.tie is not None: tie = note.tie.type else: tie = "-" alist.append((type, name, pitch, m_onset, onset, dur, tie)) score_dict = {} for part in score.parts: data_part = [] for measure in part.getElementsByClass(m21.stream.base.Measure): offset = measure.offset data_part.append(("Measure", "-", -1, -1, offset, measure.duration.quarterLength, "-")) ts = measure.timeSignature if ts is not None: data_part.append( ( "Time Signature", ts.ratioString, -1, ts.offset, ts.offset + offset, -1, "-", ) ) for note in measure.flat.notesAndRests: if isinstance(note, m21.note.Note): append_note(data_part, note, offset, "Note") elif isinstance(note, m21.note.Rest): append_note(data_part, note, offset, "Rest", name="-", pitch=-1) elif isinstance(note, m21.chord.Chord): for note in note.notes: append_note(data_part, note, offset, "Note") else: raise Exception( f"Cannot handle this type of note: {note.cache['_type']}" ) data_part = pd.DataFrame( data_part, columns=( "Type", "Name", "Value", "Measure Onset", "Part Onset", "Duration", "Tie", ), ) score_dict[part.partName] = data_part if isinstance(fname, (PurePath, str)): fname = Path(fname) fname.parent.mkdir(exist_ok=True) fname = open(fname, "wb") pickle.dump(score_dict, fname) return score_dict
[docs] def load_score_df(fname): """ Loads a score object saved with `store_score_df` from a string or Path pointing to the file or from a file-like object. Return a dictionary of dataframes """ if isinstance(fname, (PurePath, str)): fname = Path(fname) fname = open(fname, "rb") return pickle.load(fname)