Tutorial

Filtering and mangling

The filter spec is based on MongoDB:

from link.utils.filter import Filter

spec = {
    # ...
}
f = Filter(spec)

if f.match(dictionary):
    print 'match'

And the mangling spec is also based on MongoDB:

from link.utils.filter import Mangle

spec = {
    # ...
}

m = Mangle(spec)
newdict = m(dictionary)

Logging

The logging module sets a new logging class, it just needs to be imported. Hopefully, the library b3j0f.conf provides a way to do that automatically:

Edit the file $B3J0F_CONF_DIR/b3j0fconf-configurable.conf:

{
  "CONFIGURABLE": {
    "modules": [
      "link.utils.log"
    ]
  }
}

Then, configure the new logging class by editing the file $B3J0F_CONF_DIR/link/utils/logging.cong:

{
  "LOGGING": {
    "log_format": "[\%(asctime)s] [\%(levelname)s] [\%(name)s] \%(message)s",
    "log_level": "INFO",
    "log_filter": {
      "name": {"$regex": "^link\..*"}
    }
  }
}

NB: The log_filter option is a MongoDB filter used to filter logging records.

Finally, just use the logging module as usual.

Logging Records

For more information about what a logging record is, click here.

A record is transformed into a dict for filtering, it validates the following JSON schema:

{
  "$schema": "http://json-schema.org/schema#",

  "type": "object",
  "properties": {
    "name": {
      "title": "record.name",
      "description": "Logging record name attribute",
      "type": "string"
    },
    "level": {
      "title": "record.level",
      "description": "Logging record level attribute",
      "type": "integer"
    },
    "pathname": {
      "title": "record.pathname",
      "description": "Logging record pathname attribute",
      "type": "string"
    },
    "lineno": {
      "title": "record.lineno",
      "description": "Logging record lineno attribute",
      "type": "integer"
    },
    "msg": {
      "title": "record.msg % record.args",
      "description": "Logging record msg attribute formatted with args attribute",
      "type": "string"
    },
    "func": {
      "title": "record.func",
      "description": "Logging record func attribute",
      "type": "string"
    },
    "sinfo": {
      "title": "record.sinfo",
      "description": "Logging record sinfo attribute (null if Python 2)",
      "$oneOf": [
         {"type": "string"},
         {"type": "null"}
      ]
    },
    "exc_info": {
      "title": "record.exc_info",
      "description": "Logging record exc_info attribute",
      "$oneOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "title": "record.exc_info[0].__name__",
              "description": "Exception's name",
              "type": "string"
            },
            "msg": {
              "title": "str(record.exc_info[1])",
              "description": "Exception's value",
              "type": "string"
            },
            "traceback": {
              "title": "''.join(traceback.format_tb(record.exc_info[2]))",
              "description": "Exception's traceback",
              "type": "string"
            }
          }
        },
        {"type": "null"}
      ]
    }
  }
}

Code generation

Based on the library Grako, parser code generation from BNF is provided with:

from link.utils.grammar import codegenerator


with open('grammar.bnf') as f:
    module = codegenerator('mydsl', 'MyDSL', f.read())

parser = module.MyDSLParser()

with open('mycode') as f:
    # 'start' is the default rule name used to start parsing
    model = parser.parse(f.read(), rule_name='start')

When using the NodeWalker class to traverse the model (built with the ModelBuilderSemantics class), those two functions are usefull:

from link.utils.grammar import codegenerator, adopt_children, find_ancestor
from grako.model import ModelBuilderSemantics


with open('grammar.bnf') as f:
    module = codegenerator('mydsl', 'MyDSL', f.read())

parser = module.MyDSLParser(semantics)

with open('mycode') as f:
    # 'start' is the default rule name used to start parsing
    model = parser.parse(f.read(), rule_name='start')

Use this before calling the NodeWalker in order to have nodes parent member set:

adopt_children(model._ast, parent=model)

When traversing the model, you may need to get informations about a parent node:

pnode = find_ancestor(node, ‘ParentNode’)

if pnode is not None:
# parent node was found