Source code for pdpy_lib.utilities.utils

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# **************************************************************************** #
# This file is part of the pdpy project: https://github.com/pdpy-org
# Copyright (C) 2021-22 Fede Camara Halac
# **************************************************************************** #
"""
Utilities
=========
"""

import sys
import re

__all__ = [
  "log",
  "splitAtChar",
  "splitByEscapedChar",
  "parsePdBinBuf",
  "parsePdFileLines",
  "printer",
  "checknum",
  "quit_help",
  "loadPdData",
  "loadPdFile",
]

[docs]def checknum(num): try: int(num) return True except ValueError: try: float(num) return True except ValueError: return False
# def printer2(argument): # def decorator(function): # def wrapper(*args, **kwargs): # log(0, argument) # result = function(*args, **kwargs) # return result # return wrapper # return decorator
[docs]def printer(func): def wrapper(*arg): # log(0, func.__name__) result = func(*arg) if result: # log(0, "="*80) log(0, func.__name__, repr(arg[1:] if 1 < len(arg) else arg)) return result return wrapper
# def log_decorator(func): # def wrapper(*arg): # # log(0, func.__name__) # result = func(*arg) # if result: # print("="*80) # print(func.__file__, func.__name__, repr(*arg)) # return result # return wrapper
[docs]def log(level, *argv): """ log utility with level and variable arguments This function printt to console with the following `level`: 1. Normal 2. Warning 3. Error Parameters ---------- level : :class:`int` The log level (defaults: None) """ __repr__ = " ".join(map(repr, argv)) if 0 == level: print("NORMAL", __repr__) elif 1 == level: print("DEBUG", __repr__) elif 2 == level: print("ERROR", __repr__) elif level < 0: pass else: print("WARNING", __repr__)
def findIndices(data, cond_func): """ Find the start and stop slice indices of elements in a list that satisfy a condition function ``cond_func`` Return ------ :class:`list` list of tuples (start, stop) Example ------- indices = findIndices(data, lambda x: not len(x)) """ indices = [] start_index = 0 for stop_index, datum in enumerate(data): if cond_func(datum): indices.append((start_index, stop_index)) start_index = stop_index + 1 return indices def splitByNone(data): """ Split a list by None elements This function splits a list by None elements and returns a list of lists Return ------ :class:`list` A list of lists """ indices = findIndices(data, lambda x: not len(x)) if not len(indices): return data else: # if there are indices, split the list # iterate over the indices and split accordingly return [ data[i[0]:i[1]] for i in indices ]
[docs]def splitByEscapedChar(data, char=";"): """ Split a string by escaped char This function splits a string by escaped char and returns a list of lists or the original string Parameters ---------- data : :class:`list` list to be split char : :class:`str` char to split by (defaults: ``;``) """ regex = r"(?<=\\)" + re.escape(char) idx = [i for i, d in enumerate(data) if re.search(regex,d) ] if not len(idx): return data else: result = [list(data[1+idx[i]:idx[i+1]]) for i in range(len(idx)-1)] # result = list(filter(None,result)) # do not filter out empty elements # account for the first index if len(data[:idx[0]]): if not re.search(regex, " ".join(data[:idx[0]])): result = [" ".join(data[:idx[0]])] + result return result
def splitSemi(argv): lines = [] line = '' if "\\;" in argv: for arg in argv: if arg == "\\;": lines.append(line) line = '' else: if line == '': line += arg else: line += ' ' + arg lines.append(line) else: lines = [" ".join(argv)] lines = list(filter(None, lines)) return lines
[docs]def splitAtChar(line, char=",", escaped=True, double=False): """ Split a string by a default character """ if escaped: if double: regex = r"(?<=\\\\)" + re.escape(char) # offset = 4 else: regex = r"(?<=\s\\)" + re.escape(char) # offset = 2 else: regex = r"(?<!\\)" + re.escape(char) # offset = 0 result = re.split(regex,line) return result
def tokenize(line): """ Return a list of tokens from a string """ # account for comma chararcter delimiting obj border box line = splitAtChar(line, escaped=False) if len(line) == 2: tokens = [] for t in line: tokens += splitAtChar(t, char=" ", escaped=False) else: tokens = splitAtChar(line[0], char=" ", escaped=False) # filter out empty tokens tokens = list(filter(None,tokens)) print(tokens) return tokens
[docs]def parsePdFileLines(file_lines): """ Feed in file lines and return a list with pure data lines This function returns a nodes list containing pure data lines split by the semicolon char, accounting specially for lines that span multiple rows """ pd_start = re.compile(r"^#[XNA]", re.MULTILINE) lines = [] for line in file_lines: line = line.strip() if re.search(pd_start, line): lines.append(line) else: # line does not start in a pd-way # append to the last stored line if len(line): lines[-1] += ' ' + line nodes = [ tokenize(line[:-1]) for line in lines ] return nodes
[docs]def parsePdBinBuf(binbuf): """ Feed in a pd file string and return a list with pure data lines This function returns a nodes list containing pure data lines split by the semicolon char, accounting specially for lines that span multiple rows """ pd_start = re.compile(r"(#[XNA].*);(?=\r\n)") lines = re.findall(pd_start, binbuf) nodes = [ tokenize(line) for line in list(filter(None,lines)) ] return nodes
[docs]def quit_help(msg=None, parser=None): if parser is not None: parser.print_help(sys.stderr) if msg is not None: print("_"*80) log(2,"REASON:", msg) else: log(2,"Unknown error...") sys.exit(1)
[docs]def loadPdData(encoding, filename): """ Load a Pure Data file with the given encoding """ # log(1,"Trying", encoding) with open(filename, "r", encoding=encoding) as fp: lines = [line for line in fp.readlines()] return lines, encoding
[docs]def loadPdFile(filename, encoding='utf-8'): """ Attempt to load a pd file with the correct encoding Return ------ :class:`list` A list of pure data file lines """ try: pd_data, encoding = loadPdData(encoding, filename) except UnicodeDecodeError: try: pd_data, encoding = loadPdData("ascii", filename) except UnicodeDecodeError: try: pd_data, encoding = loadPdData("latin-1", filename) except Exception as e: pd_data = None raise ValueError("Could not load input file", e) finally: return pd_data