Manipulating Elisp code in Python

The emacs.elisp module contains utilities for building and manipulating Emacs Lisp (Elisp) expressions in Python. These can then be passed to an EmacsBatch or EmacsClient instance to be executed.

Expr Objects

Elisp expressions are represented by subtypes of the Expr abstract base class:

  • Literal(value) wraps Python ints, strs, and floats.

  • Symbol(name: str) represents a symbol.

  • Cons(car: Expr, cdr: Expr) represents a cons cell.

  • List(items: Iterable[Expr]) represents a list.

  • Quote(expr: Expr) represents a quoted expression.

  • Raw(src: str) can be used to wrap a raw Elisp code string.

Generally you should use the functions detailed in the following section to build expressions rather than instantiating them directly.

You can use str(expr) to produce (hopefully) syntactically-correct Elisp code.

Building Elisp expressions

The to_elisp() function can be used to convert various Python values to Elisp expressions. Elements of composite data types (lists, tuples, dicts) are converted recursively. Most parts of this package’s API will use to_elisp() to convert arguments that are not already instances of Expr, so it is often not necessary to use it directly.

Basic data types

to_elisp() converts numbers and strings to literals and bools and None to the correct symbols:

>>> import emacs.elisp as el

>>> el.to_elisp(123)
<el 123>

>>> el.to_elisp(1.23)
<el 1.23>

>>> el.to_elisp('foo')
<el "foo">

>>> el.to_elisp(True)
<el t>

>>> el.to_elisp(False)
<el nil>

>>> el.to_elisp(None)
<el nil>

The nill and t symbols are also available as nil and el_true.

Symbols

Create a symbol with the symbol() function:

>>> el.symbol('foo')
<el foo>

The symbols() function can be used to create a list of symbols:

>>> el.symbols('a', 'b', 'c')
<el (a b c)>

Lists

el_list() converts any iterable to a list expression:

>>> el.el_list(range(1, 5))
<el (1 2 3 4)>

to_elisp() converts Python lists to quoted Elisp lists, while tuples are left unquoted:

>>> el.to_elisp([1, 2, 3])
<el '(1 2 3)>

>>> el.to_elisp(('a', 'b', 'c'))
<el ("a" "b" "c")>

Function calls

Function call expressions can be created with funccall(), or by calling a Symbol instance. Keyword arguments are converted to kebab-case and prefixed with a “:” character.

>>> el.funccall('+', 1, 2)
<el (+ 1 2)>

>>> foo = el.symbol('foo')
>>> foo(el.symbol('x'), el.symbol('y'), kw_arg=123)
<el (foo x y :kw-arg 123)>

Quoting

The quote() method produces a quoted version of an expression:

>>> s = el.symbol('foo')
>>> s.quote()
<el 'foo>

>>> el.symbols('a', 'b', 'c').quote()
<el '(a b c)>

The q property acts as a shortcut:

>>> s.q
<el 'foo>

Cons cells

An expression that must be constructed directly because it has no Python equivalent is the cons cell, represented with the class Cons:

>>> c = el.cons(el.symbol('a'), 1)
>>> c
<el (cons a 1)>

>>> c.q
<el '(a . 1)>

Mapping formats (alists and plists)

You can use make_alist() or make_plist() to convert mapping types like dicts to their Elisp equivalents. These functions will always treat string keys as symbols:

>>> el.make_alist({'a': 1, 'b': 2}).q
<el '((a . 1) (b . 2))>

>>> el.make_plist({':x': 1, ':y': 2}).q
<el '(:x 1 :y 2)>

to_elisp() converts mapping types like dicts to plists or alists, depending on the value of the dict_format argument (defaults to "alist".

Raw code strings

Finally, use Raw to wrap a raw Elisp code string to be inserted verbatim in the given location:

>>> el.Raw('(print "hi")')
<el (print "hi")>

>>> el.el_list([1, 2, el.Raw('(+ a b)')])
<el (1 2 (+ a b))>

Elisp DSL

This package also includes an unholy abomination of a DSL that lets you write Elisp code in Python. It is implemented through the singleton object emacs.elisp.E.

Calling the singleton as a function converts a Python object into an Elisp object using to_elisp():

>>> from emacs.elisp import E
>>> E(3)
<el 3>

>>> E('foo')
<el "foo">

>>> E(['a', 'b', 'c'])
<el '("a" "b" "c")>

Attribute access produces Elisp symbols, converting snake_case to kebab-case. The same can be done by indexing with a string (without the case conversion):

>>> E.abc
<el abc>

>>> E.foo_bar
<el foo-bar>

>>> E[':baz']
<el :baz>

Symbols can be called as functions, generating Elisp function calls:

>>> E.message("Hello from %s", E('python-emacs'))
<el (message "Hello from %s" "python-emacs")>

>>> E['='](E.a, E.b)
<el (= a b)>

Additionally, the C, S, and R methods are aliases for cons, symbols(), and Raw, respectively.

Using just the E object, it is possible to write complex Elisp expressions:

>>> E.defun(E.my_elisp_function, E.S('a', 'b'),
...   E.message("I am a crime against God."),
...   E['+'](E.a, E.b))
<el (defun my-elisp-function (a b) (message "I am a crime against God.") (+ a b))>