Skip to content

Commit 7581758

Browse files
committed
Fix completion of callable with __getattr__ and without __name__ (#3149)
1 parent 61793d4 commit 7581758

2 files changed

Lines changed: 66 additions & 1 deletion

File tree

lib/livebook/intellisense/python/intellisense.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def get_info(name, object):
169169
or inspect.isbuiltin(object)
170170
# NumPy functions are not regular functions, but they are callable,
171171
# so we want to treat those as functions.
172-
or (isinstance(object, Callable) and hasattr(object, "__name__"))
172+
or (isinstance(object, Callable) and inspect.getattr_static(object, "__name__", None))
173173
):
174174
return {
175175
"kind": "function",

test/livebook/intellisense/python_test.exs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,71 @@ defmodule Livebook.Intellisense.PythonTest do
308308
)
309309
end
310310

311+
test "callables with name" do
312+
# NumPy functions are not regular functions, but they are callable,
313+
# so we want to treat those as functions.
314+
315+
context =
316+
intellisense_context_from_eval do
317+
import Pythonx
318+
319+
~PY"""
320+
class GoodCallable:
321+
def __init__(self):
322+
self.__name__ = "good_callable"
323+
self.__doc__ = "This is a good callable."
324+
325+
def __call__(self, number):
326+
return number * 2
327+
328+
callable_good = GoodCallable()
329+
330+
class BadCallable:
331+
def __init__(self):
332+
self.__doc__ = "This is a bad callable."
333+
334+
# This is going to be invoked when accessing __name__, but it
335+
# can return anything, and we should not consider this callable
336+
# a function.
337+
def __getattr__(self, attr):
338+
return None
339+
340+
def __call__(self, number):
341+
return number * 2
342+
343+
callable_bad = BadCallable()
344+
"""
345+
end
346+
347+
assert %{
348+
items: [
349+
%{
350+
label: "callable_bad",
351+
kind: :variable,
352+
documentation: "(variable)",
353+
insert_text: "callable_bad"
354+
},
355+
%{
356+
label: "callable_good",
357+
kind: :function,
358+
documentation: """
359+
This is a good callable.
360+
361+
```
362+
good_callable(number)
363+
```\
364+
""",
365+
insert_text: "callable_good(${})"
366+
}
367+
]
368+
} =
369+
Intellisense.Python.handle_request(
370+
{:completion, "callable_"},
371+
context,
372+
node()
373+
)
374+
end
375+
311376
test "class" do
312377
context =
313378
intellisense_context_from_eval do

0 commit comments

Comments
 (0)