Merge pull request #1505 from vodik/metaclasses
Add metaclass support, support PEP 3115 and PEP 487
This commit is contained in:
commit
a48f009f1e
7
NEWS.rst
7
NEWS.rst
@ -14,19 +14,18 @@ Other Breaking Changes
|
||||
* Non-shadow unary `=`, `is`, `<`, etc. now evaluate their argument
|
||||
instead of ignoring it. This change increases consistency a bit
|
||||
and makes accidental unary uses easier to notice.
|
||||
* `hy-repr` uses registered functions instead of methods
|
||||
|
||||
New Features
|
||||
------------------------------
|
||||
* Added `mangle` and `unmangle` as core functions
|
||||
* `defclass` in Python 3 now supports specifying metaclasses and other
|
||||
keyword arguments
|
||||
|
||||
Bug Fixes
|
||||
------------------------------
|
||||
* Fix `(return)` so it works correctly to exit a Python 2 generator
|
||||
|
||||
Other Breaking Changes
|
||||
-----------------------------
|
||||
* `hy-repr` uses registered functions instead of methods
|
||||
|
||||
Misc. Improvements
|
||||
----------------------------
|
||||
* `hy-repr` supports more standard types
|
||||
|
@ -2057,11 +2057,12 @@ class HyASTCompiler(object):
|
||||
|
||||
bases_expr = []
|
||||
bases = Result()
|
||||
keywords = []
|
||||
if expressions:
|
||||
base_list = expressions.pop(0)
|
||||
if not isinstance(base_list, HyList):
|
||||
raise HyTypeError(base_list, "Base classes must be a list.")
|
||||
bases_expr, bases, _ = self._compile_collect(base_list)
|
||||
bases_expr, bases, keywords = self._compile_collect(base_list, with_kwargs=PY3)
|
||||
|
||||
body = Result()
|
||||
|
||||
@ -2092,7 +2093,7 @@ class HyASTCompiler(object):
|
||||
expressions,
|
||||
decorator_list=[],
|
||||
name=ast_str(class_name),
|
||||
keywords=[],
|
||||
keywords=keywords,
|
||||
starargs=None,
|
||||
kwargs=None,
|
||||
bases=bases_expr,
|
||||
|
@ -214,6 +214,13 @@ def test_ast_good_defclass():
|
||||
can_compile("(defclass a [])")
|
||||
|
||||
|
||||
@pytest.mark.skipif(not PY3, reason="Python 3 supports class keywords")
|
||||
def test_ast_good_defclass_with_metaclass():
|
||||
"Make sure AST can compile valid defclass with keywords"
|
||||
can_compile("(defclass a [:metaclass b])")
|
||||
can_compile("(defclass a [:b c])")
|
||||
|
||||
|
||||
def test_ast_bad_defclass():
|
||||
"Make sure AST can't compile invalid defclass"
|
||||
cant_compile("(defclass)")
|
||||
|
@ -37,3 +37,11 @@
|
||||
(setv x (+ x a))
|
||||
(else (setv x (+ x 50))))
|
||||
(assert (= x 53)))))
|
||||
|
||||
(defn test-pep-487 []
|
||||
(defclass QuestBase []
|
||||
[--init-subclass-- (fn [cls swallow &kwargs kwargs]
|
||||
(setv cls.swallow swallow))])
|
||||
|
||||
(defclass Quest [QuestBase :swallow "african"])
|
||||
(assert (= (. (Quest) swallow) "african")))
|
||||
|
@ -84,3 +84,26 @@
|
||||
(assert (= (foo :b 20 :a 10 :c 30)
|
||||
(, 10 20 30)))))
|
||||
|
||||
(defn test-pep-3115 []
|
||||
(defclass member-table [dict]
|
||||
[--init-- (fn [self] (setv self.member-names []))
|
||||
|
||||
--setitem-- (fn [self key value]
|
||||
(if (not-in key self)
|
||||
(.append self.member-names key))
|
||||
(dict.--setitem-- self key value))])
|
||||
|
||||
(defclass OrderedClass [type]
|
||||
[--prepare-- (classmethod (fn [metacls name bases] (member-table)))
|
||||
|
||||
--new-- (fn [cls name bases classdict]
|
||||
(setv result (type.--new-- cls name bases (dict classdict)))
|
||||
(setv result.member-names classdict.member-names)
|
||||
result)])
|
||||
|
||||
(defclass MyClass [:metaclass OrderedClass]
|
||||
[method1 (fn [self] (pass))
|
||||
method2 (fn [self] (pass))])
|
||||
|
||||
(assert (= (. (MyClass) member-names)
|
||||
["__module__" "__qualname__" "method1" "method2"])))
|
||||
|
@ -12,7 +12,7 @@ import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from hy._compat import PY3, PY35, builtins
|
||||
from hy._compat import PY3, PY35, PY36, builtins
|
||||
from hy.importer import get_bytecode_path
|
||||
|
||||
|
||||
@ -229,9 +229,11 @@ def test_hy2py():
|
||||
for dirpath, dirnames, filenames in os.walk("tests/native_tests"):
|
||||
for f in filenames:
|
||||
if f.endswith(".hy"):
|
||||
if f == "py3_only_tests.hy" and not PY3:
|
||||
if "py3_only" in f and not PY3:
|
||||
continue
|
||||
if f == "py35_only_tests.hy" and not PY35:
|
||||
if "py35_only" in f and not PY35:
|
||||
continue
|
||||
if "py36_only" in f and not PY36:
|
||||
continue
|
||||
i += 1
|
||||
output, err = run_cmd("hy2py -s -a " + quote(os.path.join(dirpath, f)))
|
||||
|
Loading…
x
Reference in New Issue
Block a user