View Single Post
Old 07-05-2023, 07:21 AM   #1120
capink
Wizard
capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.
 
Posts: 1,199
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
Quote:
Originally Posted by Venia Legendi View Post
Trying to use an old chain (which has worked AFAIK), I get "EXCEPTION: Formatter: Unknown function set_persistent_vars"?

Problem in front of the screen?
Persistent vars functions were removed after the introduction of python templates, which provides better solution if you know python.

I thought no one used them. But if you want to get them back, copy the code below into your module editor:

Code:
from calibre.utils.config import JSONConfig
from calibre_plugins.action_chains.templates.functions.base import TemplateFunction

###########################################
# Persistent Vars
###########################################

persistent_storage = JSONConfig('plugins/action_chains/action-chains-persistent-storage')

# initialize root namespace if not present
if not persistent_storage.get('root_namespace'):
    persistent_storage['root_namespace'] = {}

class VarStorage(object):

    def __init__(self):
        self.root_namespace = persistent_storage['root_namespace']
        # prefixes are used to avoid vars overwriting namespaces
        self.var_type = 'VAR'
        self.ns_type = 'NS'

    # Naming rules : {{{{
    def mangle_name(self, name, typ):
        if not self.validate_name(name):
            raise ValueError('Invalid {} name: {}'.format(typ, name))
        prefix = '_{}_'.format(typ)
        name = name if name.startswith(prefix) else prefix + name
        return name

    def unmangle_name(self, name, typ):
        prefix = '_{}_'.format(typ)
        if name.startswith(prefix):
            name = name.replace(prefix, '')
        return name

    def is_var(self, name):
        prefix = '_{}_'.format(self.var_type)
        if name.startswith(prefix):
            return True
        return False

    def is_namespace(self, name):
        prefix = '_{}_'.format(self.ns_type)
        if name.startswith(prefix):
            return True
        return False

    def validate_namespace_path(self, namespace_path):
        for node in namespace_path:
            if not self.validate_name(node):
                return False
        return True

    def validate_name(self, name):
        if not name.replace('_', '').isalnum():
            return False
        return True

    def mangle_namespace_path(self, namespace_path):
        if not self.validate_namespace_path(namespace_path):
            raise ValueError('Invalid namespace path: {}'.format(namespace_path))
        mangled_path = []
        for node in namespace_path:
            mangled_node = self.mangle_name(node, self.ns_type)
            mangled_path.append(mangled_node)
        return mangled_path
    # }}}}

    def commit(self):
        persistent_storage.commit()

    def get_namespace(self, namespace_path=[]):
        mangled_path = self.mangle_namespace_path(namespace_path)
        ns = self.root_namespace
        for node in mangled_path:
            try:
                ns = ns[node]
            except KeyError as e:
                raise ValueError('Namespace cannot be found: {}'.format(namespace_path))
        return ns

    def create_namespace_if_not_present(self, namespace_path=[]):
        mangled_path = self.mangle_namespace_path(namespace_path)
        ns = self.root_namespace
        for node in mangled_path:
            if not ns.get(node):
                ns[node] = {}
            ns = ns[node]

    def get_var(self, var_name, namespace_path=[]):
        try:
            namespace = self.get_namespace(namespace_path)
        except:
            return ''
        name = self.mangle_name(var_name, self.var_type)
        return namespace.get(name, '')

    def set_var(self, var_name, var_value, namespace_path=[]):
        if not isinstance(var_value, str):
            raise ValueError('Persistent variable values can only of string type')
        self.create_namespace_if_not_present(namespace_path)
        namespace = self.get_namespace(namespace_path)
        name = self.mangle_name(var_name, self.var_type)
        namespace[name] = var_value
        self.commit()

    def list_vars(self, namespace_path=[]):
        namespace = self.get_namespace(namespace_path)
        all_vars = []
        for name in namespace.keys():
            if self.is_var(name):
                var = self.unmangle_name(name, self.var_type)
                all_vars.append(var)
        return all_vars

    def list_namespaces(self, namespace_path=[]):
        if not namespace_path:
            namespace = self.root_namespace
        else:
            namespace = self.get_namespace(namespace_path)
        all_ns = []
        for name in namespace.keys():
            if self.is_namespace(name):
                ns = self.unmangle_name(name, self.ns_type)
                all_ns.append(ns)
        return all_ns

    def delete_namespace(self, namespace_path=[]):
        if not namespace_path:
            self.root_namespace.clear()
        else:
            if len(namespace_path) == 1:
                parent = self.root_namespace
            elif len(namespace_path) > 1:
                parent_path = namespace_path[:-1]
                parent = self.get_namespace(parent_path)
            mangled_name = self.mangle_name(namespace_path[-1], self.ns_type)
            del parent[mangled_name]
        self.commit()

    def delete_var(self, var_name, namespace_path=[]):
        namespace = self.get_namespace(namespace_path)
        mangled_name = self.mangle_name(var_name, self.var_type)
        del namespace[mangled_name]
        self.commit()

