Merge pull request #1817 from Kodiologist/inline-python
Allow inline Python
This commit is contained in:
commit
84d1a116f6
2
NEWS.rst
2
NEWS.rst
@ -13,6 +13,8 @@ Removals
|
|||||||
|
|
||||||
New Features
|
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
|
* All augmented assignment operators (except `%=` and `^=`) now allow
|
||||||
more than two arguments.
|
more than two arguments.
|
||||||
|
|
||||||
|
@ -1406,16 +1406,42 @@ parameter will be returned.
|
|||||||
True
|
True
|
||||||
|
|
||||||
|
|
||||||
print
|
.. _py-specialform:
|
||||||
-----
|
|
||||||
|
|
||||||
``print`` is used to output on screen. Example usage:
|
py
|
||||||
|
--
|
||||||
|
|
||||||
.. code-block:: clj
|
``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 "Hello world!")
|
(print "A result from Python:" (py "'hello' + 'world'"))
|
||||||
|
|
||||||
.. note:: ``print`` always returns ``None``.
|
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
|
||||||
====================
|
====================
|
||||||
|
|
||||||
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):
|
def greet(name):
|
||||||
print("hello," 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`)
|
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,
|
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.
|
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
|
You can even :ref:`mix Python and Hy code in the same project, or even the same
|
||||||
can be a good way to get your feet wet in Hy.
|
file,<interop>` which can be a good way to get your feet wet in Hy.
|
||||||
|
|
||||||
|
|
||||||
Hy versus other Lisps
|
Hy versus other Lisps
|
||||||
|
@ -20,6 +20,7 @@ from hy.macros import require, load_macros, macroexpand, tag_macroexpand
|
|||||||
import hy.core
|
import hy.core
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import textwrap
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import traceback
|
import traceback
|
||||||
import importlib
|
import importlib
|
||||||
@ -1589,6 +1590,22 @@ class HyASTCompiler(object):
|
|||||||
if ast_str(root) == "eval_and_compile"
|
if ast_str(root) == "eval_and_compile"
|
||||||
else Result())
|
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)
|
@builds_model(HyExpression)
|
||||||
def compile_expression(self, expr):
|
def compile_expression(self, expr):
|
||||||
# Perform macro expansions
|
# Perform macro expansions
|
||||||
|
@ -639,3 +639,10 @@ def test_futures_imports():
|
|||||||
|
|
||||||
assert hy_ast.body[0].module == '__future__'
|
assert hy_ast.body[0].module == '__future__'
|
||||||
assert hy_ast.body[1].module == 'hy.core.language'
|
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))]
|
(with [c1 (closing (Closeable)) c2 (closing (Closeable))]
|
||||||
(setv c1.x "v1")
|
(setv c1.x "v1")
|
||||||
(setv c2.x "v2"))
|
(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 issubclass(m.C2, m.C1)
|
||||||
assert (m.C2.attr1, m.C2.attr2) == (5, 6)
|
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