Skip to main content

cl4py - Common Lisp for Python

The library cl4py (pronounce as clappy) allows Python programs to call Common Lisp libraries. Its official mascot is the cl4py-bird:

./cl4py.png

Motivation

You are a Python programmer, but you want access to some of the powerful features of Lisp, for example to compile code at run time? Or you want to use some awesome Lisp libraries? Or you are a Lisp programmer and want to show your work to your Python friends. In all these cases, cl4py is here to help you.

Tutorial

You can start any number of Lisp subprocesses within Python, like this:

>>> import cl4py
>>> lisp = cl4py.Lisp()

Of course, this requires you have some Lisp installed. If not, use something like apt install sbcl, pacman -S sbcl or brew install sbcl to correct this deficiency. Once you have a running Lisp process, you can execute Lisp code on it:

# In Lisp, numbers evaluate to themselves.
>>> lisp.eval( 42 )
42

# ('+', 2, 3) is a short notation for cl4py.List(cl4py.Symbol('+'), 2, 3).
# For convenience, whenever a Python tuple is converted to Lisp
# data, any strings therein are automatically converted to Lisp symbols.
>>> lisp.eval( ('+', 2, 3) )
5

# Nested expressions are allowed, too.
>>> lisp.eval( ('/', ('*', 3, 5), 2) )
Fraction(15, 2)

# Use cl4py.List instead of tuples to avoid the automatic conversion of
# strings to symbols.
>>> lisp.eval( cl4py.List(cl4py.Symbol('STRING='), 'foo', 'bar') )
()
>>> lisp.eval( cl4py.List(cl4py.Symbol('STRING='), 'foo', 'foo') )
True

# Here is how you can lookup a symbol's value:
>>> lisp.eval(cl4py.Symbol('*PRINT-BASE*', 'COMMON-LISP'))
10

# Of course you can also use Lisp macros:
>>> lisp.eval( ('loop', 'for', 'i', 'below', 5, 'collect', 'i') )
List(0, 1, 2, 3, 4)

>>> lisp.eval( ('with-output-to-string', ('stream',),
                  ('princ', 12, 'stream'),
                  ('princ', 34, 'stream')) )
'1234'

A cl4py.Lisp object not only provides eval, but also methods for looking up functions and packages:

>>> add = lisp.function('+')
>>> add(1, 2, 3, 4)
10

>>> div = lisp.function('/')
>>> div(2, 4)
Fraction(1, 2)

# Lisp packages are automatically converted to Python modules.
>>> cl = lisp.find_package('CL')
>>> cl.oddp(5)
True

>>> cl.cons(5, None)
List(5)

>>> cl.remove(5, [1, -5, 2, 7, 5, 9], key=cl.abs)
[1, 2, 7, 9]

# Higher-order functions work, too!
>>> cl.mapcar(cl.constantly(4), (1, 2, 3))
List(4, 4, 4)

# cl4py even supports macros and special forms as a thin
# wrapper around lisp.eval.
>>> cl.loop('repeat', 5, 'collect', 42)
List(42, 42, 42, 42, 42)

>>> cl.progn(5, 6, 7, ('+', 4, 4))
8

When converting Common Lisp packages to Python modules, we run into the problem that not every Common Lisp symbol name is a valid Python identifier. As a remedy, so we attempt to substitute problematic characters and symbols with something that Python can digest. Here you can see this substitution rules in action:

# hyphens are turned into underscores
>>> cl.type_of("foo")
List(Symbol("SIMPLE-ARRAY", "COMMON-LISP"), Symbol("CHARACTER", "COMMON-LISP"), List(3))

# The functions +, -, *, /, 1+, and 1- are renamed to add, sub,
# mul, div, inc, and dec, respectively.
>>> cl.add(2,3,4,5)
14

# Within a string, occurrences of -, *, +, <=, <, =, /=, >=, gt, and ~,
# are replaced by _, O, X, le, lt, sim, ne, ge, ge, gt, and tilde, respectively.
>>> cl.stringgt('baz', 'bar')
2

# Earmuffs are stripped
>>> cl.print_base
10

# Constants are capitalized
>>> cl.MOST_POSITIVE_DOUBLE_FLOAT
1.7976931348623157e+308

The cl4py module provides a Cons class that mimics cons cells in Lisp.

>>> lisp.eval( ('CONS', 1, 2) )
Cons(1, 2)

>>> lst = lisp.eval( ('CONS', 1, ('CONS', 2, () )) )
List(1, 2)
>>> lst.car
1
>>> lst.cdr
List(2) # an abbreviation for Cons(2, ())

# cl4py Conses are iterable!
>>> list(lst)
[1, 2]
>>> sum(lst)
3

# cl4py also supports dotted and circular lists.
>>> lisp.eval( ('CONS', 1, ('CONS', 2, 3 )) )
DottedList(1, 2, 3)

>>> twos = cl.cons(2,2)
>>> twos.cdr = twos
>>> twos
DottedList(2, ...)

>>> cl.mapcar(lisp.function('+'), (1, 2, 3, 4), twos)
List(3, 4, 5, 6)

Frequently Asked Problems

Why does my Lisp subprocess complain about Package QL does not exist.

By default, cl4py starts a Lisp subprocess with sbcl --script. This means, that the Lisp process will ignore any user initialization files, including the Quicklisp setup. However, we provide an extra option for installing and loading Quicklisp automatically: quicklisp=True

>>> lisp = cl4py.Lisp(quicklisp=True);
>>> ql = lisp.find_package('QL')
>>> ql.quickload('YOUR-SYSTEM')

Metadata

Release files for cl4py 1.8.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cl4py 1.8.2
File Size Uploaded Provenance Actions
cl4py-1.8.2.tar.gz 27.2 kB Without provenance Details for cl4py-1.8.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cl4py 1.8.2
File Interpreter ABI Platform Actions
cl4py-1.8.2-py3-none-any.whl Python 3 none any Details for cl4py-1.8.2-py3-none-any.whl

Total release size: 51.3 kB

Release files / cl4py-1.8.2.tar.gz

Download URL cl4py-1.8.2.tar.gz
Size 27.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e202c6bab24e1b5390e37e52c897c0186251f1a341bfb1a04b05b58ffb791d90
BLAKE2b-256 checksum
How to use checksums
6c5f8018862022f66c88676c8a1f21c26bb3cc6a6a6e9a77f878cd13bbb292d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.0.1 CPython/3.10.15

Release files / cl4py-1.8.2-py3-none-any.whl

Download URL cl4py-1.8.2-py3-none-any.whl
Size 24.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c5e6797b374647b249d730ee8c44ee7e78aa9101aa0fd90529a4e427e539439
BLAKE2b-256 checksum
How to use checksums
653083f1f82dbabfad0fb66b1437c61abd478440a3c5f717be376d5940cb9cca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.0.1 CPython/3.10.15

Security for cl4py 1.8.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.8.2 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page