persistent_vars = VarStorage()


###########################################
# Persistent Vars Template Functions
###########################################

class PersistentVars(TemplateFunction):

    doc = _('Retrieve value for variable stored in persistent memory using set_persistent_vars()\n'
            'Usage: persistent_vars(var_name, [namespace])\n'
            '`var_name`: name of the variable\n'
            '`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
            'are possible and are represented by dot notation e.g. namespace1.namspace2 '
            'If no namespace is provided, the default namespace will be used.\n'
            'This function is part of the action chain plugin and will not work elsewhere.')
    name = 'persistent_vars'
    arg_count = -1

    def evaluate(self, formatter, kwargs, mi, locals, *args):
        if len(args) not in [1, 2]:
            raise ValueError('Incorrect number of arguments')
        var_name = args[0]
        if len(args) == 2:
            namespace_path = args[1].split('.')
        else:
            namespace_path = []
        if not persistent_vars.validate_namespace_path(namespace_path):
            raise ValueError('Namespaces must contain only letter, numbers and underscores')
        try:
            return persistent_vars.get_var(var_name, namespace_path)
        except:
            # if namespace cannot be found
            return ''

class SetPersistentVars(TemplateFunction):

    doc = _('Set variables to presistent memory\n'
            'Usage: set_persistent_vars(var_name, var_value, [namespace])\n'
            '`var_name`: name of the variable\n'
            '`var_value`: value of the variable\n'
            '`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
            'are possible and are represented by dot notation e.g. namespace1.namspace2 '
            'If no namespace is provided, the default namespace will be used.\n'
            'This function is part of the action chain plugin and will not work elsewhere.')
    name = 'set_persistent_vars'
    arg_count = -1

    def evaluate(self, formatter, kwargs, mi, locals, *args):
        if len(args) not in [2, 3]:
            raise ValueError('Incorrect number of arguments')
        var_name = args[0]
        var_value = args[1]
        if not persistent_vars.validate_name(var_name):
            raise ValueError('Invalid variable name: {}'.format(var_name))
        if len(args) == 3:
            namespace_path = args[2].split('.')
        else:
            namespace_path = []
        if not persistent_vars.validate_namespace_path(namespace_path):
            raise ValueError('Namespaces must contain only letter, numbers and underscores')
        # We don't want to write vars when the user is typing in the template
        if not self.context == 'template_dialog_mode':
            persistent_vars.set_var(var_name, var_value, namespace_path)
        return ''

