Skip to content

Extending the Engine

Alex Grönholm edited this page May 18, 2016 · 5 revisions

Currently there is a single published API.

Plugin Registration

Extensions to cinje functionality are registered using the Python standard entry_points mechanism using the cinje.translator namespace. Within your setup.py file you register plugins by writing something similar to the following:

setup(
	name = "myproject",
	# ...
	
	entry_points = {'cinje.translator': [
			'mytag = myproject.tag:MyTag'
		]}
)

The entry point must refer to the class used as the code transformer. Cinje, when transforming template source into Python source, will automatically discover and utilize any plugins declared in this way.

It is important to note that pages are not "re-compiled" if they have a current and valid byte code cache file (matching .pyc or .pyo). Because of this, newly registered extensions won't be automatically utilized by previously compiled templates. This is a design feature; it armours production code (or third-party templates) from changes within your own application as a side-effect of highly performant caching. If ever you notice your directive wasn't used after adding, remember to clear byte code caches.

Line Transformation

The basis for the cinje engine is the act of transforming lines from the cinje domain-specific-language into standard Python code. The API for an inline transformer is relatively simple:

class MagicWord:
	"""Do something neat when someone uses the magic word."""
	
	priority = 0  # Transformers are sorted on this attribute.
	
	def match(self, context, line):
		"""Are we to handle this line?"""
		return line.kind == 'code' and line.stripped == "xyzzy"
	
	def __call__(self, context):
		"""Stream transform input lines into Python source."""
		
		declaration = context.input.next()
				
		yield declaration.clone(line="# Ssh, that's a secret.")

There's a few things going on here. Each translator has a priority; because of the way things are sorted, higher priorities are actually lower numbers. The built-in translators offer a spread of priorities between -100 and 100, allowing you to easily inject your own transformers in such a way as to override any of the built-in ones. The match method is used, passing in the current cinje.util.Context instance and cinje.util.Line instance, to evaluate if your plugin can process that line. If a truthy value is returned, the translator is then called (executing the __call__ method). Any state should be stored against the context; as this code is run at import time use of global state is extremely dangerous.

If the translator matches, it's the job of the __call__ method to accept lines from context.input and yield Line instances of output.

Clone this wiki locally