1+ import atexit
12import os
2- import signal
33import platform
44import socket
55import subprocess
6- import atexit
76import time
8- from typing import Union , List , Dict , Any
7+ from typing import Any , Dict , List , Union
8+
99import grpc
10+
11+ import feast
1012from feast import errors
11- from feast .type_map import python_values_to_proto_values
1213from feast .feature_service import FeatureService
13- from feast .repo_config import RepoConfig
1414from feast .online_response import OnlineResponse
15- from feast .protos .feast .serving .ServingService_pb2 import GetOnlineFeaturesRequest , GetFeastServingInfoRequest
15+ from feast .protos .feast .serving .ServingService_pb2 import (
16+ GetFeastServingInfoRequest ,
17+ GetOnlineFeaturesRequest ,
18+ )
1619from feast .protos .feast .serving .ServingService_pb2_grpc import ServingServiceStub
17- import feast
20+ from feast .repo_config import RepoConfig
21+ from feast .type_map import python_values_to_proto_values
1822
1923
2024class GoServer :
@@ -30,10 +34,10 @@ def __init__(self, repo_path: str, config: RepoConfig):
3034 self ._connect ()
3135
3236 def get_online_features (
33- self ,
34- features : Union [List [str ], FeatureService ],
35- entities : Dict [str , List [Any ]],
36- full_feature_names : bool = False ,
37+ self ,
38+ features : Union [List [str ], FeatureService ],
39+ entities : Dict [str , List [Any ]],
40+ full_feature_names : bool = False ,
3741 ) -> OnlineResponse :
3842
3943 if not self .grpcServerStarted :
@@ -49,7 +53,7 @@ def get_online_features(
4953
5054 for key , values in entities .items ():
5155 request .entities [key ].val .extend (python_values_to_proto_values (values ))
52-
56+
5357 try :
5458 response = self .client .GetOnlineFeatures (request = request )
5559 except grpc .RpcError as rpc_error :
@@ -69,7 +73,9 @@ def get_online_features(
6973 parsed_error_message = error_message .split (": " )[1 ].split ("; " )
7074 collided_feature_refs = parsed_error_message [0 ].split (", " )
7175 full_feature_names = parsed_error_message [1 ] == "true"
72- raise errors .FeatureNameCollisionError (collided_feature_refs , full_feature_names )
76+ raise errors .FeatureNameCollisionError (
77+ collided_feature_refs , full_feature_names
78+ )
7379 elif error_message .startswith (self .ValueError_STRING ):
7480 parsed_error_message = error_message .split (": " )[1 ]
7581 raise ValueError (parsed_error_message )
@@ -88,34 +94,39 @@ def _connect(self):
8894 # pass a random unused port to go subprocess, so that there's no conflicts
8995 # if multiple Python processes start Go subprocess on the same host
9096 "FEAST_GRPC_PORT" : unused_port ,
91- ** os .environ
97+ ** os .environ ,
9298 }
9399 cwd = feast .__path__ [0 ]
94100
95101 if "dev" in feast .__version__ :
96- self .process = subprocess .Popen (["go" , "run" , "github.com/feast-dev/feast/go/server" ],
97- cwd = cwd , env = env ,
98- stdin = subprocess .PIPE )
102+ self .process = subprocess .Popen (
103+ ["go" , "run" , "github.com/feast-dev/feast/go/server" ],
104+ cwd = cwd ,
105+ env = env ,
106+ stdin = subprocess .PIPE ,
107+ )
99108 else :
100109 goos = platform .system ().lower ()
101110 goarch = "amd64" if platform .machine () == "x86_64" else "arm64"
102111 executable = feast .__path__ [0 ] + f"/binaries/go_server_{ goos } _{ goarch } "
103- self .process = subprocess .Popen ([executable ], cwd = cwd , env = env , stdin = subprocess .PIPE )
112+ self .process = subprocess .Popen (
113+ [executable ], cwd = cwd , env = env , stdin = subprocess .PIPE
114+ )
104115
105116 # Make sure the subprocess is terminated when the parent process dies
106117 # Note: this doesn't handle cases where the parent process is abruptly killed (e.g. with SIGKILL)
107- atexit .register (lambda : self .stop () )
118+ atexit .register (lambda : self .stop ())
108119 self .start_grpc_server ()
109120 self .pipeClosed = False
110121
111122 def start_grpc_server (self ):
112123 if self .grpcServerStarted :
113124 return
114125 # Try connecting to the go server using a gPRC client
115-
126+
116127 for i in range (5 ):
117128 try :
118- self .process .stdin .write (b' startGrpc\n ' )
129+ self .process .stdin .write (b" startGrpc\n " )
119130 self .process .stdin .flush ()
120131 self .grpcServerStarted = True
121132 break
@@ -146,7 +157,9 @@ def start_http_server(self, host: str, port: int):
146157 return
147158 for i in range (10 ):
148159 try :
149- self .process .stdin .write (bytes (f"startHttp { host } :{ port } \n " , encoding = 'utf8' ))
160+ self .process .stdin .write (
161+ bytes (f"startHttp { host } :{ port } \n " , encoding = "utf8" )
162+ )
150163 self .process .stdin .flush ()
151164 self .httpServerStarted = True
152165 break
@@ -163,7 +176,7 @@ def stop(self):
163176 # Otherwise, let go subprocess clean up and shut down itself
164177 if not self .pipeClosed :
165178 try :
166- self .process .stdin .write (bytes (f "stop\n " , encoding = ' utf8' ))
179+ self .process .stdin .write (bytes ("stop\n " , encoding = " utf8" ))
167180 self .process .stdin .flush ()
168181 # TODO (Ly): Review: We don't close stdin here
169182 # since if the call succeeds go process closes
@@ -172,11 +185,12 @@ def stop(self):
172185 except subprocess .CalledProcessError as error :
173186 self .process .terminate ()
174187 raise errors .GoSubprocessConnectionFailed () from error
175-
188+
176189 self .grpcServerStarted = False
177190 self .httpServerStarted = False
178191 self .pipeClosed = True
179-
192+
193+
180194def _get_unused_port () -> str :
181195 sock = socket .socket ()
182196 # binding port 0 means os will choose an unused port for us
0 commit comments