Merge pull request #1656 from Kodiologist/keyword-call

Make HyKeyword callable
This commit is contained in:
Kodi Arfer 2018-07-24 09:28:16 -07:00 committed by GitHub
commit 5bfd4592f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 39 additions and 10 deletions

View File

@ -7,6 +7,12 @@ Removals
------------------------------
* Empty expressions (`()`) are no longer legal at the top level.
New Features
------------------------------
* Keyword objects (not just literal keywords) can be called, as
shorthand for `(get obj :key)`, and they accept a default value
as a second argument.
0.15.0
==============================

View File

@ -83,6 +83,11 @@ the error ``Keyword argument :foo needs a value``. To avoid this, you can quote
the keyword, as in ``(f ':foo)``, or use it as the value of another keyword
argument, as in ``(f :arg :foo)``.
Keywords can be called like functions as shorthand for ``get``. ``(:foo obj)``
is equivalent to ``(get obj :foo)``. An optional ``default`` argument is also
allowed: ``(:foo obj 2)`` or ``(:foo obj :default 2)`` returns ``2`` if ``(get
obj :foo)`` raises a ``KeyError``.
.. _mangling:
symbols

View File

@ -1552,13 +1552,6 @@ class HyASTCompiler(object):
fn = expression[0]
func = None
if isinstance(fn, HyKeyword):
if len(expression) > 2:
raise HyTypeError(
expression, "keyword calls take only 1 argument")
expression.append(expression.pop(0))
expression.insert(0, HySymbol("get"))
return self.compile(expression)
if isinstance(fn, HySymbol):

View File

@ -146,6 +146,16 @@ class HyKeyword(HyObject):
def __bool__(self):
return bool(self.name)
_sentinel = object()
def __call__(self, data, default=_sentinel):
try:
return data[self]
except KeyError:
if default is HyKeyword._sentinel:
raise
return default
def strip_digit_separators(number):
# Don't strip a _ or , if it's the first character, as _42 and

View File

@ -1380,9 +1380,24 @@
(assert (= (len "ℵℵℵ♥♥♥\t♥♥\r\n") 11)))
(defn test-keyword-dict-access []
"NATIVE: test keyword dict access"
(assert (= "test" (:foo {:foo "test"}))))
(defn test-keyword-get []
(assert (= (:foo {:foo "test"}) "test"))
(setv f :foo)
(assert (= (f {:foo "test"}) "test"))
(with [(pytest.raises KeyError)] (:foo {:a 1 :b 2}))
(assert (= (:foo {:a 1 :b 2} 3) 3))
(assert (= (:foo {:a 1 :b 2 :foo 5} 3) 5))
(with [(pytest.raises TypeError)] (:foo "Hello World"))
(with [(pytest.raises TypeError)] (:foo (object)))
; The default argument should work regardless of the collection type.
(defclass G [object]
(defn __getitem__ [self k]
(raise KeyError)))
(assert (= (:foo (G) 15) 15)))
(defn test-break-breaking []