(tested in Blender 2.79, there may be bugs so please modify use at your own risk)
"""methods to be used for joint driver scripted expressions. 
they would need to be added to bpy driver namespace
"""
"""
tested in blender 2.79
please modify use at your own risk
"""
import bpy
def addExprToDriver():
    """add expressions to driver name space
    """
    #between expression
    exprName = "expr_between"
    #replace expression if it exists
    bpy.app.driver_namespace[exprName] = eval(exprName)
    
    #blink blendshape expression
    exprName = "expr_blink"
    bpy.app.driver_namespace[exprName] = eval(exprName)
    #lip corner blendshape expression
    exprName = "expr_lipCorner"
    bpy.app.driver_namespace[exprName] = eval(exprName)
    
    #add additional driver methods here
#add wrapper functions here - to be called in driver expression
def expr_between(value, maxValue=1, minValue=-1):
    """see class for parameter description
    """
    driverObj = LimitDriver()
    return driverObj.between(value, maxValue=maxValue, minValue=minValue)
def expr_blink(value, inbetween="halfclose"):
    """see class for parameter description
    """
    driverObj = BlinkBlendshapeDriver()
    return driverObj.blink(value, inbetween=inbetween)
def expr_lipCorner(valueX, valueY, inbetween="out"):
    """see class for parameter description
    """
    driverObj = LipCornerBlendshapeDriver()
    return driverObj.lipCorner(valueX, valueY, inbetween=inbetween)
#add additional wrapper functions here
    
class LimitDriver(object):
    """drivers related to limiting movement
    """
    def between(self, value, maxValue=1, minValue=-1):
        """when used on a joint limit its movement to be between min and max
        example for brow. 
        example for lip corner/2d pupil could use two of these one for each axis for joint
        @param value (double) an unrestricted value for joint
        @param maxValue (double) maximum local value allowed
        @param minValue (double) minumum local value allowed
        """
        if value > maxValue:
            return maxValue
        if value < minValue:
            return minValue
        return value
class BlinkBlendshapeDriver(object):
    """half way and full shape blink driver
    todo: support multiple inbetween shapes
    """
    def blink(self, value, inbetween="halfclose"):
        """get weights to use to drive blink blendshape. get different weight depending on which inbetween picked
        @param value (double) value for driver slider that turns on blink blendshapes
        @param inbetween (str) which inbetween we need the driver value supported ("halfclose","fullclose")
        """
        #currently half shape is on at 0.5 and full shape is 0.  and full shape is 1 at 1.0
        halfDriverValue=0.0
        fullDriverValue=0.0
        if value <= 0.5 and value >= 0:
            #for half mapping [0,0.5] to [0,1]
            halfDriverValue = value*2.0 #at 0.5 half is 1 at 0 half is 0
            fullDriverValue = 0.0
            
        if value > 0.5:
            #turn off half
            #map (0.5,1) to (1,0)
            halfDriverValue = self._mapInterval(value, 0.5, 1.0, 1.0, 0.0)
            #turn on full
            #map (0.5,1) to (0,1)
            fullDriverValue = self._mapInterval(value, 0.5, 1.0, 0.0, 1.0)
        if value >= 1.0:
            halfDriverValue = 0.0
            fullDriverValue = 1.0
            
        #possibly could edit to support more inbetweens
        if inbetween == "halfclose":
            return halfDriverValue
        else:
            return fullDriverValue
    def _mapInterval(self, t, a, b, c, d):
        """map interval [a,b] to [c,d]
        """
        return c + ((d-c)/(b-a))*(t-a) #can check end points. when t=a get c. when t=b get d
        
class LipCornerBlendshapeDriver(object):
    """out, in, up, dn, up out, up in, dn out, dn in blendshape driver
    even if not all slots are used should have the capability
    """
    def lipCorner(self, valueX, valueY, inbetween="out"):
        """get weights for driving lip corner blendshapes
        @param valueX (double) input x value
        @param valueY (double) input y value
        @param inbetween (str) queried inbetween supported ("out","in","up","dn","upOut","dnOut","upIn","dnIn")
        """
        outDriverValue = 0.0
        inDriverValue = 0.0
        upDriverValue = 0.0
        dnDriverValue = 0.0
        upOutDriverValue = 0.0
        dnOutDriverValue = 0.0
        upInDriverValue = 0.0
        dnInDriverValue = 0.0
        #out, in
        if valueX > 0.0:
            outDriverValue = valueX
        if valueX < 0.0:
            inDriverValue = -1*valueX
        #up, dn
        if valueY > 0.0:
            upDriverValue = valueY
        if valueY < 0.0:
            dnDriverValue = -1*valueY  
        
        #up out, dn out
        if valueX > 0.0 and valueY > 0.0:
            upOutDriverValue = valueX*valueY
                 
        if valueX > 0.0 and valueY < 0.0:
            dnOutDriverValue = valueX*(-1*valueY)
        
        #up in, dn in
        if valueX < 0.0 and valueY > 0.0:
            upInDriverValue = -1*valueX*valueY
                 
        if valueX < 0.0 and valueY < 0.0:
            dnInDriverValue = (-1*valueX)*(-1*valueY)
        if inbetween == "out":
            return outDriverValue
        if inbetween == "in":
            return inDriverValue
        if inbetween == "up":
            return upDriverValue
        if inbetween == "dn":
            return dnDriverValue
        if inbetween == "upOut":
            return upOutDriverValue
        if inbetween == "dnOut":
            return dnOutDriverValue
        if inbetween == "upIn":
            return upInDriverValue
        if inbetween == "dnIn":
            return dnInDriverValue
"""
import bpy
import imp
testTool = imp.load_source("tool","/Users/Nathaniel/Documents/src_blender/python/riggingTools/faceTools/joint_driver.py") #change to path to python file
testTool.addExprToDriver()
"""
#inspired by
#https://math.stackexchange.com/questions/914823/shift-numbers-into-a-different-range
#https://docs.blender.org/manual/en/latest/animation/drivers/workflow_examples.htmlHappy Scripting! and Happy Sketching!
