42 lines
1.5 KiB
Python
42 lines
1.5 KiB
Python
"""Composition read boundary, independent of optional audition/reference tools."""
|
|
from __future__ import annotations
|
|
import contextlib
|
|
import contextvars
|
|
import os
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
ACTIVE=contextvars.ContextVar('composition_reads',default=None)
|
|
|
|
|
|
def _read(event,args):
|
|
scope=ACTIVE.get()
|
|
if scope is None or event!='open':return
|
|
name,mode,flags=args
|
|
if not isinstance(name,(str,bytes,os.PathLike)):return
|
|
# r+ and w+ can read; they must obey the same boundary as a plain read.
|
|
if flags & os.O_WRONLY:return
|
|
path=Path(os.fsdecode(name)).resolve()
|
|
data,code,cache,compiled,reads=scope
|
|
python_runtime=any(path.is_relative_to(Path(root).resolve()) for root in (sys.base_prefix,sys.prefix))
|
|
allowed=path.is_relative_to(data) or path.is_relative_to(code) or python_runtime
|
|
if cache and path.is_relative_to(cache):allowed=True
|
|
# Only the content-addressed generic catalog directory is shared by roles.
|
|
# Concrete musical caches remain confined to the requested role.
|
|
if compiled and path.is_relative_to(compiled):allowed=True
|
|
if not allowed:raise ValueError('Forbidden composition input: '+str(path))
|
|
reads.add(str(path))
|
|
|
|
|
|
sys.addaudithook(_read)
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def composition(data,role,cache=None):
|
|
reads=set()
|
|
code=Path(__file__).resolve().parent
|
|
token=ACTIVE.set((Path(data).resolve(),code,Path(cache).resolve()/role if cache else None,
|
|
Path(cache).resolve()/'_resources' if cache else None,reads))
|
|
try:yield reads
|
|
finally:ACTIVE.reset(token)
|