Cement is an advanced Application Framework for Python, with a primary focus on CLI

Overview

Cement Framework

Built on Cementβ„’ Continuous Integration Status Code Quality: Python Total Alerts

Cement is an advanced Application Framework for Python, with a primary focus on Command Line Interfaces (CLI). Its goal is to introduce a standard, and feature-full platform for both simple and complex command line applications as well as support rapid development needs without sacrificing quality. Cement is flexible, and it's use cases span from the simplicity of a micro-framework to the complexity of a mega-framework. Whether it's a single file script, or a multi-tier application, Cement is the foundation you've been looking for.

The first commit to Git was on Dec 4, 2009. Since then, the framework has seen several iterations in design, and has continued to grow and improve since it's inception. Cement is the most stable, and complete framework for command line and backend application development.

Core Features

Cement core features include (but are not limited to):

  • Core pieces of the framework are customizable via handlers/interfaces
  • Handler system connects implementation classes with Interfaces
  • Extension handler interface to easily extend framework functionality
  • Config handler supports parsing multiple config files into one config
  • Argument handler parses command line arguments and merges with config
  • Log handler supports console and file logging
  • Plugin handler provides an interface to easily extend your application
  • Output handler interface renders return dictionaries to console
  • Cache handler interface adds caching support for improved performance
  • Controller handler supports sub-commands, and nested controllers
  • Hook support adds a bit of magic to apps and also ties into framework
  • Zero external dependencies* (not including optional extensions)
  • 100% test coverage (pytest)
  • 100% PEP8 compliant (flake8)
  • Extensive API Reference (sphinx)
  • Tested on Python 3.5+
  • Does not support Python 2.x

Some optional extensions that are shipped with the mainline Cement sources do require external dependencies. It is the responsibility of the application developer to include these dependencies along with their application, as Cement explicitly does not include them.

More Information

License

The Cement CLI Application Framework is Open Source and is distributed under the BSD License (three clause). Please see the LICENSE file included with this software.

Development

Docker

This project includes a docker-compose configuration that sets up all required services, and dependencies for development and testing. This is the recommended path for local development, and is the only fully supported option.

The following creates all required docker containers, and launches an BASH shell within the cement dev container for development.

$ make dev

|> cement <| src #

The above is the equivalent of running:

$ docker-compose up -d

$ docker-compose exec cement /bin/bash

Testing Alternative Versions of Python

The latest stable version of Python 3 is the default, and target version accessible as the cement container within Docker Compose. For testing against alternative versions of python, additional containers are created (ex: cement-py37, cement-py38, etc). You can access these containers via:

$ docker-compose ps
        Name                      Command               State     Ports
-------------------------------------------------------------------------
cement_cement-py35_1   /bin/bash                        Up
cement_cement-py36_1   /bin/bash                        Up
cement_cement-py37_1   /bin/bash                        Up
cement_cement-py38_1   /bin/bash                        Up
cement_cement-py39_1   /bin/bash                        Up
cement_cement_1        /bin/bash                        Up
cement_memcached_1     docker-entrypoint.sh memcached   Up      11211/tcp
cement_redis_1         docker-entrypoint.sh redis ...   Up      6379/tcp


$ docker-compose exec cement-py37 /bin/bash

|> cement-py37 <| src #

VirtualENV

An traditional VirtualENV helper is available:

$ make virtualenv

$ source env/bin/activate

|> cement <| $

Vagrant

An alternative option is included to run Vagrant for development. This is partially supported, primarily for the purpose of developing/testing on Windows as well as testing specific issues on target operating systems.

To see a list of configured systems:

$ vagrant status

Linux

$ vagrant up linux

$ vagrant ssh linux

vagrant@linux $ cd /vagrant

vagrant@linux $ bash scripts/vagrant/bootstrap.sh

vagrant@linux $ make virtualenv

vagrant@linux $ source env/bin/activate

|> cement >| $

Windows

Windows development and support is not 100% complete. Cement is known to run and work on Windows, however it is not a primary target for development and as such the setup is not as streamlined and currently has several known errors.

The following assumes you're running these two initial commands from a unix based system:

$ make clean

$ vagrant up windows

RDP or Login to Desktop/Console, and open a PowerShell terminal:

C:\> cd C:\Vagrant

C:\Vagrant> powershell.exe scripts\vagrant\bootstrap.ps1

C:\Vagrant> make virtualenv-windows

C:\Vagrant> .\env-windows\Scripts\activate.ps1

C:\Vagrant> make test-core

Note that only the core library is fully tested on Windows.

Running Tests and Compliance

