Skip to content

初始化项目

在本章节,你讲学习到一下内容:

  • 使用 cookiecutter 初始化一个项目结构
  • 查看初始化项目里面的主要文件内容
  • 对项目进行 git 初始化和内容提交
  • 使用 poetry 初始化项目的 python 环境
  • 使用 tox 自动化测试项目,检查初始化的项目有没有问题

初始化项目时,使用 cookiecutter 加载 项目模板 创建。 通过交互操作,可以选择使用的功能。

创建项目骨架

在终端运行命令:

cookiecutter https://github.com/pyloong/cookiecutter-pythonic-project

然后根据交互提示,选择需要的内容。最终输入如下:

❯ cookiecutter https://github.com/pyloong/cookiecutter-pythonic-project
project_name [My Project]: example-etl
project_slug [example_etl]: 
project_description [My Awesome Project!]: This is my first etl project.
author_name [Author]: test
author_email [test@example.com]: test@example.com
version [0.1.0]: 
Select python_version:
1 - 3.10
2 - 3.11
Choose from 1, 2 [1]: 
use_src_layout [y]: 
use_poetry [y]: 
use_docker [n]: 
Select ci_tools:
1 - none
2 - Gitlab
3 - Github
Choose from 1, 2, 3 [1]: 
init_skeleton [n]: y

然后使用 vscode 打开项目:

code example_etl

建议在项目开始的时候就初始化 git 仓库,并在后续及时提交功能修改。

git init
git config user.name test
git config user.email test@example.com
git commit -m "feat: init project."

项目内容

在 IDE 中查看项目,可以看到目录结构如下:

❯ tree example_etl
example_etl
├── .editorconfig
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── README.md
├── docs
│   └── development.md
├── pyproject.toml
├── src
│   └── example_etl
│       ├── __init__.py
│       ├── cmdline.py
│       ├── config
│       │   ├── __init__.py
│       │   └── settings.yml
│       └── log.py
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   ├── settings.yml
│   ├── test_cmdline.py
│   ├── test_log.py
│   └── test_version.py
└── tox.ini

6 directories, 19 files

pyproject.toml

pyproject.toml 是项目的打包配置文件。文件前面 tool.poetry 中设置了项目的基本信息。 tool.poetry.dependencies 中配置了此项目的依赖库。

文件内容如下:

[tool.poetry]
name = "example_etl"
version = "0.1.0"
description = "This is my first etl project."
readme = "README.md"
authors = ["test <test@example.com>"]
license = "MIT"
classifiers = [
    "Operating System :: OS Independent",
    "Programming Language :: Python :: 3.10",
]

[tool.poetry.dependencies]
python = "^3.10"
dynaconf = "^3.1.12"
click = "^8.1.3"

[tool.poetry.group.dev.dependencies]
pylint = "^2.17.4"
isort = "^5.12.0"
pytest = "^7.3.1"
tox = "^4.5.2"
mkdocs = "^1.4.3"
mkdocs-material = "^8.5.11"
pytest-pylint = "^0.19.0"
pre-commit = "^3.3.2"

[tool.poetry.scripts]
example_etl = "example_etl.cmdline:main"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.pytest.ini_options]
testpaths = "tests"
python_files = "tests.py test_*.py *_tests.py"

[tool.pylint.design]
max-line-length = 120

src/example_etl/cmdline.py

src/example_etl/cmdline.py 是使用 click 编写的一个命令行入口文件,通过一些自定义命令和参数来控制程序的逻辑。

"""Command line"""
import click
from click import Context

from example_etl import __version__
from example_etl.config import settings
from example_etl.log import init_log


@click.group(invoke_without_command=True)
@click.pass_context
@click.option(
    '-V',
    '--version',
    is_flag=True,
    help='Show version and exit.'
)  # If it's true, it will override `settings.VERBOSE`
@click.option('-v', '--verbose', is_flag=True, help='Show more info.')
@click.option(
    '--debug',
    is_flag=True,
    help='Enable debug.'
)  # If it's true, it will override `settings.DEBUG`
def main(ctx: Context, version: str, verbose: bool, debug: bool):
    """Main commands"""
    if version:
        click.echo(__version__)
    elif ctx.invoked_subcommand is None:
        click.echo(ctx.get_help())
    else:
        if verbose:
            settings.set('VERBOSE', True)
        if debug:
            settings.set('DEBUG', True)