class DeletePersistentVars(TemplateFunction):

    doc = _('Delete variable from presistent memory\n'
            'Usage: set_persistent_vars(var_name, var_value, [namespace])\n'
            '`var_name`: name of the variable\n'
            '`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
            'are possible and are represented by dot notation e.g. namespace1.namspace2 '
            'If no namespace is provided, the default namespace will be used.\n'
            'This function is part of the action chain plugin and will not work elsewhere.')
    name = 'delete_persistent_vars'
    arg_count = -1

    def evaluate(self, formatter, kwargs, mi, locals, *args):
        if len(args) not in [1, 2]:
            raise ValueError('Incorrect number of arguments')
        var_name = args[0]
        if not persistent_vars.validate_name(var_name):
            raise ValueError('Invalid variable name: {}'.format(var_name))
        if len(args) == 2:
            namespace_path = args[1].split('.')
        else:
            namespace_path = []
        if not persistent_vars.validate_namespace_path(namespace_path):
            raise ValueError('Namespaces must contain only letter, numbers and underscores')
        # We don't want to delete vars when the user is typing in the template
        if not self.context == 'template_dialog_mode':
            try:
                persistent_vars.delete_var(var_name, namespace_path)
            except Exception as e:
                import traceback
                print(traceback.format_exc())
        return ''

class ListPersistentVars(TemplateFunction):

    doc = _('List variables stored in presistent memory using set_persistent_vars()\n'
            'Usage: list_persistent_vars([namespace])\n'
            '`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
            'are possible and are represented by dot notation e.g. namespace1.namspace2 '
            'If no namespace is provided, the default namespace will be used.\n'
            'This function is part of the action chain plugin and will not work elsewhere.')
    name = 'list_persistent_vars'
    arg_count = -1

    def evaluate(self, formatter, kwargs, mi, locals, *args):
        if len(args) not in [0, 1]:
            raise ValueError('Incorrect number of arguments')
        if len(args) == 1:
            namespace_path = args[0].split('.')
        else:
            namespace_path = []
        if not persistent_vars.validate_namespace_path(namespace_path):
            raise ValueError('Namespaces must contain only letter, numbers and underscores')
        all_vars = persistent_vars.list_vars(namespace_path)
        return ','.join(all_vars)

class ListPersistentNamespaces(TemplateFunction):

    doc = _('List namespaces in presistent memory used by set_persistent_vars()\n'
            'Usage: list_persistent_namespaces([namespace])\n'
            '`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
            'are possible and are represented by dot notation e.g. namespace1.namspace2 '
            'If no namespace is provided, all top level namespaces will be listed.\n'
            'This function is part of the action chain plugin and will not work elsewhere.')
    name = 'list_persistent_namespaces'
    arg_count = -1

    def evaluate(self, formatter, kwargs, mi, locals, *args):
        if len(args) not in [0, 1]:
            raise ValueError('Incorrect number of arguments')
        if len(args) == 1:
            namespace_path = args[0].split('.')
        else:
            namespace_path = []
        if not persistent_vars.validate_namespace_path(namespace_path):
            raise ValueError('Namespaces must contain only letter, numbers and underscores')
        all_ns = persistent_vars.list_namespaces(namespace_path)
        return ','.join(all_ns)

class DeletePersistentNamespaces(TemplateFunction):

    doc = _('Delete a namespace in presistent memory used by set_persistent_vars()\n'
            'Usage: delete_persistent_namespaces([namespace])\n'
            '`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
            'are possible and are represented by dot notation e.g. namespace1.namspace2 '
            'If no namespace is provided, the top level namespace including all namespaces and '
            'vars will be deleted\n'
            'This function is part of the action chain plugin and will not work elsewhere.')
    name = 'delete_persistent_namespaces'
    arg_count = -1

    def evaluate(self, formatter, kwargs, mi, locals, *args):
        if len(args) not in [0, 1]:
            raise ValueError('Incorrect number of arguments')
        if len(args) == 1:
            namespace_path = args[0].split('.')
        else:
            namespace_path = []
        if not persistent_vars.validate_namespace_path(namespace_path):
            raise ValueError('Namespaces must contain only letter, numbers and underscores')
        # We don't want to delete namespaces when the user is typing in the template
        if not self.context == 'template_dialog_mode':
            try:
                persistent_vars.delete_namespace(namespace_path)
            except Exception as e:
                import traceback
                print(traceback.format_exc())
        return ''
capink is offline   Reply With Quote