Configuring a SAS Application Server to Support the DS2 Pymas Package

The SAS DS2 pymas package provides interfaces that enable users to publish and execute Python code using the SAS Micro Analytic Service. This section describes how SAS Micro Analytic Service can be configured in a SAS Application Server, enabling you to test DS2 pymas package usage using PROC DS2 running in a SAS session. Examples of a SAS session are a workspace server that has been launched by SAS Studio or SAS Decision Manager.
Two user modifications are needed when configuring a SAS Application Server to support the use of the SAS DS2 pymas package in a SAS server, such as the workspace server. Here is a brief example:
  1. Copy the SAS Micro Analytic Service shared libraries from SASHome/SASFoundation on the middle tier to the appropriate directory under SAS Foundation, on the server tier.
  2. Add environment settings to the server's corresponding _usermods.sh(.bat) file provided in the server's configuration directory.
Because the _usermods files are sourced within each of the server wrapper scripts, the server inherits any logic or environment. SAS preserves _usermods files during software updates, unlike the server wrapper scripts, which SAS overwrites. For this reason, editing the wrapper scripts is discouraged.
One place that SAS Micro Analytic Service is installed in the middle tier is a platform-specific location within SASHome/SASFoundation. Here is an example on UNIX platforms:
  • SASHome/SASFoundation/<release>/sasexe
A specific instance on a UNIX platform can look like this:
  • /install/SASServer/SASHome/SASFoundation/9.4/sasexe
Here is a list of the shared libraries that must be copied from that directory on a UNIX platform:
  • libtksf.so
  • pymas.so
  • tkmaspy2.so
  • tkmaspy3.so
  • t1j8en.so
The library name t1j8en.so is the English translation of the SAS Micro Analytic Service message file. Copy any other SAS Micro Analytic Service language files matching t1j8??.so as well.
On Windows, the list is almost the same as UNIX except that the file extensions are .dll instead of .so, and libtksf.so is tksf.dll:
  • tksf.dll
  • pymas.dll
  • tkmaspy2.dll
  • tkmaspy3.dll
  • t1j8en.dll
On Windows, those libraries are located at core\sasext, instead of sasexe. Here is an example:
  • C:\Program Files\SASHome\SASFoundation\9.4\core\sasext
With respect to environment setting changes needed in the _usermodes.sh(.bat) file, the environment must be updated to enable the server to find Python and any Python modules needed by your Python code. When configuring the workspace server on UNIX, you are updating the following environment sas-configuration-directory/<LevN>/SASApp/WorkspaceServer/WorkspaceServer_usermods.sh:
First make a backup copy. Here is an example:
cp --preserve=timestamps WorkspaceServer_usermods.sh \
          WorkspaceServer_usermods.orig.sh
Then, make the following changes to WorkspaceServer_usermods.sh. This example assumes that Anaconda Python is installed and configured on the server machine, and that at least one Python environment has been created.
  1. Update the name of the shell in the first line from sh to bash. Here is an example:
    #!/bin/bash -p
    Note: The remainder of the updates are the same commands that you use to activate and configure your Anaconda Python environment normally.
  2. Source the Anaconda activate script to activate the desired environment. Here is an example:
    source /anaconda3/bin/activate python27
    Note: A dot (.) is an alias for the source command.
  3. Add ${CONDA_PREFIX}/lib to the LD_LIBRARY_PATH environment variable. Here is an example:
    LD_LIBRARY_PATH=${CONDA_PREFIX}/lib:${LD_LIBRARY_PATH}
    export LD_LIBRARY_PATH=${LD_LIBRARY_PATH%:}
    
  4. If the server has multiple Python installations that are conflicting, you could possibly encounter an error, such as Python DateTime API failed to load. If so, set the PYTHONHOME to your Python home directory. Here is an example:
    PYTHONHOME=/anaconda3
    export PYTHONHOME
    
On Windows, when configuring the workspace server, you are updating WorkspaceServer_usermods.bat. Add the command that you used to activate the Anaconda Python environment that you created previously. Here is an example:
c:\Anaconda3\Scripts\activate python27
If you have other environment settings needed for your Anaconda Python environment, add those commands as well.
You can test your configuration by submitting a PROC DS2 program to make sure it can successfully use the DS2 pymas package. Here is an example:
%macro chkrc; if rc then put rc=; %mend;
%macro addln( line ); rc = py.appendSrcLine( &line ); %chkrc; %mend;

/* Input data for the test.*/
data tstinput; a = 8;  b = 4; output; a = 10; b = 2; output;
run;

proc ds2;
    ds2_options sas;
    package testpkg /overwrite=yes;
        dcl package pymas py();
        dcl package logger logr('App.tk.MAS');
        dcl varchar(67108864) character set utf8 pycode;
        dcl int rc revision;

        method testpkg( varchar(256) modulename, 
                        varchar(256) pyfuncname );
            %addln( '# The first Python function:' )
            %addln( 'def domath1(a, b):' )
            %addln( '  "Output: c, d"' )
            %addln( '  c = a * b' )
            %addln( '  d = a / b' )
            %addln( '  return c, d' )
            %addln( '' )
            %addln( '# Here is the second function:' )
            %addln( 'def domath2(a, b):' )
            %addln( '  "Output: c, d"' )
            %addln( '  c,d = domath1( a, b )' )
            if rc then logr.log( 'E', 'py.appendSrcLine() failed.');
            rc = py.appendSrcLine('  return c, d' );
            pycode = py.getSource();
            revision = py.publish( pycode, modulename );
            if revision lt 1 then 
                logr.log( 'E', 'py.publish() failed.');
            rc = py.useMethod( pyfuncname );
            if rc then logr.log( 'E', 'py.useMethod() failed.');
        end;

        method usefunc( varchar(256) pyfuncname );
            rc = py.useMethod( pyfuncname );
            if rc then logr.log( 'E', 'py.useMethod() failed.');
        end;

        method exec( double a, double b, in_out int rc,
                     in_out double c, in_out double d );
            rc = py.setDouble( 'a', a );   if rc then return;
            rc = py.setDouble( 'b', b );   if rc then return;
            rc = py.execute();             if rc then return;
            c = py.getDouble( 'c' );
            d = py.getDouble( 'd' );
        end;
    endpackage;

    data _null_;
        dcl package logger logr( 'App.tk.MAS' );
        dcl package testpkg t( 'my Py Module Ctxt name', 'domath1' );
        dcl int rc;
        dcl double a b c d;

        method run();
            a = b = c = d = 0.0;
            set tstinput;
            t.exec( a, b, rc, c, d );
            logr.log( 'I', '##### Results: a=$s   b=$s   c=$s   d=$s',
                      a, b, c, d );
        end;

        method term();
            t.usefunc( 'domath2' );
            a = 6; b = 3;
            t.exec( a, b, rc, c, d );
            logr.log( 'I', '##### Results: a=$s   b=$s   c=$s   d=$s',
                      a, b, c, d );
        end;

    enddata;
    run;
quit;