Implement Python 2 exec

The implementation of `hy.core.language.exec` draws code from the `exec_` function in commit f574c7be6ebc80041ef58ca29588f310248ebed4 of the library Six, which is copyright 2010–2017 Benjamin Peterson and licensed under the Expat license.
This commit is contained in:
Kodi Arfer 2017-07-25 10:28:42 -07:00
parent 3dcf91ab39
commit ecc974de1e
7 changed files with 120 additions and 2 deletions

1
NEWS
View File

@ -23,6 +23,7 @@ Changes from 0.13.0
mistaken for shebang lines
* Fixed a bug where REPL history wasn't saved if you quit the REPL with
`(quit)` or `(exit)`
* `exec` now works under Python 2
Changes from 0.12.1

View File

@ -5,6 +5,8 @@
import re, os, sys, time, cgi
sys.path.append(os.path.abspath(".."))
extensions = ['sphinx.ext.intersphinx']
from get_version import __version__ as hy_version
# Read the Docs might dirty its checkout, so strip the dirty flag.
hy_version = re.sub('[+.]dirty\Z', '', hy_version)
@ -48,3 +50,7 @@ html_show_sphinx = False
html_context = dict(
hy_descriptive_version = hy_descriptive_version)
intersphinx_mapping = dict(
py2 = ('https://docs.python.org/2', None),
py = ('https://docs.python.org/3', None))

View File

@ -281,6 +281,19 @@ otherwise ``False``. Return ``True`` if *coll* is empty.
True
.. _exec-fn:
exec
----
Equivalent to Python 3's built-in function :py:func:`exec`.
.. code-block:: clj
=> (exec "print(a + b)" {"a" 1} {"b" 2})
3
.. _float?-fn:
float?

View File

@ -778,6 +778,19 @@ class HyASTCompiler(object):
def compile_unpack_mapping(self, expr):
raise HyTypeError(expr, "`unpack-mapping` isn't allowed here")
@builds_if("exec*", not PY3)
# Under Python 3, `exec` is a function rather than a statement type, so Hy
# doesn't need a special form for it.
@checkargs(min=1, max=3)
def compile_exec(self, expr):
expr.pop(0)
return ast.Exec(
lineno=expr.start_line,
col_offset=expr.start_column,
body=self.compile(expr.pop(0)).force_expr,
globals=self.compile(expr.pop(0)).force_expr if expr else None,
locals=self.compile(expr.pop(0)).force_expr if expr else None)
@builds("do")
def compile_do(self, expression):
expression.pop(0)

View File

@ -117,6 +117,24 @@
range range
zip zip))
(if-python2
(defn exec [$code &optional $globals $locals]
"Execute Python code.
The parameter names contain weird characters to discourage calling this
function with keyword arguments, which isn't supported by Python 3's
`exec`."
(if
(none? $globals) (do
(setv frame (._getframe sys (int 1)))
(try
(setv $globals frame.f_globals $locals frame.f_locals)
(finally (del frame))))
(none? $locals)
(setv $locals $globals))
(exec* $code $globals $locals))
(def exec exec))
;; infinite iterators
(def
count itertools.count
@ -461,8 +479,8 @@
(def *exports*
'[*map accumulate butlast calling-module-name chain coll? combinations
comp complement compress cons cons? constantly count cycle dec distinct
disassemble drop drop-last drop-while empty? eval even? every? first filter
flatten float? fraction gensym group-by identity inc input instance?
disassemble drop drop-last drop-while empty? eval even? every? exec first
filter flatten float? fraction gensym group-by identity inc input instance?
integer integer? integer-char? interleave interpose islice iterable?
iterate iterator? juxt keyword keyword? last list* macroexpand
macroexpand-1 map merge-with multicombinations name neg? none? nth

View File

@ -593,3 +593,27 @@ def test_setv_builtins():
def test_lots_of_comment_lines():
# https://github.com/hylang/hy/issues/1313
can_compile(1000 * ";\n")
def test_exec_star():
code = can_compile('(exec* "print(5)")').body[0]
assert type(code) == (ast.Expr if PY3 else ast.Exec)
if not PY3:
assert code.body.s == "print(5)"
assert code.globals is None
assert code.locals is None
code = can_compile('(exec* "print(a)" {"a" 3})').body[0]
assert type(code) == (ast.Expr if PY3 else ast.Exec)
if not PY3:
assert code.body.s == "print(a)"
assert code.globals.keys[0].s == "a"
assert code.locals is None
code = can_compile('(exec* "print(a + b)" {"a" "x"} {"b" "y"})').body[0]
assert type(code) == (ast.Expr if PY3 else ast.Exec)
if not PY3:
assert code.body.s == "print(a + b)"
assert code.globals.keys[0].s == "a"
assert code.locals.keys[0].s == "b"

View File

@ -170,6 +170,49 @@
(assert-false (every? even? [2 4 5]))
(assert-true (every? even? [])))
(setv globalvar 1)
(defn test-exec []
(setv localvar 1)
(setv code "
result['localvar in locals'] = 'localvar' in locals()
result['localvar in globals'] = 'localvar' in globals()
result['globalvar in locals'] = 'globalvar' in locals()
result['globalvar in globals'] = 'globalvar' in globals()
result['x in locals'] = 'x' in locals()
result['x in globals'] = 'x' in globals()
result['y in locals'] = 'y' in locals()
result['y in globals'] = 'y' in globals()")
(setv result {})
(exec code)
(assert-true (get result "localvar in locals"))
(assert-false (get result "localvar in globals"))
(assert-false (get result "globalvar in locals"))
(assert-true (get result "globalvar in globals"))
(assert-false (or
(get result "x in locals") (get result "x in globals")
(get result "y in locals") (get result "y in globals")))
(setv result {})
(exec code {"x" 1 "result" result})
(assert-false (or
(get result "localvar in locals") (get result "localvar in globals")
(get result "globalvar in locals") (get result "globalvar in globals")))
(assert-true (and
(get result "x in locals") (get result "x in globals")))
(assert-false (or
(get result "y in locals") (get result "y in globals")))
(setv result {})
(exec code {"x" 1 "result" result} {"y" 1})
(assert-false (or
(get result "localvar in locals") (get result "localvar in globals")
(get result "globalvar in locals") (get result "globalvar in globals")))
(assert-false (get result "x in locals"))
(assert-true (get result "x in globals"))
(assert-true (get result "y in locals"))
(assert-false (get result "y in globals")))
(defn test-filter []
"NATIVE: testing the filter function"
(setv res (list (filter pos? [ 1 2 3 -4 5])))