@main.command()
def run():
    """Run command"""
    init_log()
    click.echo('run......')

src/example_etl/log.py

src/example_etl/log.py 是预定义日志配置文件,当项目启动时,会自动初始化默认的日志配置。

"""Log"""
import logging
import os
from logging.config import dictConfig

from example_etl.config import settings

os.makedirs(settings.LOGPATH, exist_ok=True)


def verbose_formatter(verbose: int) -> str:
    """formatter factory"""
    if verbose is True:
        return 'verbose'
    return 'simple'


def update_log_level(debug: bool, level: str) -> str:
    """update log level"""
    if debug is True:
        level_num = logging.DEBUG
    else:
        level_num = logging.getLevelName(level)
    settings.set('LOGLEVEL', logging.getLevelName(level_num))
    return settings.LOGLEVEL


def init_log() -> None:
    """Init log config."""
    log_level = update_log_level(settings.DEBUG, str(settings.LOGLEVEL).upper())

    log_config = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            'verbose': {
                'format': '%(asctime)s %(levelname)s %(name)s %(process)d %(thread)d %(message)s',
            },
            'simple': {
                'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
            },
        },
        "handlers": {
            "console": {
                "formatter": verbose_formatter(settings.VERBOSE),
                'level': 'DEBUG',
                "class": "logging.StreamHandler",
            },
            'file': {
                'class': 'logging.handlers.RotatingFileHandler',
                'level': 'DEBUG',
                'formatter': verbose_formatter(settings.VERBOSE),
                'filename': os.path.join(settings.LOGPATH, 'all.log'),
                'maxBytes': 1024 * 1024 * 1024 * 200,  # 200M
                'backupCount': '5',
                'encoding': 'utf-8'
            },
        },
        "loggers": {
            '': {'level': log_level, 'handlers': ['console']},
        }
    }

    dictConfig(log_config)

src/example_etl/config/__init__.py

src/example_etl/config/__init__.py 是使用 dynaconf 初始化的配置中心,项目所有的配置都是 从 settings 对象中获取,它会读取项目级别的默认配置文件,也会读取自定义配置文件。

默认情况下,加载的配置文件如下:

  • src/example_etl/config/settings.yml :项目默认配置文件
  • src/example_etl/config/settings.local.yml :这个在项目中是不会 git 追踪的,属于本地自定义配置
  • <sys.prefix>/etc/example_etl/settings.yml :操作系统外部配置文件。默认这个配置文件和项目默认配置文件的内容一致。
  • 使用 EXAMPLE_ETL_<name>=<value> 环境变量传递

优先级从从上到下依次增大,优先级高的会覆盖优先级低的配置。

"""
Configuration center.
Use https://www.dynaconf.com/
"""""
import os
import sys
from pathlib import Path

from dynaconf import Dynaconf


_base_dir = Path(__file__).parent.parent

_settings_files = [
    # All config file will merge.
    Path(__file__).parent / 'settings.yml',  # Load default config.
]

# User configuration. It will be created automatically by the pip installer .
_external_files = [
    Path(sys.prefix, 'etc', 'example_etl', 'settings.yml')
]


settings = Dynaconf(
    # Set env `EXAMPLE_ETL_FOO='bar'`,use `settings.FOO` .
    envvar_prefix='EXAMPLE_ETL',
    settings_files=_settings_files,  # load user configuration.
    # environments=True,  # Enable multi-level configuration,eg: default, development, production
    load_dotenv=True,  # Enable load .env
    # env_switcher='EXAMPLE_ETL_ENV',
    lowercase_read=False,  # If true, can't use `settings.foo`, but can only use `settings.FOO`
    includes=_external_files,  # Customs settings.
    base_dir=_base_dir,  # `settings.BASE_DIR`
)

初始化环境

项目使用 poetry 管理虚拟环境,运行命令自动创建虚拟环境,同时安装开发环境依赖

安装项目默认依赖

在项目目录执行 poetry install -v 以可视化过程安装依赖:

