Colifrapy¶
Colifrapy is a Command Line Framework for Python.
Its aim is to provide several tools to build robust and structured command line tools very easily.
Its logic is very similar to a MVC framework and is therefore easy to use.
The github repository can be found there.
Summary¶
Quickstart¶
Creating New Project¶
Having installed colifrapy, initialize a new project with the Scaffolder by typing into your console:
colifrapy new [name-of-project]
options :
{-a/--author: Author of the project}
{-o/--organization: Organization of the author}
To test your new project, enter your project’s directory and type:
python [name-of-project].py test
It should launch it and output a header and some strings in the console telling you everything is going to be OK.
Command Line Hub¶
[name-of-project].py
A colifrapy project relies on a command line hub extending Colifrapy base class. This is the file you have to call in your shell to launch the program. The duty of this hub is to initialize your tool, analyze the arguments given to it and call upon the relevant controller methods.
In fact, this hub can be compared to a basic router for web frameworks.
This is the hub as generated by the scaffolder
# Dependencies
#=============
from colifrapy import Colifrapy
from model.controller import Controller
# Hub
#======
class NameOfYourProject(Colifrapy):
# From this hub, you can access several things :
# self.settings (Settings Instance)
# self.log (Logger Instance)
# self.opts (Options passed to your hub)
# self.controller (Your Controller)
def launch(self):
# Welcoming visitors
self.log.header('main:title')
# Calling upon the controller
self.controller.test()
# Launching
#===========
if __name__ == '__main__':
# By default, the hub will load config/settings.yml
hub = NameOfYourProject(
controller=Controller,
settings_path='path/to/your/settings.yml'
)
hub.launch()
Note that if you just want to use colifrapy features but don’t want to be tied to its architecture, you can just use this hub which can access any critical utilities as well as any colifrapy Model would.
Settings¶
config/settings.yml
The Settings class is the first class loaded by colifrapy to perform its magic. It will parse your settings.yml file and configure your logger, cacher, arguments and every other configuration you want for your application.
# Basic Informations
version: '[project-name] 0.1.0'
description: 'Description of the program.'
usage: 'How to deal with your program'
arguments:
- [ ['-t', '--test'], {'help' : 'Test', 'type' : 'int'} ]
- [ ['positionnal'] ]
# Logger Settings
logger:
strings: 'config/strings.yml'
flavor: 'default'
# Generic Settings needed by your program
settings:
hello: 'world'
bonjour: 3.4
hash: {'test' : 2}
Also, note that paths are automatically considered by colifrapy either as relative (config/test.yml) or absolute ones (/var/usr/test.yml).
For further information see Settings.
Arguments¶
config/settings.yml[‘arguments’]
Settings Usage¶
Arguments are to be defined as for the python ArgParser class. In fact, the colifrapy Commander class extends the ArgParser one, so if you need complicated things not handled by colifrapy, just use the Commander class like the ArgParser one.
arguments:
- [ ['-t', '--test'], {'help' : 'Test', 'type' : 'int', 'default' : 5} ]
- [ ['-b', '--blue'], {'help' : 'Blue option', 'type' : 'int', 'required' : 'True'} ]
- [ ['some_positionnal_argument'] ]
In the command hub and in your models, you can access the options passed to your commander through self.opts . However, even if those are accessible in models for commodity, only the main hub should use them and one should restrain their usage in models.
Special Arguments¶
Help, Version, Verbose and Settings
As for standard python command line tool, yours will accept three default arguments you should not try to override (verbose is the only one you can override because it is not one of ArgumentParser defaults):
-v/--version (outputting your program's version)
-h/--help (displaying your program's help)
-V/--verbose (overriding settings to enable the logger to display every messages)
--settings (overriding settings file if needed)
Controller¶
model/controller.py
The controller is a class whose goal is to call upon other models. The controller itself is in fact also a colifrapy model and is more a convention that something enforced by colifrapy’s code.
The controller is totally optional and just illustrate a way to organize your code. If you don’t want to follow this logic, just don’t pass a controller to your hub instance.
Controller as generated by the scaffolder
# Dependencies
#=============
from colifrapy import Model
from example_model import ExampleModel
# Main Class
#=============
class Controller(Model):
# Properties
example_model = None
def __init__(self):
self.example_model = ExampleModel()
# Example of controller action
def test(self):
self.log.write('main:controller')
self.example_model.hello()
Model¶
model/example_model.py
Models are the bulk of Colifrapy. You can extend them to access your settings and commands easily.
A standard model is generated for you by the Scaffolder when you create a new project.
Minimalist example of a model usage
from colifrapy import Model
class MyModel(Model):
def test(self):
print self.settings.hello
m = MyModel()
m.test()
>>> 'world'
- Reserved attributes names are:
- cache (access to cache)
- log (access to the logger described hereafter)
- opts (access to the command line options)
- settings (access to the program’s settings)
Logger¶
Basic¶
The logger is the outputting class of colifrapy. It should be loaded with some strings by the settings. If no strings are given, the logger will just output normally the argument strings you give to it.
For full logger documentation, see Logger.
Levels¶
- The logger accepts five levels :
- INFO (green output)
- VERBOSE (cyan output)
- DEBUG (blue output)
- WARNING (yellow ouput)
- ERROR (red output)
- CRITICAL (purple output) –> will throw an exception for you to catch or not
By default, if no level is specified for a message, DEBUG will always be taken.
Strings¶
config/strings.yml
Colifrapy offers to externalize your strings in order to enable you to quickly modify them if needed, or even translate them easily.
The string format used is a mustache-like one, so variables come likewise : {{some_variable}}
Strings given must follow this yaml layout
main:
process:
# String with a variable contained within the mustaches
start: 'Starting corpus analysis (path : {{path}})//INFO'
# Simply write two slashes at the end to specify the level of the message
end: 'Exiting//WARNING'
test_line_break: '\nBonjour'
title: 'Colifrapy'
other_string_category:
test: 'Hello everyone//INFO'
you:
can:
make: 'any levels that you want'
so: 'you can organize your strings however you need.'
Usage¶
This is how you would use the logger in a colifrapy model
from colifrapy import Model
class MyModel(Model):
def test(self):
# Main method
#------------
# Outputting a message
self.log.write('main:process:end')
>>> '[WARNING] :: Exiting'
# Overriding the message level
self.log.write('main:process:end', level='INFO')
>>> '[INFO] :: Exiting'
# Passing variables
self.log.write('main:protocol:start', {'path' : 'test'})
>>> '[INFO] :: Starting corpus analysis (path : test)'
# Variables can be passed to the logger as:
# a hash, a list, a tuple, a single string or integer or float
# Examples
self.log.write('{{variable}}', 'test')
>>> '[DEBUG] :: test'
self.log.write('{{var1}} is {{var2}}', ['python', 'cool'])
>>> '[DEBUG] :: python is cool'
# When yml file is not specified or if message does not match
self.log.write('Test string')
>>> '[DEBUG] :: Test string'
# Named arguments of write
# variables --> mixed
# level --> log level
# Helper methods
#---------------
# Printing a header (yellow color by default)
self.log.header('main:title', [optional]color)
>>> Colifrapy
>>> ---------
# Write methods shorteners
self.log.critical(message, vars)
self.log.error(...)
self.log.warning(...)
self.log.info(...)
self.log.debug(...)
self.log.verbose(...)
Asking for confirmation
from colifrapy import Model
class MyModel(Model):
def test(self):
# Confirmation
#---------------
# 'y' will be taken by default in arg 2
# will return True for y and False for n
response = self.log.confirm('Are you sure you want to continue?')
>>> 'Are you sure you want to continue? (Y/n)'
>>> y --> True
response = self.log.confirm('Are you sure you want to continue?', 'n')
>>> 'Are you sure you want to continue? (y/N)'
>>> n --> False
Getting user input
from colifrapy import Model
class MyModel(Model):
def test(self):
# User Input
#---------------
response = self.log.input('What up ?')
>>> 'What up ?'
>>> 'feeling fine' --> 'feeling fine'
# You can also provide a lambda to the function as second argument
# This lambda will affect the input given
response = self.log.input('What up ?', lambda x: x.upper())
>>> 'What up ?'
>>> 'feeling fine' --> 'FEELING FINE'
Cacher¶
Colifrapy gives you acces, in your hub and models to a caching class able to store data on files for long-term access. There are currently to types of cacher : line and yaml. The first one consist in a text file containing one line read by the cacher while the second archive any python key-value data in a yaml file.
To enable the cacher in the settings.yml file
cache:
kind: 'line'
directory: 'config'
filename: 'last_update.txt'
# Whether you want the cache to be written each time a value is changed
# Defaults to False
auto_write: True
Then in your model
from colifrapy import Model
class MyModel(Model):
def test(self):
# Line Cacher
#-------------
# Setting cache
self.cache.set("test")
# Getting cache
self.cache.get()
>>> "test"
# Yaml Cacher
#-------------
# Setting cache
self.cache.set("one", "red")
self.cache.set("two:deep", "blue")
# Getting cache
self.cache.get("one")
>>> "red"
self.cache.get("two")
>>> {"deep" : "blue"}
self.cache.get("two:deep")
>>> "blue"
self.cache.get
>>> {"two" : "red", {"deep" : "blue"}}
Note that the path separator for deep levels in yaml is always ”:” in Colifrapy.
For full documentation see Cacher.
Settings¶
Class¶
The Settings class’ aim is to load a ‘settings.yml’ file containing every piece of configuration for a colifrapy program. This class is initialized by default by colifrapy’s hub and is a singleton so its state won’t change throughout your code.
Example of a settings.yml file (created by default in ‘config/’ by the Scaffolder)
# Basic Informations
version: '[project-name] 0.1.0'
description: 'Description of the program.'
usage: 'How to deal with your program'
arguments:
- [ ['-t', '--test'], {'help' : 'Test', 'type' : 'int'} ]
- [ ['positionnal'] ]
# Logger Settings
logger:
strings: 'config/strings.yml'
flavor: 'default'
title_flavor: 'default'
# Delete the path line not to write the log to a file
directory: 'logs'
threshold: ['DEBUG', 'ERROR', 'INFO', 'WARNING', 'VERBOSE']
# Generic Settings needed by your program
settings:
hello: 'world'
bonjour: 3.4
hash: {'test' : 2}
Model Usage¶
In every class that extends colifrapy model, you can access your generic settings with the property settings. The following code assume the precedent yml file was loaded.
from colifrapy import Model
class MyModel(Model):
def test(self):
print self.settings.hello
>>> "world"
print self.settings.hash['test']
>>> 2
Standalone Usage¶
As every colifrapy class, it is possible to use the Settings one as a standalone. The following example presents how you could do that and what you pass to it to make it work.
from colifrapy import Settings
# Loading the YAML file
s = Settings()
s.load('path/to/settings.yml')
# Accessing the generic settings
d = s.getDict()
print d.hello
>>> "world"
# Accessing cache
c = s.getCache()
Once loaded, you can use it anywhere in your code and even reinstanciate it if convenient with it keeping the same state.
Options¶
Standard¶
- Standard program options are the following:
- version : Name and version of your program (outputted with -v/–version option)
- description : Short description of your program and what it does
- usage : How to use your program
- prog : Program’s name to display along usage string.
- epilog : A final string to output when help is displayed.
Arguments¶
A good command line tool often comes with arguments, you can register those in the yaml file for convenience. Once loaded with arguments, the Settings class will load the Commander one with them.
Those are to be defined as for the python ArgParser class.
Example of argument definition (under ‘arguments’ key).
arguments:
- [ ['-t', '--test'], {'help' : 'Test', 'type' : 'int', 'default' : 5} ]
- [ ['-b', '--blue'], {'help' : 'Blue option', 'type' : 'int', 'required' : 'True'} ]
- [ ['some_positionnal_argument'] ]
Logger¶
The Logger class can be given some options through settings. If none are supplied, logger will still be initialized with its default values.
For more precise information see Logger.
For more precise information about the logger’s styles see Styles.
logger:
# {string} [None] YAML string file.
strings: 'example_strings.yml'
# {boolean} [True] Should the CRITICAL level trigger exceptions?
exceptions: False
# {boolean} [True] Should we activate both logger's handlers?
activated: True
# {string} ['VERBOSE'] Threshold for both logger's handlers.
threshold: 'INFO'
# {string} The log message formatter for both logger's handlers.
formatter: '%(msg)s -- %(asctime)'
# {string|lambda} The flavor for colored levelname.
flavor: 'elegant'
# Console specific options
console:
# {boolean} [True] Should the console handler be activated?
activated: True
# {string} ['VERBOSE'] Threshold.
threshold: 'DEBUG'
# {string} ['%(flavored_levelname)s :: %(msg)s'] Formatter.
formatter: '%(msg)s'
# File specific options:
# {boolean} [False] Should the file handler be activated?
activated: False
# {string} ['VERBOSE'] Threshold.
threshold: 'ERROR'
# {string} ['%(asctime)s %(levelname)s :: %(msg)s'] Formatter.
formatter: '%(msg)s'
# {string} ['.'] Directory where the file handler will write.
directory: 'logs'
# {string} ['program.log'] Filename for the log file.
filename: 'current.log'
# {string} ['simple'] File logging mode
mode: 'rotation'
# Rotation mode options
# {int} [1048576] Max bytes for a current log file.
max_bytes: 2097152
# {int} [5] Max number of log files
backup_count: 4
N.B.: Options passed at the logger level such as activated or threshold override the console and file one and apply to both.
Cacher¶
If needed, the Settings class can also handle the initialization of a cacher. Just provide a ‘cache’ key to the settings and populate it.
For more precise information see Cacher.
cache:
# Cache Directory
# Default: cache
directory: 'cache'
# Cache filename
# Default: 'cache.txt' for line mode and 'cache.yml' for yaml mode
filename: 'project.log'
# Kind of cache
# Default: 'line' (choose between line and yaml)
type: 'yaml'
# Auto Write
# Default: False
auto_write: True
If you need more than one cache instance, just pass an array to the cache key in your YAML settings file. In this case, don’t forget to pass a name to your settings to access it. Else it will earn a standardized name like __cacheInstance0.
cache
- name: 'infos'
filename: 'cache1.yml'
type: 'yaml'
- name: 'last_update'
filename: 'last_update.txt'
type: 'line'
Then access your cache likewise.
from colifrapy import Model
class MyModel(Model):
def test(self):
print self.cache['infos']
print self.cache['last_update']
General¶
If you need any other settings you consider necessary, just provide a settings key to your yaml file and populate it as in the following example.
settings:
mysql:
host: localhost
user: root
password: foo
to_index: ["books", "notes"]
limit: 5
It is also possible to include other yaml files into those generic settings by following this procedure.
# Syntax is 'include::path/to/file.yml'
# Warning, will only work on the first level (not on a nested one)
settings:
hello: 'world'
hello2: 'include::path/to/another_config_file.yml'
N.B.¶
For every path given, colifrapy will try and decide whether it is absolute or relative (unix-style):
'/usr/local/settings.yml' is an absolute path
'config/settings.yml' is a relative path (relative to the colifrapy hub file)
Logger¶
Class¶
The Logger class is the voice of colifrapy. Its aim is to display feedback from your program into the console and to write it to a file if necessary. It may also feed on externalized strings written in a YAML file.
N.B.: this custom logger is built around the python logging module and spawn a logging instance named “colifrapy”.
Model Usage¶
By default, and even if no settings were initialized before, every colifrapy model is loaded with the Logger so you may use it when convenient.
from colifrapy import Model
class MyModel(Model):
# In every colifrapy model, the Logger is
# accessible through the "log" property.
def test(self):
self.log.write('Hello World!')
>>> '[DEBUG] :: Hello World!'
Standalone Usage¶
If you want to use the colifrapy Logger without messing with the whole framework, it is obviously possible. Note that as a lot of colifrapy classes, the Logger is actually a singleton. You may then instanciate it in different files, it will always be the same one for convenience.
from colifrapy import Logger
logger_instance = Logger()
# Run the configuration method at least one to initialize the logger
logger_instance.config(**options)
logger_instance.write('Hello World!')
>>> '[DEBUG] :: Hello World!'
# To change the logger's configuration, just rerun the config method
logger_instance.config(**options)
# Or for specific options
logger_instance.configConsole(options)
logger_instance.configFile(options)
Levels¶
The logger accepts five levels (ordered by importance):
- VERBOSE (cyan output)
- DEBUG (blue output)
- INFO (green output)
- WARNING (yellow ouput)
- ERROR (red output)
- CRITICAL (violet output) –> will throw an exception for you to catch or not
By default, if no level is specified for a message, DEBUG will always be taken.
Options¶
Colifrapy’s logger has three different configuration methods, each one dealing with a particular end. You can therefore configure the logger as a whole or rather one of both its handlers (console and file).
Note that if you want to change one of those options on the fly you can always run the config method one more time with the changed options.
Generic Options¶
The generic options you may pass to the logger’s config method (those options are automatically taken care of when the logger is loaded by the Settings class) are the following:
- strings {string}
Path leading to your externalized YAML strings.
default: None (the logger won’t use externalized strings)- exceptions {boolean}
Should the CRITICAL level trigger exceptions.
default: True- flavor {string|lambda}
The flavor to use to format %(flavored_levelname)s.
default: ‘default’- console_kwargs {dict}
A configuration dict to be run into the configConsole method.
default: None (the configConsole method will be called with its defaults)- file_kwargs {dict}
A configuration dict to be run into the configFile method.
default: None (the configFile method will be called with its defaults)
For a list of flavors, see Styles. If none of the proposed flavors suit you and you need to create your own, please note that you can pass a lambda taking the levelname variable to the flavor option
Usage example
from colifrapy import Logger
logger_instance = Logger()
logger_instance.config(strings='example_string.yml', exceptions=False)
Console Options¶
The console options you may pass to the logger’s configConsole method (those options are automatically taken care of when the logger is loaded by the Settings class under logger:console) are the following:
- activated {boolean}
Whether the console handler should be activated or not.
default: True- threshold {string}
Threshold for the console handler.
default: ‘VERBOSE’- formatter {string}
Formatter for the console handler.
default: ‘%(flavored_levelname)s :: %(msg)s’
Usage example
from colifrapy import Logger
logger_instance = Logger()
logger_instance.configConsole(threshold='WARNING', activated=True)
File Options¶
The console options you may pass to the logger’s configFile method (those options are automatically taken care of when the logger is loaded by the Settings class under logger:file) are the following:
- activated {boolean}
Whether the file handler should be activated or not.
default: False- threshold {string}
Threshold for the file handler.
default: ‘VERBOSE’- formatter {string}
Formatter for the console handler
default: ‘%(asctime)s %(levelname)s :: %(msg)s’.- directory {string}
Directory where the file handler is supposed to write its logs.
default: ‘.’- filename {string}
Name of the log files.
default: ‘program.log’- mode {string}
File logging mode. See Modes.
default: ‘simple’- max_bytes {integer}
When in rotation mode, maximum of bytes for a log file before rotating.
default: 1048576- backup_count {integer}
When in rotation mode, maximum number of archived log files.
default: 5
Note that the file handler is not activated by default.
Usage example
from colifrapy import Logger
logger_instance = Logger()
logger_instance.configFile(threshold='ERROR', activated=True, mode='overwrite')
Strings¶
Colifrapy offers to externalize your strings in order to enable you to quickly modify them if needed, or even translate them easily. I you do not provide the logger with some strings, it will simply take normal python strings.
The string format used is a mustache-like one, so variables come likewise : {{some_variable}}
Strings given must follow this yaml layout
main:
process:
# String with a variable contained within the mustaches
start: 'Starting corpus analysis (path : {{path}})//INFO'
# Simply write two slashes at the end to specify the level of the message
end: 'Exiting//WARNING'
test_line_break: '\nBonjour'
title: 'Colifrapy'
other_string_category:
test: 'Hello everyone//INFO'
you:
can:
make: 'any levels that you want'
so: 'you can organize your strings however you need.'
Modes¶
The Logger comes with three different outputting modes:
- simple: it will write everything to a single specified file.
- overwrite: the log will be completely overwritten each time you launch the program.
- rotation: each time your log file overcomes a specified number of lines, it will create a new file and archive the old one. E.g. it functions like the apache log.
For more information about file rotation, you can read the python logging module’s RotatingFileHandler documentation.
Methods¶
Writing¶
from colifrapy import Model
class MyModel(Model):
def test(self):
# Main method
#------------
# Outputting a message
self.log.write('main:process:end')
>>> '[WARNING] :: Exiting'
# Overriding the message level
self.log.write('main:process:end', level='INFO')
>>> '[INFO] :: Exiting'
# Passing variables
self.log.write('main:protocol:start', {'path' : 'test'})
>>> '[INFO] :: Starting corpus analysis (path : test)'
# Variables can be passed to the logger as:
# a hash, a list, a tuple, a single string or integer or float
# Examples
self.log.write('{{variable}}', 'test')
>>> '[DEBUG] :: test'
self.log.write('{{var1}} is {{var2}}', ['python', 'cool'])
>>> '[DEBUG] :: python is cool'
# When yml string file is not specified or if message does not exist in the yaml file
self.log.write('Test string')
>>> '[DEBUG] :: Test string'
# Named arguments of write
# variables --> mixed
# level --> log level
# Helper methods
#---------------
# Printing a header
self.log.header('main:title', [optional]flavor='default')
>>> Colifrapy
>>> ---------
# You can also pass a function as the title flavor rather
# than a predetermined one.
self.log.header('main:title', flavor=lambda msg: msg.upper())
>>> COLIFRAPY
# Write methods shorteners
self.log.critical(message, vars)
self.log.error(...)
self.log.warning(...)
self.log.info(...)
self.log.debug(...)
self.log.verbose(...)
Confirmation¶
from colifrapy import Model
class MyModel(Model):
def test(self):
# Confirmation
#---------------
# 'y' will be taken by default in arg 2
# will return True for y and False for n
response = self.log.confirm('Are you sure you want to continue?')
>>> 'Are you sure you want to continue? (Y/n)'
>>> y --> True
response = self.log.confirm('Are you sure you want to continue?', 'n')
>>> 'Are you sure you want to continue? (y/N)'
>>> n --> False
User Input¶
from colifrapy import Model
class MyModel(Model):
def test(self):
# User Input
#---------------
response = self.log.input('What up ?')
>>> 'What up ?'
>>> 'feeling fine
>>> 'feeling fine'
# You can also provide a lambda to the function as second argument
# This lambda will affect the input given
response = self.log.input('What up ?', lambda x: x.upper())
>>> 'What up ?'
>>> 'feeling fine'
>>> 'FEELING FINE'
Styles¶
Colifrapy’s logger comes with several visual alternatives that you may choose from. Those are called flavors and are available for title and standard messages.
Formatters¶
Colifrapy’s logger accepts a format string the same way as the python logging module, so you can customize your logging output. It also add a custom variable named flavored_levelname which is in fact the level name colored and stylized.
# Default formatter for console
'%(flavored_levelname)s :: %(msg)s'
>>> [DEBUG] :: message to log
# Default formatter for file
'%(asctime)s %(levelname)s :: %(msg)s'
>>> 2014-01-15 13:56:09,798 DEBUG :: message to log
For the full documentation about the variables usable by the formatter, see this page.
Title Flavors¶
default
Title
-----
heavy
#########
# Title #
#########
elegant
# Title
#-------
bold
# Title
#=======
Flavors¶
default
[DEBUG]
flat
debug
reverse
# With reverse colors
DEBUG
elegant
Debug
underline
DEBUG
-----
Models¶
Models are the bulk of Colifrapy. You can extend them to access your settings and commands easily.
Usage¶
from colifrapy import Model
class MyModel(Model):
def test(self):
print self.settings.hello
m = MyModel()
m.test()
>>> 'world'
Reserved Attributes¶
- cache (access to cache)
- log (access to the logger described right after)
- opts (access to the command line options)
- settings (access to the program’s settings)
Controller¶
The Controller, as referred in other part of this documentation, is strictly speaking a model no different than any other. It is just a convention to use it as a central point for launching other models.
Cacher¶
The Cacher classes’ aim is to save some data to files for long-term usage. One could see them as very simplistic databases.
Model Usage¶
As for the logger, if a cacher was initialized by the settings while running colifrapy, “cache” will be a reserved attribute of any of your models.
It is possible to register more than one cache instance with the Settings class. To achieve this, see cache settings.
from colifrapy import Model
class MyModel(Model):
def test(self):
# We assume that a line cache file containing
# the string "Hello" was loaded
print self.cache.get()
>>> 'Hello'
Standalone Usage¶
You can also use the Cacher classes as standalones rather than within colifrapy’s architecture.
from colifrapy import LineCacher, YAMLCacher
line_cache = LineCacher(options)
yaml_cache = YAMLCacher(options)
Modes¶
Line Cacher¶
The Line Cacher consists in a strict one-liner file and is aimed at storing really simple data.
Example of cache file content
1245
YAML Cacher¶
The YAML Cacher is designed to store more complex states of data and organized in a key-value fashion. The readability of the YAML file format makes the cache file easy to manually modify if needed. This is also possible to create deep nested data structures that will be accessible by paths.
Example of cache file content
number_of_tries: 14
countries:
albania: True
united_kingdom: "Hello"
Options¶
Here are the possible options you may pass to the Cacher classes constructors :
- directory
(string)
directory where you want to store your cache
default: “cache/”
- filename
(string)
name of the cache file
default: “cache.txt” or “cache.yml”
- auto_write
(boolean)
whether you want your cache to be automatically written when changed or not. If set to False, you’ll have to write the invoke the cache writing manually.
default: False
Methods¶
Line Cacher¶
from colifrapy import Model
class MyModel(Model):
def test(self):
# Setting cache
self.cache.set('Hello')
# Getting cache
print self.cache.get()
>>> 'Hello'
# Writing to cache
# N.B. : Useless if auto_write is set to True
self.cache.write()
# Deleting cache
self.cache.delete()
# Reading and writing filters
# Example of a single date cached
date_format = "%Y/%m/%d %H:%M:%S"
self.cache.setReadingFilter(lambda x: datetime.strptime(x, date_format))
self.cache.setWritingFilter(lambda x: x.strftime(date_format))
YAML Cacher¶
from colifrapy import Model
class MyModel(Model):
def test(self):
# Setting cache
self.cache.set("one", "red")
self.cache.set("two:deep", "blue")
# Getting cache
print self.cache.get("one")
>>> "red"
print self.cache.get("two")
>>> {"deep" : "blue"}
print self.cache.get("two:deep")
>>> "blue"
print self.cache
>>> {"one" : "red", "two" : {"deep" : "blue"}}
# Unset path
self.cache.unset("two")
print self.cache
>>> {"one" : "red"}
# Overwriting cache
self.cache.overwrite({'other' : 'structure'})
print self.cache
>>> {'other' : 'structure'}
# Writing to cache
# N.B. : Useless if auto_write is set to True
self.cache.write()
# Deleting cache
self.cache.delete()
Scaffolder¶
Colifrapy comes with a scaffolder used to generate code boilerplate. Therefore, a command is automatically added when you install colifrapy with pip.
Usage¶
To use the scaffolder:
colifrapy new [name-of-project]
options :
{-a/--author: Author of the project}
{-o/--organization: Organization of the author}
This will generate a standard colifrapy project containing the following files :
.gitignore (exluded files for git)
requirements.txt (base pip dependencies)
README.md (project documentation)
[name-of-project].py (command line hub)
- config/
- settings.yml (standard settings for your project)
- strings.yml (externalized strings)
- model/
- controller.py (basic controller)
- example_model.py (basic model)
Every relevant folder will of course come along with its __init__.py file.
Goodies¶
Colifrapy also gives access to internal functions and helpers that may prove useful.
Colorization¶
A function used to style console output. Note that not every style work on every consoles.
from colifrapy.tools.colorize import colorize
# The colorize function accepts up to four arguments
# 1. Positionnal : the string to style
# 2. fore_color : color of the string
# 3. background_color : color of background
# 4. style : a list or string of style(s)
# Available colors : black, red, green, yellow, blue, magenta, cyan, white
# Available styles : reset, bold, italic, dim, underline, blink-slow, blink-fast, reverse, hidden
# Example
print colorize('hello', fore_color='red', background_color='black', style='bold')
Singleton Decorator¶
Most of colifrapy classes are actually meant to be singletons. To perform this, the framework uses a simplistic decorator.
from colifrapy.tools.decorators import singleton
@singleton
class MySingleton():
pass
Helper Functions¶
Some functions that may prove useful
# You would rather import only functions you need,
# but for the sake of the example I use '*'
from colifrapy.tools.utilities import *
# Is the variable a number ?
is_number('test')
>>> False
# Is the variable a string (python 2/3 compatible) ?
is_string('test')
>>> True
# Is the variable a list or a tuple?
is_of_list(['red', 'blue'])
>>> True
# Is the variable a function
is_func(lambda x: x.lower())
>>> True
# Parsing a string into a lambda
# WARNING: This isn't very safe
parse_lambda('lambda x: x.upper()')
>>> lambda x: x.upper()
# Get Index with fallback
get_index(['red', 'blue'], 'green', 5)
>>> 5
# Determine whether your path is relative or absolute
# if it happens to be relative, the function will assume
# it is relative to the file called (__main__)
# For those examples, we assume that the file called by the command
# line is /home/user/test/test.py
normalize_path('/home/user/path/to/file.txt')
>>> '/home/user/path/to/file.txt'
normalize_path('/resources/file.txt')
>>> '/home/user/test/resources/file.txt'
# A second boolean argument can be passed to indicate the function if
# the path leads to a directory or a file.
# In case of a directory, the path will be returned with a correct trailing slash
# Default is False (file)
normalize_path('/resources/test_folder', False)
>>> '/home/user/test/resources/test_folder'
normalize_path('/resources/test_folder', True)
>>> '/home/user/test/resources/test_folder/'
Simplified Action Hub¶
If your program is as simple as parsing one positionnal argument given by the user in order to choose the action to perform, you might want to use colifrapy_action argument in you yaml setting file.
Example:
python my-program.py action
Your settings yaml file
version: 'Basic action program'
description: 'Let the user choose the action he wants.'
arguments:
- [ ['colifrapy_action'], {'choices' : ['test', 'hello', 'delete']}]
Once this argument setup, just write a simplistic colifrapy hub that will automatically trigger the relevant controller method named after a choice that the use can make.
Command line hub
from colifrapy import Colifrapy
from model.controller import Controller
# Hub
class MyProject(Colifrapy):
pass
# Launching
if __name__ == '__main__':
hub = MyProject(Controller)
Controller
from colifrapy import Model
class Controller(Model):
def test(self):
self.log.write('test')
def hello(self):
self.log.write('Hello World!')
def delete(self):
self.log.write('Deleting...')
Usage
python my-program.py test
>>> '[DEBUG] :: 'test'
python my-program.py hello
>>> '[DEBUG] :: 'Hello World!'
python my-program.py delete
>>> '[DEBUG] :: 'Deleting...'
Installation¶
It is recommanded to use colifrapy under a python virtualenv. (Use the excellent virtualenvwrapper to spare you some painful operations with classic virtualenvs).
Install colifrapy with pip:
pip install colifrapy
If you want to use the latest one which is still in development and hosted on github:
pip install git+https://github.com/Yomguithereal/colifrapy.git
Philosophy¶
As every framework, colifrapy aims at enable you to work immediately on critical and intersting parts of your code that will tackle the problems you need to solve instead of battling with petty things such as the console output, your settings and the arguments passed to your tool.
However, colifrapy is not a tyrant and does not force you to go its way. As such, every part of colifrapy can be used on its own and you will remain free to code the way you want to.
Concept¶
When using colifrapy, your tool is called through a command line hub which acts more or less like a router which will call upon a controller using one or several models to perform the job.
Your hub has therefore the task to load a yaml configuration file containing your command line arguments, name, version and other contextual settings.
Once those settings are loaded, every part of your application will remain able to access critical utilities such as argv opts, settings and make use of colifrapy’s logger to ouptut nicely to the console and to log files.
So, schematically colifrapy is a YAML configuration file loaded by a command line hub that will call upon a controller and other models.
Every bit of colifrapy can be used as a standalone.
- Logger (outputs to console)
- Settings (deals with your yml settings)
- Commander (deals with argv)
- Cacher (saves data to file)
Examples¶
The project furuikeya is a good example of the usage of colifrapy since the framework was originally designed for it.
Dependencies¶
- pyyaml
- argparse
License¶
Colifrapy is under a MIT license.