Select does not have a hook for that particular spot in the SQL compilation, so unless you rewrote all of visit_select, your best bet is to stick with simple string replacement.
this does not in any way preclude you from using the compiler extension, get the text from the compiler then do the process
import re
from sqlalchemy import column
from sqlalchemy import select
from sqlalchemy import table
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Select
@compiles(Select)
def _compile(element, compiler, **kw):
text = compiler.visit_select(element, **kw)
match = re.search(r"BULK COLLECT INTO (.*)", text)
if match:
text = re.sub(r"BULK COLLECT INTO (.*)", "", text)
text = text.replace("FROM", f"BULK COLLECT INTO {match.group(1)} FROM")
return text
baz = table('baz', column('foo'), column('bar'))
stmt = select(baz).with_statement_hint("BULK COLLECT INTO I_foos")
print(stmt)