Sobre la base de los comentarios a mi pregunta, logré ejecutar el procedimiento almacenado con parámetros con valores de tabla (y obtener los valores de retorno del SP). El script final es el siguiente:
import pandas as pd
import pytds
from pytds import login
import sqlalchemy as sa
from sqlalchemy import create_engine
import sqlalchemy_pytds
def connect():
return pytds.connect(dsn='ServerName',database='DBName',autocommit=True, auth=login.SspiAuth())
engine = sa.create_engine('mssql+pytds://[ServerName]', creator=connect)
conn = engine.raw_connection()
with conn.cursor() as cur:
arg = [["foo.ExternalInput","bar.ExternalInput"]]
tvp = pytds.TableValuedParam(type_name="core.MatchColumnTable", rows=arg)
cur.execute("EXEC test_proc @Target = N'[dbname].[tablename1]', @Source = N'[dbname].[table2]', @CleanTarget = 0, @UseColumnsFromTarget = 0, @MergeOnColumn = %s", (tvp,))
result = cur.fetchall()
print(result)
La confirmación automática se agrega en la conexión (para confirmar la transacción en el cursor), el parámetro con valores de tabla (marchcolumntable) espera 2 columnas, por lo que el argumento se modifica para que quepa en 2 columnas.
Los parámetros que se requieren además de tvp se incluyen en la cadena exec. El último parámetro en la cadena de ejecución es el nombre del parámetro tvp (mergeoncolumn) que se llena con tvp.
opcionalmente, puede agregar el estado del resultado o el recuento de filas como se describe en la documentación de pytds:https://python-tds.readthedocs.io/en/latest/index.html
¡Nota! :en el procedimiento almacenado, debe asegurarse de que se agregue SET NOCOUNT ON; de lo contrario, no obtendrá ningún resultado en Python