Python Workflow (PyWf) for Open Source Projects

Overview

PyWf provides a comprehensive automation framework for managing the entire lifecycle of open source Python projects. By consolidating various development tasks into a consistent interface, it eliminates the cognitive overhead of switching between different tools and commands.

Getting Started

how_to_use.py
 1# -*- coding: utf-8 -*-
 2
 3from pathlib import Path
 4from pywf_open_source.api import PyWf
 5
 6# Initialize the PyWf object.
 7pywf = PyWf.from_pyproject_toml(Path("/path/to/pyproject.toml"))
 8
 9# Perform common development operations.
10pywf.create_virtualenv()
11pywf.remove_virtualenv()
12pywf.poetry_lock()
13pywf.poetry_install_only_root()
14pywf.poetry_install()
15pywf.poetry_install_dev()
16pywf.poetry_install_test()
17pywf.poetry_install_doc()
18pywf.poetry_install_all()
19pywf.run_unit_test()
20pywf.run_cov_test()
21pywf.view_cov()
22pywf.build_doc()
23pywf.view_doc()
24pywf.poetry_build()
25pywf.twine_upload()
26pywf.setup_codecov_io_upload_token_on_github()
27pywf.setup_readthedocs_project()
28pywf.edit_github_repo_metadata()
29pywf.publish_to_github_release()

Core Functionality

Virtual Environment Management

Dependency Management

Testing

Documentation

Building and Publishing

CI/CD Integration

Configuration

PyWf reads configuration from the [tool.pywf] section in your pyproject.toml:

# python workflow tool config
[tool.pywf]
# The specific python version you use for local development
dev_python = "3.11.8"
# --- github.com
github_account = "MacHu-GWU"
# Create GitHub token in https://github.com/settings/tokens and put the token in
# ``${HOME}/home_secret.json``
github_token_field = "providers.github.accounts.sh.users.sh.secrets.dev.value"
# --- codecov.io (for code coverage test report)
codecov_account = "MacHu-GWU"
# Create Codecov token in https://app.codecov.io/account/gh/${codecov_account}/access
# and put the token in ``${HOME}/home_secret.json``
codecov_token_field = "providers.codecov_io.accounts.sh.users.sh.secrets.dev.value"
# --- readthedocs.org (for documentation hosting)
# Create Readthedocs token in https://app.readthedocs.org/accounts/tokens/
# and put the token at ``${HOME}/home_secret.json``
readthedocs_token_field = "providers.readthedocs.accounts.sh.users.sh.secrets.dev.value"
# Readthedocs project name, usually it is the same as your project name
readthedocs_project_name = "cookiecutter_pywf_open_source_demo"

Unified Command System

To simplify your workflow and avoid memorizing complex commands, PyWf includes a lightweight command pattern that can be integrated with Makefile support:

Command Wrappers

The bin/ directory contains thin Python wrappers for all PyWf functionality:

bin/g1_t2_s1_venv_create.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.create_virtualenv(real_run=True, verbose=True)
bin/g1_t2_s2_venv_remove.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.remove_virtualenv(real_run=True, verbose=True)
bin/g2_t1_s1_poetry_lock.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_lock(real_run=True, verbose=True)
bin/g2_t1_s6_poetry_export.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_export(with_hash=False, real_run=True, verbose=True)
bin/g2_t2_s1_install_only_root.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_install_only_root(real_run=True, verbose=True)
bin/g2_t2_s2_install.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_install(real_run=True, verbose=True)
bin/g2_t2_s3_install_dev.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_install_dev(real_run=True, verbose=True)
bin/g2_t2_s4_install_test.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_install_test(real_run=True, verbose=True)
bin/g2_t2_s5_install_doc.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_install_doc(real_run=True, verbose=True)
bin/g2_t2_s6_install_automation.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_install_auto(real_run=True, verbose=True)
bin/g2_t2_s7_install_all.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.poetry_install_all(real_run=True, verbose=True)
bin/g3_t1_s1_run_unit_test.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.run_unit_test(real_run=True, verbose=True)
bin/g3_t2_s1_run_cov_test.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.run_cov_test(real_run=True, verbose=True)
bin/g3_t2_s2_view_cov_result.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.view_cov(real_run=True, verbose=True)
bin/g3_t3_s1_run_int_test.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.run_int_test(real_run=True, verbose=True)
bin/g3_t4_s1_run_load_test.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.run_load_test(real_run=True, verbose=True)
bin/g4_t1_s1_nb_to_md.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.notebook_to_markdown(real_run=True, verbose=True)
bin/g4_t2_s1_build_doc.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.build_doc(real_run=True, verbose=True)
bin/g4_t2_s2_view_doc.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.view_doc(real_run=True, verbose=True)
bin/g5_t1_s1_build_package.py
 1#!/usr/bin/env python
 2# -*- coding: utf-8 -*-
 3
 4"""
 5We primarily use poetry to build the package.
 6"""
 7
 8from pywf import pywf
 9
