
    Q                        S r SSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKrSSKJr  SSKrSSKJr  \r\	R$                  S:  a  \R&                  rSrSrS	rS
rSrSrSrSrSrSrSrSrSr Sr!Sr"\ \!4r#Sr$Sr%Sr&Sr'Sr(Sr)Sr*Sr+Sr,Sr-S r.S!r/\R`                  " S"S#5      r1S$r2Sr&S%r3 " S& S'\45      r5S(r6S)r7 " S* S+\85      r9 " S, S-\:5      r; " S. S/\5      r<S0 r=S1 r>S<S2 jr?S3 r@S4 rAS5 rBS6 rCS7 rDS8 rES9 rFS: rGS; rHg)=z)Common utilities for running predictions.    N   )Model)dtypes)      zPrediction-EnginezPrediction-Engine-Run-Time	Frameworkmodelprepared_modelscikit_learnsk_xgbxgboost
tensorflowcustomzPrediction-Preprocess-TimezPrediction-Postprocess-Timezmodel.joblibz	model.pkl)zsaved_model.pbzsaved_model.pbtxt)z	model.bstinputsoutputssignature_namezPrediction-Columnarize-TimezPrediction-Unalias-TimezPrediction-Encode-TimezPrediction-Session-Run-TimezPrediction-Alias-TimezPrediction-Rowify-TimeTF_SESSION_RUNz
/tmp/modelPredictionErrorTypemessagecodepredictions	instancesc                       \ rS rSrSr\" SSS9r\" SSS9r\" S	S
S9r\" SSS9r	\" SSS9r
\" SSS9r\S 5       r\S 5       r\S 5       rS rSrg)PredictionError\   z2Customer exception for known prediction exception.zFailed to load modelr   r   zInvalid inputsr   )r   z Failed to run the provided model   z*There was a problem processing the outputsr   z,There was a problem processing the user code   z6Could not get an access token from the metadata server   c                 4    U R                   S   R                  $ Nr   )argsr   selfs    >lib/third_party/ml_sdk/cloud/ml/prediction/prediction_utils.py
error_codePredictionError.error_codeo   s    99Q<    c                 4    U R                   S   R                  $ r!   )r"   r   r#   s    r%   error_messagePredictionError.error_messages   s    99Q<r(   c                      U R                   S   $ )Nr   )r"   r#   s    r%   error_detailPredictionError.error_detailw   s    99Q<r(   c                 N    SU R                   U R                  U R                  4-  $ )Nz%s: %s (Error code: %d))r*   r-   r&   r#   s    r%   __str__PredictionError.__str__{   s.    %););)-):):DOO)M M Nr(    N)__name__
__module____qualname____firstlineno____doc__r   FAILED_TO_LOAD_MODELINVALID_INPUTSFAILED_TO_RUN_MODELINVALID_OUTPUTSINVALID_USER_CODE FAILED_TO_ACCESS_METADATA_SERVERpropertyr&   r*   r-   r0   __static_attributes__r2   r(   r%   r   r   \   s    : -$1.&'7a@.+0q:':D/)<1F%8F&"        Nr(   r   i@B i  c                   ^    \ rS rSrSrSS jrS rS r\S 5       r	\S 5       r
\S	 5       rS
rg)Timer   a0  Context manager for timing code blocks.

The object is intended to be used solely as a context manager and not
as a general purpose object.

The timer starts when __enter__ is invoked on the context manager
and stopped when __exit__ is invoked. After __exit__ is called,
the duration properties report the amount of time between
__enter__ and __exit__ and thus do not change. However, if any of the
duration properties are called between the call to __enter__ and __exit__,
then they will return the "live" value of the timer.

If the same Timer object is re-used in multiple with statements, the values
reported will reflect the latest call. Do not use the same Timer object in
nested with blocks with the same Timer context manager.

Example usage:

  with Timer() as timer:
    foo()
  print(timer.duration_secs)
Nc                 \    S U l         S U l        U=(       d    [        R                  U l        g N)startendtimeitdefault_timer	_get_time)r$   timer_fns     r%   __init__Timer.__init__   s"    DJDH5!5!5DNr(   c                 >    S U l         U R                  5       U l        U $ rD   )rF   rI   rE   r#   s    r%   	__enter__Timer.__enter__   s    DH!DJKr(   c                 .    U R                  5       U l        g)NF)rI   rF   )r$   exc_typevalue	tracebacks       r%   __exit__Timer.__exit__   s    ~~DHr(   c                 x    U R                  5       nU R                  =(       d    UU R                  =(       d    U-
  $ rD   rI   rF   rE   )r$   nows     r%   secondsTimer.seconds   s*    
