Carregamento e execução de funções personalizadas em uma sala limpa¶
Visão geral¶
Você pode carregar UDFs e UDTFs Python personalizadas em sua sala limpa e executá-las a partir de seus modelos para realizar ações de dados complexas. Essas ações incluem machine learning ou manipulação de dados personalizados em uma consulta, como parte de uma etapa única ou de um fluxo de várias etapas. Python é a única linguagem de codificação compatível com UDFs personalizadas.
O código carregado pode importar e usar pacotes de um conjunto aprovado de pacotes Python e da Snowpark API.
Tanto provedores quanto consumidores podem carregar um código Python personalizado em uma sala limpa, embora o processo seja diferente para cada. Cada pacote do código carregado pode definir várias funções que chamam umas às outras, mas um pacote expõe apenas uma função do manipulador. Essa função do manipulador pode ser chamada por modelos criados ou executados por qualquer pessoa que use a sala limpa. Se o código criar tabelas internas, essas tabelas poderão ser acessadas conforme descrito em Projeto de fluxos de várias etapas.
O código carregado não pode ser excluído, mas pode ser atualizado.
Esta página mostra como carregar e executar UDFs e UDTFs Python personalizadas como provedor ou consumidor.
Dica
Para obter informações básicas de como desenvolver as próprias UDFs Python em uma sala limpa, consulte os seguintes tópicos:
Como as UDFs funcionam no Snowflake para informações gerais sobre como escrever funções Python no Snowflake.
Como escrever UDTFs no Snowflake se você quer que as funções retornem tabelas.
Como criar e carregar modelos personalizados em uma sala limpa. As UDFs/UDTFs são chamadas de um modelo personalizado.
Uso do Snowpark em salas limpas (se você quer chamar as UDFs do Snowpark).
Atualização de funções personalizadas¶
Você pode carregar ou substituir uma função existente que carregou, mas não pode excluí-la.
O carregamento de uma função com a assinatura exata da função já carregada substitui a função existente. A assinatura é o nome da função que não diferencia maiúsculas e minúsculas do manipulador externo, e os tipos de dados de todos os parâmetros, na mesma ordem. Os nomes dos parâmetros não têm importância. Você não pode substituir uma função carregada por outra conta.
Como a assinatura deve ser igual quando você atualiza uma função, não é possível alterar a assinatura de uma função existente: se você carregar a função foo(name VARIANT age INTEGER)
e, em seguida, carregar a função foo(name VARIANT age FLOAT)
, a segunda função será adicionada à sala limpa junto com a primeira, pois os tipos de argumento são diferentes.
Código enviado pelo provedor¶
As funções enviadas pelo provedor podem ser carregadas como código em linha ou de uma área de preparação do Snowflake. Ambas as técnicas são abordadas aqui.
O código carregado pode importar e usar pacotes nativamente de um conjunto aprovado de pacotes Python. Se você precisar de um pacote não padrão, use o Snowpark Container Services em uma sala limpa para hospedar o código.
Você não pode visualizar o código carregado nem seu próprio código, portanto, inclua uma cópia exata do que você carregou em uma sala limpa.
Dica
Depois de atualizar o código escrito pelo provedor, você deverá atualizar a diretiva de lançamento padrão e chamar provider.create_or_update_cleanroom_listing
para propagar as alterações aos consumidores. Se você não chamar provider.create_or_update_cleanroom_listing
, sua versão padrão não será atualizada para os consumidores que estiverem usando a sala limpa.
Confira abaixo uma exibição de alto nível de como um provedor adiciona código a uma sala limpa:
O provedor cria e configura a sala limpa como de costume.
O provedor carrega o código chamando
provider.load_python_into_cleanroom
. Você pode carregar seu código em linha diretamente desse procedimento ou carregar um arquivo de código para uma área de preparação e, depois disso, fornecer a localização da área de preparação para esse procedimento.O código pode incluir várias funções, mas apenas um manipulador é exposto para cada carregamento. Para expor várias funções aos modelos, carregue cada manipulador chamando
provider.load_python_into_cleanroom
.Após cada carregamento de código bem-sucedido, uma nova versão de patch da sala limpa é gerada. Você deve aumentar a versão padrão chamando
provider.set_default_release_directive
com o novo número de patch. Se a sala limpa for exposta externamente, serão executadas verificações de segurança antes de instalar o código, e você deverá chamarprovider.view_cleanroom_scan_status
para confirmar que as verificações de segurança foram aprovadas antes de incrementar a versão padrão.Você cria e carrega um modelo personalizado que chama o código. O modelo chama a função do manipulador usando o escopo
cleanroom
, ou seja,cleanroom.my_function
. Por exemplo, um modelo que chama a função personalizadasimple_add
que você carregou pode ter a seguinte aparência:SELECT cleanroom.simple_add(1, 2), cleanroom.simple_add({{ price | sqlsafe | int }}, {{ tax | sqlsafe | int }})
O consumidor executa seu modelo da mesma forma que qualquer outro modelo.
Dica
Se o consumidor encontrar um erro de montagem ao instalar uma sala limpa com o código personalizado, talvez seja indicação de erro de sintaxe no código.
Você encontra exemplos de código que demonstram esse fluxo na seção de exemplo de código escrito pelo provedor.
Observações importantes sobre controle de versão¶
Toda vez que o provedor carrega uma função, ele aumenta o número do patch (e há um limite de 99 números). Portanto, faça o possível para testar e depurar seu código antes de adicioná-lo à sala limpa para reduzir as atualizações de versão durante o desenvolvimento.
Se você atualizar um número de patch, os clientes que usam a UI de salas limpas podem ter que atualizar a página para ver a alteração. Os clientes que usam a API devem ver as mudanças imediatamente, mas pode haver um atraso, dependendo dos recursos disponíveis. Saiba mais sobre o controle de versão de sala limpa.
Carregamento de funções em linha escritas pelo provedor¶
Você pode carregar o código em linha no parâmetro code
de provider.load_python_into_cleanroom
. Veja um exemplo de carregamento de uma função simples em linha:
CALL samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
$cleanroom_name,
'simple_add', -- Name used to call the UDF from a template.
['first INTEGER', 'second INTEGER'], -- Arguments of the UDF, specified as '<variable_name> <SQL type>' pairs.
['numpy', 'pandas'], -- Packages imported by the UDF.
'INTEGER', -- SQL return type of UDF.
'add_two', -- Handler function in your code called when external name is called.
$$
import numpy as np # Not used, but you can load supported packages.
import pandas as pd
def add_two(first, second):
return first + second
$$
);
O modelo que faz a chamada chama cleanroom.simple_add
para invocar essa função. Os exemplos do provedor demonstram como carregar um código em linha.
Carregamento de funções escritas pelo provedor de uma área de preparação¶
Você pode carregar arquivos Python em uma área de preparação da sala limpa e mencionar essa área quando chamar provider.load_python_into_cleanroom
. Carregar o código de uma área de preparação permite desenvolvê-lo em um editor no sistema local, evitar erros ao copiar/colar durante seu carregamento em linha e também melhorar o controle de versão. Observe que você pode carregar vários arquivos em uma única chamada de procedimento, mas apenas uma função do manipulador é exposta para cada carregamento.
O código é carregado de uma área de preparação para a sala limpa quando você chama load_python_into_cleanroom
. As alterações posteriores feitas no código na área de preparação não serão propagadas à sala limpa.
Para carregar a UDF para uma área de preparação:
Crie seu arquivo .py e disponibilize-o em uma localização onde você possa carregá-lo em uma área de preparação do Snowsight.
Para saber o nome da área de preparação da sua sala limpa, chame
provider.get_stage_for_python_files($cleanroom_name)
. Essa área é acessível pela sala limpa. Não é possível usar uma área de preparação arbitrária que você cria.Carregue o arquivo .py para a área de preparação da sua sala limpa. Há várias maneiras de fazer isso, inclusive usando a CLI, o Snowsight ou drivers específicos da linguagem.
Chame
provider.load_python_into_cleanroom
com a localização da área de preparação, o manipulador, o nome externo, os argumentos e o tipo de retorno. Os modelos em sua sala limpa agora podem chamar a função.
O código de exemplo a seguir mostra como carregar o código em uma sala limpa de uma área de preparação.
-- Save the following code as reverser.py:
--import numpy as np
--def main(some_string):
-- '''Return the reverse of a string plus a random number 1-10'''
-- return some_string[::-1] + str(np.random.randint(1,10))
-- Get the stage for your clean room.
CALL samooha_by_snowflake_local_db.provider.get_stage_for_python_files($cleanroom_name);
-- Save the file to the stage. Here is how to do it by using the Snowflake CLI
PUT file://~/reverser.py <STAGE_NAME> overwrite=True auto_compress=False;
-- Load the code from the stage into the clean room.
CALL samooha_by_snowflake_local_db.provider.load_python_into_cleanroom(
$cleanroom_name,
'reverse', -- Name used to call the function
['some_string STRING'], -- Arguments and SQL types
['numpy'], -- Any required packages
['/reverser.py'], -- Relative path to file on stage
'STRING', -- Return type
'reverser.main' -- <FILE_NAME>.<FUNCTION_NAME>
);
-- Uploading code, even from a stage, increases the patch number.
CALL samooha_by_snowflake_local_db.provider.set_default_release_directive(
$cleanroom_name, 'V1_0', <NEW_PATCH_NUMBER>);
-- Upload a template that calls the function.
CALL samooha_by_snowflake_local_db.provider.add_custom_sql_template(
$cleanroom_name,
$udf_template_name,
$$
SELECT
p.status,
cleanroom.reverse(p.status)
FROM SAMOOHA_SAMPLE_DATABASE.DEMO.CUSTOMERS AS p
LIMIT 100;
$$
);
-- Switch to the consumer account and run the template to see the results.
Os exemplos do provedor demonstram o carregamento do código de uma área de preparação.
Exemplos de código escrito pelo provedor¶
Os exemplos a seguir demonstram como adicionar UDFs e UDTFs escritas pelo provedor a uma sala limpa.
Baixe os exemplos a seguir e carregue-os como arquivos de planilha em sua conta Snowflake. Você precisa de contas separadas para o provedor e o consumidor, cada uma com a API de salas limpas instalada. Substitua as informações conforme indicado nos arquivos de amostra.
Carregamento de arquivo de uma área de preparação
. Execute este notebook depois de executar o exemplo do provedor para tentar carregar a UDF de uma área de preparação.
Código enviado pelo consumidor¶
O consumidor pode enviar um código de UDF ou UDTF e chamá-lo de um modelo personalizado. O código carregado pelo consumidor é agrupado em um único procedimento junto com um modelo personalizado, e depois carregado em uma única chamada do procedimento. O código do consumidor está diretamente vinculado a esse modelo e não pode ser chamado por outros modelos.
Para carregar o código como consumidor, você deve conhecer a sintaxe do modelo personalizado e saber como enviar um modelo definido pelo consumidor.
Observe que qualquer código carregado por um consumidor pode ser visto pelo provedor quando ele solicita permissão de carregamento. O código do consumidor também fica visível sempre que um provedor ou consumidor examina o modelo.
Confira abaixo uma visão geral das etapas para carregar o código do consumidor personalizado:
O provedor cria a sala limpa como de costume e convida o consumidor.
O consumidor instala e configura a sala limpa como de costume.
O consumidor prepara um modelo. O modelo chama a UDF ou a UDTF no namespace
cleanroom
. Por exemplo, para chamar a funçãocalculate_tax
definida pelo consumidor, um modelo simples pode parecer com o seguinte trecho:SELECT {{ cleanroom.calculate_tax(p.cost) }} AS Tax FROM my_db.my_sch.sales AS p;
O consumidor prepara o código Python. Recomendamos o uso de aspas duplas (
" "
) em vez de aspas simples (' '
) no código para evitar ter que inserir um caractere de escape extra no futuro. O código pode fazer referência a um pacote de bibliotecas Python selecionadas.O consumidor passa o código Python para
consumer.generate_python_request_template
. O procedimento retorna o código Python como um procedimento armazenado, com um espaço reservado para o modelo JinjaSQL personalizado. Há várias cadeias de caracteres multilinha no modelo que usam$$
como delimitadores de várias linhas.Substitua o espaço reservado do modelo na saída de
generate_python_request_template
por seu modelo JinjaSQL.No modelo combinado, insira um caractere de escape para aspas simples como esta:
\'
. Isso deve ser feito porque as aspas simples serão usadas como as delimitadoras mais externas em toda a cadeia de caracteres do procedimento multilinha. Por exemplo:BEGIN CREATE OR REPLACE FUNCTION CLEANROOM.custom_compare(min_status STRING, max_status STRING, this_status STRING) RETURNS boolean LANGUAGE PYTHON RUNTIME_VERSION = 3.10 PACKAGES = (\'numpy\') HANDLER = \'custom_compare\' AS $$ import numpy as np def custom_compare(min_status:str, max_status:str, this_status:str): statuses = [\'MEMBER\', \'SILVER\', \'GOLD\', \'PLATINUM\'] return ((statuses.index(this_status) >= statuses.index(min_status)) & (statuses.index(this_status) <= statuses.index(max_status))) $$; -- Custom template LET SQL_TEXT varchar := $$ SELECT c.status, c.hashed_email FROM IDENTIFIER( {{ my_table[0] }} ) as c WHERE cleanroom.custom_compare({{ min_status }}, {{ max_status }}, c.status); $$; LET RES resultset := (EXECUTE IMMEDIATE :SQL_TEXT); RETURN TABLE(RES); END;
Chame
consumer.create_template_request
com seu modelo combinado. Coloque o código que você fornece para o procedimento armazenado entre aspas simples (' '
), em vez dos delimitadores de cifrões duplos ($$...$$
), no argumentotemplate_definition
. Por exemplo:CALL samooha_by_snowflake_local_db.consumer.create_template_request( $cleanroom_name, $template_name, ' BEGIN -- First, define the Python UDF. CREATE OR REPLACE FUNCTION CLEANROOM.custom_compare(min_status STRING, max_status STRING, this_status STRING) RETURNS boolean LANGUAGE PYTHON RUNTIME_VERSION = 3.10 PACKAGES = (\'numpy\') HANDLER = \'custom_compare\' AS $$ import numpy as np def custom_compare(min_status:str, max_status:str, this_status:str): statuses = [\'MEMBER\', \'SILVER\', \'GOLD\', \'PLATINUM\'] return ((statuses.index(this_status) >= statuses.index(min_status)) & (statuses.index(this_status) <= statuses.index(max_status))) $$; -- Then define and execute the SQL query. LET SQL_TEXT varchar := $$ SELECT c.status, c.hashed_email FROM IDENTIFIER( {{ my_table[0] }} ) as c WHERE cleanroom.custom_compare({{ min_status }}, {{ max_status }}, c.status); $$; -- Execute the query and then return the result. LET RES resultset := (EXECUTE IMMEDIATE :SQL_TEXT); RETURN TABLE(RES); END; ');
O consumidor e o provedor continuam com o fluxo do modelo definido pelo consumidor padrão:
O provedor visualiza a solicitação de modelo (
provider.list_pending_template_requests
) e chamaapprove_template_request
para aprová-la. Na solicitação, o provedor pode ver o modelo e o código no pacote.O consumidor verifica o status da solicitação (
consumer.list_template_requests
) e, quando o status é APPROVED, executa o modelo (consumer.run_analysis
).
Exemplos de código escrito pelo consumidor¶
Os exemplos a seguir demonstram como adicionar UDFs escritas pelo provedor a uma sala limpa.
Baixe os exemplos a seguir e carregue-os como arquivos de planilha em sua conta Snowflake. Você precisa de contas separadas para o provedor e o consumidor, cada uma com a API de salas limpas instalada. Substitua as informações conforme indicado nos arquivos de amostra: