<commit_before># -- coding: utf-8 --

# Copyright 2018 Olivier Scholder <o.scholder@gmail.com>

import scipy.optimize as opt
"""
Provides some useful mathematical functions which are not present in numpy.
"""

import numpy as np

def closest_arg(array, value):
    return np.argmin(np.abs(array-value))

def strictly_positify(x):
    """
    Make the result strictly positive by setting the minimum value to
    the lower allowed float value
    """
    return np.fmax(x, np.finfo(x.dtype).eps)

def positify(x):
    """
    Set to zero all negative values
    """
    return np.fmax(0, x)

def clip01(x):
    """
    clip data x between 0 and 1
    """
    return np.fmax(np.fmin(x, 1), 0)

def fact(x):
    """
    Return the factors of an integer as a list.
    Warning: This function is not a factorial!
    """
    if x < 0 or type(x) is not int:
        raise ValueError("input must be a positive integer")
    if x < 2:
        return x
    f = []
    i = 2
    while True:
        while x % i == 0:
            f.append(i)
            x /= i
        i += 1
        if x == 1:
            return f

def moving_average(x, N):
    assert len(x) > N
    c = np.cumsum(x)
    return (c[N:]-c[:-N])/N

def butter_lowpass(cutOff, fs, order=5):
    import scipy.signal
    nyq = 0.5 * fs
    normalCutoff = cutOff / nyq
    b, a = scipy.signal.butter(order, normalCutoff, btype='low', analog=True)
    return b, a

def butter_lowpass_filter(data, cutOff, fs, order=4):
    import scipy.signal
    b, a = butter_lowpass(cutOff, fs, order=order)
    y = scipy.signal.lfilter(b, a, data)
    return y

def Gauss(x, x0, s, A=None):
    old = np.geterr()
    np.seterr(all='ignore')
    R = np.exp(-(x-x0)**2/(2*s**2))
    if A is None:
        R /= (s*np.sqrt(2*np.pi))
    else:
        R *= A
    R[s==0] = (x[s==0]==x0)*1.0
    np.seterr(**old)
    return R

def Lorentz(x, x0, gamma, A=None):
    R = 1/((x-x0)**2+(.5*gamma)**2)
    if A is None:
        return .5*gamma*R/np.pi
    return A*R*(.5*gamma)**2

def CDF(x,mu,sig, Amp=1, lg=0):
    from scipy.special import erf
    g = sig*np.sqrt(2*np.log(2))
    return Amp*lg*(.5+np.arctan2(x-mu,g)/np.pi)+(1-lg)*Amp*.5*(1+erf((x-mu)/(sig*np.sqrt(2))))

def LG(x, x0, sig=None, Amp=1, lg=.5, asym=1, FWHM=None):
    assert sig is not None or FWHM is not None

    if FWHM is None:
        FWHM = 2*np.sqrt(2*np.log(2))*sig
    if sig is None:
        sig = FWHM/(2*np.sqrt(2*np.log(2)))
    Y = Amp*((1-lg)*Gauss(x,x0,sig,A=1)+lg*Lorentz(x,x0,FWHM,A=1))
    if asym!=1:
        Yr = Amp*((1-lg)*Gauss(x,x0,sig*asym,A=1)+lg*Lorentz(x,x0,FWHM*asym,A=1))
        Y[x>x0] = Yr[x>x0]
    return Y

def logistic(x, lower=0, upper=1, growth=1, x0=0, nu=1, C=1):
    return lower+(upper-lower)/(C+np.exp(-growth*(x-x0)))**(1/nu)

def fitCDF1line(A):
    line = np.zeros(A.shape[1])
    for x in range(A.shape[1]):
        popt, pcov = opt.curve_fit(CDF,
            np.arange(A.shape[0]),
            A[:,x],
            (A.shape[0]/2, 1, np.max(A[:,x])),
            bounds=(0,(A.shape[1],np.inf,np.inf))
            )
        line[x] = popt[0]
    return line

def FT(x, ufunc=np.real, real=False):
    """
    shortcut for 1D/2D Fourier Transform (real and centered)
    """
    assert isinstance(x, np.ndarray)
    if len(x.shape) == 1:
        if real:
            F = np.fft.rfft(x)
        else:
            F = np.fft.fft(x)
    elif len(x.shape) == 2:
        if real:
            F = np.fft.rfft2(x)
        else:
            F = np.fft.fft2(x)
    else:
        raise TypeError("The array should be 1D or 2D")
    return ufunc(np.fft.fftshift(F))

def binning(data, N=2, axis=0, ufunc=np.sum):
    w = int(np.floor(data.shape[axis]/N))
    r = np.copy(data)
    size = list(data.shape)
    size[axis] = w
    size = size[:axis+1]+[N]+size[axis+1:]
    r.resize(size)
    return ufunc(r, axis=axis+1)

def stat_info(data):
    import matplotlib.pyplot as plt
    D = np.ravel(data)
    U = np.unique(D)
    if len(U)>1:
        sep = np.min(U[1:]-U[:-1])
        N = min(100, int(np.ceil((np.max(D)-np.min(D))/sep)))
    else:
        N = 1

    mean = np.mean(D)
    std = np.std(D)

    fig, ax = plt.subplots(2,1,figsize=(21,4))
    ax[0].boxplot(D, 0, 'ro', 0);
    ax[1].hist(D, N, density=True);
    ax[1].axvline(mean, color='r', label='mean')
    ax[1].axvline(mean+std, color='r', linestyle='--', label='1$\\sigma$')
    ax[1].axvline(mean-std, color='r', linestyle='--', label='1$\\sigma$')
    if mean-2*std >= U[0]:
        ax[1].axvline(mean-2*std, color='r', linestyle=':', label='2$\\sigma$')
    if mean+2*std <= U[-1]:
        ax[1].axvline(mean+2*std, color='r', linestyle=':', label='2$\\sigma$')
    ax[1].legend();
    print("Stats")
    print("\tAverage:", mean)
    print("\tStandard-deviation:", std)
    print("\tMinimum:", np.min(D))
    print("\tQ1:", np.percentile(D, 25))
    print("\tMedian:", np.percentile(D, 50))
    print("\tQ3:", np.percentile(D, 75))
    print("\tMaximum:", np.max(D))

def ellipse(a,b,phi):
    """
    return the x,y coordinates of an ellipse with major axis=a and minor axis=b at angle phi(radians)
    """
    r = np.zeros(phi.shape) # if a & b is zero, result is zero
    m = np.logical_and(a!=0,b!=0)
    r[m] = a*b/np.sqrt((b*np.cos(phi[m]))**2+(a*np.sin(phi[m]))**2)
    return r

def asymm_ellipse(left, right, upper, lower, phi):
    phi = np.divmod(phi+2*np.pi,2*np.pi)[1] # Be sure phi ∈ [0,2π]
    b = np.where(phi<=np.pi, upper, lower)
    a = np.where(np.logical_or(phi<=np.pi/2, phi>=3*np.pi/2), right, left)
    m = np.logical_and(a!=0,b!=0)
    r = np.zeros(phi.shape) # if a & b is zero, result is zero
    r[m] = a[m]*b[m]/np.sqrt((b[m]*np.cos(phi[m]))**2+(a[m]*np.sin(phi[m]))**2)
    return r

