Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Support namedtuples #107

Closed
dbrgn opened this Issue · 11 comments

3 participants

@dbrgn
Collaborator

Jedi should support namedtuples.

from collections import namedtuple

Location = namedtuple('Location', ['x_pos', 'y_pos'])
location = Location(1, 2)

Completions for location should contain x_pos and y_pos.

An alternate way of specifying namedtuple attributes is:

Location = namedtuple('Location', 'x_pos' y_pos')

The generated class is printed when passing verbose=True to the namedtuple() factory. That should make things easy.

@davidhalter
Owner

Hmm I doubt that namedtuple will ever work. At least in Python 2.7 it uses exec which is not supported by Jedi (and definitely won't be supported) in the future.
It's one of the few exceptions where completion will not be possible.

@dbrgn
Collaborator

Couldn't you add an exception for collections.namedtuple and process the code generated by it with Jedi? After all, it's just a string of code, which is Jedi's specialty. ;)

@dbrgn
Collaborator

You can identify a namedtuple by checking whether it is a subclass of tuple and whether it has the _fields attribute.

Python 3.3.0 (default, Sep 29 2012, 15:50:43) 
[GCC 4.7.1 20120721 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from collections import namedtuple
>>> t = tuple()
>>> Spam = namedtuple('Spam', 'ham eggs')
>>> s = Spam('hamham', '000')
>>> isinstance(t, tuple)
True
>>> isinstance(s, tuple)
True
>>> hasattr(t, '_fields')
False
>>> hasattr(s, '_fields')
True

The source code of the class can be found in _source.

>>> s._source
"from builtins import property as _property, tuple as _tuple\nfrom operator import itemgetter as _itemgetter\nfrom collections import OrderedDict\n\nclass Spam(tuple):\n    'Spam(ham, eggs)'\n\n    __slots__ = ()\n\n    _fields = ('ham', 'eggs')\n\n    def __new__(_cls, ham, eggs):\n        'Create new instance of Spam(ham, eggs)'\n        return _tuple.__new__(_cls, (ham, eggs))\n\n    @classmethod\n    def _make(cls, iterable, new=tuple.__new__, len=len):\n        'Make a new Spam object from a sequence or iterable'\n        result = new(cls, iterable)\n        if len(result) != 2:\n            raise TypeError('Expected 2 arguments, got %d' % len(result))\n        return result\n\n    def __repr__(self):\n        'Return a nicely formatted representation string'\n        return self.__class__.__name__ + '(ham=%r, eggs=%r)' % self\n\n    def _asdict(self):\n        'Return a new OrderedDict which maps field names to their values'\n        return OrderedDict(zip(self._fields, self))\n\n    __dict__ = property(_asdict)\n\n    def _replace(_self, **kwds):\n        'Return a new Spam object replacing specified fields with new values'\n        result = _self._make(map(kwds.pop, ('ham', 'eggs'), _self))\n        if kwds:\n            raise ValueError('Got unexpected field names: %r' % list(kwds))\n        return result\n\n    def __getnewargs__(self):\n        'Return self as a plain tuple.  Used by copy and pickle.'\n        return tuple(self)\n\n    ham = _property(_itemgetter(0), doc='Alias for field number 0')\n\n    eggs = _property(_itemgetter(1), doc='Alias for field number 1')\n\n"

And the fields are in _fields.

>>> s._fields
('ham', 'eggs')
@davidhalter
Owner

That's much too complicated and imprecise. If I want to know that, I just check whether the name is nametuple and the definition is coming from collections ;-)

If you want to do it, that would be the way :-) This could be checked within executions. After that we would need to generate a class which looks like the nametuple class (should be pretty easy).

@dbrgn
Collaborator

Ah of course, I forgot that you have access to the entire code tree :)

I might have some time to do it after my exams (end of January).

@davidhalter
Owner

I think it is now after your exams :smile: :sparkling_heart:

@dbrgn
Collaborator

Dang... I'll take a look at it.

@davidhalter
Owner

I think it is now after your exams

Again :-D

@reingart

I'm exploring this issue too as it would help also to build the basis to support some ORM (web2py and maybe sqlalchemy)
I've been investigating a little, and it seems adding a hook would not be so complicated.
In fact, there is jedi.evaluate.stdlib.execute that already is resolving some builtins dynamic features (getattr, super, type, etc.).

Some initial ideas (just to explore where to hook and how to build the internal objects needed):

reingart@c84a1ba

Sorry if the code is incomplete or even wrong, I didn't have time to fully read and understand jedi internals.
Any advice in how to create this kind of "Fake" objects would be great.
Thanks for this great library

@davidhalter
Owner

@reingart That's exactly the place for the hook.

I would actually use jedi.parser.Parser and feed it with the definition of namedtuple (look at the namedtuple source code. it's basically an exec. After that it's pretty easy.

@dbrgn dbrgn referenced this issue from a commit
@dbrgn dbrgn Implemented support for namedtuples (fixes #107)
Note that namedtuples are only supported for Python >2.6.
0c2caa3
@dbrgn dbrgn referenced this issue from a commit
@dbrgn dbrgn Implemented support for namedtuples (fixes #107)
Note that namedtuples are only supported for Python >2.6.
bf08fd7
@dbrgn dbrgn referenced this issue from a commit
@dbrgn dbrgn Implemented support for namedtuples (fixes #107)
Note that namedtuples are only supported for Python >2.6.
d3620fd
@davidhalter
Owner

Yes Yes Yes. You finally did it :-) :sparkles: :sparkling_heart:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.