Allow inline Python
This commit is contained in:
parent
80771ac99c
commit
8351ccf9d9
2
NEWS.rst
2
NEWS.rst
@ -13,6 +13,8 @@ Removals
|
||||
|
||||
New Features
|
||||
------------------------------
|
||||
* Added special forms ``py`` to ``pys`` that allow Hy programs to include
|
||||
inline Python code.
|
||||
* All augmented assignment operators (except `%=` and `^=`) now allow
|
||||
more than two arguments.
|
||||
|
||||
|
@ -1406,6 +1406,44 @@ parameter will be returned.
|
||||
True
|
||||
|
||||
|
||||
.. _py-specialform:
|
||||
|
||||
py
|
||||
--
|
||||
|
||||
``py`` parses the given Python code at compile-time and inserts the result into
|
||||
the generated abstract syntax tree. Thus, you can mix Python code into a Hy
|
||||
program. Only a Python expression is allowed, not statements; use
|
||||
:ref:`pys-specialform` if you want to use Python statements. The value of the
|
||||
expression is returned from the ``py`` form. ::
|
||||
|
||||
(print "A result from Python:" (py "'hello' + 'world'"))
|
||||
|
||||
The code must be given as a single string literal, but you can still use
|
||||
macros, :ref:`eval`, and related tools to construct the ``py`` form. If you
|
||||
want to evaluate some Python code that's only defined at run-time, try the
|
||||
standard Python function :func:`eval`.
|
||||
|
||||
Python code need not syntactically round-trip if you use ``hy2py`` on a Hy
|
||||
program that uses ``py`` or ``pys``. For example, comments will be removed.
|
||||
|
||||
|
||||
.. _pys-specialform:
|
||||
|
||||
pys
|
||||
---
|
||||
|
||||
As :ref:`py-specialform`, but the code can consist of zero or more statements,
|
||||
including compound statements such as ``for`` and ``def``. ``pys`` always
|
||||
returns ``None``. Also, the code string is dedented with
|
||||
:func:`textwrap.dedent` before parsing, which allows you to intend the code to
|
||||
match the surrounding Hy code, but significant leading whitespace in embedded
|
||||
string literals will be removed. ::
|
||||
|
||||
(pys "myvar = 5")
|
||||
(print "myvar is" myvar)
|
||||
|
||||
|
||||
.. _quasiquote:
|
||||
|
||||
quasiquote
|
||||
|
@ -19,9 +19,11 @@ Hy and Python. For example, Python's ``str.format_map`` can be written
|
||||
Using Python from Hy
|
||||
====================
|
||||
|
||||
Using Python from Hy is nice and easy, you just have to :ref:`import` it.
|
||||
You can embed Python code directly into a Hy program with the special operators
|
||||
:ref:`py-specialform` and :ref:`pys-specialform`.
|
||||
|
||||
If you have the following in ``greetings.py`` in Python::
|
||||
Using a Python module from Hy is nice and easy: you just have to :ref:`import`
|
||||
it. If you have the following in ``greetings.py`` in Python::
|
||||
|
||||
def greet(name):
|
||||
print("hello," name)
|
||||
|
@ -81,8 +81,8 @@ The Hy compiler works by reading Hy source code into Hy model objects and
|
||||
compiling the Hy model objects into Python abstract syntax tree (:py:mod:`ast`)
|
||||
objects. Python AST objects can then be compiled and run by Python itself,
|
||||
byte-compiled for faster execution later, or rendered into Python source code.
|
||||
You can even :ref:`mix Python and Hy code in the same project <interop>`, which
|
||||
can be a good way to get your feet wet in Hy.
|
||||
You can even :ref:`mix Python and Hy code in the same project, or even the same
|
||||
file,<interop>` which can be a good way to get your feet wet in Hy.
|
||||
|
||||
|
||||
Hy versus other Lisps
|
||||
|
@ -20,6 +20,7 @@ from hy.macros import require, load_macros, macroexpand, tag_macroexpand
|
||||
import hy.core
|
||||
|
||||
import re
|
||||
import textwrap
|
||||
import pkgutil
|
||||
import traceback
|
||||
import importlib
|
||||
@ -1589,6 +1590,22 @@ class HyASTCompiler(object):
|
||||
if ast_str(root) == "eval_and_compile"
|
||||
else Result())
|
||||
|
||||
@special(["py", "pys"], [STR])
|
||||
def compile_inline_python(self, expr, root, code):
|
||||
exec_mode = root == HySymbol("pys")
|
||||
|
||||
try:
|
||||
o = ast.parse(
|
||||
textwrap.dedent(code) if exec_mode else code,
|
||||
self.filename,
|
||||
'exec' if exec_mode else 'eval').body
|
||||
except (SyntaxError, ValueError if PY36 else TypeError) as e:
|
||||
raise self._syntax_error(
|
||||
expr,
|
||||
"Python parse error in '{}': {}".format(root, e))
|
||||
|
||||
return Result(stmts=o) if exec_mode else o
|
||||
|
||||
@builds_model(HyExpression)
|
||||
def compile_expression(self, expr):
|
||||
# Perform macro expansions
|
||||
|
@ -639,3 +639,10 @@ def test_futures_imports():
|
||||
|
||||
assert hy_ast.body[0].module == '__future__'
|
||||
assert hy_ast.body[1].module == 'hy.core.language'
|
||||
|
||||
|
||||
def test_inline_python():
|
||||
can_compile('(py "1 + 1")')
|
||||
cant_compile('(py "1 +")')
|
||||
can_compile('(pys "if 1:\n 2")')
|
||||
cant_compile('(pys "if 1\n 2")')
|
||||
|
@ -156,3 +156,14 @@ Call me Ishmael. Some years ago—never mind how long precisely—having little
|
||||
(with [c1 (closing (Closeable)) c2 (closing (Closeable))]
|
||||
(setv c1.x "v1")
|
||||
(setv c2.x "v2"))
|
||||
(setv closed1 (.copy closed))
|
||||
|
||||
(pys "
|
||||
closed = []
|
||||
pys_accum = []
|
||||
for i in range(5):
|
||||
with closing(Closeable()) as o:
|
||||
class C: pass
|
||||
o.x = C()
|
||||
pys_accum.append(i)")
|
||||
(setv py-accum (py "''.join(map(str, pys_accum))"))
|
||||
|
@ -120,4 +120,10 @@ def assert_stuff(m):
|
||||
assert issubclass(m.C2, m.C1)
|
||||
assert (m.C2.attr1, m.C2.attr2) == (5, 6)
|
||||
|
||||
assert m.closed == ["v2", "v1"]
|
||||
assert m.closed1 == ["v2", "v1"]
|
||||
|
||||
assert len(m.closed) == 5
|
||||
for a, b in itertools.combinations(m.closed, 2):
|
||||
assert type(a) is not type(b)
|
||||
assert m.pys_accum == [0, 1, 2, 3, 4]
|
||||
assert m.py_accum == "01234"
|
||||
|
Loading…
Reference in New Issue
Block a user