def LG2D(XY, amplitude=1, angle=0, sig_x=10, sig_y=10, x0=None, y0=None, LG_x=0, LG_y=0, assym_x=1, assym_y=1, bg=0):
    """
    Return a 2D Lorentz-Gauss.
    XY: (X,Y) tuple
    A: amplitude
    a: angle
    sx: sigma for x-axis
    sy: sigma fdor y-axis
    x0,y0 : center coordinates of the peak
    lgx, lgy: Lorentz-Gauss proportion (for x,y axis)
    assym_x, assym_y: The assymetry in sig_x/sig_y for the left/right or upper/lower part of the curve
    """
    if x0 is None:
        x0 = XY[0][0,XY[0].shape[1]//2]
    if y0 is None:
        y0 = XY[1][XY[1].shape[0]//2,0]
    X1 = (XY[0]-x0)*np.cos(angle) - (XY[1]-y0)*np.sin(angle)
    Y1 = (XY[0]-x0)*np.sin(angle) + (XY[1]-y0)*np.cos(angle)

    R1 = np.sqrt(X1**2+Y1**2)
    angle = np.arctan2(Y1,X1)
    sig = asymm_ellipse(sig_x, sig_x*assym_x, sig_y, sig_y*assym_y, angle)
    gamma = np.sqrt(2*np.log(2))*sig
    LG = ellipse(LG_x+1, LG_y+1, angle)-1

    Gxy = Gauss(R1, 0, sig, 1)
    Lxy = 1/((R1/gamma)**2+1)

    f = (1-LG)*Gxy+LG*Lxy
    out = bg+amplitude*f
    return out

def LG2Da(XY, amplitude=1, angle=0, sigN=10, sigS=None, sigE=10, sigW=None, x0=None, y0=None, LGN=0, LGS=None, LGE=0, LGW=None, bg=0):
    if x0 is None:
        x0 = XY[0][0,XY[0].shape[1]//2]
    if y0 is None:
        y0 = XY[1][XY[1].shape[0]//2,0]
    if sigS is None:
        sigS = sigN
    if sigW is None:
        sigW = sigE
    if LGS is None:
        LGS = LGN
    if LGW is None:
        LGW = LGE

    X1 = (XY[0]-x0)*np.cos(angle) - (XY[1]-y0)*np.sin(angle)
    Y1 = (XY[0]-x0)*np.sin(angle) + (XY[1]-y0)*np.cos(angle)

    R1 = np.sqrt(X1**2+Y1**2)
    angle = np.arctan2(Y1, X1)
    sig = asymm_ellipse(sigW, sigE, sigN, sigS, angle)
    gamma = np.sqrt(2*np.log(2))*sig # HFHM
    LG = asymm_ellipse(LGW+1, LGE+1, LGN+1, LGS+1, angle)-1

    Gxy = Gauss(R1, 0, sig, 1)
    Lxy = 1/((R1/gamma)**2+1)

    f = (1-LG)*Gxy+LG*Lxy
    out = bg+amplitude*f
    return out

def MaxwellBoltzmann(E,T):
    from . import constants as const
    return 2*const.qe*np.sqrt(E/np.pi)*np.exp(-E/(const.kb*T))/(const.kb*T)**1.5

def Voigt(x, x0, sig, gamma, A=1):
    import scipy
    L = Lorentz(x, x0, gamma, A=1)
    G = Gauss(x, x0, sig)
    out = scipy.signal.convolve(L, G, 'same')
    out /= np.max(out)
    return A*out
<commit_msg>By default Gauss, Lorentz and LG are normalized. Harmonize Amplitude parameter as Amp<commit_after># -- coding: utf-8 --

# Copyright 2018 Olivier Scholder <o.scholder@gmail.com>

import scipy.optimize as opt
"""
Provides some useful mathematical functions which are not present in numpy.
"""

import numpy as np

def closest_arg(array, value):
    return np.argmin(np.abs(array-value))

def strictly_positify(x):
    """
    Make the result strictly positive by setting the minimum value to
    the lower allowed float value
    """
    return np.fmax(x, np.finfo(x.dtype).eps)

def positify(x):
    """
    Set to zero all negative values
    """
    return np.fmax(0, x)

def clip01(x):
    """
    clip data x between 0 and 1
    """
    return np.fmax(np.fmin(x, 1), 0)

def fact(x):
    """
    Return the factors of an integer as a list.
    Warning: This function is not a factorial!
    """
    if x < 0 or type(x) is not int:
        raise ValueError("input must be a positive integer")
    if x < 2:
        return x
    f = []
    i = 2
    while True:
        while x % i == 0:
            f.append(i)
            x /= i
        i += 1
        if x == 1:
            return f

def moving_average(x, N):
    assert len(x) > N
    c = np.cumsum(x)
    return (c[N:]-c[:-N])/N

def butter_lowpass(cutOff, fs, order=5):
    import scipy.signal
    nyq = 0.5 * fs
    normalCutoff = cutOff / nyq
    b, a = scipy.signal.butter(order, normalCutoff, btype='low', analog=True)
    return b, a

def butter_lowpass_filter(data, cutOff, fs, order=4):
    import scipy.signal
    b, a = butter_lowpass(cutOff, fs, order=order)
    y = scipy.signal.lfilter(b, a, data)
    return y

def Gauss(x, x0, s, Amp=None, **kargs):
    if 'A' in kargs:
        from warnings import warn
        warn("Parameter A is deprecated. Please use Amp in order to set the amplitude!")
        Amp = kargs['A']
    R = np.exp(-(x-x0)**2/(2*s**2))
    if Amp is None:
        R /= (s*np.sqrt(2*np.pi))
    else:
        R *= Amp
    R[s==0] = (x[s==0]==x0)*1.0
    return R

def Lorentz(x, x0, gamma, Amp=None, **kargs):
    if 'A' in kargs:
        from warnings import warn
        warn("Parameter A is deprecated. Please use Amp in order to set the amplitude!")
        Amp = kargs['A']
    R = 1/((x-x0)**2+(.5*gamma)**2)
    if Amp is None:
        return .5*gamma*R/np.pi
    return Amp*R*(.5*gamma)**2

def CDF(x,mu,sig, Amp=1, lg=0):
    from scipy.special import erf
    g = sig*np.sqrt(2*np.log(2))
    return Amp*lg*(.5+np.arctan2(x-mu,g)/np.pi)+(1-lg)*Amp*.5*(1+erf((x-mu)/(sig*np.sqrt(2))))

def LG(x, x0, sig=None, Amp=None, lg=.5, asym=1, FWHM=None):
    assert sig is not None or FWHM is not None

    if FWHM is None:
        FWHM = 2*np.sqrt(2*np.log(2))*sig
    if sig is None:
        sig = FWHM/(2*np.sqrt(2*np.log(2)))
    Y = (1-lg)*Gauss(x,x0,sig,Amp=Amp)+lg*Lorentz(x,x0,FWHM,Amp=Amp)
    if asym!=1:
        Yr = (1-lg)*Gauss(x,x0,sig*asym,Amp=Amp)+lg*Lorentz(x,x0,FWHM*asym,Amp=Amp)
        Y[x>x0] = Yr[x>x0]
    return Y

def logistic(x, lower=0, upper=1, growth=1, x0=0, nu=1, C=1):
    return lower+(upper-lower)/(C+np.exp(-growth*(x-x0)))**(1/nu)

def fitCDF1line(A):
    line = np.zeros(A.shape[1])
    for x in range(A.shape[1]):
        popt, pcov = opt.curve_fit(CDF,
            np.arange(A.shape[0]),
            A[:,x],
            (A.shape[0]/2, 1, np.max(A[:,x])),
            bounds=(0,(A.shape[1],np.inf,np.inf))
            )
        line[x] = popt[0]
    return line

def FT(x, ufunc=np.real, real=False):
    """
    shortcut for 1D/2D Fourier Transform (real and centered)
    """
    assert isinstance(x, np.ndarray)
    if len(x.shape) == 1:
        if real:
            F = np.fft.rfft(x)
        else:
            F = np.fft.fft(x)
    elif len(x.shape) == 2:
        if real:
            F = np.fft.rfft2(x)
        else:
            F = np.fft.fft2(x)
    else:
        raise TypeError("The array should be 1D or 2D")
    return ufunc(np.fft.fftshift(F))

def binning(data, N=2, axis=0, ufunc=np.sum):
    w = int(np.floor(data.shape[axis]/N))
    r = np.copy(data)
    size = list(data.shape)
    size[axis] = w
    size = size[:axis+1]+[N]+size[axis+1:]
    r.resize(size)
    return ufunc(r, axis=axis+1)

def stat_info(data):
    import matplotlib.pyplot as plt
    D = np.ravel(data)
    U = np.unique(D)
    if len(U)>1:
        sep = np.min(U[1:]-U[:-1])
        N = min(100, int(np.ceil((np.max(D)-np.min(D))/sep)))
    else:
        N = 1

    mean = np.mean(D)
    std = np.std(D)

    fig, ax = plt.subplots(2,1,figsize=(21,4))
    ax[0].boxplot(D, 0, 'ro', 0);
    ax[1].hist(D, N, density=True);
    ax[1].axvline(mean, color='r', label='mean')
    ax[1].axvline(mean+std, color='r', linestyle='--', label='1$\\sigma$')
    ax[1].axvline(mean-std, color='r', linestyle='--', label='1$\\sigma$')
    if mean-2*std >= U[0]:
        ax[1].axvline(mean-2*std, color='r', linestyle=':', label='2$\\sigma$')
    if mean+2*std <= U[-1]:
        ax[1].axvline(mean+2*std, color='r', linestyle=':', label='2$\\sigma$')
    ax[1].legend();
    print("Stats")
    print("\tAverage:", mean)
    print("\tStandard-deviation:", std)
    print("\tMinimum:", np.min(D))
    print("\tQ1:", np.percentile(D, 25))
    print("\tMedian:", np.percentile(D, 50))
    print("\tQ3:", np.percentile(D, 75))
    print("\tMaximum:", np.max(D))

def ellipse(a,b,phi):
    """
    return the x,y coordinates of an ellipse with major axis=a and minor axis=b at angle phi(radians)
    """
    r = np.zeros(phi.shape) # if a & b is zero, result is zero
    m = np.logical_and(a!=0,b!=0)
    r[m] = a*b/np.sqrt((b*np.cos(phi[m]))**2+(a*np.sin(phi[m]))**2)
    return r

def asymm_ellipse(left, right, upper, lower, phi):
    phi = np.divmod(phi+2*np.pi,2*np.pi)[1] # Be sure phi ∈ [0,2π]
    b = np.where(phi<=np.pi, upper, lower)
    a = np.where(np.logical_or(phi<=np.pi/2, phi>=3*np.pi/2), right, left)
    m = np.logical_and(a!=0,b!=0)
    r = np.zeros(phi.shape) # if a & b is zero, result is zero
    r[m] = a[m]*b[m]/np.sqrt((b[m]*np.cos(phi[m]))**2+(a[m]*np.sin(phi[m]))**2)
    return r

def LG2D(XY, amplitude=1, angle=0, sig_x=10, sig_y=10, x0=None, y0=None, LG_x=0, LG_y=0, assym_x=1, assym_y=1, bg=0):
    """
    Return a 2D Lorentz-Gauss.
    XY: (X,Y) tuple
    A: amplitude
    a: angle
    sx: sigma for x-axis
    sy: sigma fdor y-axis
    x0,y0 : center coordinates of the peak
    lgx, lgy: Lorentz-Gauss proportion (for x,y axis)
    assym_x, assym_y: The assymetry in sig_x/sig_y for the left/right or upper/lower part of the curve
    """
    if x0 is None:
        x0 = XY[0][0,XY[0].shape[1]//2]
    if y0 is None:
        y0 = XY[1][XY[1].shape[0]//2,0]
    X1 = (XY[0]-x0)*np.cos(angle) - (XY[1]-y0)*np.sin(angle)
    Y1 = (XY[0]-x0)*np.sin(angle) + (XY[1]-y0)*np.cos(angle)

    R1 = np.sqrt(X1**2+Y1**2)
    angle = np.arctan2(Y1,X1)
    sig = asymm_ellipse(sig_x, sig_x*assym_x, sig_y, sig_y*assym_y, angle)
    gamma = np.sqrt(2*np.log(2))*sig
    LG = ellipse(LG_x+1, LG_y+1, angle)-1

    Gxy = Gauss(R1, 0, sig, 1)
    Lxy = 1/((R1/gamma)**2+1)

    f = (1-LG)*Gxy+LG*Lxy
    out = bg+amplitude*f
    return out

def LG2Da(XY, amplitude=1, angle=0, sigN=10, sigS=None, sigE=10, sigW=None, x0=None, y0=None, LGN=0, LGS=None, LGE=0, LGW=None, bg=0):
    if x0 is None:
        x0 = XY[0][0,XY[0].shape[1]//2]
    if y0 is None:
        y0 = XY[1][XY[1].shape[0]//2,0]
    if sigS is None:
        sigS = sigN
    if sigW is None:
        sigW = sigE
    if LGS is None:
        LGS = LGN
    if LGW is None:
        LGW = LGE

    X1 = (XY[0]-x0)*np.cos(angle) - (XY[1]-y0)*np.sin(angle)
    Y1 = (XY[0]-x0)*np.sin(angle) + (XY[1]-y0)*np.cos(angle)

    R1 = np.sqrt(X1**2+Y1**2)
    angle = np.arctan2(Y1, X1)
    sig = asymm_ellipse(sigW, sigE, sigN, sigS, angle)
    gamma = np.sqrt(2*np.log(2))*sig # HFHM
    LG = asymm_ellipse(LGW+1, LGE+1, LGN+1, LGS+1, angle)-1

    Gxy = Gauss(R1, 0, sig, 1)
    Lxy = 1/((R1/gamma)**2+1)

    f = (1-LG)*Gxy+LG*Lxy
    out = bg+amplitude*f
    return out

def MaxwellBoltzmann(E,T):
    from . import constants as const
    return 2*const.qe*np.sqrt(E/np.pi)*np.exp(-E/(const.kb*T))/(const.kb*T)**1.5

def Voigt(x, x0, sig, gamma, A=1):
    import scipy
    L = Lorentz(x, x0, gamma, A=1)
    G = Gauss(x, x0, sig)
    out = scipy.signal.convolve(L, G, 'same')
    out /= np.max(out)
    return A*out
<|endoftext|>


<commit_before>#
# Copyright SAS Institute
#
#  Licensed under the Apache License, Version 2.0 (the License);
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
# Configuration Names for SAS - python List
# This is the list of allowed configuration definitions that can be used. The definition are defined below.
# The various options for the different access methods can be specified on the SASsession() i.e.:
# sas = SASsession(cfgname='default', options='-fullstimer', user='me')
#
# based upon the lock_down configuration option below, you may or may not be able to override option
# that are defined already. Any necessary option (like user, pw for IOM or HTTP) that are not defined will be
# prompted for at run time. To dissallow overrides of OPTION, when you don't have any specified, simply
# specify options=''. This way it's specified so it can't be overridden, even though you don't have any
# extra options you want applied.
#
# SAS_config_names=['default', 'sasother', 'sas_en', 'sas_utf8', 'http', 'ssh', 'httptest']
#
SAS_config_names = ['default', 'SASgrid',  'http', 'httptest', 'ssh', 'httpfred', 'grid', 'tdi', 'iomj', 'iomc', 'iomjwin', 'winiomj', 'winiomjwin', 'winlocal', 'gridiom', 'wingridiom']

# Configuration options for pysas - python Dict
# valid key are:
#
# 'lock_down' - True | False. True = Prevent runtime overrides of SAS_Config values below
#
SAS_config_options = {'lock_down': True}

# Configuration Definitions
#
# For STDIO and STDIO over SSH access methods
# These need path to SASHome and optional startup options - python Dict
# The default path to the sas start up script is: /opt/sasinside/SASHome/SASFoundation/9.4/sas
# A usual install path is: /opt/sasinside/SASHome
#
# Since python uses utf-8, running SAS with encoding=utf-8 is the expected use case. By default
# linux SAS runs in Latin1, which works fine as long as you stick with the lower half of the code page.
# SAS is installed with a link ('sas') to the bin/sas_en startup script, and the link can be swapped
# to point to the utf8 start up script (bin/sas_u8), or another link made (sasutf8) to point to that.
#
# valid keys are:
# 'saspath' - [REQUIRED] path to SAS startup script i.e.: /opt/sasinside/SASHome/SASFoundation/9.4/sas
# 'options' - SAS options to include in the start up command line - Python List
#
# For passwordless ssh connection, the following are also reuqired:
# 'ssh'     - [REQUIRED] the ssh command to run
# 'host'    - [REQUIRED] the host to connect to
#
default  = {'saspath': '/opt/sasinside/SASHome/SASFoundation/9.4/bin/sas_u8'
            }

tdi      = {'saspath' : '/tdi/mva-v940m4/usrlibsas/laxno/SASFoundation/9.4/bin/sas_u8'
            }

ssh      = {'saspath' : '/opt/sasinside/SASHome/SASFoundation/9.4/bin/sas_u8',
            'ssh'     : '/usr/bin/ssh',
            'host'    : 'tom64-2',
            'options' : ["-fullstimer"]
            }

# sas_en   = {'saspath': '/opt/sasinside/SASHome/SASFoundation/9.4/sas'
#             }
#
# sas_utf8 = {'saspath': '/opt/sasinside/SASHome/SASFoundation/9.4/sasutf8'
#             }
#
# sasother = {'saspath' : '/some/other/directory/SASHome/SASFoundation/9.4/sas',
#             'options' : ["-autoexec", "/my/home_dir/my_autoexec.sas", "-set",
#                          "Env_var", "Value", "-fullstimer"]
#             }

# For IOM (Grid Manager or any IOM) and Local Windows via IOM access method
# These configuration definitions are for connecting over IOM. This is designed to be used to connect to a SAS Grid, via Grid Manager
# and also to connect to a local Windows SAS session. The client side (python and java) for this access method can be either Linux or Windows.
# The STDIO access method above is only for Linux. PC SAS requires this IOM interface.
#
# The 'saspath' option triggers local Windows SAS mode. In this case neither 'iomhost' nor 'iomport' are needed. localhost is used and an available port
# is used for the connection. In this way, you can run multiple python sessions to seperate Windows SAS instances (i.e.: different notbooks in Jupyter).
#
# Since python uses utf-8, running SAS with encoding=utf-8 is the expected use case. By default Windows SAS runs in WindowsLatin1 (cp1252),
# which does not work well as utf-8. So, transcoding has been implemented in the python layer. The 'encoding' option can be specified to match
# the SAS session encoding (see https://docs.python.org/3.5/library/codecs.html#standard-encodings for python encoding values). cp1252 is appropriate
# for the default Windows SAS session encoding
#
# Since this IOM access method used the Java IOM client, a classpath is required for the java process to find the necessary jars. Use the template below
# to build out a classpath variable and assign that to the 'classpath' option in the configuration definition. The IOM client jars are delivered as part
# of a Base SAS install, so should be available in any SAS install. The saspyiom.jar is available in the saspy repo/install.
#
# valid keys are:
# 'saspath'   - [REQUIRED for local Windows connection only] path to SAS executable (sas.exe) i.e.: C:\Program Files\SASHome\SASFoundation\9.4\sas.exe
# 'java'      - [REQUIRED] the path to the java executable to use
# 'iomhost'   - [REQUIRED for remote IOM case, not local Windows] the resolvable host name, or ip to the IOM server to connect to
# 'iomport'   - [REQUIRED for remote IOM case, not local Windows] the port IOM is listening on
# 'omruser'   - not suggested [REQUIRED but PROMTED for at runtime] For local Windows SAS, this is a local user account/pw
# 'omrpw'     - really not suggested [REQUIRED but PROMTED for at runtime]
# 'encoding'  - This is the python encoding value that matches the SAS session encoding of the IOM server you are connecting to
# 'classpath' - classpath to IOM client jars and saspy client jar.
#
# build out a local classpath variable to use below
cp  =  "/opt/tom/gitlab/metis/java/lib/sas.svc.connection.jar"
cp += ":/opt/tom/gitlab/metis/java/lib/log4j.jar"
cp += ":/opt/tom/gitlab/metis/java/lib/sas.security.sspi.jar"
cp += ":/opt/tom/gitlab/metis/java/lib/sas.core.jar"
cp += ":/opt/tom/gitlab/metis/java/saspyiom.jar"

# build out a local classpath variable to use below
cp2  =  "/u/sastpw/tkpy2j/lib2/sas.svc.connection.jar"
cp2 += ":/u/sastpw/tkpy2j/lib2/log4j.jar"
cp2 += ":/u/sastpw/tkpy2j/lib2/sas.security.sspi.jar"
cp2 += ":/u/sastpw/tkpy2j/lib2/sas.core.jar"
cp2 += ":/opt/tom/gitlab/metis/java/saspyiom.jar"

iomj     = {'java'      : '/usr/bin/java',
            'omruser'   : 'sas',
            'omrpw'     : 'sas',
            'iomhost'   : 'tom64-3.na.sas.com',
            'iomport'   : 8591,
            'classpath' : cp
            }

iomjwin  = {'java'      : '/usr/bin/java',
            'omruser'   : 'sasiom1@carynt',
            'omrpw'     : '1bridge',
            'iomhost'   : 'd77422.na.sas.com',
            'iomport'   : 18591,
            'encoding'  : 'cp1252',
            'classpath' : cp
            }

gridiom  = {'java'      : '/usr/bin/java',
           #'omruser'   : 'sascnn1',
           #'omrpw'     : '1connect',
            'iomhost'   : 'bb04cnt10.unx.sas.com',
            'iomport'   : 8594,
            'encoding'  : 'iso-8859-1',
            'classpath' : cp2
            }

SASgrid   = {'java'     : '/usr/bin/java',
           #'omruser'   : 'sascnn1',
           #'omrpw'     : '1connect',
            'iomhost'   : 'sgm001.unx.sas.com',
            'iomport'   : 8591,
            'encoding'  : 'iso-8859-1',
            'classpath' : cp2
            }

# build out a local classpath variable to use below
cpl  =  "C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\sas.svc.connection.jar"
cpl += ";C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\log4j.jar"
cpl += ";C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\sas.security.sspi.jar"
cpl += ";C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\sas.core.jar"
cpl += ";E:\metis-master\java\saspyiom.jar"

winlocal = {'saspath'   : 'C:\Program Files\SASHome\SASFoundation\9.4\sas.exe',
            'java'      : 'java',
            'omruser'   : 'sastpw',
            'encoding'  : 'cp1252',
            'classpath' : cpl
            }

winiomj  = {'java'      : 'java',
            'omruser'   : 'sas',
            'omrpw'     : 'sas',
            'iomhost'   : 'tom64-3.na.sas.com',
            'iomport'   : 8591,
            'classpath' : cpl
            }

winiomjwin = {'java'    : 'java',
            'omruser'   : 'sasiom1@carynt',
            'omrpw'     : '1bridge',
            'iomhost'   : 'd77422.na.sas.com',
            'iomport'   : 18591,
            'encoding'  : 'cp1252',
            'classpath' : cpl
            }

wingridiom = {'java'      : 'java',
            'omruser'   : 'sascnn1',
            'omrpw'     : '1connect',
            'iomhost'   : 'bb04cnt10.unx.sas.com',
            'iomport'   : 8594,
            'encoding'  : 'iso-8859-1',
            'classpath' : cpl
            }

iomc     = {'iomc'    : '/u/sastpw/tkpy2c/tkext/com/laxnd/tktom',
            'omruser' : 'sas',
            'omrpw'   : 'sas',
            'host'    : 'tom64-3.na.sas.com',
            'port'    : 8591
            }

# test case for sasgsub grid testing
grid     = {'saspath' : '/sas3rd/wky/mva-v940/lax_sgm/SASHome/SASFoundation/9.4/bin/sas_u8',
            'ssh'     : '/usr/bin/ssh',
            'metapw'  : '1connect',
            'host'    : 'sascnn@sgm001.unx.sas.com',
            'options' : ["/sas3rd/wky/mva-v940/lax_sgm/SASAppServerConfig/Lev1/Applications/SASGridManagerClientUtility/9.4/sasgsub", "-gridrunsaslm"]
            }
#           'options' : ["/sas3rd/wky/mva-v940/lax_sgm/SASAppServerConfig/Lev1/Applications/SASGridManagerClientUtility/9.4/sasgsub", "-gridruncmdint"]
#           'options' : ["/sas3rd/wky/mva-v940/lax_sgm/SASAppServerConfig/Lev1/Applications/SASGridManagerClientUtility/9.4/sasgsub", "-gridrunsaslm"]

# Future - for the HTTP access method to connect to the Compute Service
# These need ip addr and port, other values will be prompted for - python Dict
# valid keys are:
# 'ip'      - [REQUIRED] host address
# 'port'    - [REQUIRED] port; the code Defaults this to 80 (the Compute Services default port)
# 'context' - context name defined on the compute service  [PROMTED for at runtime if more than one defined]
# 'options' - SAS options to include (no '-' (dashes), just option names and values)
# 'user'    - not suggested [REQUIRED but PROMTED for at runtime]
# 'pw'      - really not suggested [REQUIRED but PROMTED for at runtime]
#
#

http     = {'ip'      : 'tomspc',
            'port'    :  80,
            'context' : 'Tom2'
            }

httpfred = {'ip'      : '10.63.24.180',
            'port'    :  7980,
            'context' : 'Tom2'
            }

httptest = {'ip'      : 'tomspc',
            'port'    :  80,
            'options' : ["fullstimer", "memsize=1G"]
            }

<commit_msg>update sascfg default paths<commit_after>#
# Copyright SAS Institute
#
#  Licensed under the Apache License, Version 2.0 (the License);
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
# Configuration Names for SAS - python List
# This is the list of allowed configuration definitions that can be used. The definition are defined below.
# The various options for the different access methods can be specified on the SASsession() i.e.:
# sas = SASsession(cfgname='default', options='-fullstimer', user='me')
#
# based upon the lock_down configuration option below, you may or may not be able to override option
# that are defined already. Any necessary option (like user, pw for IOM or HTTP) that are not defined will be
# prompted for at run time. To dissallow overrides of OPTION, when you don't have any specified, simply
# specify options=''. This way it's specified so it can't be overridden, even though you don't have any
# extra options you want applied.
#
# SAS_config_names=['default', 'sasother', 'sas_en', 'sas_utf8', 'http', 'ssh', 'httptest']
#
SAS_config_names = ['default', 'SASgrid',  'http', 'httptest', 'ssh', 'httpfred', 'grid', 'tdi', 'iomj', 'iomc', 'iomjwin', 'winiomj', 'winiomjwin', 'winlocal', 'gridiom', 'wingridiom']

# Configuration options for pysas - python Dict
# valid key are:
#
# 'lock_down' - True | False. True = Prevent runtime overrides of SAS_Config values below
#
SAS_config_options = {'lock_down': True}

# Configuration Definitions
#
# For STDIO and STDIO over SSH access methods
# These need path to SASHome and optional startup options - python Dict
# The default path to the sas start up script is: /opt/sasinside/SASHome/SASFoundation/9.4/sas
# A usual install path is: /opt/sasinside/SASHome
#
# Since python uses utf-8, running SAS with encoding=utf-8 is the expected use case. By default
# linux SAS runs in Latin1, which works fine as long as you stick with the lower half of the code page.
# SAS is installed with a link ('sas') to the bin/sas_en startup script, and the link can be swapped
# to point to the utf8 start up script (bin/sas_u8), or another link made (sasutf8) to point to that.
#
# valid keys are:
# 'saspath' - [REQUIRED] path to SAS startup script i.e.: /opt/sasinside/SASHome/SASFoundation/9.4/sas
# 'options' - SAS options to include in the start up command line - Python List
#
# For passwordless ssh connection, the following are also reuqired:
# 'ssh'     - [REQUIRED] the ssh command to run
# 'host'    - [REQUIRED] the host to connect to
#
default  = {'saspath': '/opt/sasinside/SASHome/SASFoundation/9.4/bin/sas_u8'
            }

tdi      = {'saspath' : '/tdi/mva-v940m4/usrlibsas/laxno/SASFoundation/9.4/bin/sas_u8'
            }

ssh      = {'saspath' : '/opt/sasinside/SASHome/SASFoundation/9.4/bin/sas_u8',
            'ssh'     : '/usr/bin/ssh',
            'host'    : 'tom64-2',
            'options' : ["-fullstimer"]
            }

# sas_en   = {'saspath': '/opt/sasinside/SASHome/SASFoundation/9.4/sas'
#             }
#
# sas_utf8 = {'saspath': '/opt/sasinside/SASHome/SASFoundation/9.4/sasutf8'
#             }
#
# sasother = {'saspath' : '/some/other/directory/SASHome/SASFoundation/9.4/sas',
#             'options' : ["-autoexec", "/my/home_dir/my_autoexec.sas", "-set",
#                          "Env_var", "Value", "-fullstimer"]
#             }

# For IOM (Grid Manager or any IOM) and Local Windows via IOM access method
# These configuration definitions are for connecting over IOM. This is designed to be used to connect to a SAS Grid, via Grid Manager
# and also to connect to a local Windows SAS session. The client side (python and java) for this access method can be either Linux or Windows.
# The STDIO access method above is only for Linux. PC SAS requires this IOM interface.
#
# The 'saspath' option triggers local Windows SAS mode. In this case neither 'iomhost' nor 'iomport' are needed. localhost is used and an available port
# is used for the connection. In this way, you can run multiple python sessions to seperate Windows SAS instances (i.e.: different notbooks in Jupyter).
#
# Since python uses utf-8, running SAS with encoding=utf-8 is the expected use case. By default Windows SAS runs in WindowsLatin1 (cp1252),
# which does not work well as utf-8. So, transcoding has been implemented in the python layer. The 'encoding' option can be specified to match
# the SAS session encoding (see https://docs.python.org/3.5/library/codecs.html#standard-encodings for python encoding values). cp1252 is appropriate
# for the default Windows SAS session encoding
#
# Since this IOM access method used the Java IOM client, a classpath is required for the java process to find the necessary jars. Use the template below
# to build out a classpath variable and assign that to the 'classpath' option in the configuration definition. The IOM client jars are delivered as part
# of a Base SAS install, so should be available in any SAS install. The saspyiom.jar is available in the saspy repo/install.
#
# valid keys are:
# 'saspath'   - [REQUIRED for local Windows connection only] path to SAS executable (sas.exe) i.e.: C:\Program Files\SASHome\SASFoundation\9.4\sas.exe
# 'java'      - [REQUIRED] the path to the java executable to use
# 'iomhost'   - [REQUIRED for remote IOM case, not local Windows] the resolvable host name, or ip to the IOM server to connect to
# 'iomport'   - [REQUIRED for remote IOM case, not local Windows] the port IOM is listening on
# 'omruser'   - not suggested [REQUIRED but PROMTED for at runtime] For local Windows SAS, this is a local user account/pw
# 'omrpw'     - really not suggested [REQUIRED but PROMTED for at runtime]
# 'encoding'  - This is the python encoding value that matches the SAS session encoding of the IOM server you are connecting to
# 'classpath' - classpath to IOM client jars and saspy client jar.
#
# build out a local classpath variable to use below
cp  =  "/opt/tom/gitlab/metis/java/lib/sas.svc.connection.jar"
cp += ":/opt/tom/gitlab/metis/java/lib/log4j.jar"
cp += ":/opt/tom/gitlab/metis/java/lib/sas.security.sspi.jar"
cp += ":/opt/tom/gitlab/metis/java/lib/sas.core.jar"
cp += ":/opt/tom/gitlab/metis/java/saspyiom.jar"

# build out a local classpath variable to use below
cp2  =  "/u/sastpw/tkpy2j/lib2/sas.svc.connection.jar"
cp2 += ":/u/sastpw/tkpy2j/lib2/log4j.jar"
cp2 += ":/u/sastpw/tkpy2j/lib2/sas.security.sspi.jar"
cp2 += ":/u/sastpw/tkpy2j/lib2/sas.core.jar"
cp2 += ":/opt/tom/gitlab/metis/java/saspyiom.jar"

iomj     = {'java'      : '/usr/bin/java',
            'omruser'   : 'sas',
            'omrpw'     : 'sas',
            'iomhost'   : 'tom64-3.na.sas.com',
            'iomport'   : 8591,
            'classpath' : cp
            }

iomjwin  = {'java'      : '/usr/bin/java',
            'omruser'   : 'sasiom1@carynt',
            'omrpw'     : '1bridge',
            'iomhost'   : 'd77422.na.sas.com',
            'iomport'   : 18591,
            'encoding'  : 'cp1252',
            'classpath' : cp
            }

gridiom  = {'java'      : '/usr/bin/java',
           #'omruser'   : 'sascnn1',
           #'omrpw'     : '1connect',
            'iomhost'   : 'bb04cnt10.unx.sas.com',
            'iomport'   : 8594,
            'encoding'  : 'iso-8859-1',
            'classpath' : cp2
            }

SASgrid   = {'java'     : '/usr/bin/java',
           #'omruser'   : 'sascnn1',
           #'omrpw'     : '1connect',
            'iomhost'   : 'sgm001.unx.sas.com',
            'iomport'   : 8591,
            'encoding'  : 'iso-8859-1',
            'classpath' : cp2
            }

# build out a local classpath variable to use below
cpl  =  "C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\sas.svc.connection.jar"
cpl += ";C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\log4j.jar"
cpl += ";C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\sas.security.sspi.jar"
cpl += ";C:\Program Files\SASHome\SASDeploymentManager\9.4\products\deploywiz__94472__prt__xx__sp0__1\deploywiz\sas.core.jar"
cpl += ";C:\programData\Anaconda3\Lib\site-packages\saspy\java\saspyiom.jar"

winlocal = {'saspath'   : 'C:\Program Files\SASHome\SASFoundation\9.4\sas.exe',
            'java'      : 'java',
            'omruser'   : '',
            'encoding'  : 'cp1252',
            'classpath' : cpl
            }

winiomj  = {'java'      : 'java',
            'omruser'   : 'sas',
            'omrpw'     : 'sas',
            'iomhost'   : 'tom64-3.na.sas.com',
            'iomport'   : 8591,
            'classpath' : cpl
            }

winiomjwin = {'java'    : 'java',
            'omruser'   : 'sasiom1@carynt',
            'omrpw'     : '1bridge',
            'iomhost'   : 'd77422.na.sas.com',
            'iomport'   : 18591,
            'encoding'  : 'cp1252',
            'classpath' : cpl
            }

wingridiom = {'java'      : 'java',
            'omruser'   : 'sascnn1',
            'omrpw'     : '1connect',
            'iomhost'   : 'bb04cnt10.unx.sas.com',
            'iomport'   : 8594,
            'encoding'  : 'iso-8859-1',
            'classpath' : cpl
            }

iomc     = {'iomc'    : '/u/sastpw/tkpy2c/tkext/com/laxnd/tktom',
            'omruser' : 'sas',
            'omrpw'   : 'sas',
            'host'    : 'tom64-3.na.sas.com',
            'port'    : 8591
            }

# test case for sasgsub grid testing
grid     = {'saspath' : '/sas3rd/wky/mva-v940/lax_sgm/SASHome/SASFoundation/9.4/bin/sas_u8',
            'ssh'     : '/usr/bin/ssh',
            'metapw'  : '1connect',
            'host'    : 'sascnn@sgm001.unx.sas.com',
            'options' : ["/sas3rd/wky/mva-v940/lax_sgm/SASAppServerConfig/Lev1/Applications/SASGridManagerClientUtility/9.4/sasgsub", "-gridrunsaslm"]
            }
#           'options' : ["/sas3rd/wky/mva-v940/lax_sgm/SASAppServerConfig/Lev1/Applications/SASGridManagerClientUtility/9.4/sasgsub", "-gridruncmdint"]
#           'options' : ["/sas3rd/wky/mva-v940/lax_sgm/SASAppServerConfig/Lev1/Applications/SASGridManagerClientUtility/9.4/sasgsub", "-gridrunsaslm"]

# Future - for the HTTP access method to connect to the Compute Service
# These need ip addr and port, other values will be prompted for - python Dict
# valid keys are:
# 'ip'      - [REQUIRED] host address
# 'port'    - [REQUIRED] port; the code Defaults this to 80 (the Compute Services default port)
# 'context' - context name defined on the compute service  [PROMTED for at runtime if more than one defined]
# 'options' - SAS options to include (no '-' (dashes), just option names and values)
# 'user'    - not suggested [REQUIRED but PROMTED for at runtime]
# 'pw'      - really not suggested [REQUIRED but PROMTED for at runtime]
#
#

http     = {'ip'      : 'tomspc',
            'port'    :  80,
            'context' : 'Tom2'
            }

httpfred = {'ip'      : '10.63.24.180',
            'port'    :  7980,
            'context' : 'Tom2'
            }

httptest = {'ip'      : 'tomspc',
            'port'    :  80,
            'options' : ["fullstimer", "memsize=1G"]
            }

<|endoftext|>


<commit_before># Copyright 2012 Nebula, Inc.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import functools

from keystoneauth1 import _utils as utils
from keystoneauth1.access import service_catalog
from keystoneauth1.access import service_providers

# gap, in seconds, to determine whether the given token is about to expire
STALE_TOKEN_DURATION = 30

__all__ = ['AccessInfo',
           'AccessInfoV2',
           'AccessInfoV3',
           'create']

@utils.positional()
def create(resp=None, body=None, auth_token=None):
    if resp and not body:
        body = resp.json()

    if 'token' in body:
        if resp and not auth_token:
            auth_token = resp.headers.get('X-Subject-Token')

        return AccessInfoV3(body, auth_token)
    elif 'access' in body:
        return AccessInfoV2(body, auth_token)

    raise ValueError('Unrecognized auth response')

def missingproperty(f):

    @functools.wraps(f)
    def inner(self):
        try:
            return f(self)
        except KeyError:
            return None

    return property(inner)

class AccessInfo(object):
    """Encapsulates a raw authentication token from keystone.

    Provides helper methods for extracting useful values from that token.

    """

    _service_catalog_class = None

    def __init__(self, body, auth_token=None):
        self._data = body
        self._auth_token = auth_token
        self._service_catalog = None
        self._service_providers = None

    @property
    def service_catalog(self):
        if not self._service_catalog:
            self._service_catalog = self._service_catalog_class.from_token(
                self._data)

        return self._service_catalog

    def will_expire_soon(self, stale_duration=STALE_TOKEN_DURATION):
        """Determines if expiration is about to occur.

        :returns: true if expiration is within the given duration
        :rtype: boolean

        """
        norm_expires = utils.normalize_time(self.expires)
        # (gyee) should we move auth_token.will_expire_soon() to timeutils
        # instead of duplicating code here?
        soon = utils.from_utcnow(seconds=stale_duration)
        return norm_expires < soon

    def has_service_catalog(self):
        """Returns true if the authorization token has a service catalog.

        :returns: boolean
        """
        raise NotImplementedError()

    @property
    def auth_token(self):
        """Returns the token_id associated with the auth request, to be used
        in headers for authenticating OpenStack API requests.

        :returns: str
        """
        return self._auth_token

    @property
    def expires(self):
        """Returns the token expiration (as datetime object)

        :returns: datetime
        """
        raise NotImplementedError()

    @property
    def issued(self):
        """Returns the token issue time (as datetime object)

        :returns: datetime
        """
        raise NotImplementedError()

    @property
    def username(self):
        """Returns the username associated with the authentication request.
        Follows the pattern defined in the V2 API of first looking for 'name',
        returning that if available, and falling back to 'username' if name
        is unavailable.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def user_id(self):
        """Returns the user id associated with the authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def user_domain_id(self):
        """Returns the domain id of the user associated with the authentication
        request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def user_domain_name(self):
        """Returns the domain name of the user associated with the
        authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def role_ids(self):
        """Returns a list of role ids of the user associated with the
        authentication request.

        :returns: a list of strings of role ids
        """
        raise NotImplementedError()

    @property
    def role_names(self):
        """Returns a list of role names of the user associated with the
        authentication request.

        :returns: a list of strings of role names
        """
        raise NotImplementedError()

    @property
    def domain_name(self):
        """Returns the domain name associated with the authentication token.

        :returns: str or None (if no domain associated with the token)
        """
        raise NotImplementedError()

    @property
    def domain_id(self):
        """Returns the domain id associated with the authentication token.

        :returns: str or None (if no domain associated with the token)
        """
        raise NotImplementedError()

    @property
    def project_name(self):
        """Returns the project name associated with the authentication request.

        :returns: str or None (if no project associated with the token)
        """
        raise NotImplementedError()

    @property
    def tenant_name(self):
        """Synonym for project_name."""
        return self.project_name

    @property
    def scoped(self):
        """Returns true if the authorization token was scoped to a tenant
           (project), and contains a populated service catalog.

           This is deprecated, use project_scoped instead.

        :returns: bool
        """
        return self.project_scoped or self.domain_scoped

    @property
    def project_scoped(self):
        """Returns true if the authorization token was scoped to a tenant
           (project).

        :returns: bool
        """
        raise NotImplementedError()

    @property
    def domain_scoped(self):
        """Returns true if the authorization token was scoped to a domain.

        :returns: bool
        """
        raise NotImplementedError()

    @property
    def trust_id(self):
        """Returns the trust id associated with the authentication token.

        :returns: str or None (if no trust associated with the token)
        """
        raise NotImplementedError()

    @property
    def trust_scoped(self):
        """Returns true if the authorization token was scoped as delegated in a
        trust, via the OS-TRUST v3 extension.

        :returns: bool
        """
        raise NotImplementedError()

    @property
    def trustee_user_id(self):
        """Returns the trustee user id associated with a trust.

        :returns: str or None (if no trust associated with the token)
        """
        raise NotImplementedError()

    @property
    def trustor_user_id(self):
        """Returns the trustor user id associated with a trust.

        :returns: str or None (if no trust associated with the token)
        """
        raise NotImplementedError()

    @property
    def project_id(self):
        """Returns the project ID associated with the authentication
        request, or None if the authentication request wasn't scoped to a
        project.

        :returns: str or None (if no project associated with the token)
        """
        raise NotImplementedError()

    @property
    def tenant_id(self):
        """Synonym for project_id."""
        return self.project_id

    @property
    def project_domain_id(self):
        """Returns the domain id of the project associated with the
        authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def project_domain_name(self):
        """Returns the domain name of the project associated with the
        authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def oauth_access_token_id(self):
        """Return the access token ID if OAuth authentication used.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def oauth_consumer_id(self):
        """Return the consumer ID if OAuth authentication used.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def is_federated(self):
        """Returns true if federation was used to get the token.

        :returns: boolean
        """
        raise NotImplementedError()

    @property
    def audit_id(self):
        """Return the audit ID if present.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def audit_chain_id(self):
        """Return the audit chain ID if present.

        In the event that a token was rescoped then this ID will be the
        :py:attr:`audit_id` of the initial token. Returns None if no value
        present.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def initial_audit_id(self):
        """The audit ID of the initially requested token.

        This is the :py:attr:`audit_chain_id` if present or the
        :py:attr:`audit_id`.
        """
        return self.audit_chain_id or self.audit_id

    @property
    def service_providers(self):
        """Return a object representing the list of trusted service providers.

        Used for Keystone2Keystone federating-out.

        :returns: :py:class:`keystoneauth1.service_providers.ServiceProviders`
                  or None
        """
        raise NotImplementedError()

class AccessInfoV2(AccessInfo):
    """An object for encapsulating a raw v2 auth token from identity
       service.
    """

    version = 'v2.0'
    _service_catalog_class = service_catalog.ServiceCatalogV2

    def has_service_catalog(self):
        return 'serviceCatalog' in self

    @missingproperty
    def auth_token(self):
        set_token = super(AccessInfoV2, self).auth_token
        return set_token or self._data['access']['token']['id']

    @missingproperty
    def _token(self):
        return self._data['access']['token']

    @missingproperty
    def expires(self):
        return utils.parse_isotime(self._token.get('expires'))

    @missingproperty
    def issued(self):
        return self._token['issued_at']

    @property
    def _user(self):
        return self._data['access']['user']

    @missingproperty
    def username(self):
        return self._user.get('name') or self._user.get('username')

    @missingproperty
    def user_id(self):
        return self._user['id']

    @property
    def user_domain_id(self):
        return None

    @property
    def user_domain_name(self):
        return None

    @missingproperty
    def role_ids(self):
        return self.get('metadata', {}).get('roles', [])

    @missingproperty
    def role_names(self):
        return [r['name'] for r in self._user.get('roles', [])]

    @property
    def domain_name(self):
        return None

    @property
    def domain_id(self):
        return None

    @property
    def project_name(self):
        try:
            tenant_dict = self._token['tenant']
        except KeyError:
            pass
        else:
            return tenant_dict.get('name')

        # pre grizzly
        try:
            return self._user['tenantName']
        except KeyError:
            pass

        # pre diablo, keystone only provided a tenantId
        try:
            return self._token['tenantId']
        except KeyError:
            pass

    @property
    def project_scoped(self):
        return 'tenant' in self._token

    @property
    def domain_scoped(self):
        return False

    @property
    def _trust(self):
        return self._data['access']['trust']

    @missingproperty
    def trust_id(self):
        return self._trust['id']

    @property
    def trust_scoped(self):
        return bool(self._trust)

    @missingproperty
    def trustee_user_id(self):
        return self._trust['trustee_user_id']

    @property
    def trustor_user_id(self):
        # this information is not available in the v2 token bug: #1331882
        return None

    @property
    def project_id(self):
        try:
            tenant_dict = self._token['tenant']
        except KeyError:
            pass
        else:
            return tenant_dict.get('id')

        # pre grizzly
        try:
            return self._user['tenantId']
        except KeyError:
            pass

        # pre diablo
        try:
            return self._token['tenantId']
        except KeyError:
            pass

    @property
    def project_domain_id(self):
        return None

    @property
    def project_domain_name(self):
        return None

    @property
    def oauth_access_token_id(self):
        return None

    @property
    def oauth_consumer_id(self):
        return None

    @property
    def is_federated(self):
        return False

    @property
    def audit_id(self):
        try:
            return self._token.get('audit_ids', [])[0]
        except IndexError:
            return None

    @property
    def audit_chain_id(self):
        try:
            return self._token.get('audit_ids', [])[1]
        except IndexError:
            return None

    @property
    def service_providers(self):
        return None

class AccessInfoV3(AccessInfo):
    """An object for encapsulating a raw v3 auth token from identity
       service.
    """

    version = 'v3'
    _service_catalog_class = service_catalog.ServiceCatalogV3

    def has_service_catalog(self):
        return 'catalog' in self._data['token']

    @property
    def _user(self):
        return self._data['token']['user']

    @property
    def is_federated(self):
        return 'OS-FEDERATION' in self._user

    @missingproperty
    def expires(self):
        return utils.parse_isotime(self._data['token']['expires_at'])

    @missingproperty
    def issued(self):
        return utils.parse_isotime(self._data['token']['issued_at'])

    @missingproperty
    def user_id(self):
        return self._user['id']

    @property
    def user_domain_id(self):
        try:
            return self._user['domain']['id']
        except KeyError:
            if self.is_federated:
                return None
            raise

    @property
    def user_domain_name(self):
        try:
            return self._user['domain']['name']
        except KeyError:
            if self.is_federated:
                return None
            raise

    @missingproperty
    def role_ids(self):
        return [r['id'] for r in self._data['token'].get('roles', [])]

    @missingproperty
    def role_names(self):
        return [r['name'] for r in self._data['token'].get('roles', [])]

    @missingproperty
    def username(self):
        return self._user['name']

    @property
    def _domain(self):
        return self._data['token']['domain']

    @missingproperty
    def domain_name(self):
        return self._domain['name']

    @missingproperty
    def domain_id(self):
        return self._domain['id']

    @property
    def _project(self):
        return self._data['token']['project']

    @missingproperty
    def project_id(self):
        return self._project['id']

    @missingproperty
    def project_domain_id(self):
        return self._project['domain']['id']

    @missingproperty
    def project_domain_name(self):
        return self._project['domain']['name']

    @missingproperty
    def project_name(self):
        return self._project['name']

    @property
    def project_scoped(self):
        try:
            return bool(self._project)
        except KeyError:
            return False

    @property
    def domain_scoped(self):
        try:
            return bool(self._domain)
        except KeyError:
            return False

    @property
    def _trust(self):
        return self._data['token']['OS-TRUST:trust']

    @missingproperty
    def trust_id(self):
        return self._trust['id']

    @property
    def trust_scoped(self):
        try:
            return bool(self._trust)
        except KeyError:
            return False

    @missingproperty
    def trustee_user_id(self):
        return self._trust['trustee_user']['id']

    @missingproperty
    def trustor_user_id(self):
        return self._trust['trustor_user']['id']

    @property
    def _oauth(self):
        return self._data['token']['OS-OAUTH1']

    @missingproperty
    def oauth_access_token_id(self):
        return self._oauth['access_token_id']

    @missingproperty
    def oauth_consumer_id(self):
        return self._oauth['consumer_id']

    @missingproperty
    def audit_id(self):
        try:
            return self._data['token']['audit_ids'][0]
        except IndexError:
            return None

    @missingproperty
    def audit_chain_id(self):
        try:
            return self._data['token']['audit_ids'][1]
        except IndexError:
            return None

    @property
    def service_providers(self):
        if not self._service_providers:
            self._service_providers = (
                service_providers.ServiceProviders.from_token(self._data))

        return self._service_providers
<commit_msg>Make missingproperty private<commit_after># Copyright 2012 Nebula, Inc.
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import functools

from keystoneauth1 import _utils as utils
from keystoneauth1.access import service_catalog
from keystoneauth1.access import service_providers

# gap, in seconds, to determine whether the given token is about to expire
STALE_TOKEN_DURATION = 30

__all__ = ['AccessInfo',
           'AccessInfoV2',
           'AccessInfoV3',
           'create']

@utils.positional()
def create(resp=None, body=None, auth_token=None):
    if resp and not body:
        body = resp.json()

    if 'token' in body:
        if resp and not auth_token:
            auth_token = resp.headers.get('X-Subject-Token')

        return AccessInfoV3(body, auth_token)
    elif 'access' in body:
        return AccessInfoV2(body, auth_token)

    raise ValueError('Unrecognized auth response')

def _missingproperty(f):

    @functools.wraps(f)
    def inner(self):
        try:
            return f(self)
        except KeyError:
            return None

    return property(inner)

class AccessInfo(object):
    """Encapsulates a raw authentication token from keystone.

    Provides helper methods for extracting useful values from that token.

    """

    _service_catalog_class = None

    def __init__(self, body, auth_token=None):
        self._data = body
        self._auth_token = auth_token
        self._service_catalog = None
        self._service_providers = None

    @property
    def service_catalog(self):
        if not self._service_catalog:
            self._service_catalog = self._service_catalog_class.from_token(
                self._data)

        return self._service_catalog

    def will_expire_soon(self, stale_duration=STALE_TOKEN_DURATION):
        """Determines if expiration is about to occur.

        :returns: true if expiration is within the given duration
        :rtype: boolean

        """
        norm_expires = utils.normalize_time(self.expires)
        # (gyee) should we move auth_token.will_expire_soon() to timeutils
        # instead of duplicating code here?
        soon = utils.from_utcnow(seconds=stale_duration)
        return norm_expires < soon

    def has_service_catalog(self):
        """Returns true if the authorization token has a service catalog.

        :returns: boolean
        """
        raise NotImplementedError()

    @property
    def auth_token(self):
        """Returns the token_id associated with the auth request, to be used
        in headers for authenticating OpenStack API requests.

        :returns: str
        """
        return self._auth_token

    @property
    def expires(self):
        """Returns the token expiration (as datetime object)

        :returns: datetime
        """
        raise NotImplementedError()

    @property
    def issued(self):
        """Returns the token issue time (as datetime object)

        :returns: datetime
        """
        raise NotImplementedError()

    @property
    def username(self):
        """Returns the username associated with the authentication request.
        Follows the pattern defined in the V2 API of first looking for 'name',
        returning that if available, and falling back to 'username' if name
        is unavailable.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def user_id(self):
        """Returns the user id associated with the authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def user_domain_id(self):
        """Returns the domain id of the user associated with the authentication
        request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def user_domain_name(self):
        """Returns the domain name of the user associated with the
        authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def role_ids(self):
        """Returns a list of role ids of the user associated with the
        authentication request.

        :returns: a list of strings of role ids
        """
        raise NotImplementedError()

    @property
    def role_names(self):
        """Returns a list of role names of the user associated with the
        authentication request.

        :returns: a list of strings of role names
        """
        raise NotImplementedError()

    @property
    def domain_name(self):
        """Returns the domain name associated with the authentication token.

        :returns: str or None (if no domain associated with the token)
        """
        raise NotImplementedError()

    @property
    def domain_id(self):
        """Returns the domain id associated with the authentication token.

        :returns: str or None (if no domain associated with the token)
        """
        raise NotImplementedError()

    @property
    def project_name(self):
        """Returns the project name associated with the authentication request.

        :returns: str or None (if no project associated with the token)
        """
        raise NotImplementedError()

    @property
    def tenant_name(self):
        """Synonym for project_name."""
        return self.project_name

    @property
    def scoped(self):
        """Returns true if the authorization token was scoped to a tenant
           (project), and contains a populated service catalog.

           This is deprecated, use project_scoped instead.

        :returns: bool
        """
        return self.project_scoped or self.domain_scoped

    @property
    def project_scoped(self):
        """Returns true if the authorization token was scoped to a tenant
           (project).

        :returns: bool
        """
        raise NotImplementedError()

    @property
    def domain_scoped(self):
        """Returns true if the authorization token was scoped to a domain.

        :returns: bool
        """
        raise NotImplementedError()

    @property
    def trust_id(self):
        """Returns the trust id associated with the authentication token.

        :returns: str or None (if no trust associated with the token)
        """
        raise NotImplementedError()

    @property
    def trust_scoped(self):
        """Returns true if the authorization token was scoped as delegated in a
        trust, via the OS-TRUST v3 extension.

        :returns: bool
        """
        raise NotImplementedError()

    @property
    def trustee_user_id(self):
        """Returns the trustee user id associated with a trust.

        :returns: str or None (if no trust associated with the token)
        """
        raise NotImplementedError()

    @property
    def trustor_user_id(self):
        """Returns the trustor user id associated with a trust.

        :returns: str or None (if no trust associated with the token)
        """
        raise NotImplementedError()

    @property
    def project_id(self):
        """Returns the project ID associated with the authentication
        request, or None if the authentication request wasn't scoped to a
        project.

        :returns: str or None (if no project associated with the token)
        """
        raise NotImplementedError()

    @property
    def tenant_id(self):
        """Synonym for project_id."""
        return self.project_id

    @property
    def project_domain_id(self):
        """Returns the domain id of the project associated with the
        authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def project_domain_name(self):
        """Returns the domain name of the project associated with the
        authentication request.

        :returns: str
        """
        raise NotImplementedError()

    @property
    def oauth_access_token_id(self):
        """Return the access token ID if OAuth authentication used.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def oauth_consumer_id(self):
        """Return the consumer ID if OAuth authentication used.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def is_federated(self):
        """Returns true if federation was used to get the token.

        :returns: boolean
        """
        raise NotImplementedError()

    @property
    def audit_id(self):
        """Return the audit ID if present.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def audit_chain_id(self):
        """Return the audit chain ID if present.

        In the event that a token was rescoped then this ID will be the
        :py:attr:`audit_id` of the initial token. Returns None if no value
        present.

        :returns: str or None.
        """
        raise NotImplementedError()

    @property
    def initial_audit_id(self):
        """The audit ID of the initially requested token.

        This is the :py:attr:`audit_chain_id` if present or the
        :py:attr:`audit_id`.
        """
        return self.audit_chain_id or self.audit_id

    @property
    def service_providers(self):
        """Return a object representing the list of trusted service providers.

        Used for Keystone2Keystone federating-out.

        :returns: :py:class:`keystoneauth1.service_providers.ServiceProviders`
                  or None
        """
        raise NotImplementedError()

class AccessInfoV2(AccessInfo):
    """An object for encapsulating a raw v2 auth token from identity
       service.
    """

    version = 'v2.0'
    _service_catalog_class = service_catalog.ServiceCatalogV2

    def has_service_catalog(self):
        return 'serviceCatalog' in self

    @_missingproperty
    def auth_token(self):
        set_token = super(AccessInfoV2, self).auth_token
        return set_token or self._data['access']['token']['id']

    @_missingproperty
    def _token(self):
        return self._data['access']['token']

    @_missingproperty
    def expires(self):
        return utils.parse_isotime(self._token.get('expires'))

    @_missingproperty
    def issued(self):
        return self._token['issued_at']

    @property
    def _user(self):
        return self._data['access']['user']

    @_missingproperty
    def username(self):
        return self._user.get('name') or self._user.get('username')

    @_missingproperty
    def user_id(self):
        return self._user['id']

    @property
    def user_domain_id(self):
        return None

    @property
    def user_domain_name(self):
        return None

    @_missingproperty
    def role_ids(self):
        return self.get('metadata', {}).get('roles', [])

    @_missingproperty
    def role_names(self):
        return [r['name'] for r in self._user.get('roles', [])]

    @property
    def domain_name(self):
        return None

    @property
    def domain_id(self):
        return None

    @property
    def project_name(self):
        try:
            tenant_dict = self._token['tenant']
        except KeyError:
            pass
        else:
            return tenant_dict.get('name')

        # pre grizzly
        try:
            return self._user['tenantName']
        except KeyError:
            pass

        # pre diablo, keystone only provided a tenantId
        try:
            return self._token['tenantId']
        except KeyError:
            pass

    @property
    def project_scoped(self):
        return 'tenant' in self._token

    @property
    def domain_scoped(self):
        return False

    @property
    def _trust(self):
        return self._data['access']['trust']

    @_missingproperty
    def trust_id(self):
        return self._trust['id']

    @property
    def trust_scoped(self):
        return bool(self._trust)

    @_missingproperty
    def trustee_user_id(self):
        return self._trust['trustee_user_id']

    @property
    def trustor_user_id(self):
        # this information is not available in the v2 token bug: #1331882
        return None

    @property
    def project_id(self):
        try:
            tenant_dict = self._token['tenant']
        except KeyError:
            pass
        else:
            return tenant_dict.get('id')

        # pre grizzly
        try:
            return self._user['tenantId']
        except KeyError:
            pass

        # pre diablo
        try:
            return self._token['tenantId']
        except KeyError:
            pass

    @property
    def project_domain_id(self):
        return None

    @property
    def project_domain_name(self):
        return None

    @property
    def oauth_access_token_id(self):
        return None

    @property
    def oauth_consumer_id(self):
        return None

    @property
    def is_federated(self):
        return False

    @property
    def audit_id(self):
        try:
            return self._token.get('audit_ids', [])[0]
        except IndexError:
            return None

    @property
    def audit_chain_id(self):
        try:
            return self._token.get('audit_ids', [])[1]
        except IndexError:
            return None

    @property
    def service_providers(self):
        return None

class AccessInfoV3(AccessInfo):
    """An object for encapsulating a raw v3 auth token from identity
       service.
    """

    version = 'v3'
    _service_catalog_class = service_catalog.ServiceCatalogV3

    def has_service_catalog(self):
        return 'catalog' in self._data['token']

    @property
    def _user(self):
        return self._data['token']['user']

    @property
    def is_federated(self):
        return 'OS-FEDERATION' in self._user

    @_missingproperty
    def expires(self):
        return utils.parse_isotime(self._data['token']['expires_at'])

    @_missingproperty
    def issued(self):
        return utils.parse_isotime(self._data['token']['issued_at'])

    @_missingproperty
    def user_id(self):
        return self._user['id']

    @property
    def user_domain_id(self):
        try:
            return self._user['domain']['id']
        except KeyError:
            if self.is_federated:
                return None
            raise

    @property
    def user_domain_name(self):
        try:
            return self._user['domain']['name']
        except KeyError:
            if self.is_federated:
                return None
            raise

    @_missingproperty
    def role_ids(self):
        return [r['id'] for r in self._data['token'].get('roles', [])]

    @_missingproperty
    def role_names(self):
        return [r['name'] for r in self._data['token'].get('roles', [])]

    @_missingproperty
    def username(self):
        return self._user['name']

    @property
    def _domain(self):
        return self._data['token']['domain']

    @_missingproperty
    def domain_name(self):
        return self._domain['name']

    @_missingproperty
    def domain_id(self):
        return self._domain['id']

    @property
    def _project(self):
        return self._data['token']['project']

    @_missingproperty
    def project_id(self):
        return self._project['id']

    @_missingproperty
    def project_domain_id(self):
        return self._project['domain']['id']

    @_missingproperty
    def project_domain_name(self):
        return self._project['domain']['name']

    @_missingproperty
    def project_name(self):
        return self._project['name']

    @property
    def project_scoped(self):
        try:
            return bool(self._project)
        except KeyError:
            return False

    @property
    def domain_scoped(self):
        try:
            return bool(self._domain)
        except KeyError:
            return False

    @property
    def _trust(self):
        return self._data['token']['OS-TRUST:trust']

    @_missingproperty
    def trust_id(self):
        return self._trust['id']

    @property
    def trust_scoped(self):
        try:
            return bool(self._trust)
        except KeyError:
            return False

    @_missingproperty
    def trustee_user_id(self):
        return self._trust['trustee_user']['id']

    @_missingproperty
    def trustor_user_id(self):
        return self._trust['trustor_user']['id']

    @property
    def _oauth(self):
        return self._data['token']['OS-OAUTH1']

    @_missingproperty
    def oauth_access_token_id(self):
        return self._oauth['access_token_id']

    @_missingproperty
    def oauth_consumer_id(self):
        return self._oauth['consumer_id']

    @_missingproperty
    def audit_id(self):
        try:
            return self._data['token']['audit_ids'][0]
        except IndexError:
            return None

    @_missingproperty
    def audit_chain_id(self):
        try:
            return self._data['token']['audit_ids'][1]
        except IndexError:
            return None

    @property
    def service_providers(self):
        if not self._service_providers:
            self._service_providers = (
                service_providers.ServiceProviders.from_token(self._data))

        return self._service_providers
<|endoftext|>