CLPython is developed with portability in mind. The latest status of the supported Common Lisp implementations is as follows:
Your help is appreciated in making CLPython work on even more implementations, see Task: Porting CLPython.
[back to top] 1.2 Library dependenciesCLPython depends on the following libraries:
The libraries above are all dependencies to run CLPython in Allegro CL. But in other Lisp implementations the following libraries are also required:
CLPython is loaded with ASDF:
(asdf:operate 'asdf:load-op :clpython)
If this leads to an error that the
"clpython component can not be found"
, it might be possible to continue by loading the system definition file manually:
(load "clpython.asd")
(asdf:operate 'asdf:load-op :clpython)
Upon loading of CLPython a "quick start guide" is printed. It shows a few ways in which Python source code can be evaluated:
CLPython quick start guide: Run a string of Python code: (clpython:run "for i in range(4): print i") Run a Python file: (clpython:run #p"~/example/foo.py") Start the Python "interpreter" (REPL): (clpython.app.repl:repl) To start mixed Python/Lisp input mode: (clpython:enter-mixed-lisp-python-syntax) Run the test suite: (asdf:operate 'asdf:test-op :clpython)[back to top] 1.4 Running the test suite
The test suite can be run to verify that CLPython runs fine in your Lisp implementation. It generates a lot of output followed by the number of test successes and failures. There can be errors, but they should all be marked "known failures".
[back to top] 2. Running Python code 2.1 A note on completeness(asdf:operate 'asdf:test-op :clpython)
... a lot of output ...
End CLPython test
Errors detected in this test: 4 (all known failures)
Successes this test: 1305
While many built-in Python classes, functions, and modules are implemented in CLPython, there is also a lot of functionality not available yet. Running valid Python code in CLPython will often fail due to missing functionality. Please see Completeness and compatibility for the details. Of course it would be appreciated if you would contribute missing functionality.
[back to top] 2.2 Evaluating a Python expressionThe simplest way to execute a string of Python code is by calling generic function
runon it:
(clpython:run "2 + 3")
5
As for the return value of run: this depends on whether the source code is a statement or an expression. Statements are used for definitions and control flow, like: if
, def
, class
, try/except
, import
. Statements have expressions inside them. Expressions are forms that evaluate to a value, like: a+b
, [a,b]
, f(1,2,3)
.
If the source supplied to run is an expression then its value is returned, like 5
in the example above. In case of statements the return value is an undefined implementation detail. Below is an example of running a statement:
(run "for i in range(4): print i")
0
1
2
3
nil
(Please see Mixed-syntax Lisp/Python mode on how to run Python code directly in the Lisp listener, as alternative to using run.)
[back to top] 2.3 Executing a Python moduleGiven a file foo.py
with this contents:
def f():
print 'hello world'
f()
It can be executed using run passing the filename as absolute or relative pathname:
(run #p"foo.py")
hello world
None
To load a "package", which is basically a directory containing an __init__.py
file, load that file:
(run #p"/tmp/zut/__init__.py")
Relative filenames will be looked up in the directories in *clpython-module-search-paths* and the Python value sys.path
.
Filenames should always be passed as pathnames, not strings, because strings are treated directly as source code (see § 2.2) leading to errors like:
(run "foo.py")[back to top] 2.4 Python runtime environments
NameError: Variable `foo' is unbound.
By default every time a Python expression is executed with run a fresh, empty execution environment is used. Referring to a variable bound in a previous call won't work, for example:
(run "a = 3")
(run "print a")
Error: NameError: Variable `a' is unbound.
This can be made to work if the calls share the execution environment, called habitat. If *habitat* is bound, it is used as execution environment, otherwise a new habitat is created and used:
(setf *habitat* (make-habitat))
#<habitat stdin=nil stdout=nil stderr=nil #loaded-modules=0 cmd-line-args=nil search-paths=#(".") @ #x2292eb52>
(run "a = 3") ;; stores the variable binding in *habitat*
(run "print a") ;; looks up the variable in *habitat*
3
Moreover you can use different habitats concurrently, and explicitly pass the one to use to run:
(defvar *other-habitat* (make-habitat))
*other-habitat*
(run "a = 4" :habitat *other-habitat*)
(run "print a" :habitat *other-habitat*)
4
And back to the orignal environment:
(run "print a" :habitat *habitat*)
3
(run "print a")[back to top] 2.5 Compiling versus interpreting
3
Function run takes the keyword argument compile
which determines whether the source should be compiled before being run. This options is only relevant if the Lisp implementation has both an interpreter and compiler, which is the case in e.g. LispWorks and Allegro CL. In compiler-only implementations like SBCL this option has no effect.
This option is in particular relevant when running benchmarks:
(run #p"/usr/lib/python2.6/test/pystone.py" <b>:compile nil</b> :args '("pystone.py" "3000"))
Pystone(1.1) time for 3000 passes = 26.709991
This machine benchmarks at 112.31752 pystones/second
None
(run #p"/usr/lib/python2.6/test/pystone.py" <b>:compile t</b> :args '("pystone.py" "3000"))[back to top] 2.6 Python interpreter
Pystone(1.1) time for 3000 passes = 0.11001587
This machine benchmarks at 27268.793 pystones/second
None
A common way to evaluate Python expressions is by typing them into an "interpreter" or "read-eval-print loop" (REPL). CLPython comes with a REPL that acts like the regular Python interpreter:
(clpython:repl)
Welcome to CLPython, an implementation of Python in Common Lisp.
Running on: International Allegro CL Free Express Edition 8.2 [Windows] (May 16, 2010 12:01)
REPL shortcuts: `:q' = quit, `:h' = help.
Using 1 default module search paths set in *clpython-module-search-paths*
>>> 1 + 23 24 >>> print 'hello Lisp world!' hello Lisp world! >>>
For more information, please see the output of typing :h
in the repl, Handling of errors, and the documentation for function repl.
This interpreter is useful for evaluating Python code, but not so practical when it comes to mixing Python and Lisp code. Use the more advanced mixed-mode interpreter for that instead .
[back to top] 2.7 Mixed-syntax Lisp/Python modeCLPython is able to turn a regular Lisp listener (REPL) into a "mixed-mode" listener that supports both Lisp and Python source as input:
clpython(213):(clpython:enter-mixed-lisp-python-syntax)
; The mixed Lisp/Python syntax mode is now enabled;
; Lispy *readtable* is now set.
clpython(214):print 123 * 2
246
clpython(215):range(100)[98:2:-2]
#(98 96 94 92 90 88 86 84 82 80 ...)
clpython(216):(+ 1 2)
3
It supports multi-line Python statements as long as the next lines are properly indented:
clpython(70): for i in range(4): print i, print i*2
0 0 1 2 2 4 3 6
See the documentation for macro enter-mixed-lisp-python-syntax for all "mixed-mode" options, and also see Handling of errors.
This mode can also be enabled in Lisp source files:
TODO
You may find this mixed-mode handy for Python development using the Slime IDE.
[back to top] 2.8 Handling of errorsIn case an unhandled error happens during evaluation of Python code, the behaviour of CLPython is different from standard Python. You may find yourself suddenly in the Lisp debugger without a clear idea of what is happening or how to continue.
In standard Python, when an unhandled error occurs the error message is printed and the stack is unwound:
>>> 123 + 4/0
Traceback (most recent call last):
File "<stdin>", line 1, in
ZeroDivisionError: integer division or modulo by zero
>>>
CLPython implements Python exceptions as Common Lisp errors (see Python object representation). The Common Lisp way to deal with errors is:
The same error in the CLPython REPL:
>>> 123 + 4/0
ZeroDivisionError: Attempt to divide 4 by zero.Restart actions (select using :continue):
0: Use another divisor instead of zero
1: Retry evaluating this top-level form in module `__main__'
2: Skip this top-level form in module `__main__'
3: Retry the expression: "123 + 4/0" (:re).
4: Return to Python top level (:pt).
5: Return to Top Level (an "abort" restart).
6: Abort entirely from this (lisp) process.
[1] cl-user(30):
The execution is now paused, not aborted. The most interesting restarts in the list are:
0: Use another divisor instead of zero
This allows you to specific another divisor value to be used in place of 0
. The calculation will continue with the provided value:
[1] cl-user(30): :continue 0
New divisor value: 2
125
Here the calculation proceeded as if it were: 123 + 4/2 = 125
.
3: Retry the expression: "123 + 4/0" (:re).
For this calculation a retry does not make sense, as 4/0 will always fail. But in other situations a retry might make sense. For example if an attempt is made to read a non-existent file, the file could now be created and then the file operation can be retried.
[1] cl-user(30): :continue 3
ZeroDivisionError: Attempt to divide 4 by zero.Restart actions (select using :continue):
0: Use another divisor instead of zero
1: Retry evaluating this top-level form in module `__main__'
2: Skip this top-level form in module `__main__'
3: Retry the expression: "123 + 4/0" (:re).
4: Return to Python top level (:pt).
5: Return to Top Level (an "abort" restart).
6: Abort entirely from this (lisp) process.
[1] cl-user(31):
4: Return to Python top-level
This will abort the evaluation of the current input, then unwind the stack down to the top-level of the Python interpreter. This option implements what the standard Python interpreter does:
[1] cl-user(30): :continue 4
>>>
5: Return to Top Level
This will unwind the stack even further than the previous option does, down to the Lisp listener. The Python interpreter will be aborted along the way:
[1] cl-user(30): :continue 5
cl-user(31):
The exact way to invoke a restart is implementation-dependent:
The examples above show the interaction in Allegro CL.
Instead of :continue number
also the abbreviation :cont number
can be used.
Two handy restarts in the CLPython REPL have their own shortcut: :re
will invoke: Retry the expression
:pt
will invoke: Return to Python top level
A restart is invoked by typing its number, e.g. 3
A restart is invoked by typing c
("c" followed by space) and its number, e.g. c 3
A restart is invoked by typing r
and its number, e.g. r3
A restart is invoked by typing c
("c" followed by space) and its number, e.g. c 3
A restart is invoked by typing its number, e.g. 3
In the Mixed-syntax Lisp/Python mode you can access Python values (like global variables, functions, and modules) from Lisp directly:
clpython(70): def py(n): print "python got: %s" % n
#<python-function py (interpreted) (__main__/py) @ #x218e6b52>
clpython(71): (py (+ 1 2))
python got: 3
For Python expressions that are not a simple identifier there is the ~
(tilde) reader macro. Think of the tilde as a snake! It works for attribute lookup:
clpython(72): import math
#<The clpython.module.math package>
clpython(73): (list cl:pi ~math.pi)
(3.141592653589793d0 3.141592653589793d0)
And it works for object subscription:
clpython(74): x = [1,2,3]
#(1 2 3)
clpython(75): (list ~x[1])[back to top] 4.2 Python class instantiating
(2)
Section § 4.1 showed how to access Python values including class objects. Python classes are instantiated using py-call:
clpython(76): class C: pass
#<class C @ #x21eca942>
clpython(77): (setq x (py-call ~C))
#<C @ #x21f1d9e2>
clpython(78): x.a = 3
clpython(79): print x.a[back to top] 9. Compiler implementation details 9.1 Python object representation
3
Python objects are represented by an equivalent Lisp value where possible, and as CLOS instances otherwise:
Python data type Representation in CLPython Class CLOS class Instance of user-defined class CLOS instance Exception Condition Function Function Generator function Closure Dict Hashtable (Unicode) String Unicode string List Adjustable vector Tuple Consed list Long, Integer, Boolean Integer Float Double-float Complex ComplexCLPython first translates Python code into an abstract syntax tree (AST), and then translates the AST into Lisp code. A lot of work is carried out by macros. Example:
if 4 > 3: print 'y' else: print 'n')
This is parse into the following abstract syntax tree:
clpython(1): (defvar *source* " if 4 > 3: print 'y' else: print 'n'")
*source*
clpython(2): (defvar *ast* (parse *source*))
*ast*
clpython(3): (with-ast-user-pprinter () (prin1 *ast*) (values))
([module-stmt] ([suite-stmt] (([if-stmt] ((([comparison-expr] [>] ([literal-expr] :number 4) ([literal-expr] :number 3)) ([suite-stmt] (([print-stmt] nil (([literal-expr] :string "y")) nil))))) ([suite-stmt] (([print-stmt] nil (([literal-expr] :string "n")) nil)))))))
Here
[if-stmt]
is a macro that expands a Python
if
statement into a Lisp
cond
form:
(with-ast-user-pprinter () (prin1 (macroexpand-1 '([if-stmt] ((([comparison-expr] [>] ([literal-expr] :number 4) ([literal-expr] :number 3)) ([suite-stmt] (([print-stmt] nil (([literal-expr] :string "y")) nil))))) ([suite-stmt] (([print-stmt] nil (([literal-expr] :string "n")) nil)))))) (values))
(cond ((clpython::py-val->lisp-bool ([comparison-expr] [>] ([literal-expr] :number 4) ([literal-expr] :number 3))) ([suite-stmt] (([print-stmt] nil (([literal-expr] :string "y")) nil)))) (t ([suite-stmt] (([print-stmt] nil (([literal-expr] :string "n")) nil)))))
The expansion of [if-stmt]
thus contains a call to function py-val->lisp-bool. Expansion of the [comparison-expr]
form includes a call to py->, and the expansion of [print-stmt]
calls py-print.
These functions implement the Python semantics for truth, comparison and printing, which can be hairy at times. They are part of the CLPython runtime. The generated Lisp code is thus not self-contained and independent, but requires CLPython to be loaded every time the code is executed.
[back to top]RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4