跳过主要内容

项目结构

本页面描述了如何在您的项目中组织文件,以遵循软件开发最佳实践,这也会导致轻松的远程执行

将代码分离到模块

让我们从一个简单的例子开始。随着你的项目发展,将业务逻辑和建模代码结构化为单独的 Python 模块 是一个好主意,也就是说,将它们分开存放在不同的文件中,而不是将所有内容打包在一个单一的流文件中。

逻辑结构清晰的模块化代码易于理解、调试,并且可以使用像 unittest这样的标准Python测试工具进行测试。 重要的是,模块可以在不同的流程之间共享,使您能够创建可重用的共享功能库。

要查看此功能,创建一个名为 teatime.py 的Python文件,内容如下:

from datetime import datetime

def is_tea_time():
return 15 < datetime.utcnow().hour < 18

创建一个单独的文件, teaflow.py ,其内容如下:

from metaflow import FlowSpec, step

class TeaFlow(FlowSpec):

@step
def start(self):
import teatime
self.tea_time = teatime.is_tea_time()
self.next(self.end)

@step
def end(self):
print(f'is it tea time? {self.tea_time}')

if __name__ == '__main__':
TeaFlow()

请注意我们在 start 步骤中 import teatime 以使用该模块。您可以像往常一样运行流程:

python teaflow.py run

teatime.py模块开箱即用。如果您已设置 远程执行, 您可以运行代码--with batch--with kubernetes,它们同样有效!

远程执行是有效的,因为 Metaflow 会自动将所有 .py 文件打包在与流程文件相同的目录以及其子目录中。您可以通过执行以下命令来查看包含了哪些文件

python teaflow.py package list

将代码分离到包中

一个 Python 包 是一个包含多个模块的库,这些模块存储在一个公共目录中。 这些也可以与 Metaflow 一起开箱即用。

要测试一个本地包,crumpet,请创建一个具有以下文件的目录结构:

crumpetflow.py
crumpet/__init__.py
crumpet/teatime.py
crumpet/raisin.py

在这里, __init__.py 应该是一个空文件。它告诉 Python crumpet 应该被视为一个包。你可以从上面的例子中复制 teatime.py 模块。

应该如下所示的 raisin.py 文件:

import random

def is_dry():
return random.random() > 0.5

crumpetflow.py 像这样:

from metaflow import FlowSpec, step

class CrumpetFlow(FlowSpec):

@step
def start(self):
from crumpet import teatime, raisin
self.tea_time = teatime.is_tea_time()
self.is_dry = raisin.is_dry()
self.next(self.end)

@step
def end(self):
print(f'is it tea time? {self.tea_time}')
print(f'are raisins dry? {self.is_dry}')

if __name__ == '__main__':
CrumpetFlow()

您可以像往常一样执行流程

python crumpetflow.py run

像之前一样,您可以远程运行代码 --with kubernetes--with batch 并且您无需担心手动打包和安装 crumpet 包。

在流程中共享的通用包

您可以在上面的目录层次结构中,在crumpetflow.py旁边添加其他流程,它们将共享公共的crumpet包。

随着项目的增长,可能希望将每个流程分离到它自己的子目录中,以便每个人或团队可以独立管理他们的文件。所有人可能共享一个或多个公共包。

例如,我们可以有两个流程, crumpetflowteatimeflow 作为独立的子目录,各自拥有README文件,以及一个共享的 crumpet 包:

crumpetflow/flow.py
crumpetflow/README.md
teatimeflow/flow.py
teatimeflow/README.md
crumpet/__init__.py
crumpet/teatime.py
crumpet/raisin.py

不幸的是,这无法直接使用,因为Metaflow仅打包crumpetflowteatimeflow目录下的文件,默认忽略crumpet

解决方案是在每个流目录中包含一个符号链接(symlink),指向应该包含的公共包。您可以按如下方式创建符号链接

cd crumpetflow
ln -s ../crumpet .

包含符号链接的层次结构如下:

crumpetflow/flow.py
crumpetflow/README.md
crumpetflow/crumpet -> ../crumpet
teatimeflow/flow.py
teatimeflow/README.md
teatimeflow/crumpet -> ../crumpet
crumpet/__init__.py
crumpet/teatime.py
crumpet/raisin.py

从其他Git仓库使用常见包

上述层次结构在所有内容都存储在单个 Git 仓库时表现良好。从技术上讲,你也可以将 crumpetflowteatimeflowcrumpet 作为单独的仓库,但你需要确保各个仓库之间的符号链接保持有效。这可能会很脆弱。

如果您想从单独的库中包含一个包, mejor 的方法是使用 git subtree 命令 ,这是 Git 子模块的增强版本。使用 git subtree,您可以将一个库嵌套为另一个库的子目录。例如,crumpet 包可以作为一个独立的库,被包含为每个希望使用它的流程项目中的子树。

或者,您可以将包发布为私有Python包,您可以使用@pypi@conda来包含它

非Python依赖

默认情况下,Metaflow会将.py文件打包在流程的目录层次结构中。您还可以通过在--package-suffixes选项中包含文件后缀,将任意文件包含在远程执行的包中。

例如,下面的示例展示了如何包含SQL文件,但您也可以包含自定义二进制文件或配置文件。

创建一个目录 sql 并在那里存储两个文件:

sql/populate_table.sql

CREATE TABLE IF NOT EXISTS movie(title, year, score)
INSERT INTO movie VALUES ('Moana', 2016, 7.6), ('Scream', 2022, 6.3)

sql/get_movies.sql

SELECT title, year FROM movie

这是一个访问SQL文件的示例流程,保存在 moviesqlflow.py 中:

from metaflow import FlowSpec, step

class MovieSQLFlow(FlowSpec):

@step
def start(self):
import sqlite3
with sqlite3.connect("movies.db") as conn:
cur = conn.cursor()
with open('sql/populate_table.sql') as f:
for line in f:
cur.execute(line)
with open('sql/get_movies.sql') as f:
self.movies = cur.execute(f.read()).fetchall()
self.next(self.end)

@step
def end(self):
print("Found movies:")
for title, year in self.movies:
print(title, year)

if __name__ == '__main__':
MovieSQLFlow()

注意,您需要通过相对目录引用包文件,如上面的 sql/,而不是像 /home/alice/sql/ 这样的绝对路径。目录结构是远程可用的,但不保证在任何绝对位置。

按照如下方式执行代码:

python moviesqlflow.py --package-suffixes .sql run

在本地,它可以在没有 --package-suffixes 的情况下工作,但当在远程运行 --with batch--with kubernetes 时,它会抱怨缺少 .sql 文件,除非指定 --package-suffixes

您可以通过执行来确认所有依赖项是否正确包含

python moviesqlflow.py --package-suffixes .sql package list