jorgenschaefer / elpy

Emacs Python Development Environment
GNU General Public License v3.0
1.9k stars 261 forks source link

'MergedNamesDict' object has no attribute 'get' #861

Closed Kurvivor19 closed 8 years ago

Kurvivor19 commented 8 years ago

Submitting bug report as for some reason elpy proves to be nearly unusable due to getting this error every 10 seconds or so, any time it should do something useful

Configuration

Virtualenv........: None RPC Python........: 2.7.10 (c:/Python27/python.exe) Interactive Python: python (c:/Python27/python.exe) Emacs.............: 24.5.1 Elpy..............: 1.8.1 Jedi..............: 0.9.0 Rope..............: Not found (0.10.3 available) Importmagic.......: Not found (0.1.7 available) Syntax checker....: pyflakes.exe (c:/Python27/Scripts/pyflakes.exe)

Traceback

Traceback (most recent call last): File "c:\Users\Ivan.emacs.d\elpa\elpy-20150702.1202\elpy\jedibackend.py", line 306, in run_with_debug return getattr(script, name)() File "c:\Python27\lib\site-packages\jedi\apiinit.py", line 188, in completions completion_names = get_completions(user_stmt, b) File "c:\Python27\lib\site-packages\jedi\apiinit.py", line 174, in get_completions completion_names += self._simple_complete(path, dot, like) File "c:\Python27\lib\site-packages\jedi\apiinit.py", line 250, in _simple_complete scopes = list(self._prepare_goto(path, True)) File "c:\Python27\lib\site-packages\jedi\apiinit.py", line 294, in _prepare_goto scopes = self._evaluator.eval_element(eval_stmt) File "c:\Python27\lib\site-packages\jedi\evaluate\cache.py", line 41, in wrapper rv = function(obj, _args, _kwargs) File "c:\Python27\lib\site-packages\jedi\evaluateinit.py", line 169, in eval_element return self._eval_atom(element) File "c:\Python27\lib\site-packages\jedi\evaluateinit.py", line 230, in _eval_atom return self.find_types(scope, atom, stmt.start_pos, search_global=True) File "c:\Python27\lib\site-packages\jedi\evaluateinit.py", line 120, in find_types return f.find(scopes, search_global) File "c:\Python27\lib\site-packages\jedi\debug.py", line 52, in wrapper result = func(_args, _kwargs) File "c:\Python27\lib\site-packages\jedi\evaluate\finder.py", line 86, in find names = self.filter_name(scopes) File "c:\Python27\lib\site-packages\jedi\evaluate\finder.py", line 177, in filter_name names = self.names_dict_lookup(names_dict, position) File "c:\Python27\lib\site-packages\jedi\evaluate\finder.py", line 119, in names_dict_lookup names = names_dict[search_str] File "c:\Python27\lib\site-packages\jedi\parser\fast.py", line 78, in getitem return list(chain.from_iterable(dct.get(value, []) for dct in self.dicts)) File "c:\Python27\lib\site-packages\jedi\parser\fast.py", line 78, in return list(chain.from_iterable(dct.get(value, []) for dct in self.dicts)) AttributeError: 'MergedNamesDict' object has no attribute 'get'

Jedi Debug Information

[N] dbg: Parsed d:\hq-git\builder\games\wh40k\chaos_marines_v2init.py, with 0 parsers in 78 splits. [W] warning: No statement under the cursor. [N] dbg: start: u'ut' in <Function: check_rules@342-350> [N] dbg: eval_element <Name: ut@344,24>@(344, 24)


Reproduction:

