Skip to content
Closed
47 changes: 42 additions & 5 deletions python/pyarrow/_flight.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ cdef class FlightCallOptions(_Weakrefable):
cdef:
CFlightCallOptions options

def __init__(self, timeout=None, write_options=None):
def __init__(self, timeout=None, write_options=None, headers=None):
"""Create call options.

Parameters
Expand All @@ -118,12 +118,18 @@ cdef class FlightCallOptions(_Weakrefable):
write_options : pyarrow.ipc.IpcWriteOptions, optional
IPC write options. The default options can be controlled
by environment variables (see pyarrow.ipc).

headers : List[Tuple[str, str]], optional
A list of arbitrary headers as key, value tuples
"""
cdef IpcWriteOptions options = _get_options(write_options)
cdef IpcWriteOptions c_write_options

if timeout is not None:
self.options.timeout = CTimeoutDuration(timeout)
self.options.write_options = options.c_options
if write_options is not None:
c_write_options = _get_options(write_options)
self.options.write_options = c_write_options.c_options
if headers is not None:
self.options.headers = headers

@staticmethod
cdef CFlightCallOptions* unwrap(obj):
Expand Down Expand Up @@ -1150,6 +1156,38 @@ cdef class FlightClient(_Weakrefable):
self.client.get().Authenticate(deref(c_options),
move(handler)))

def authenticate_basic_token(self, username, password,
options: FlightCallOptions = None):
"""Authenticate to the server with HTTP basic authentication.

Parameters
----------
username : string
Username to authenticate with
password : string
Password to authenticate with
options : FlightCallOptions
Options for this call

Returns
-------
tuple : Tuple[str, str]
A tuple representing the FlightCallOptions authorization
header entry of a bearer token.
"""
cdef:
CResult[pair[c_string, c_string]] result
CFlightCallOptions* c_options = FlightCallOptions.unwrap(options)
c_string user = tobytes(username)
c_string pw = tobytes(password)

with nogil:
result = self.client.get().AuthenticateBasicToken(deref(c_options),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't there be a FlightCallOption implementation to do this? How do you use HTTP basic auth without using authenticateBasicToken? (eg in Java you can directly call getFlightInfo without calling an authenticate function and supply a FlightCallOption with the basic auth info).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on how the C++ code was implemented , the Python wrapper can only wrap the AuthenticateBasicToken method in the C++ client. Seems like we would need to expand the C++ client implementation if we were to have C++/Python impl match the Java client implementation. #8724 -> C++ impl only exposed authenticateBasicToken.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jduo are the changes in this PR sufficient?

user, pw)
check_flight_status(result.status())

return GetResultValue(result)

def list_actions(self, options: FlightCallOptions = None):
"""List the actions available on a service."""
cdef:
Expand Down Expand Up @@ -1871,7 +1909,6 @@ cdef CStatus _server_authenticate(void* self, CServerAuthSender* outgoing,
reader.poison()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a typo "poison"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a typo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to ensure a server doesn't use the Python reader beyond the lifetime of the C++ reader it wraps.

return CStatus_OK()


cdef CStatus _is_valid(void* self, const c_string& token,
c_string* peer_identity) except *:
"""Callback for implementing authentication in Python."""
Expand Down
6 changes: 6 additions & 0 deletions python/pyarrow/includes/libarrow_flight.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ cdef extern from "arrow/flight/api.h" namespace "arrow" nogil:
CFlightCallOptions()
CTimeoutDuration timeout
CIpcWriteOptions write_options
vector[pair[c_string, c_string]] headers

cdef cppclass CCertKeyPair" arrow::flight::CertKeyPair":
CCertKeyPair()
Expand Down Expand Up @@ -307,6 +308,11 @@ cdef extern from "arrow/flight/api.h" namespace "arrow" nogil:
CStatus Authenticate(CFlightCallOptions& options,
unique_ptr[CClientAuthHandler] auth_handler)

CResult[pair[c_string, c_string]] AuthenticateBasicToken(
CFlightCallOptions& options,
const c_string& username,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the uninitiated, what are the coding standards on Class & variable names? I see a mixture of camel case and underscore separated names.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mixture here is because this is a set of Cython type definitions for C++ code, which uses the C++ conventions (e.g. CResult), but the overall project is in Python, which uses Python conventions (e.g. c_string)

const c_string& password)

CStatus DoAction(CFlightCallOptions& options, CAction& action,
unique_ptr[CResultStream]* results)
CStatus ListActions(CFlightCallOptions& options,
Expand Down
Loading