Cement has a strict policy that all code and tests meet PEP8 guidelines, therefore flake8 is called before any unit tests run. All code submissions require 100% test coverage and PEP8 compliance:

Execute the following to run all compliance and unit tests:

$ make test

A coverage report is printed to console, as well as the HTML version created in coverage-report:

$ open coverage-report/index.html

See Makefile for all other common development actions.

Comments
  • Unandled exception if ArgparseController is missing default function

    Unandled exception if ArgparseController is missing default function

    Using ArgparseController without a default command results in an unhandled exception.

    Code:

    from cement.core.foundation import CementApp
    from cement.ext.ext_argparse import ArgparseController, expose
    
    
    class MyBaseController(ArgparseController):
        @expose(help='<Subcommand description>')
        def subcommand(self):
            self.app.log.info("subcommand successful")
    
    
    class MyApp(CementApp):
        class Meta:
            label = 'demo'
            handlers = [MyBaseController]
    
    
    with MyApp() as app:
        app.run()
    

    The help output looks as expected:

    > python3 demo4.py --help
    usage: demo [-h] [--debug] [--quiet] {subcommand} ...
    
    optional arguments:
      -h, --help    show this help message and exit
      --debug       toggle debug output
      --quiet       suppress all output
    
    sub-commands:
      {subcommand}
        subcommand  <Subcommand description>
    

    But running with no command raises an exception:

    > python3 demo4.py
    Traceback (most recent call last):
      File "demo4.py", line 18, in <module>
        app.run()
      File "C:\Python34\lib\site-packages\cement\core\foundation.py", line 882, in run
        return_val = self.controller._dispatch()
      File "C:\Python34\lib\site-packages\cement\ext\ext_argparse.py", line 927, in _dispatch
        (contr.__class__.__name__, func_name))      # pragma: nocover
    cement.core.exc.FrameworkError: Controller function does not exist MyBaseController.default()
    

    It seems that, if there is no default function, the best thing would be to output a parse error, as if the user had typed in an invalid function.

    Otherwise, I take it that the default function is required by cement, in which case a doc update might be appropriate: http://builtoncement.com/2.10/api/ext/ext_argparse.html#cement-ext-ext-argparse

    Thoughts?

    (Obviously a good workaround is to create a default command and print the help text.)

    undecided dev/2.11.x 
    opened by jtpereyda 14
  • Change the way ext_colorlog.py locates its config

    Change the way ext_colorlog.py locates its config

    By having the Meta.label be dynamically fetched when "colorize" is set, one can subclass ColorLogHandler to add further functionality (ie. mailing when a fatal error occurs). Now a subclass Handler can be defined, its own class Meta sets the config_defaults and the original ColorLogHandler receives the config from it.


    Issue: #571

    opened by devspyrosv 13
  • Identify All Tests That Do Not Include Atleast 1 Assertion

    Identify All Tests That Do Not Include Atleast 1 Assertion

    A lot of tests have been added over the years which were added only for coverage but not actually asserting anything valuable for testing. Would be helpful to have someone identify those tests, and possibly add at least one assertion to them.

    This is a low-priority, good first issue... Please note if you're interested in this issue that it is only relevant to the portland branch (Cement 3) currently (or master once portland is merged).

    portland testing chore low-hanging-fruit help-wanted 
    opened by derks 13
  • [Question] Why are my commands appearing as subcommands?

    [Question] Why are my commands appearing as subcommands?

    Hello,

    I'm wondering why the commands I've defined, appear as subcommands, instead of "main" commands.

    For example, I've defined something like the following:

        @expose(
            help='some help',
            aliases=['alias'],
            arguments=[
                (['args'], dict(action='store', nargs='*')),
            ]
        )
        def some_command(self):
            pass
    

    And, when I execute the application for showing the help, this is how appears:

    usage: app [-h] [--debug] [--quiet] {some_command, alias} ...
    
    An app
    
    optional arguments:
      -h, --help            show this help message and exit
      --debug               toggle debug output
      --quiet               suppress all output
    
    sub-commands:
      {some_command, alias}
        some_command (alias) some help
    

    The command invocation works fine; it's just that I was expecting to make the commands appear as in the quickstart.

    Many thanks for your work.

    opened by julenpardo 12
  • Use ujson as optional dependence

    Use ujson as optional dependence

    ujson is fast json encoder/decoder with same interface as in json, I propose to try import ujson first:

    try:
        import ujson as json
    except:
        import json
    
    stable/2.10.x 
    opened by akhilman 12
  • inconsistent behaviour when using colorlog feature of cement 2.6

    inconsistent behaviour when using colorlog feature of cement 2.6

    when using colorlog(cement2.6) log messages are not written to the 'log file'(mentioned in the config) when 'level' is set to 'debug' but when level is set to other than 'debug' , messages are written to log file as expected .

    bug stable/2.6.x stable/2.10.x 
    opened by rjdp 10
  • Possible option to display myapp debug messages only.

    Possible option to display myapp debug messages only.

    Hello, When I use --debug flag, with is also display cement.core debug messages, we dont want that Cement specific log messages.

    ./my.py --debug
    
    2015-01-14 10:30:13,421 (DEBUG) cement.core.foundation : no cache handler defined, skipping.
    2015-01-14 10:30:13,422 (DEBUG) cement.core.foundation : setting up myapp.log handler
    2015-01-14 10:30:13,423 (DEBUG) cement.core.handler : merging config defaults from '<cement.ext.ext_logging.LoggingLogHandler object at 0x7fa1a139cf98>' into section 'log.logging'
    2015-01-14 10:30:13,424 (DEBUG) myapp : logging initialized for 'myapp' using LoggingLogHandler
    2015-01-14 10:30:13,425 (DEBUG) cement.core.foundation : setting up myapp.plugin handler
    2015-01-14 10:30:13,427 (DEBUG) cement.ext.ext_plugin : plugin config dir /etc/myapp/plugins.d does not exist.
    2015-01-14 10:30:13,428 (DEBUG) cement.ext.ext_plugin : plugin config dir /root/.myapp/plugins.d does not exist.
    2015-01-14 10:30:13,429 (DEBUG) cement.core.foundation : setting up myapp.arg handler
    2015-01-14 10:30:13,435 (DEBUG) cement.core.foundation : setting up myapp.output handler
    2015-01-14 10:30:13,436 (DEBUG) cement.core.foundation : setting up application controllers
    2015-01-14 10:30:13,437 (DEBUG) cement.core.hook : running hook 'post_setup' (<function add_handler_override_options at 0x7fa1a25ec6a8>) from cement.core.foundation
    2015-01-14 10:30:13,438 (DEBUG) cement.core.hook : running hook 'post_argument_parsing' (<function handler_override at 0x7fa1a19e1ea0>) from cement.core.foundation
    2015-01-14 10:30:13,439 (DEBUG) myapp : This is a debug message.
    2015-01-14 10:30:13,440 (INFO) myapp : This is an info message.
    2015-01-14 10:30:13,441 (WARNING) myapp : This is a warning message.
    

    Is there any simple way to do that?

    feature stable/2.6.x 
    opened by gau1991 10
  • Add conf.d/*.conf to config handler

    Add conf.d/*.conf to config handler

    I would like to add support for conf.d/*.conf files to default config handler.

    I need it because I have plugins with multiple handlers and would like to use standard interface.handler config sections in plugin's config files. Current plugin handler takes only first section from plugins.d/pluginname.conf.

    Also conf.d more generic and obvious solution for modular application.

    I will need some help with English spelling to write good documentation.

    feature dev/2.11.x 
    opened by akhilman 8
  • Refactor Hooks/Handlers into CementApp

    Refactor Hooks/Handlers into CementApp

    Every time I instantiate a new cement application, it cleans cement.core.backend.__handlers__ and cement.core.backend.__hooks__. Considering the following (simple!) code:

    from cement.core.foundation import CementApp
    from cement.core.interface import Interface
    from cement.core import handler
    from cement.core import backend
    
    class MyInterface(Interface):
        class IMeta:
            label = 'myinterface'
    
    app1 = CementApp('app1')
    handler.define(MyInterface)
    app2 = CementApp('app2')
    print(MyInterface.IMeta.label in backend.__handlers__)
    

    False will be printed. Quite unexpectedly.

    If backend.__handlers__ is truly global object, consider to not clean it on every CementApp instantiation. If it is local to specific CementApp object, consider moving from global containers and use app attributes and methods to work with them.

    refactoring portland stable/2.10.x 
    opened by malinoff 8
  • Add support for colorized logging output

    Add support for colorized logging output

    In apps that heavily rely on logging to the console, having colorized output is very useful. For example:

    • INFO => No Color
    • WARN => Orange
    • ERROR => Red
    • Critical => Magenta or maybe just Bold Red
    feature 
    opened by derks 8
  • Replace the Interface class with the standard ABC module and remove the 'I' prefix

    Replace the Interface class with the standard ABC module and remove the 'I' prefix

    The zope interface concept is outdated and the ABC module is the standard way of creating abstract classes/interfaces. It is supported from 2.6 onwards so there is no incompatibility with cement's supported python versions and it removes the responsibility of maintaining and documenting how the interface works. Moreover, more people are familiar with the ABC module then with your own implementation so it decreases the learning curve greatly. Should we start refactoring?

    refactoring portland 
    opened by thedrow 8
  • Generate commands documentation

    Generate commands documentation

    Hello,

    First of all, thank you so much @derks for developing and maintaning cement! I have been using it intensely for a web3 project of mine and I am loving it πŸ’ͺ

    That said, I was wondering if there was a way to print the full documentation of all commands. Something like my-app --help but that outputs all of the controllers and subcommands, including their descriptions and arguments, in a structured way.

    If this feature does not exist, I am happy to try and make a pull request.

    Cheers, Cocco

    opened by coccoinomane 0
  • Help wanted - Packaging and distribution

    Help wanted - Packaging and distribution

    Help wanted - Packaging and distribution

    Need help with packaging and distributing a CLI application built using Cement.

    The documentation section on the same is incomplete. Please provide detailed info on packaging and distributing CLI apps.

    System Information

    • Cement Version: stable/3.0
    • Python Version: 3.8
    • Operating System and Version: Windows 10
    opened by PraneethKarnena 0
  • Issue #611: added option to add timestamp to backups

    Issue #611: added option to add timestamp to backups

    Issue: #611

    import os
     import tempfile
     import shutil
    +from datetime import datetime
     
     
     class Tmp(object):
    @@ -170,7 +171,7 @@ def ensure_parent_dir_exists(path):
         return ensure_dir_exists(parent_dir)
     
     
    -def backup(path, suffix='.bak'):
    +def backup(path, suffix='.bak', *, add_timestamp=False):
         """
         Rename a file or directory safely without overwriting an existing
         backup of the same name.
    @@ -178,6 +179,7 @@ def backup(path, suffix='.bak'):
         Args:
             path (str): The path to the file or directory to make a backup of.
             suffix (str): The suffix to rename files with.
    +        add_timestamp(bool): whether to add a timestamp to the backup suffix
     
         Returns:
             str: The new path of backed up file/directory
    @@ -194,7 +196,13 @@ def backup(path, suffix='.bak'):
         count = -1
         new_path = None
         path = abspath(path)
    +
    +    if add_timestamp:
    +        timestamp = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
    +        suffix = '-'.join((suffix, timestamp))
    +
    
    opened by muddi900 2
  • Flet Extension

    Flet Extension

    Flet is a Python library for building GUIs on Google Flutter. Out of the box, Cement can be combined with Flet to build a hybrid application (ex: Using Cement features like config file parsing, cli commands/options, hooks, etc as a foundation for launching the Flet GUI app).

    Not sure if an Interface/Handler would make sense here... but would be nice to build documentation and a working example of combining Cement + Flet.

    feature doc extension 
    opened by derks 0
  • Default Quiet/Argument Options to None

    Default Quiet/Argument Options to None

    To be changed in Cement 3.2.0.

    The framework should be as minimal as possible by default, and setting argument options by default isn't ideal for that. This can still be setup in the project template, however the options in the framework should default to None:

    class App(object):
        quiet_argument_options = None
        debug_argument_options = None
    

    Ref:

    • https://github.com/datafolklabs/cement/issues/613
    opened by derks 0
Owner
Data Folk Labs, LLC
Data Folk Labs, LLC
Python Command-line Application Tools

Clint: Python Command-line Interface Tools Clint is a module filled with a set of awesome tools for developing commandline applications. C ommand L in

Kenneth Reitz Archive 82 Dec 28, 2022
Textual is a TUI (Text User Interface) framework for Python using Rich as a renderer.

Textual is a TUI (Text User Interface) framework for Python using Rich as a renderer. The end goal is to be able to rapidly create rich termin

Will McGugan 17k Jan 2, 2023
A fast, stateless http slash commands framework for scale. Built by the Crunchy bot team.

Roid ?? A fast, stateless http slash commands framework for scale. Built by the Crunchy bot team. ?? Installation You can install roid in it's default

Harrison Burt 7 Aug 9, 2022
Python Fire is a library for automatically generating command line interfaces (CLIs) from absolutely any Python object.

Python Fire Python Fire is a library for automatically generating command line interfaces (CLIs) from absolutely any Python object. Python Fire is a s

Google 23.6k Dec 31, 2022
A simple terminal Christmas tree made with Python

Python Christmas Tree A simple CLI Christmas tree made with Python Installation Just clone the repository and run $ python terminal_tree.py More opti

Francisco B. 64 Dec 27, 2022
Python composable command line interface toolkit

$ click_ Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It's the "Comm

The Pallets Projects 13.3k Dec 31, 2022
Library for building powerful interactive command line applications in Python

Python Prompt Toolkit prompt_toolkit is a library for building powerful interactive command line applications in Python. Read the documentation on rea

prompt-toolkit 8.1k Dec 30, 2022
Simple cross-platform colored terminal text in Python

Colorama Makes ANSI escape character sequences (for producing colored terminal text and cursor positioning) work under MS Windows. PyPI for releases |

Jonathan Hartley 3k Jan 1, 2023
Rich is a Python library for rich text and beautiful formatting in the terminal.

Rich δΈ­ζ–‡ readme β€’ lengua espaΓ±ola readme β€’ LΓ€s pΓ₯ svenska Rich is a Python library for rich text and beautiful formatting in the terminal. The Rich API

Will McGugan 41.4k Jan 2, 2023
emoji terminal output for Python

Emoji Emoji for Python. This project was inspired by kyokomi. Example The entire set of Emoji codes as defined by the unicode consortium is supported

Taehoon Kim 1.6k Jan 2, 2023
Python and tab completion, better together.

argcomplete - Bash tab completion for argparse Tab complete all the things! Argcomplete provides easy, extensible command line tab completion of argum

Andrey Kislyuk 1.1k Jan 8, 2023
Python library that measures the width of unicode strings rendered to a terminal

Introduction This library is mainly for CLI programs that carefully produce output for Terminals, or make pretend to be an emulator. Problem Statement

Jeff Quast 305 Dec 25, 2022
A thin, practical wrapper around terminal capabilities in Python

Blessings Coding with Blessings looks like this... from blessings import Terminal t = Terminal() print(t.bold('Hi there!')) print(t.bold_red_on_brig

Erik Rose 1.4k Jan 7, 2023
Typer, build great CLIs. Easy to code. Based on Python type hints.

Typer, build great CLIs. Easy to code. Based on Python type hints. Documentation: https://typer.tiangolo.com Source Code: https://github.com/tiangolo/

SebastiΓ‘n RamΓ­rez 10.1k Jan 2, 2023
Corgy allows you to create a command line interface in Python, without worrying about boilerplate code

corgy Elegant command line parsing for Python. Corgy allows you to create a command line interface in Python, without worrying about boilerplate code.

Jayanth Koushik 7 Nov 17, 2022
prompt_toolkit is a library for building powerful interactive command line applications in Python.

Python Prompt Toolkit prompt_toolkit is a library for building powerful interactive command line applications in Python. Read the documentation on rea

prompt-toolkit 8.1k Jan 4, 2023
Terminalcmd - a Python library which can help you to make your own terminal program with high-intellegence instruments

Terminalcmd - a Python library which can help you to make your own terminal program with high-intellegence instruments, that will make your code clear and readable.

Dallas 0 Jun 19, 2022
Fully Automated YouTube Channel ▢️with Added Extra Features.

Fully Automated Youtube Channel β–’β–ˆβ–€β–€β–ˆ β–ˆβ–€β–€β–ˆ β–€β–€β–ˆβ–€β–€ β–€β–€β–ˆβ–€β–€ β–ˆβ–‘β–‘β–ˆ β–ˆβ–€β–€β–„ β–ˆβ–€β–€ β–ˆβ–€β–€β–ˆ β–’β–ˆβ–€β–€β–„ β–ˆβ–‘β–‘β–ˆ β–‘β–‘β–ˆβ–‘β–‘ β–‘β–’β–ˆβ–‘β–‘ β–ˆβ–‘β–‘β–ˆ β–ˆβ–€β–€β–„ β–ˆβ–€β–€ β–ˆβ–„β–„β–€ β–’β–ˆβ–„β–„β–ˆ β–€β–€β–€β–€ β–‘β–‘β–€β–‘β–‘ β–‘β–’β–ˆβ–‘β–‘ β–‘β–€β–€β–€ β–€β–€β–€β–‘

sam-sepiol 249 Jan 2, 2023
AutoPilot is a game where the player controls a car and tries to get the highest score he can while not dying under falling cement blocks.

AutoPilot AutoPilot is a game where the player controls a car and tries to get the highest score he can while not dying under falling cement blocks. C

Enoc Mena 1 Nov 17, 2021
A starter template for building a backend with Django and django-rest-framework using docker with PostgreSQL as the primary DB.

Django-Rest-Template! This is a basic starter template for a backend project with Django as the server and PostgreSQL as the database. About the templ

Akshat Sharma 11 Dec 6, 2022