$ poetry install -v
Creating virtualenv example-etl-B-7RVLBy-py3.10 in /Users/kevin/Library/Caches/pypoetry/virtualenvs
Using virtualenv: /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10
Updating dependencies
Resolving dependencies... (7.5s)

Finding the necessary packages for the current system

Package operations: 52 installs, 1 update, 0 removals

   Installing six (1.16.0)
   Installing lazy-object-proxy (1.9.0)
   Installing markupsafe (2.1.2)
   Installing python-dateutil (2.8.2)
   Installing pyyaml (6.0)
   Installing typing-extensions (4.6.2)
   Installing wrapt (1.15.0)
   Installing astroid (2.15.5)
   Installing certifi (2023.5.7)
   Installing charset-normalizer (3.1.0)
   Installing click (8.1.3)
   Installing dill (0.3.6)
   Installing filelock (3.12.0)
   Installing exceptiongroup (1.1.1)
   Installing ghp-import (2.1.0)
   Installing idna (3.4)
   Installing distlib (0.3.6)
   Installing iniconfig (2.0.0)
   Installing isort (5.12.0)
   Installing jinja2 (3.1.2)
   Installing markdown (3.3.7)
   Installing mccabe (0.7.0)
   Installing mergedeep (1.3.4)
   Installing packaging (23.1)
   Installing platformdirs (3.5.1)
   Installing pyyaml-env-tag (0.1)
   Installing pluggy (1.0.0)
   Updating setuptools (67.7.2 -> 67.8.0)
   Installing tomli (2.0.1)
   Installing urllib3 (2.0.2)
   Installing tomlkit (0.11.8)
   Installing watchdog (3.0.0)
   Installing cachetools (5.3.1)
   Installing cfgv (3.3.1)
   Installing chardet (5.1.0)
   Installing colorama (0.4.6)
   Installing identify (2.5.24)
   Installing mkdocs (1.4.3)
   Installing mkdocs-material-extensions (1.1.1)
   Installing nodeenv (1.8.0)
   Installing pygments (2.15.1)
   Installing pylint (2.17.4)
   Installing pymdown-extensions (10.0.1)
   Installing pyproject-api (1.5.1)
   Installing pytest (7.3.1)
   Installing requests (2.31.0)
   Installing toml (0.10.2)
   Installing virtualenv (20.23.0)
   Installing dynaconf (3.1.12)
   Installing mkdocs-material (8.5.11)
   Installing pre-commit (3.3.2)
   Installing pytest-pylint (0.19.0)
   Installing tox (4.5.2)

Writing lock file

Installing the current project: example_etl (0.1.0)

然后执行 poetry shell 进入到虚拟环境。

在使用 vscode 的时候,可以运行 Ctrl + Shift + p 打开指令,输入 > Python: Select Interpreter 选择刚刚创建的虚拟环境。 如果看不到,只需要点击旁边的刷新按钮即可。然后重新打开一个新的终端,会自动进入虚拟环境。

运行测试

为了保证初始化项目是正常的,在进行后续步骤之前,运行自动化测试逻辑:

tox

可以看到最后输出如下:

$ tox
py310: install_deps> python -I -m pip install poetry
.pkg: install_requires> python -I -m pip install poetry-core
.pkg: _optional_hooks> python /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10/lib/python3.10/site-packages/pyproject_api/_backend.py True poetry.core.masonry.api
.pkg: get_requires_for_build_sdist> python /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10/lib/python3.10/site-packages/pyproject_api/_backend.py True poetry.core.masonry.api
.pkg: prepare_metadata_for_build_wheel> python /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10/lib/python3.10/site-packages/pyproject_api/_backend.py True poetry.core.masonry.api
.pkg: build_sdist> python /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10/lib/python3.10/site-packages/pyproject_api/_backend.py True poetry.core.masonry.api
py310: install_package_deps> python -I -m pip install 'click<9.0.0,>=8.1.3' 'dynaconf<4.0.0,>=3.1.12'
py310: install_package> python -I -m pip install --force-reinstall --no-deps /private/tmp/example_etl/.tox/.tmp/package/1/example_etl-0.1.0.tar.gz
py310: commands[0]> poetry install -v
Using virtualenv: /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10
Installing dependencies from lock file

