Cement Application Framework

repository·main·Indexed 23 days ago

https://github.com/datafolklabs/cement

An advanced, highly customizable Application Framework for Python focused on building robust Command Line Interfaces (CLI). It supports a wide range of applications from simple micro-framework scripts to complex, multi-tier applications using a handler/interface pattern. Key architectural components include handlers for configuration, arguments, controllers, output, logging, caching, and support for extensions, plugins, and hooks.

Tokens
6.2K
Snippets
30
Records
40
Agent score
79%

What's inside cement

  1. Use the `generate` extension with typed variables

    main

    The generate extension allows you to create project templates using .generate.yml files. It uses a unified variables: list where each entry defines a type: (string, boolean, or choice).

    Resolved variables are placed at the top level of the template context. This allows you to use them directly in Jinja2 or Mustache templates using standard conditional logic (e.g., {% if docker %} or {% if web_framework == 'flask' %}).

    Note on Boolean Interpolation: When interpolating a boolean as text (e.g., {{ docker }}), it renders as the capitalized Python representation (True or False), not true/false. Use conditionals for logic instead of text interpolation.

    variables:
        -   name: project_name
            type: string
            prompt: "Project Name"
            default: "myproject"
  2. How Cement's core architecture works

    main

    Cement is a flexible Python application framework designed for Command Line Interfaces (CLI). It uses a handler/interface pattern to allow deep customization.

    Key architectural components include:

    • Handlers and Interfaces: The framework is customizable via handlers that connect implementation classes to specific interfaces.
    • Extension Handler: An interface used to easily extend framework functionality.
    • Plugin Handler: Provides an interface to extend your application via plugins.
    • Config Handler: Supports parsing multiple configuration files into a single configuration object.
    • Argument Handler: Parses command line arguments and merges them with configuration.
    • Controller Handler: Supports the creation of sub-commands and nested controllers.
    • Hook Support: Allows adding custom logic at specific points in the application lifecycle.
    • Output Handler: Renders return dictionaries to the console.
    • Log Handler: Supports both console and file-based logging.
    • Cache Handler: Adds caching support for performance improvements.
  3. Set up a development environment

    main

    To develop on this project, you need Python 3.10+ and PDM. Use the provided Makefile to automate environment setup and testing.

    1. Install dependencies: Run make setup to install dependencies and automatically create a virtual environment.
    2. Run the application: Use pdm run {{ label }} --help to execute the CLI application within the managed environment.
    3. Run tests: Use make test to execute pytest and coverage reports.
    $ make setup
    $ pdm run {{ label }} --help
    $ make test
  4. Set up native Windows development

    main

    Windows development is not the primary target and is not 100% complete. If you must develop natively on Windows, follow these steps:

    Prerequisites

    • Python 3.x (latest stable)
    • pip, pipx, and pdm
    • Visual C++ 14.0 or Greater Build Tools (including CMake)

    Setup Steps

    1. Install pdm via pipx:
      pip install pipx
      pipx install pdm
    2. Create a development virtual environment:
       ```bash
    pdm venv create
    1. Install dependencies (excluding memcached):
      pdm install --without memcached
    4. Run core tests:
       ```bash
    pdm run pytest --cov=cement.core tests/core
       # OR
       make test-core
    1. Run the Cement CLI:
      pdm run cement --help
  5. Route Cement framework debug logs to a file

    main

    You can route Cement's internal framework debug logs (the minimal_logger output covering setup, run, and close internals) to a file in addition to the console. This is separate from your application's own log handlers.

    To enable file logging, you must satisfy two conditions:

    1. Enable framework logging: Use CEMENT_LOG=1, debug=True, or the --debug flag.
    2. Specify a destination: Set the CEMENT_FRAMEWORK_LOG_FILE environment variable to the desired file path.

    Behavioral Rules:

    • Additive only: Setting CEMENT_FRAMEWORK_LOG_FILE does not enable logging by itself. You must also enable logging via one of the methods mentioned above.
    • Lazy creation: The log file is created only when the first log entry is written. If logging is disabled, no empty file is created.
    • Error resilience: If the path provided to CEMENT_FRAMEWORK_LOG_FILE is invalid or unwritable, the error is silently ignored and the console handler continues to function normally.
    CEMENT_LOG=1 CEMENT_FRAMEWORK_LOG_FILE=./framework.log python app.py
  6. Set up development using Docker

    main

    If you prefer Docker, the project includes a Docker Compose configuration that sets up all required services and dependencies.

    Launch Development Container

    To create the containers and launch a BASH shell within the cement container, run:

    make dev

    This is equivalent to:

    docker compose up -d
    docker compose exec cement /bin/bash

    Testing Alternative Python Versions

    Docker containers are provided for testing against different Python versions (e.g., cement-py310, cement-py311).

    1. List available containers:
      docker-compose ps
    2. Execute a shell in a specific version container:
      docker-compose exec cement-py310 /bin/bash
  7. Run the `generate` command

    main

    You can use the generate command to scaffold projects either interactively or using defaults. Run these commands from the directory containing your entry point (e.g., myapp.py).

    # Generate using all default values (no interactive prompts)
    pdm run python myapp.py generate webapp /tmp/myproject --defaults
    
    # Generate interactively (prompts for each variable)
    pdm run python myapp.py generate webapp /tmp/myproject
    
    # Generate using defaults and overwrite existing files
    pdm run python myapp.py generate webapp /tmp/myproject --defaults --force
    pdm run python myapp.py generate webapp /tmp/myproject --defaults