pip install cfgr
Create at cfgr.json file (see examples below) and run;
python -m cfgr.app
pycfgr is part of a cross-language exploration in search of new ways to create software systems.
pycfgr builds on the work of earlier projects;
The cfgr
module provides a set of APIs that encourage separation of configuration from logic. It helps organising the application logic in small, modular components and lets you put those together through configuration (json) files to create complex systems.
The approach borrows concepts from Visual Programming Languages (VPLs) which generally let you create instances of pre-built building blocks and connect them together to perform complex tasks. pycfgr
translates these concepts back into a text-based development workflow.
three well known examples of VPLs (from left to right); Cyling '74's Max, Unreal Engine's Blueprints and Derivative's TouchDesigner
Why?
Motivation for this exploration came from many years of experience in professional software development, specifically non web-based UI software development in C++ and Java using frameworks like OpenFrameworks, Cinder and Processing and noticing patterns of repetition, both in code and in general workflow. This experience lead to, initially, the JavaLibUiBuilder which builds on top of a UI framework (JavaLibUi, inspired by the ofxInterface library for OpenFrameworks/C++), which started the concept of building and configuring application logic from json config files (which feels a bit like writing CSS from a web-page).
This concept was translated into C++ using in the ciCMS package for the Cinder framework and extended to provide "native" events and states to further reduce the amount of custom application code that needs to be written to make the different components communicate.
Below are some basic examples, see the examples folder for more.
Each of these configurations can be invoked by saving them as a json file and then running:
python -m cfgr.app --data <path/to/configuration.json>
{
"App": {"started":"#sayhello", "stop":"#stopapp"},
"App.string": {"value": "Hello World!", "in-emit":"#sayhello", "out-emit":"#print,#stopapp"},
"App.Print": {"on": "#print"}
}
The below configuration will send an OSC /play
message to 127.0.0.1:3002
at an interval of 30 seconds (30000 milliseconds).
{
"App": {"started":"#start_play_interval", "update":"#update_play_interval"},
"App/Interval": {"start": "#start_play_interval", "ms":30000, "update":"#update_play_interval", "action":"#send_play"},
"App/OscOut": {"host": "127.0.0.1", "port": 3002, "in-send": "#msg"},
"App/OscOut/OscMessage": {"address": "/play", "in-send": "#send_play", "out-send": "#msg"}
}
The below configuration starts an HTTP server on port 8080 that serves all files in the ./public
folder via the 127.0.0.1:8080/static
url and provides two remote control operations via its "API endpoints": /api/start
and /api/stop
.
{
"App": {"started": "#starthttp"},
"App/HttpServer": {"port": 8080, "request-out":"#httprequest", "start":"#starthttp"},
"App/HttpServer/static": {"type":"HttpScope", "scope":"/static", "request-in": "#httprequest", "servePath": "./public"},
"App/HttpServer/api": {"type":"HttpScope", "scope":"/api", "request-in": "#httprequest", "unscoped":"#apirequest"},
"App/HttpServer/api/start": {"type":"HttpScope","scope":"/start", "request-in": "#apirequest", "match": "#action_start", "response":200, "verbose": true},
"App/HttpServer/api/stop": {"type":"HttpScope","scope":"/stop", "request-in": "#apirequest", "match": "#action_stop", "response":200, "verbose": true},
"App/string": {"value": "STARTED, woohoo!", "in-emit":"#action_start", "out-emit":"#print"},
"App/string": {"value": "STOPPED, boring :/", "in-emit":"#action_stop", "out-emit":"#print"},
"App/Print": {"on": "#print"}
}