Finding the necessary packages for the current system

Package operations: 0 installs, 0 updates, 0 removals, 53 skipped

   Installing astroid (2.15.5): Skipped for the following reason: Already installed
   Installing identify (2.5.24): Skipped for the following reason: Already installed
   Installing chardet (5.1.0): Skipped for the following reason: Already installed
   Installing charset-normalizer (3.1.0): Skipped for the following reason: Already installed
   Installing click (8.1.3): Skipped for the following reason: Already installed
   Installing colorama (0.4.6): Skipped for the following reason: Already installed
   Installing distlib (0.3.6): Skipped for the following reason: Already installed
   Installing isort (5.12.0): Skipped for the following reason: Already installed
   Installing jinja2 (3.1.2): Skipped for the following reason: Already installed
   Installing idna (3.4): Skipped for the following reason: Already installed
   Installing certifi (2023.5.7): Skipped for the following reason: Already installed
   Installing iniconfig (2.0.0): Skipped for the following reason: Already installed
   Installing lazy-object-proxy (1.9.0): Skipped for the following reason: Already installed
   Installing mkdocs-material (8.5.11): Skipped for the following reason: Already installed
   Installing dynaconf (3.1.12): Skipped for the following reason: Already installed
   Installing mkdocs-material-extensions (1.1.1): Skipped for the following reason: Already installed
   Installing markdown (3.3.7): Skipped for the following reason: Already installed
   Installing packaging (23.1): Skipped for the following reason: Already installed
   Installing mergedeep (1.3.4): Skipped for the following reason: Already installed
   Installing mkdocs (1.4.3): Skipped for the following reason: Already installed
   Installing pre-commit (3.3.2): Skipped for the following reason: Already installed
   Installing nodeenv (1.8.0): Skipped for the following reason: Already installed
   Installing filelock (3.12.0): Skipped for the following reason: Already installed
   Installing mccabe (0.7.0): Skipped for the following reason: Already installed
   Installing markupsafe (2.1.2): Skipped for the following reason: Already installed
   Installing pytest (7.3.1): Skipped for the following reason: Already installed
   Installing pytest-pylint (0.19.0): Skipped for the following reason: Already installed
   Installing ghp-import (2.1.0): Skipped for the following reason: Already installed
   Installing pylint (2.17.4): Skipped for the following reason: Already installed
   Installing pyyaml-env-tag (0.1): Skipped for the following reason: Already installed
   Installing requests (2.31.0): Skipped for the following reason: Already installed
   Installing dill (0.3.6): Skipped for the following reason: Already installed
   Installing platformdirs (3.5.1): Skipped for the following reason: Already installed
   Installing exceptiongroup (1.1.1): Skipped for the following reason: Already installed
   Installing python-dateutil (2.8.2): Skipped for the following reason: Already installed
   Installing pygments (2.15.1): Skipped for the following reason: Already installed
   Installing pyproject-api (1.5.1): Skipped for the following reason: Already installed
   Installing cfgv (3.3.1): Skipped for the following reason: Already installed
   Installing six (1.16.0): Skipped for the following reason: Already installed
   Installing virtualenv (20.23.0): Skipped for the following reason: Already installed
   Installing pyyaml (6.0): Skipped for the following reason: Already installed
   Installing cachetools (5.3.1): Skipped for the following reason: Already installed
   Installing tomlkit (0.11.8): Skipped for the following reason: Already installed
   Installing tox (4.5.2): Skipped for the following reason: Already installed
   Installing urllib3 (2.0.2): Skipped for the following reason: Already installed
   Installing setuptools (67.8.0): Skipped for the following reason: Already installed
   Installing toml (0.10.2): Skipped for the following reason: Already installed
   Installing wrapt (1.15.0): Skipped for the following reason: Already installed
   Installing typing-extensions (4.6.2): Skipped for the following reason: Already installed
   Installing pymdown-extensions (10.0.1): Skipped for the following reason: Already installed
   Installing watchdog (3.0.0): Skipped for the following reason: Already installed
   Installing tomli (2.0.1): Skipped for the following reason: Already installed
   Installing pluggy (1.0.0): Skipped for the following reason: Already installed

