Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ dist

artifacts

bin
50 changes: 50 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## Makefile for PyPortfolioOpt: developer tasks orchestrated via go-task
#
# This Makefile wraps the Taskfile.yml commands and provides a friendly
# `make help` index. Lines with `##` after a target are parsed into help text,
# and lines starting with `##@` create section headers in the help output.
#
# Colors for pretty output in help messages
BLUE := \033[36m
BOLD := \033[1m
GREEN := \033[32m
RED := \033[31m
RESET := \033[0m

# Default goal when running `make` with no target
.DEFAULT_GOAL := help

# Declare phony targets (they don't produce files)
.PHONY: install-task install clean test fmt

UV_INSTALL_DIR := "./bin"

##@ Bootstrap
install-task: ## ensure go-task (Taskfile) is installed
@mkdir -p ${UV_INSTALL_DIR}

@if [ ! -x "${UV_INSTALL_DIR}/task" ]; then \
printf "$(BLUE)Installing go-task (Taskfile)$(RESET)\n"; \
curl --location https://taskfile.dev/install.sh | sh -s -- -d -b ${UV_INSTALL_DIR}; \
fi

install: install-task ## install
@./bin/task build:install --silent

clean: install-task ## clean
@./bin/task cleanup:clean --silent

##@ Development and Testing
test: install-task ## run all tests
@./bin/task docs:test --silent

fmt: install-task ## check the pre-commit hooks and the linting
@./bin/task quality:lint --silent


##@ Meta
help: ## Display this help message
+@printf "$(BOLD)Usage:$(RESET)\n"
+@printf " make $(BLUE)<target>$(RESET)\n\n"
+@printf "$(BOLD)Targets:$(RESET)\n"
+@awk 'BEGIN {FS = ":.*##"; printf ""} /^[a-zA-Z_-]+:.*?##/ { printf " $(BLUE)%-15s$(RESET) %s\n", $$1, $$2 } /^##@/ { printf "\n$(BOLD)%s$(RESET)\n", substr($$0, 5) }' $(MAKEFILE_LIST)
65 changes: 65 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Taskfile: Root Taskfile
#
# Purpose: Provide a unified entry point for developer tasks by grouping
# build, quality, documentation, and cleanup commands. Exposes
# helpful default and help targets.
#
# Components:
# - includes: build, quality, docs, cleanup
# - tasks:
# - default: show top-level tasks
# - help: show all tasks including nested ones

version: '3'
silent: true

# Variables
vars:
TESTS_FOLDER: tests
BOOK_TITLE: '{{env "REPO_NAME"}}' # defined below env section
BOOK_SUBTITLE: 'Documentation and Reports'
OPTIONS: ''

# Environment variables
env:
# Colors for pretty output
BLUE: '\x1b[36m'
GREEN: '\x1b[32m'
RED: '\x1b[31m'
YELLOW: '\x1b[33m'
CYAN: '\x1b[36m'
MAGENTA: '\x1b[35m'
BOLD: '\x1b[1m'
RESET: '\x1b[0m'
# Set repository name
REPO_NAME:
sh: basename $(pwd)

# Include task groups
includes:
build:
taskfile: ./taskfiles/build.yml
desc: Build tasks for managing dependencies and building packages
quality:
taskfile: ./taskfiles/quality.yml
desc: Code quality tasks for formatting, linting, and checking code
docs:
taskfile: ./taskfiles/docs.yml
desc: Documentation tasks for building docs, notebooks, and books
cleanup:
taskfile: ./taskfiles/cleanup.yml
desc: Cleanup tasks for removing generated files and directories

# Task definitions
tasks:
default:
desc: Display help information
silent: true
cmds:
- ./bin/task --list

help:
desc: Display help information with all tasks
silent: true
cmds:
- ./bin/task --list-all
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies = [
"scikit-base<0.14.0",
"scikit-learn>=0.24.1",
"scipy>=1.3.0",
"scikit-base<0.14.0",
]

[project.optional-dependencies]
Expand Down
47 changes: 47 additions & 0 deletions taskfiles/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Taskfile: Build tasks
#
# Purpose: Provide build-related utilities for Python projects using uv, including:
# - Installing uv and uvx into a local ./bin directory
# - Creating a virtual environment and syncing dependencies
#
# Components:
# - uv: install uv/uvx if missing
# - install: create venv and install dependencies (if pyproject.toml exists)

version: '3'

dir: .

tasks:
uv:
desc: Install uv and uvx
env:
UV_INSTALL_DIR: ./bin
cmds:
- |
if [ -x "${UV_INSTALL_DIR}/uv" ] && [ -x "${UV_INSTALL_DIR}/uvx" ]; then
# already installed — stay quiet
exit 0
fi
printf "${BLUE}Installing uv${RESET}\n"
curl -LsSf https://astral.sh/uv/install.sh | sh 2>/dev/null || { printf "${RED}[ERROR] Failed to install uv ${RESET}\n"; exit 1; }


install:
desc: Install all dependencies using uv
deps: [uv]
env:
UV_VENV_CLEAR: 1
cmds:
- |
# we need the virtual environment at least for the tests to work
# even if we don't install anything

printf "${BLUE}[INFO] Creating virtual environment...${RESET}\n"

# Create the virtual environment
./bin/uv venv --python 3.12 || { printf "${RED}[ERROR] Failed to create virtual environment${RESET}\n"; exit 1; }

printf "${BLUE}[INFO] Installing dependencies${RESET}\n"
./bin/uv sync --all-extras --frozen || { printf "${RED}[ERROR] Failed to install dependencies${RESET}\n"; exit 1; }

26 changes: 26 additions & 0 deletions taskfiles/cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Taskfile: Cleanup tasks
#
# Purpose: Remove build artifacts, caches, and orphaned Git branches to keep the
# working tree clean while preserving environment files.
#
# Components:
# - clean: delete generated files (dist, build, egg-info, caches) and prune
# local branches without a remote counterpart

version: '3'

tasks:
clean:
desc: Clean generated files and directories
cmds:
- |
printf "${BLUE}Cleaning project...${RESET}\n"
# do not clean .env files
git clean -d -X -f -e .env -e '.env.*'
rm -rf dist build *.egg-info .coverage .pytest_cache
printf "${BLUE}Removing local branches with no remote counterpart...${RESET}\n"
git fetch --prune
git branch -vv \
| grep ': gone]' \
| awk '{print $1}' \
| xargs -r git branch -D 2>/dev/null || true
29 changes: 29 additions & 0 deletions taskfiles/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Taskfile: Documentation and testing tasks
#
# Purpose: Provide tasks to run tests, generate API docs, export Marimo notebooks,
# assemble a combined documentation site ("book"), and run a Marimo server.
#
# Components:
# - test: run pytest with coverage and HTML report
# - docs: build API documentation with pdoc
# - marimushka: export Marimo notebooks to HTML and generate an index
# - book: assemble docs, test reports, and notebooks into a static site
# - marimo: start a Marimo server for interactive editing

version: '3'

includes:
build:
taskfile: ./build.yml
internal: true

tasks:
test:
desc: Run all tests
cmds:
- ./bin/task build:install
- printf "${BLUE}[INFO] Running tests...${RESET}\n"
- |
./bin/uv pip install pytest pytest-cov pytest-html
mkdir -p _tests/html-coverage _tests/html-report
./bin/uv run pytest tests --cov=pypfopt --cov-report=term --cov-report=html:_tests/html-coverage --html=_tests/html-report/report.html
35 changes: 35 additions & 0 deletions taskfiles/quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Taskfile: Code quality tasks
#
# Purpose: Run automated code quality checks to maintain a healthy codebase.
# Uses pre-commit for formatting/linting and deptry for dependency hygiene.
#
# Components:
# - lint: run pre-commit hooks over the entire repository
# - deptry: analyze dependencies and detect issues
# - check: aggregate target to run both lint and deptry

version: '3'

includes:
build:
taskfile: ./build.yml
internal: true

tasks:
lint:
desc: Run pre-commit hooks
cmds:
- ./bin/task build:uv --silent
- ./bin/uvx pre-commit run --all-files

deptry:
desc: Check for dependency issues
cmds:
- ./bin/task build:uv --silent
- ./bin/uvx deptry .

check:
desc: Run all code quality checks
deps: [lint, deptry]
cmds:
- printf "${GREEN} All checks passed!${RESET}\n"