```Python
import jedi

source = '''\
# -*- coding: utf-8 -*-
__author__ = 'Denis Romanov'
__maintainer__ = 'Ivan Truskov'
summary = ['Codex Chaos Space Marines 6th Edition', 'Crimson Slaughter supplement',
           'Black Legion supplement', 'Helbrutes dataslate']

from builder.games.wh40k.roster import Wh40kBase, Wh40k7ed, CombinedArmsDetachment,\
    AlliedDetachment, HQSection, ElitesSection, TroopsSection, FastSection,\
    HeavySection, Fort, PrimaryDetachment, Formation, Wh40kImperial
from builder.games.wh40k.imperial_armour.apocalypse import chaos as ia_apoc_chaos
from builder.games.wh40k.escalation.chaos import LordsOfWar as BaseLordsOfWar
from builder.games.wh40k.dataslates.cypher import *
from builder.games.wh40k.dataslates.belakor import *
from builder.games.wh40k.imperial_armour.volume13 import *
from builder.games.wh40k.imperial_armour.volume5_7 import ChaosMarinesCharacters
from builder.games.wh40k.imperial_armour.dataslates import ChaosKnights

from hq import *
from elites import *
from troops import *
from fast import *
from heavy import *

class BaseHQ(HQSection):
    def __init__(self, parent):
        super(BaseHQ, self).__init__(parent)
        self.abaddon = UnitType(self, Abaddon)
        UnitType(self, Huron)
        self.kharn = UnitType(self, Kharn)
        self.ahriman = UnitType(self, Ahriman)
        self.typhus = UnitType(self, Typhus)
        self.lucius = UnitType(self, Lucius)
        UnitType(self, Fabius)
        self.lord = UnitType(self, Lord)
        self.sorc = UnitType(self, Sorcerer)
        UnitType(self, DaemonPrince)
        self.smith = UnitType(self, Warpsmith)
        UnitType(self, Apostle)

class BaseElites(ElitesSection):
    def __init__(self, parent):
        super(BaseElites, self).__init__(parent)
        self.chosen = UnitType(self, Chosen)
        self.possessed = UnitType(self, Possessed)
        self.terminators = UnitType(self, Terminators)
        UnitType(self, Helbrute)
        UnitType(self, Mutilators)
        self.berz = UnitType(self, Berzerks)
        self.thousand = UnitType(self, ThousandSons)
        self.plague = UnitType(self, PlagueMarines)
        self.noise = UnitType(self, NoiseMarines)

class Troops(TroopsSection):
    def __init__(self, parent):
        super(Troops, self).__init__(parent)
        UnitType(self, ChaosSpaceMarines)
        UnitType(self, ChaosCultists)
        self.zombie = UnitType(self, PlagueZombie)
        self.chosen = UnitType(self, Chosen)
        self.possessed = UnitType(self, Possessed)
        self.berz = UnitType(self, Berzerks)
        self.thousand = UnitType(self, ThousandSons)
        self.plague = UnitType(self, PlagueMarines)
        self.noise = UnitType(self, NoiseMarines)

class BaseFastAttack(FastSection):
    def __init__(self, parent):
        super(BaseFastAttack, self).__init__(parent)
        UnitType(self, Bikers)
        UnitType(self, Spawn)
        UnitType(self, Raptors)
        UnitType(self, WarpTalons)
        UnitType(self, Heldrake)

class BaseHeavySupport(HeavySection):
    def __init__(self, parent):
        super(BaseHeavySupport, self).__init__(parent)
        UnitType(self, Havoks)
        UnitType(self, Obliterators)
        UnitType(self, Defiler)
        UnitType(self, Forgefiend)
        UnitType(self, Maulerfiend)
        UnitType(self, LandRaider)
        UnitType(self, Vindicator)
        UnitType(self, Predator)

class HQ(ChaosMarinesCharacters, BaseHQ):
    def __init__(self, parent):
        super(HQ, self).__init__(parent)
        UnitType(self, Cypher, slot=0)
        UnitType(self, Belakor)

class LordsOfWar(ia_apoc_chaos.ChaosLordsOfWar, BaseLordsOfWar):
    pass

class HeavySupport(ia_apoc_chaos.ChaosHeavySupport, BaseHeavySupport):
    pass

class FastAttack(ia_apoc_chaos.ChaosFastAttack, BaseFastAttack):
    pass

class Elites(ia_apoc_chaos.ChaosElites, BaseElites):
    pass

class ChaosMarinesElites(MarinesElites, BaseElites):
    pass

class ChaosMarinesHeavy(MarinesHeavy, BaseHeavySupport):
    pass

class ChaosMarinesFast(MarinesFast, BaseFastAttack):
    pass

class ChaosMarinesLordsOfWar(ChaosKnights, MarinesLords, BaseLordsOfWar):
    pass

class CsmV2Base(Wh40kBase):
    army_id = 'csm_v2_base'
    army_name = 'Chaos Space Marines'
    ia_enabled = True

    class SupplementOptions(OneOf):
        def __init__(self, parent):
            super(CsmV2Base.SupplementOptions, self).__init__(parent=parent, name='Codex')
            self.base = self.variant(name=CsmV2.army_name)
            self.black = self.variant(name='Black Legion')
            self.crimson = self.variant(name='Crimson Slaughter')

    def __init__(self):
        self.hq = HQ(parent=self)
        self.elites = Elites(parent=self)
        self.troops = Troops(parent=self)
        self.fast = FastAttack(parent=self)
        self.heavy = HeavySupport(parent=self)
        super(CsmV2Base, self).__init__(
            hq=self.hq, elites=self.elites, troops=self.troops, fast=self.fast, heavy=self.heavy,
            fort=Fort(parent=self),
            lords=LordsOfWar(parent=self)
        )

        self.codex = self.SupplementOptions(self)

    @property
    def is_base(self):
        return self.codex.cur == self.codex.base

    @property
    def is_black(self):
        return self.codex.cur == self.codex.black

    @property
    def is_crimson(self):
        return self.codex.cur == self.codex.crimson

    @property
    def has_abaddon(self):
        return self.hq.abaddon.count > 0

    def check_rules(self):
        super(CsmV2Base, self).check_rules()

        if self.is_black:
            bringers = sum(u.is_bringers for u in self.elites.terminators.units)
            if bringers > 1:
                self.error('Only one Chaos Terminators unit can be ungraded to Bringers of Despair. '
                           '(taken: {})'.format(bringers))

        self.move_units((self.has_abaddon and type(self.parent.parent) is PrimaryDetachment) or self.is_black,
                        self.elites.chosen, self.troops.chosen)
        self.move_units(self.is_crimson, self.elites.possessed, self.troops.possessed)

        self.troops.zombie.active = self.hq.typhus.count > 0
        if self.hq.typhus.count == 0 and self.troops.zombie.count > 0:
            self.error("You can't have Plague Zombie without Typhus")

        if type(self.parent.parent) is PrimaryDetachment:
            self.move_units(self.hq.kharn.count + sum(u.marks.khorne for u in self.hq.lord.units) > 0,
                            self.elites.berz, self.troops.berz)
            self.move_units(self.hq.typhus.count + sum(u.marks.nurgle for u in self.hq.lord.units) > 0,
                            self.elites.plague, self.troops.plague)
            self.move_units(self.hq.lucius.count + sum(u.marks.slaanesh for u in self.hq.lord.units) > 0,
                            self.elites.noise, self.troops.noise)
            self.move_units(self.hq.ahriman.count + sum(u.marks.tzeentch for u in self.hq.sorc.units) > 0,
                            self.elites.thousand, self.troops.thousand)
        else:
            self.troops.berz.visible = self.troops.thousand.visible = self.troops.plague.visible = \
                self.troops.noise.visible = False
            self.troops.chosen.visible = self.is_black
            self.troops.possessed.visible = self.is_crimson

class CsmV2CAD(CsmV2Base, CombinedArmsDetachment):
    army_id = 'csm_v2_cad'
    army_name = 'Chaos Space Marines (Combined arms detachment)'

class CsmV2AD(CsmV2Base, AlliedDetachment):
    army_id = 'csm_v2_ad'
    army_name = 'Chaos Space Marines (Allied detachment)'

class Mayhem(ChaosSpaceMarinesLegaciesFormation):
    army_id = 'csm_v2_mayhem'
    army_name = 'Mayhem pack'

    def __init__(self):
        super(Mayhem, self).__init__()
        UnitType(self, Helbrute, min_limit=3, max_limit=3)

class Helcult(ChaosSpaceMarinesLegaciesFormation):
    army_id = 'csm_v2_helcult'
    army_name = 'Helcult'

    def __init__(self):
        super(Helcult, self).__init__()
        UnitType(self, Helbrute, min_limit=1, max_limit=1)
        UnitType(self, ChaosCultists, min_limit=2, max_limit=2)

class Helfist(ChaosSpaceMarinesLegaciesFormation):
    army_id = 'csm_v2_helfist'
    army_name = 'Helfist Murderpack'

    def __init__(self):
        super(Helfist, self).__init__()
        UnitType(self, Helbrute, min_limit=5, max_limit=5)

class Butcherhorde(ChaosSpaceMarinesLegaciesFormation):
    army_id = 'csm_v2_butcherhorde'
    army_name = "KhГўrn's Butcherhorde"

    @property
    def is_base(self):
        return True

    @property
    def is_black(self):
        return False

    @property
    def is_crimson(self):
        return False

    def __init__(self):
        super(Butcherhorde, self).__init__()
        UnitType(self, Kharn, min_limit=1, max_limit=1)
        self.csm = UnitType(self, ChaosSpaceMarines, min_limit=4, max_limit=4)
        UnitType(self, Berzerks, min_limit=4, max_limit=4)

    def check_rules(self):
        super(Butcherhorde, self).check_rules()
        for unit in self.csm.units:
            if not unit.marks.khorne:
                self.error("All units of Chaos Space Marines must take the mark of Khorne upgrade")
                break

class FallenChampions(Formation):
    army_name = 'Fallen Champions'
    army_id = 'fallen_champion'

    @property
    def is_base(self):
        return True

    @property
    def is_black(self):
        return False

    @property
    def is_crimson(self):
        return False

    class FallenChosen(Chosen):
        allow_marks = False
        allow_transport = False
        allow_veterans = False

    def __init__(self):
        super(FallenChampions, self).__init__()
        UnitType(self, Cypher, min_limit=1, max_limit=1)
        UnitType(self, self.FallenChosen, min_limit=1, max_limit=3)

class BlackFormation(Formation):
    @property
    def is_base(self):
        return False

    @property
    def is_black(self):
        return True

    @property
    def is_crimson(self):
        return False

class Bringers(BlackFormation):
    army_name = 'The Bringers of Despair'
    army_id = 'csm_v2_5_despair'

    def __init__(self):
        super(Bringers, self).__init__()
        UnitType(self, Abaddon, min_limit=1, max_limit=1)
        UnitType(self, Terminators, min_limit=1, max_limit=1)

class BlackChosen(BlackFormation):
    army_name = 'The Chosen of Abaddon'
    army_id = 'csm_v2_5_chosen'

    def __init__(self):
        super(BlackChosen, self).__init__()
        self.lords = [
            UnitType(self, Lord),
            UnitType(self, Sorcerer)
        ]
        self.toadies = [
            UnitType(self, Terminators),
            UnitType(self, Chosen)
        ]
        self.add_type_restriction(self.lords, 1, 4)

    def check_rules(self):
        super(BlackChosen, self).check_rules()
        lcount = sum(ut.)
        tcount = 0
        for ut in self.lords:
            lcount += ut.count

class CsmV2_5Base(Wh40kBase, ChaosSpaceMarinesLegaciesRoster):
    army_id = 'csm_v2_5_base'
    army_name = 'Chaos Space Marines'

    class SupplementOptions(OneOf):
        def __init__(self, parent):
            super(CsmV2_5Base.SupplementOptions, self).__init__(parent=parent, name='Codex')
            self.base = self.variant(name=CsmV2.army_name)
            self.black = self.variant(name='Black Legion')
            self.crimson = self.variant(name='Crimson Slaughter')

    def __init__(self):
        self.hq = HQ(parent=self)
        self.elites = ChaosMarinesElites(parent=self)
        self.troops = Troops(parent=self)
        self.fast = ChaosMarinesFast(parent=self)
        self.heavy = ChaosMarinesHeavy(parent=self)
        super(CsmV2_5Base, self).__init__(
            hq=self.hq, elites=self.elites, troops=self.troops, fast=self.fast, heavy=self.heavy,
            fort=Fort(parent=self),
            lords=ChaosMarinesLordsOfWar(parent=self)
        )

        self.codex = self.SupplementOptions(self)

    @property
    def is_base(self):
        return self.codex.cur == self.codex.base

    @property
    def is_black(self):
        return self.codex.cur == self.codex.black

    @property
    def is_crimson(self):
        return self.codex.cur == self.codex.crimson

    @property
    def has_abaddon(self):
        return self.hq.abaddon.count > 0

    def check_rules(self):
        super(CsmV2_5Base, self).check_rules()

        if self.is_black:
            bringers = sum(u.is_bringers for u in self.elites.terminators.units)
            if bringers > 1:
                self.error('Only one Chaos Terminators unit can be ungraded to Bringers of Despair. '
                           '(taken: {})'.format(bringers))

        self.move_units((self.has_abaddon and type(self.parent.parent) is PrimaryDetachment) or self.is_black,
                        self.elites.chosen, self.troops.chosen)
        self.move_units(self.is_crimson, self.elites.possessed, self.troops.possessed)

        self.troops.zombie.active = (self.hq.typhus.count + self.hq.necrosius.count > 0)
        if self.hq.typhus.count == 0 and self.troops.zombie.count > 0:
            if self.hq.necrosius.count == 0 or not (type(self.parent.parent) is PrimaryDetachment):
                self.error("You can't have Plague Zombie without Typhus in army or Necrosius as a warlord")
            else:
                if len(self.troops.units) - self.troops.zombie.count < self.troops.min:
                    self.error("Plague Zombies cannot be compulsory Troops choices")

        if type(self.parent.parent) is PrimaryDetachment:
            self.move_units(self.hq.kharn.count + sum(u.marks.khorne for u in self.hq.lord.units) > 0,
                            self.elites.berz, self.troops.berz)
            self.move_units(self.hq.typhus.count + sum(u.marks.nurgle for u in self.hq.lord.units) > 0,
                            self.elites.plague, self.troops.plague)
            self.move_units(self.hq.lucius.count + sum(u.marks.slaanesh for u in self.hq.lord.units) > 0,
                            self.elites.noise, self.troops.noise)
            self.move_units(self.hq.ahriman.count + sum(u.marks.tzeentch for u in self.hq.sorc.units) > 0,
                            self.elites.thousand, self.troops.thousand)
        else:
            self.troops.berz.visible = self.troops.thousand.visible = self.troops.plague.visible = \
                self.troops.noise.visible = False
            self.troops.chosen.visible = self.is_black
            self.troops.possessed.visible = self.is_crimson

        # technomancer check
        technomancer = self.has_abaddon or self.hq.sorc.count > 0\
                       or self.hq.smith.count > 0
        relic_limit = 1 if type(self.parent.parent) is PrimaryDetachment else 0
        relic_count = self.elites.count_relics() + self.fast.count_relics()\
                      + self.heavy.count_relics()
        if relic_count > relic_limit and technomancer < 1:
            self.error('Detachment must include a Technomancer to take {} Infernal Relics'.format(relic_count))

class CsmV2_5CAD(CsmV2_5Base, CombinedArmsDetachment):
    army_id = 'csm_v2_5_cad'
    army_name = 'Chaos Space Marines (Combined arms detachment)'

class CsmV2_5AD(CsmV2_5Base, AlliedDetachment):
    army_id = 'csm_v2_ad'
    army_name = 'Chaos Space Marines (Allied detachment)'

faction = 'Chaos_Marines'

class CsmV2(Wh40k7ed):
    army_id = 'csm_v2'
    army_name = 'Chaos Space Marines'
    faction = faction
    obsolete = True

    def __init__(self):
        super(CsmV2, self).__init__([CsmV2CAD, Mayhem, Helcult, Helfist, FallenChampions], [CsmV2AD])

    def check_rules(self):
        super(CsmV2, self).check_rules()
        roster_check = lambda roster: 1 if roster.is_base else (2 if roster.is_black else 3)
        if any([isinstance(m.sub_roster.roster, CsmV2CAD)
               for m in self.primary.units]):
            ally_marines = [m.sub_roster.roster
                            for m in self.secondary.units
                            if isinstance(m.sub_roster.roster, CsmV2AD)]
            ally_chapters = set([roster_check(ally) for ally in ally_marines])
            own_chapter = set([roster_check(self.primary.units[0].sub_roster.roster)])
            if len(own_chapter & ally_chapters):
                self.error("Cannot have primary and allied detachment from same codex or supplement")

class CsmV2_5(Wh40kImperial, Wh40k7ed):
    army_id = 'csm_v2_5'
    army_name = 'Chaos Space Marines'
    faction = faction

    def __init__(self):
        super(CsmV2_5, self).__init__([CsmV2_5CAD, Mayhem, Helcult, Helfist, FallenChampions, Butcherhorde], [CsmV2_5AD])

    def check_rules(self):
        super(CsmV2_5, self).check_rules()
        roster_check = lambda roster: 1 if roster.is_base else (2 if roster.is_black else 3)
        if any([isinstance(m.sub_roster.roster, CsmV2_5CAD)
               for m in self.primary.units]):
            ally_marines = [m.sub_roster.roster
                            for m in self.secondary.units
                            if isinstance(m.sub_roster.roster, CsmV2_5AD)]
            ally_chapters = set([roster_check(ally) for ally in ally_marines])
            own_chapter = set([roster_check(self.primary.units[0].sub_roster.roster)])
            if len(own_chapter & ally_chapters):
                self.error("Cannot have primary and allied detachment from same codex or supplement")

detachments = [CsmV2_5CAD, CsmV2_5AD, Mayhem, Helfist, Helcult, FallenChampions, Butcherhorde]
'''

script = jedi.Script(column=24, source=source, line=344, encoding='utf-8', path=u'd:/hq-git/builder/games/wh40k/chaos_marines_v2/__init__.py')
script.completions()
jorgenschaefer commented 8 years ago

Hello and thank you for the report! This bug in Jedi was reported as #563 and fixed in Elpy v1.10.

Elpy..............: 1.8.1

Could you upgrade to a current version of Elpy and see if the problem persists?

jorgenschaefer commented 8 years ago

Lacking further updates, I assume this problem is resolved. Please do not hesitate to open a new issue if you have further questions!

Kurvivor19 commented 8 years ago

Sorry for not answering earlier. I have upgraded used elpy, and the problem is yet to surface. I will make an issue again if i encounter it