Installing the current project: example_etl (0.1.0)
py310: commands[1]> poetry run pytest tests
================================================================================================================================= test session starts =================================================================================================================================
platform darwin -- Python 3.10.11, pytest-7.3.1, pluggy-1.0.0
cachedir: .tox/py310/.pytest_cache
rootdir: /private/tmp/example_etl
configfile: pyproject.toml
plugins: pylint-0.19.0
collected 10 items                                                                                                                                                                                                                                                                    

tests/test_cmdline.py .....                                                                                                                                                                                                                                                     [ 50%]
tests/test_exceptions.py .                                                                                                                                                                                                                                                      [ 50%]
tests/test_log.py .....                                                                                                                                                                                                                                                         [ 91%]
tests/test_version.py .                                                                                                                                                                                                                                                         [100%]


================================================================================================================================== warnings summary ===================================================================================================================================
../../../Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10/lib/python3.10/site-packages/pkg_resources/__init__.py:121
  /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10/lib/python3.10/site-packages/pkg_resources/__init__.py:121: DeprecationWarning: pkg_resources is deprecated as an API
    warnings.warn("pkg_resources is deprecated as an API", DeprecationWarning)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================================================================================================ 10 passed, 1 warning in 0.01s ============================================================================================================================
py310: OK  in 14.05 seconds
isort: install_deps> python -I -m pip install isort
isort: install_package_deps> python -I -m pip install 'click<9.0.0,>=8.1.3' 'dynaconf<4.0.0,>=3.1.12'
isort: install_package> python -I -m pip install --force-reinstall --no-deps /private/tmp/example_etl/.tox/.tmp/package/2/example_etl-0.1.0.tar.gz
isort: commands[0]> isort . --check-only --diff
Skipped 1 files
isort: OK  in 3.05 seconds
pylint: install_deps> python -I -m pip install poetry
pylint: install_package_deps> python -I -m pip install 'click<9.0.0,>=8.1.3' 'dynaconf<4.0.0,>=3.1.12'
pylint: install_package> python -I -m pip install --force-reinstall --no-deps /private/tmp/example_etl/.tox/.tmp/package/3/example_etl-0.1.0.tar.gz
pylint: commands[0]> poetry install -v
Using virtualenv: /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10
Installing dependencies from lock file

Finding the necessary packages for the current system