..
CHHO

 1c22r(   c                 :    [        [        U R                  -  5      $ rD   )intMICROrY   r#   s    r%   microsecondsTimer.microseconds       ut||#$$r(   c                 :    [        [        U R                  -  5      $ rD   )r\   MILLIrY   r#   s    r%   millisecondsTimer.milliseconds   r`   r(   rW   rD   )r3   r4   r5   r6   r7   rK   rN   rT   r>   rY   r^   rc   r?   r2   r(   r%   rA   rA      sR    .6

 3 3 % % % %r(   rA   c                   @    \ rS rSrSr\R                  SS j5       rSrg)Stats   a  An object for tracking stats.

This class is dict-like, so stats are accessed/stored like so:

  stats = Stats()
  stats["count"] = 1
  stats["foo"] = "bar"

This class also facilitates collecting timing information via the
context manager obtained using the "time" method. Reported timings
are in microseconds.

Example usage:

  with stats.time("foo_time"):
    foo()
  print(stats["foo_time"])
Nc              #   z   #    [        U5       nUv   S S S 5        WR                  X'   g ! , (       d  f       N= f7frD   )rA   r^   )r$   namerJ   timers       r%   time
Stats.time   s,     	xEk 
##DJ 
s   ;*;
8;r2   rD   )	r3   r4   r5   r6   r7   
contextlibcontextmanagerrk   r?   r2   r(   r%   rf   rf      s!    & $ $r(   rf   c                   L    \ rS rSrSrS rSS jrSS jrSS jrS r	SS	 jr
S
rg)	BaseModel   z3The base definition of an internal Model interface.c                     Xl         SU l        g)zdConstructs a BaseModel.

Args:
  client: An instance of PredictionClient for performing prediction.
N_client_user_processor)r$   clients     r%   rK   BaseModel.__init__   s     LDr(   Nc                     g)a\  Runs the preprocessing function on the instances.

Args:
  instances: list of instances as provided to the predict() method.
  stats: Stats object for recording timing information.
  **kwargs: Additional keyword arguments for preprocessing.

Returns:
  A new list of preprocessed instances. Each instance is as described
  in the predict() method.
Nr2   )r$   r   statskwargss       r%   
preprocessBaseModel.preprocess   s     	r(   c                     g)a  Runs the postprocessing function on the instances.

Args:
  predicted_output: list of instances returned by the predict() method on
    preprocessed instances.
  original_input: List of instances, before any pre-processing was applied.
  stats: Stats object for recording timing information.
  **kwargs: Additional keyword arguments for postprocessing.

Returns:
  A new list of postprocessed instances.
