Extract, resolve and replace [SIGILS] embedded in text.

## Project description

A sigil is a token embedded in text, used as a magical placeholder for a future value. When resolved, the actual value will be extracted from an user-provided or global context.

This library contains tools to extract, replace and resolve such sigils.

## Installing

Install and update using pip:

pip install -U sigils


## Structure of Sigils

A typical sigil has one of the following forms:

[USERNAME]
[SETTINGS.BASE_DIR]
[MODEL='natural-key'.FIELD]


Each sigil is a list of nodes separated by a dot. Nodes can be standalone or parametrized. If parametrized, they can take one argument by using the equals sign. The argument can be a number, a single-quoted string, or another sigil.

[NODE1='ARG1'.NODE2=[ARG2].NODE3=10.ETC]


Sigils used as arguments can be nested to any depth.

## Resolving Sigils

The resolve function will replace any sigils found in a string, given a context:

from sigils import resolve

text = "[USERNAME]: The BASE_DIR is [SETTINGS.BASE_DIR]."
context = {
"SETTINGS": {"BASE_DIR": "/home/arth/webapp"},
}
result = list(resolve(text, context))
assert result == "arthexis: The BASE_DIR is /home/arth/webapp"


Note that resolve returns a generator, so you will need to pass it to list or some other kind os sequence, or iterate it in a loop or comprehension.

All keys in the context mapping should be strings. Values can be anything, but usually it will be a string, another dict, a callable or an instance with public fields:

class Model:
owner = "arthexis"
# Valid tokens
context = {
"SETTINGS": {"NAME": "webapp"},  # [SETTINGS.NAME]
"MODEL": Model,                  # [MODEL.OWNER]
"UPPER": lambda x: x.upper(),    # [UPPER='text']
"RNG": lambda _: randint(),      # [RNG]
}


Instead of passing the context explicitly, a global default context can be set to be used by all calls to resolve, you can see an example in the Django integration below.

## Django Integration

You can create a simple tag to resolve sigils in templates. Create <your_app>/templatetags/sigils.py with the following code:

import sigils
from django import templates

register = template.Library()

@register.simple_tag
def resolve(text, **context):
return sigils.resolve(text, context)


In app.py add the following to register a model in the global context (rename MyModel to the name of your model class):

import sigils
from django.apps import AppConfig

class MyAppConfig(AppConfig):
from .models import MyModel

def my_model_lookup(parent, slug):
if not parent:
return MyModel.objects.filter(slug=slug)
return parent.my_models.get(slug=slug)

sigils.set_context("MyModel", my_model_lookup)


You can change the lambda to make your model searchable with a different argument or manager, here the primary key is used.

Then you can use something like this in your template:

{% load sigils %}
Some stuff: {% sigil '[MyModel=[obj.slug].some_field]' obj=foo %}


## Dependencies

• lark: Allows us to parse arbitrarily complex sigils fast.

## Project details

Uploaded source