Package operations: 0 installs, 0 updates, 0 removals, 53 skipped

   Installing astroid (2.15.5): Skipped for the following reason: Already installed
   Installing certifi (2023.5.7): Skipped for the following reason: Already installed
   Installing cfgv (3.3.1): Skipped for the following reason: Already installed
   Installing cachetools (5.3.1): Skipped for the following reason: Already installed
   Installing dill (0.3.6): Skipped for the following reason: Already installed
   Installing click (8.1.3): Skipped for the following reason: Already installed
   Installing colorama (0.4.6): Skipped for the following reason: Already installed
   Installing chardet (5.1.0): Skipped for the following reason: Already installed
   Installing distlib (0.3.6): Skipped for the following reason: Already installed
   Installing markdown (3.3.7): Skipped for the following reason: Already installed
   Installing filelock (3.12.0): Skipped for the following reason: Already installed
   Installing identify (2.5.24): Skipped for the following reason: Already installed
   Installing idna (3.4): Skipped for the following reason: Already installed
   Installing isort (5.12.0): Skipped for the following reason: Already installed
   Installing charset-normalizer (3.1.0): Skipped for the following reason: Already installed
   Installing dynaconf (3.1.12): Skipped for the following reason: Already installed
   Installing markupsafe (2.1.2): Skipped for the following reason: Already installed
   Installing ghp-import (2.1.0): Skipped for the following reason: Already installed
   Installing mergedeep (1.3.4): Skipped for the following reason: Already installed
   Installing iniconfig (2.0.0): Skipped for the following reason: Already installed
   Installing mkdocs-material (8.5.11): Skipped for the following reason: Already installed
   Installing nodeenv (1.8.0): Skipped for the following reason: Already installed
   Installing exceptiongroup (1.1.1): Skipped for the following reason: Already installed
   Installing pyproject-api (1.5.1): Skipped for the following reason: Already installed
   Installing pluggy (1.0.0): Skipped for the following reason: Already installed
   Installing python-dateutil (2.8.2): Skipped for the following reason: Already installed
   Installing jinja2 (3.1.2): Skipped for the following reason: Already installed
   Installing pyyaml-env-tag (0.1): Pending...
   Installing pymdown-extensions (10.0.1): Skipped for the following reason: Already installed
   Installing packaging (23.1): Skipped for the following reason: Already installed
   Installing mccabe (0.7.0): Skipped for the following reason: Already installed
   Installing pytest-pylint (0.19.0): Skipped for the following reason: Already installed
   Installing pyyaml (6.0): Skipped for the following reason: Already installed
   Installing pygments (2.15.1): Skipped for the following reason: Already installed
   Installing pymdown-extensions (10.0.1): Skipped for the following reason: Already installed
   Installing packaging (23.1): Skipped for the following reason: Already installed
   Installing mccabe (0.7.0): Skipped for the following reason: Already installed
   Installing pytest-pylint (0.19.0): Skipped for the following reason: Already installed
   Installing pyyaml (6.0): Skipped for the following reason: Already installed
   Installing pygments (2.15.1): Skipped for the following reason: Already installed
   Installing pyyaml-env-tag (0.1): Skipped for the following reason: Already installed
   Installing pymdown-extensions (10.0.1): Skipped for the following reason: Already installed
   Installing packaging (23.1): Skipped for the following reason: Already installed
   Installing mccabe (0.7.0): Skipped for the following reason: Already installed
   Installing pytest-pylint (0.19.0): Skipped for the following reason: Already installed
   Installing pyyaml (6.0): Skipped for the following reason: Already installed
   Installing pygments (2.15.1): Skipped for the following reason: Already installed
   Installing pylint (2.17.4): Skipped for the following reason: Already installed
   Installing tox (4.5.2): Skipped for the following reason: Already installed
   Installing setuptools (67.8.0): Skipped for the following reason: Already installed
   Installing platformdirs (3.5.1): Skipped for the following reason: Already installed
   Installing toml (0.10.2): Skipped for the following reason: Already installed
   Installing watchdog (3.0.0): Skipped for the following reason: Already installed
   Installing tomlkit (0.11.8): Skipped for the following reason: Already installed
   Installing mkdocs-material-extensions (1.1.1): Skipped for the following reason: Already installed
   Installing typing-extensions (4.6.2): Skipped for the following reason: Already installed
   Installing urllib3 (2.0.2): Skipped for the following reason: Already installed
   Installing lazy-object-proxy (1.9.0): Skipped for the following reason: Already installed
   Installing six (1.16.0): Skipped for the following reason: Already installed
   Installing tomli (2.0.1): Skipped for the following reason: Already installed
   Installing mkdocs (1.4.3): Skipped for the following reason: Already installed
   Installing requests (2.31.0): Skipped for the following reason: Already installed
   Installing pytest (7.3.1): Skipped for the following reason: Already installed
   Installing virtualenv (20.23.0): Skipped for the following reason: Already installed
   Installing wrapt (1.15.0): Skipped for the following reason: Already installed
   Installing pre-commit (3.3.2): Skipped for the following reason: Already installed

Installing the current project: example_etl (0.1.0)
pylint: commands[1]> poetry run pylint tests src

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

.pkg: _exit> python /Users/kevin/Library/Caches/pypoetry/virtualenvs/example-etl-B-7RVLBy-py3.10/lib/python3.10/site-packages/pyproject_api/_backend.py True poetry.core.masonry.api
  py310: OK (14.05=setup[9.47]+cmd[3.84,0.75] seconds)
  isort: OK (3.05=setup[2.67]+cmd[0.38] seconds)
  pylint: OK (12.92=setup[7.86]+cmd[2.91,2.15] seconds)
  congratulations :) (30.10 seconds)

至此,项目环境初始化完成。一切正常。

提交代码

在开发时,需要养成及时提交代码的好习惯。

git add .
git commit -m "feat: init project env."