10# pywf.python_build(real_run=True, verbose=True)
11pywf.poetry_build(real_run=True, verbose=True)
bin/g5_t2_s1_publish_package.py
 1#!/usr/bin/env python
 2# -*- coding: utf-8 -*-
 3
 4"""
 5We primarily use twine to publish the package for open source project.
 6"""
 7
 8from pywf import pywf
 9
10pywf.twine_upload()
bin/g5_t2_s3_create_release.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.publish_to_github_release(real_run=True, verbose=True)
bin/g6_t1_s1_setup_codecov.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.setup_codecov_io_upload_token_on_github(real_run=True, verbose=True)
bin/g6_t1_s2_setup_readthedocs.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.setup_readthedocs_project(real_run=True, verbose=True)
bin/g6_t1_s3_edit_github_repo.py
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4from pywf import pywf
5
6pywf.edit_github_repo_metadata(real_run=True, verbose=True)

These wrappers initialize PyWf using your project configuration and execute specific functions with sensible defaults.

Makefile Integration

The included Makefile provides a unified command interface:

  1# -*- coding: utf-8 -*-
  2
  3help: ## ⭐ Show this help message
  4	@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-40s\033[0m %s\n", $$1, $$2}'
  5
  6
  7venv-create: ## ⭐ Create Virtual Environment
  8	~/.pyenv/shims/python ./bin/g1_t2_s1_venv_create.py
  9
 10
 11venv-remove: ## Remove Virtual Environment
 12	~/.pyenv/shims/python ./bin/g1_t2_s2_venv_remove.py
 13
 14
 15poetry-lock: ## ⭐ Resolve dependencies using poetry, update poetry.lock file
 16	~/.pyenv/shims/python ./bin/g2_t1_s1_poetry_lock.py
 17
 18
 19poetry-export: ## Export dependencies to requirements.txt
 20	~/.pyenv/shims/python ./bin/g2_t1_s6_poetry_export.py
 21
 22
 23install-root: ## Install Package itself without any dependencies
 24	~/.pyenv/shims/python ./bin/g2_t2_s1_install_only_root.py
 25
 26
 27install: ## ⭐ Install main dependencies and Package itself
 28	~/.pyenv/shims/python ./bin/g2_t2_s2_install.py
 29
 30
 31install-dev: ## Install Development Dependencies
 32	~/.pyenv/shims/python ./bin/g2_t2_s3_install_dev.py
 33
 34
 35install-test: ## Install Test Dependencies
 36	~/.pyenv/shims/python ./bin/g2_t2_s4_install_test.py
 37
 38
 39install-doc: ## Install Document Dependencies
 40	~/.pyenv/shims/python ./bin/g2_t2_s5_install_doc.py
 41
 42
 43install-automation: ## Install Dependencies for Automation Script
 44	~/.pyenv/shims/python ./bin/g2_t2_s6_install_automation.py
 45
 46
 47install-all: ## Install All Dependencies
 48	~/.pyenv/shims/python ./bin/g2_t2_s7_install_all.py
 49
 50
 51test-only: ## Run test without checking test dependencies
 52	~/.pyenv/shims/python ./bin/g3_t1_s1_run_unit_test.py
 53
 54
 55test: install install-test test-only ## ⭐ Run test
 56
 57
 58cov-only: ## Run code coverage test without checking test dependencies
 59	~/.pyenv/shims/python ./bin/g3_t2_s1_run_cov_test.py
 60
 61
 62cov: install install-test cov-only ## ⭐ Run code coverage test
 63
 64
 65view-cov: ## ⭐ View code coverage test report
 66	~/.pyenv/shims/python ./bin/g3_t2_s2_view_cov_result.py
 67
 68
 69int-only: ## Run integration test without checking test dependencies
 70	~/.pyenv/shims/python ./bin/g3_t3_s1_run_int_test.py
 71
 72
 73int: install install-test int-only ## ⭐ Run integration test
 74
 75
 76nb-to-md: ## Convert Notebook to Markdown
 77	~/.pyenv/shims/python ./bin/g4_t1_s1_nb_to_md.py
 78
 79
 80build-doc: install install-doc ## ⭐ Build documentation website locally
 81	~/.pyenv/shims/python ./bin/g4_t2_s1_build_doc.py
 82
 83
 84view-doc: ## ⭐ View documentation website locally
 85	~/.pyenv/shims/python ./bin/g4_t2_s2_view_doc.py
 86
 87
 88build: ## Build Python library distribution package
 89	~/.pyenv/shims/python ./bin/g5_t1_s1_build_package.py
 90
 91
 92publish: build ## ⭐ Publish Python library to Public PyPI
 93	~/.pyenv/shims/python ./bin/g5_t2_s1_publish_package.py
 94
 95
 96release: ## ⭐ Create Github Release using current version
 97	~/.pyenv/shims/python ./bin/g5_t2_s3_create_release.py
 98
 99
100setup-codecov: ## ⭐ Setup Codecov Upload token in GitHub Action Secrets
101	~/.pyenv/shims/python ./bin/g6_t1_s1_setup_codecov.py
102
103
104setup-rtd: ## ⭐ Create ReadTheDocs Project
105	~/.pyenv/shims/python ./bin/g6_t1_s2_setup_readthedocs.py
106
107
108edit-github: ## ⭐ Edit GitHub Repository Metadata
109	~/.pyenv/shims/python ./bin/g6_t1_s3_edit_github_repo.py

When you type make, you will see:

$ make
help                                      Show this help message
venv-create                               Create Virtual Environment
venv-remove                              Remove Virtual Environment
poetry-lock                               Resolve dependencies using poetry, update poetry.lock file
poetry-export                            Export dependencies to requirements.txt
install-root                             Install Package itself without any dependencies
install                                   Install main dependencies and Package itself
install-dev                              Install Development Dependencies
install-test                             Install Test Dependencies
install-doc                              Install Document Dependencies
install-automation                       Install Dependencies for Automation Script
install-all                              Install All Dependencies
test-only                                Run test without checking test dependencies
test                                      Run test
cov-only                                 Run code coverage test without checking test dependencies
cov                                       Run code coverage test
view-cov                                  View code coverage test report
int-only                                 Run integration test without checking test dependencies
int                                       Run integration test
nb-to-md                                 Convert Notebook to Markdown
build-doc                                 Build documentation website locally
view-doc                                  View documentation website locally
build                                    Build Python library distribution package
publish                                   Publish Python library to Public PyPI
release                                   Create Github Release using current version
setup-codecov                             Setup Codecov Upload token in GitHub Action Secrets
setup-rtd                                 Create ReadTheDocs Project
edit-github                               Edit GitHub Repository Metadata

When you type make cov, it actually runs python bin/s03_2_run_cov_test.py

You may also edit the Makefile yourself to use different global Python instead of ~/.pyenv/shims/python.

This approach offers several advantages:

  • Consistent command syntax across projects

  • Self-documenting commands with make help

  • No need to remember underlying tools or syntax

Getting Started with Cookiecutter

For new projects, use our Cookiecutter Template cookiecutter-pywf_open_source automatically set up the entire PyWf structure:

The template provides:

  • Pre-configured directory structure

  • Default Makefile with all common commands

  • Command wrappers in the bin/ directory

  • Properly configured pyproject.toml

  • GitHub Actions integration files

  • Documentation templates

Simply run:

pip install "cookiecutter>=2.6.0,<3.0.0" && cookiecutter https://github.com/MacHu-GWU/cookiecutter-pywf_open_source

Then follow the prompts to create a new project with the entire PyWf infrastructure ready to use.