Nr2   )r$   predicted_outputoriginal_inputry   rz   s        r%   postprocessBaseModel.postprocess   s     	r(   c                    U=(       d
    [        5       nU R                  U5        UR                  [        5         U R                  " U4SU0UD6nSSS5        UR                  [
        5         U R                  R                  " W4SU0UD6nSSS5        UR                  [        5         U R                  " W4XS.UD6nSSS5        U$ ! , (       d  f       N= f! , (       d  f       NT= f! , (       d  f       W$ = f)z=Runs preprocessing, predict, and postprocessing on the input.ry   N)r   ry   )
rf   _validate_kwargsrk   PREPROCESS_TIMEr{   ENGINE_RUN_TIMErt   predictPOSTPROCESS_TIMEr   )r$   r   ry   rz   preprocessedpredicted_outputspostprocesseds          r%   r   BaseModel.predict   s     UWE&!	O	$__YFeFvFl 
%	O	$,,..
/#/'-/ 
% 
$	%&&
N,5NFLNm 
&  
%	$	$	$ 
&	% s#   C
- C*C,

C
C),
C;c                     g)ax  Validates and sets defaults for extra predict keyword arguments.

Modifies the keyword args dictionary in-place. Keyword args will be included
into pre/post-processing and the client predict method.
Can raise Exception to error out of request on bad keyword args.
If no additional args are required, pass.

Args:
  kwargs: Dictionary (str->str) of keyword arguments to check.
Nr2   )r$   rz   s     r%   r   BaseModel._validate_kwargs  s     	r(   c                     g)a  Gets model signature of inputs and outputs.

Currently only used for Tensorflow model. May be extended for use with
XGBoost and Sklearn in the future.

Args:
  signature_name: str of name of signature

Returns:
  (str, SignatureDef): signature key, SignatureDef
NNr2   )r$   r   s     r%   get_signatureBaseModel.get_signature  s     r(   rs   rD   r   )r3   r4   r5   r6   r7   rK   r{   r   r   r   r   r?   r2   r(   r%   rp   rp      s#    ; 		  	r(   rp   c                     U [         :H  =(       a9    [        U[        5      (       + =(       d    [        UR	                  U5      S   5      $ )a  Determines if base64 decoding is required.

Returns False if framework is not TF.
Returns True if framework is TF and is a user model.
Returns True if framework is TF and model contains a str input.
Returns False if framework is TF and model does not contain str input.

Args:
  framework: ML framework of prediction app
  model: model object
  signature_name: str of name of signature

Returns:
  bool

r   )TENSORFLOW_FRAMEWORK_NAME
isinstancerp   does_signature_contain_strr   )	frameworkr	   r   s      r%   should_base64_decoder   )  sD    " 0
0 O%++ N%e&9&9.&I!&LMPr(   c                 z   [        U [        5      (       a  U  Vs/ s H  n[        U5      PM     sn$ [        U [        5      (       ai  [        R
                  " U 5      S1:X  a  [        R                  " U S   5      $ [        R                  " U 5       VVs0 s H  u  p#U[        U5      _M     snn$ U $ s  snf s  snnf )Nb64)	r   listdecode_base64dictsixviewkeysbase64	b64decode	iteritems)datavalkvs       r%   r   r   ?  s    d*./$3M#$//$
||DeW$d5k**.1mmD.AB.Adaaq!!.ABBK 0
 Cs   B2B7c                 ^    U c  g[        S U R                  R                  5        5       5      $ )zReturn true if input signature contains a string dtype.

This is used to determine if we should proceed with base64 decoding.

Args:
  signature: SignatureDef protocol buffer

Returns:
  bool
Tc              3   p   #    U  H,  nUR                   [        R                  R                  :H  v   M.     g 7frD   )dtyper   stringas_datatype_enum).0r   s     r%   	<genexpr>-does_signature_contain_str.<locals>.<genexpr>\  s*      1/ WW666/s   46)anyr   values)	signatures    r%   r   r   K  s6     	 1&&--/1 
1 1r(   c                    [         R                   " 5       n[        R                  " SX5        [        R                  R                  U5      (       d  [        R                  " U5        [        R                  R                  U S5      n  [        R                  " SSSX/[        R                  S9  [        R                  " SU U[         R                   " 5       U-
  5        g	! [        R                   a    [        R                  " S5        e f = f)
a  Copy files from gcs to a local path.

Copies files directly to the dest_path.
Sample behavior:
dir1/
  file1
  file2
  dir2/
    file3

copy_model_to_local("dir1", "/tmp")
After copy:
tmp/
  file1
  file2
  dir2/
    file3

Args:
  gcs_path: Source GCS path that we're copying from.
  dest_path: Destination local path that we're copying to.

Raises:
  Exception: If gsutil is not found.
z$Starting to copy files from %s to %s*gsutilcpz-R)stdinz"Could not copy model using gsutil.z+Files copied from %s to %s: took %f secondsN)rk   loggingdebugospathexistsmakedirsjoin
subprocess
check_callPIPECalledProcessError	exception)gcs_path	dest_pathcopy_start_times      r%   copy_model_to_localr   `  s    4 IIK/	--6L			"	"KK	WW\\(C((
 $h3:D//K
 
--=x499;8: 
	&	& 
:;	
s   'C ,Dc                    U R                  S5      (       a  [        U [        5        [        n  [        R                  R                  U [        5      n[        R                  R                  U [        5      n[        R                  R                  U5      (       a2  Un SSK	J
n   [        R                   " SU5        UR#                  U5      $ [        R                  R                  U5      (       aS  Un[        R                   " SU5        [-        US	5       n	[.        R0                  " U	R3                  5       5      sSSS5        $ g! [         aS  n SSK
n SnAN! [         a7  nSn[        R                  " U5        [        [        R                  U5      eSnAff = fSnAff = f! [$         aA    [        R                   " SU5        SSKnUR)                  5       nUR+                  U5        Us $ f = f! , (       d  f       g= f! [         a}  n[5        U5      n
S
U
;   a%  SR7                  WU
[8        R:                  S   5      nOSR7                  WU
5      n[        R                  " U5        [        [        R                  U5      eSnAff = f)aG  Loads either a .joblib or .pkl file from GCS or from local.

Loads one of DEFAULT_MODEL_FILE_NAME_JOBLIB or DEFAULT_MODEL_FILE_NAME_PICKLE
files if they exist. This is used for both sklearn and xgboost.

Arguments:
  model_path: The path to the directory that contains the model file. This
    path can be either a local path or a GCS path.

Raises:
  PredictionError: If there is a problem while loading the file.

Returns:
  A loaded scikit-learn or xgboost predictor object or None if neither
  DEFAULT_MODEL_FILE_NAME_JOBLIB nor DEFAULT_MODEL_FILE_NAME_PICKLE files are
  found.
zgs://r   )joblibNzCould not import joblib module.zLoading model %s using joblib.zRLoading model %s using joblib failed. Loading model using xgboost.Booster instead.zLoading model %s using pickle.rbzunsupported pickle protocolzCould not load the model: {}. {}. Please make sure the model was exported using python {}. Otherwise, please specify the correct 'python_version' parameter when deploying the model.z!Could not load the model: {}. {}.)
startswithr   LOCAL_MODEL_PATHr   r   r   DEFAULT_MODEL_FILE_NAME_JOBLIBDEFAULT_MODEL_FILE_NAME_PICKLEr   sklearn.externalsr   	Exceptionr   r   r   r8   infoloadKeyErrorr   Booster
load_modelopenpickleloadsreadstrformatsysversion_info)
model_pathmodel_file_name_joblibmodel_file_name_picklemodel_file_namer   e	error_msgr   boosterfraw_error_msgs              r%   load_joblib_or_pickle_modelr     s*   $ 7##
$45!J7KWW\\**HJWW\\**HJ	ww~~,--.oQ 	-5G{{?++ 
.	/	/.oll3_E&!||AFFH% '& =  Q	Q - 	Q7)


I
& D DiP
P	QQ  	(*9	;
 	//#?+	 '& 	 KFM$5ABH&}c.>.>q.ACC  6<<
=*ii 
/>>	
JJKs   A.H E $'F$ A	H $G29	H 
F!EH 
F"2FFFF!!H $AG/,H .G//H 2
H <H  H 
J
A8JJ
c                 N   S[        U 5      R                  ;   a  [        $ S[        U 5      R                  ;   a  [        $ SR	                  [        U 5      R                  [        U 5      R
                  5      n[        R                  " U5        [        [        R                  U5      e)zDistinguish scikit-learn and xgboost using model object.

Arguments:
  model_obj: A loaded model object

Raises:
  PredictionError: If there is a problem detecting framework from object.

Returns:
  Either scikit-learn framework or xgboost framework
sklearnr   z|Invalid model type detected: {}.{}. Please make sure the model file is an exported sklearn model, xgboost model or pipeline.)
typer4   SCIKIT_LEARN_FRAMEWORK_NAMEXGBOOST_FRAMEWORK_NAMEr   r3   r   criticalr   r8   )	model_objr   s     r%    detect_sk_xgb_framework_from_objr     s     $y/,,,&&DO...!!	%&,fO&&O$$'&  Y
/>>	
JJr(   c                     SnU HK  n[         R                  R                  [         R                  R                  X5      5      (       d  MF  US-  nMM     U$ )a  Count how many specified files exist in model_path.

Args:
  model_path: The local path to the directory that contains the model file.
  specified_file_names: The file names to be checked

Returns:
  An integer indicating how many specified_file_names are found in model_path.
r   r   )r   r   r   r   )r   specified_file_namesnum_matches	file_names       r%   _count_num_files_in_pathr     sE     +'i	ww~~bggll:9::Qk ( 
r(   c                    [        U [        5      n[        U [        5      n[        U [        5      nX-   U-   nUS:  aA  SR	                  U 5      n[
        R                  " U5        [        [        R                  U5      eUS:X  a  [        $ US:X  a  [        $ US:X  a  [        U 5      n[        U5      $ [
        R                  " S5        g)aS  Detect framework from model_path by analyzing file extensions.

Args:
  model_path: The local path to the directory that contains the model file.

Raises:
  PredictionError: If framework can not be identified from model path.

Returns:
  A string representing the identified framework or None (custom code is
  assumed in this situation).
r   z4Multiple model files are found in the model_path: {}zFModel files are not found in the model_path.Assumed to be custom code.N)r   $TENSORFLOW_SPECIFIC_MODEL_FILE_NAMES!XGBOOST_SPECIFIC_MODEL_FILE_NAMESSCIKIT_LEARN_MODEL_FILE_NAMESr   r   r   r   r8   r   r   r   r   warning)r   num_tensorflow_modelsnum_xgboost_modelsnum_sklearn_modelsr   r   r   s          r%   detect_frameworkr   	  s     368/35/
0MO &:=OO+1_FMMIY
/>>	
JJa$$Q!!Q+J7I+I66OO 2 4r(   c                    [         R                  R                  S5      (       d  g[        R                  " [         R                  R                  S5      5      nU(       a  [        U[        5      (       d  gUR                  S5      nU(       a  [        U[        5      (       d  g[        R                  " SUR                  U 5      U 5        UR                  U 5      $ )aK  Gets the value of field_name in the version being created, if it exists.

Args:
  field_name: Name of the key used for retrieving the corresponding value from
    version json object.

Returns:
The value of the given field in the version object or the user provided create
version request if it exists. Otherwise None is returned.
create_version_requestNversionz:Found value: %s, for field: %s from create_version_request)	r   environgetjsonr   r   r   r   r   )
field_namerequestr   s      r%   get_field_in_version_jsonr  1  s     
0	1	1JJrzz~~&>?@'	
7D11KK	"'	
7D11	,,K{{:&
4	Z	  r(   c                    [        U [        R                  5      (       d#  [        SR	                  [        U 5      5      5      e[        U ;  a(  [        SR	                  [        [        U 5      5      5      eU R                  [        5      $ )zParses the predictions from the json response from prediction server.

Args:
  response_json(Text): The JSON formatted response to parse.

Returns:
  Predictions from the response json.

Raises:
  ValueError if response_json is malformed.
4Invalid response received from prediction server: {}=Required field '{}' missing in prediction server response: {})r   collections_libMapping
ValueErrorr   reprPREDICTIONS_KEYpopresponse_jsons    r%   parse_predictionsr  J  s|     
M?#:#:	;	;
>EE	!" " M)
GNNT-0	23 3 
		?	++r(   c                    [        U [        R                  5      (       d#  [        SR	                  [        U 5      5      5      e[        U ;  a(  [        SR	                  [        [        U 5      5      5      eU R                  [        5      $ )zParses the outputs from the json response from prediction server.

Args:
  response_json(Text): The JSON formatted response to parse.

Returns:
  Outputs from the response json.

Raises:
  ValueError if response_json is malformed.
r  r  )r   r  r	  r
  r   r  OUTPUTS_KEYr  r  s    r%   parse_outputsr  a  s|     
M?#:#:	;	;
>EE	!" " %
GNNm,	./ / 
		;	''r(   c                    [        U [        R                  5      (       d#  [        SR	                  [        U 5      5      5      e[        U ;  a(  [        SR	                  [        [        U 5      5      5      eU R                  [        5      $ )zParses instances from the json request sent to prediction server.

Args:
  request_json(Text): The JSON formatted request to parse.

Returns:
  Instances from the request json.

Raises:
  ValueError if request_json is malformed.
z-Invalid request sent to prediction server: {}z<Required field '{}' missing in prediction server request: {})r   r  r	  r
  r   r  INSTANCES_KEYr  )request_jsons    r%   parse_instancesr  x  sz     
L/"9"9	:	:
DKK\  ,&
FMM4-	/0 0 
		-	((r(   rD   )Ir7   r   collectionsrm   r  r   r   r   r   r   rk   rG   _interfacesr   r   tensorflow.python.frameworkr   r  r   abcENGINEr   	FRAMEWORKMODEL_SUBDIRECTORYPREPARED_MODEL_SUBDIRECTORYr   SK_XGB_FRAMEWORK_NAMEr   r   CUSTOM_FRAMEWORK_NAMEr   r   r   r   r   r   r   
INPUTS_KEYr  SIGNATURE_KEYCOLUMNARIZE_TIMEUNALIAS_TIMEENCODE_TIMESESSION_RUN_TIME
ALIAS_TIMEROWIFY_TIMESESSION_RUN_ENGINE_NAMEr   
namedtupler   r  r  r   r   r]   rb   objectrA   r   rf   rp   r   r   r   r   r   r   r   r   r  r  r  r  r2   r(   r%   <module>r-     s   0      	   
    
 .fOO/
 
.	 . ,   " (   .0  "0 !, ( $
 #"!  %3 ! 
  1 (&0 $
&*    !,,.0   !Ni !NH 	1%F 1%h$D $6S SlP,	1*(:VMK`K8$%P!2,.(.)r(   