1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686 |
- # Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
- # Copyright (c) 2011, 2013-2015 Google, Inc.
- # Copyright (c) 2013-2016 Claudiu Popa <pcmanticore@gmail.com>
- # Copyright (c) 2015-2016 Cara Vinson <ceridwenv@gmail.com>
- # Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
-
- # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
- # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
-
-
- """
- This module contains the classes for "scoped" node, i.e. which are opening a
- new local scope in the language definition : Module, ClassDef, FunctionDef (and
- Lambda, GeneratorExp, DictComp and SetComp to some extent).
- """
-
- import sys
- import io
- import itertools
- import warnings
-
- import six
-
- from astroid import bases
- from astroid import context as contextmod
- from astroid import exceptions
- from astroid import decorators as decorators_mod
- from astroid.interpreter import objectmodel
- from astroid.interpreter import dunder_lookup
- from astroid import manager
- from astroid import mixins
- from astroid import node_classes
- from astroid import util
-
-
- BUILTINS = six.moves.builtins.__name__
- ITER_METHODS = ('__iter__', '__getitem__')
-
-
- def _c3_merge(sequences, cls, context):
- """Merges MROs in *sequences* to a single MRO using the C3 algorithm.
-
- Adapted from http://www.python.org/download/releases/2.3/mro/.
-
- """
- result = []
- while True:
- sequences = [s for s in sequences if s] # purge empty sequences
- if not sequences:
- return result
- for s1 in sequences: # find merge candidates among seq heads
- candidate = s1[0]
- for s2 in sequences:
- if candidate in s2[1:]:
- candidate = None
- break # reject the current head, it appears later
- else:
- break
- if not candidate:
- # Show all the remaining bases, which were considered as
- # candidates for the next mro sequence.
- raise exceptions.InconsistentMroError(
- message="Cannot create a consistent method resolution order "
- "for MROs {mros} of class {cls!r}.",
- mros=sequences, cls=cls, context=context)
-
- result.append(candidate)
- # remove the chosen candidate
- for seq in sequences:
- if seq[0] == candidate:
- del seq[0]
- return None
-
-
- def _verify_duplicates_mro(sequences, cls, context):
- for sequence in sequences:
- names = [(node.lineno, node.qname()) for node in sequence if node.name]
- if len(names) != len(set(names)):
- raise exceptions.DuplicateBasesError(
- message='Duplicates found in MROs {mros} for {cls!r}.',
- mros=sequences, cls=cls, context=context)
-
-
- def function_to_method(n, klass):
- if isinstance(n, FunctionDef):
- if n.type == 'classmethod':
- return bases.BoundMethod(n, klass)
- if n.type != 'staticmethod':
- return bases.UnboundMethod(n)
- return n
-
-
- MANAGER = manager.AstroidManager()
- def builtin_lookup(name):
- """lookup a name into the builtin module
- return the list of matching statements and the astroid for the builtin
- module
- """
- builtin_astroid = MANAGER.ast_from_module(six.moves.builtins)
- if name == '__dict__':
- return builtin_astroid, ()
- try:
- stmts = builtin_astroid.locals[name]
- except KeyError:
- stmts = ()
- return builtin_astroid, stmts
-
-
- # TODO move this Mixin to mixins.py; problem: 'FunctionDef' in _scope_lookup
- class LocalsDictNodeNG(node_classes.LookupMixIn,
- node_classes.NodeNG):
- """ this class provides locals handling common to Module, FunctionDef
- and ClassDef nodes, including a dict like interface for direct access
- to locals information
- """
-
- # attributes below are set by the builder module or by raw factories
-
- locals = {}
- """A map of the name of a local variable to the node defining the local.
-
- :type: dict(str, NodeNG)
- """
-
- def qname(self):
- """Get the 'qualified' name of the node.
-
- For example: module.name, module.class.name ...
-
- :returns: The qualified name.
- :rtype: str
- """
- # pylint: disable=no-member; github.com/pycqa/astroid/issues/278
- if self.parent is None:
- return self.name
- return '%s.%s' % (self.parent.frame().qname(), self.name)
-
- def frame(self):
- """The first parent frame node.
-
- A frame node is a :class:`Module`, :class:`FunctionDef`,
- or :class:`ClassDef`.
-
- :returns: The first parent frame node.
- :rtype: Module or FunctionDef or ClassDef
- """
- return self
-
- def scope(self):
- """The first parent node defining a new scope.
-
- :returns: The first parent scope node.
- :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
- """
- return self
-
- def _scope_lookup(self, node, name, offset=0):
- """XXX method for interfacing the scope lookup"""
- try:
- stmts = node._filter_stmts(self.locals[name], self, offset)
- except KeyError:
- stmts = ()
- if stmts:
- return self, stmts
- if self.parent: # i.e. not Module
- # nested scope: if parent scope is a function, that's fine
- # else jump to the module
- pscope = self.parent.scope()
- if not pscope.is_function:
- pscope = pscope.root()
- return pscope.scope_lookup(node, name)
- return builtin_lookup(name) # Module
-
- def set_local(self, name, stmt):
- """Define that the given name is declared in the given statement node.
-
- .. seealso:: :meth:`scope`
-
- :param name: The name that is being defined.
- :type name: str
-
- :param stmt: The statement that defines the given name.
- :type stmt: NodeNG
- """
- #assert not stmt in self.locals.get(name, ()), (self, stmt)
- self.locals.setdefault(name, []).append(stmt)
-
- __setitem__ = set_local
-
- def _append_node(self, child):
- """append a child, linking it in the tree"""
- # pylint: disable=no-member; depending by the class
- # which uses the current class as a mixin or base class.
- # It's rewritten in 2.0, so it makes no sense for now
- # to spend development time on it.
- self.body.append(child)
- child.parent = self
-
- def add_local_node(self, child_node, name=None):
- """Append a child that should alter the locals of this scope node.
-
- :param child_node: The child node that will alter locals.
- :type child_node: NodeNG
-
- :param name: The name of the local that will be altered by
- the given child node.
- :type name: str or None
- """
- if name != '__class__':
- # add __class__ node as a child will cause infinite recursion later!
- self._append_node(child_node)
- self.set_local(name or child_node.name, child_node)
-
- def __getitem__(self, item):
- """The first node the defines the given local.
-
- :param item: The name of the locally defined object.
- :type item: str
-
- :raises KeyError: If the name is not defined.
- """
- return self.locals[item][0]
-
- def __iter__(self):
- """Iterate over the names of locals defined in this scoped node.
-
- :returns: The names of the defined locals.
- :rtype: iterable(str)
- """
- return iter(self.keys())
-
- def keys(self):
- """The names of locals defined in this scoped node.
-
- :returns: The names of the defined locals.
- :rtype: list(str)
- """
- return list(self.locals.keys())
-
- def values(self):
- """The nodes that define the locals in this scoped node.
-
- :returns: The nodes that define locals.
- :rtype: list(NodeNG)
- """
- return [self[key] for key in self.keys()]
-
- def items(self):
- """Get the names of the locals and the node that defines the local.
-
- :returns: The names of locals and their asociated node.
- :rtype: list(tuple(str, NodeNG))
- """
- return list(zip(self.keys(), self.values()))
-
- def __contains__(self, name):
- """Check if a local is defined in this scope.
-
- :param name: The name of the local to check for.
- :type name: str
-
- :returns: True if this node has a local of the given name,
- False otherwise.
- :rtype: bool
- """
- return name in self.locals
-
-
- class Module(LocalsDictNodeNG):
- """Class representing an :class:`ast.Module` node.
-
- >>> node = astroid.extract_node('import astroid')
- >>> node
- <Import l.1 at 0x7f23b2e4e5c0>
- >>> node.parent
- <Module l.0 at 0x7f23b2e4eda0>
- """
- _astroid_fields = ('body',)
-
- fromlineno = 0
- """The first line that this node appears on in the source code.
-
- :type: int or None
- """
- lineno = 0
- """The line that this node appears on in the source code.
-
- :type: int or None
- """
-
- # attributes below are set by the builder module or by raw factories
-
- file = None
- """The path to the file that this ast has been extracted from.
-
- This will be ``None`` when the representation has been built from a
- built-in module.
-
- :type: str or None
- """
- file_bytes = None
- """The string/bytes that this ast was built from.
-
- :type: str or bytes or None
- """
- file_encoding = None
- """The encoding of the source file.
-
- This is used to get unicode out of a source file.
- Python 2 only.
-
- :type: str or None
- """
- name = None
- """The name of the module.
-
- :type: str or None
- """
- pure_python = None
- """Whether the ast was built from source.
-
- :type: bool or None
- """
- package = None
- """Whether the node represents a package or a module.
-
- :type: bool or None
- """
- globals = None
- """A map of the name of a global variable to the node defining the global.
-
- :type: dict(str, NodeNG)
- """
-
- # Future imports
- future_imports = None
- """The imports from ``__future__``.
-
- :type: set(str) or None
- """
- special_attributes = objectmodel.ModuleModel()
- """The names of special attributes that this module has.
-
- :type: objectmodel.ModuleModel
- """
-
- # names of module attributes available through the global scope
- scope_attrs = {'__name__', '__doc__', '__file__', '__path__', '__package__'}
- """The names of module attributes available through the global scope.
-
- :type: str(str)
- """
-
- _other_fields = ('name', 'doc', 'file', 'path', 'package',
- 'pure_python', 'future_imports')
- _other_other_fields = ('locals', 'globals')
-
- def __init__(self, name, doc, file=None, path=None, package=None,
- parent=None, pure_python=True):
- """
- :param name: The name of the module.
- :type name: str
-
- :param doc: The module docstring.
- :type doc: str
-
- :param file: The path to the file that this ast has been extracted from.
- :type file: str or None
-
- :param path:
- :type path: str or None
-
- :param package: Whether the node represents a package or a module.
- :type package: bool or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
-
- :param pure_python: Whether the ast was built from source.
- :type pure_python: bool or None
- """
- self.name = name
- self.doc = doc
- self.file = file
- self.path = path
- self.package = package
- self.parent = parent
- self.pure_python = pure_python
- self.locals = self.globals = {}
- """A map of the name of a local variable to the node defining the local.
-
- :type: dict(str, NodeNG)
- """
- self.body = []
- """The contents of the module.
-
- :type: list(NodeNG) or None
- """
- self.future_imports = set()
- # pylint: enable=redefined-builtin
-
- def postinit(self, body=None):
- """Do some setup after initialisation.
-
- :param body: The contents of the module.
- :type body: list(NodeNG) or None
- """
- self.body = body
-
- def _get_stream(self):
- if self.file_bytes is not None:
- return io.BytesIO(self.file_bytes)
- if self.file is not None:
- stream = open(self.file, 'rb')
- return stream
- return None
-
- def stream(self):
- """Get a stream to the underlying file or bytes.
-
- .. deprecated:: 1.5
-
- :type: file or io.BytesIO or None
- """
- return self._get_stream()
-
- def block_range(self, lineno):
- """Get a range from where this node starts to where this node ends.
-
- :param lineno: Unused.
- :type lineno: int
-
- :returns: The range of line numbers that this node belongs to.
- :rtype: tuple(int, int)
- """
- return self.fromlineno, self.tolineno
-
- def scope_lookup(self, node, name, offset=0):
- """Lookup where the given variable is assigned.
-
- :param node: The node to look for assignments up to.
- Any assignments after the given node are ignored.
- :type node: NodeNG
-
- :param name: The name of the variable to find assignments for.
- :type name: str
-
- :param offset: The line offset to filter statements up to.
- :type offset: int
-
- :returns: This scope node and the list of assignments associated to the
- given name according to the scope where it has been found (locals,
- globals or builtin).
- :rtype: tuple(str, list(NodeNG))
- """
- if name in self.scope_attrs and name not in self.locals:
- try:
- return self, self.getattr(name)
- except exceptions.AttributeInferenceError:
- return self, ()
- return self._scope_lookup(node, name, offset)
-
- def pytype(self):
- """Get the name of the type that this node represents.
-
- :returns: The name of the type.
- :rtype: str
- """
- return '%s.module' % BUILTINS
-
- def display_type(self):
- """A human readable type of this node.
-
- :returns: The type of this node.
- :rtype: str
- """
- return 'Module'
-
- def getattr(self, name, context=None, ignore_locals=False):
- result = []
- name_in_locals = name in self.locals
-
- if name in self.special_attributes and not ignore_locals and not name_in_locals:
- result = [self.special_attributes.lookup(name)]
- elif not ignore_locals and name_in_locals:
- result = self.locals[name]
- elif self.package:
- try:
- result = [self.import_module(name, relative_only=True)]
- except (exceptions.AstroidBuildingError, SyntaxError):
- util.reraise(exceptions.AttributeInferenceError(target=self,
- attribute=name,
- context=context))
- result = [n for n in result if not isinstance(n, node_classes.DelName)]
- if result:
- return result
- raise exceptions.AttributeInferenceError(target=self, attribute=name,
- context=context)
-
- def igetattr(self, name, context=None):
- """Infer the possible values of the given variable.
-
- :param name: The name of the variable to infer.
- :type name: str
-
- :returns: The inferred possible values.
- :rtype: iterable(NodeNG) or None
- """
- # set lookup name since this is necessary to infer on import nodes for
- # instance
- context = contextmod.copy_context(context)
- context.lookupname = name
- try:
- return bases._infer_stmts(self.getattr(name, context),
- context, frame=self)
- except exceptions.AttributeInferenceError as error:
- util.reraise(exceptions.InferenceError(
- error.message, target=self, attribute=name, context=context))
-
- def fully_defined(self):
- """Check if this module has been build from a .py file.
-
- If so, the module contains a complete representation,
- including the code.
-
- :returns: True if the module has been built from a .py file.
- :rtype: bool
- """
- return self.file is not None and self.file.endswith('.py')
-
- def statement(self):
- """The first parent node, including self, marked as statement node.
-
- :returns: The first parent statement.
- :rtype: NodeNG
- """
- return self
-
- def previous_sibling(self): #pylint: disable=useless-return
- """The previous sibling statement.
-
- :returns: The previous sibling statement node.
- :rtype: NodeNG or None
- """
- return
-
- def next_sibling(self): #pylint: disable=useless-return
- """The next sibling statement node.
-
- :returns: The next sibling statement node.
- :rtype: NodeNG or None
- """
- return
-
- if six.PY2:
- @decorators_mod.cachedproperty
- def _absolute_import_activated(self):
- for stmt in self.locals.get('absolute_import', ()):
- if isinstance(stmt, node_classes.ImportFrom) and stmt.modname == '__future__':
- return True
- return False
- else:
- _absolute_import_activated = True
-
- def absolute_import_activated(self):
- """Whether :pep:`328` absolute import behaviour has been enabled.
-
- :returns: True if :pep:`328` has been enabled, False otherwise.
- :rtype: bool
- """
- return self._absolute_import_activated
-
- def import_module(self, modname, relative_only=False, level=None):
- """Get the ast for a given module as if imported from this module.
-
- :param modname: The name of the module to "import".
- :type modname: str
-
- :param relative_only: Whether to only consider relative imports.
- :type relative_only: bool
-
- :param level: The level of relative import.
- :type level: int or None
-
- :returns: The imported module ast.
- :rtype: NodeNG
- """
- if relative_only and level is None:
- level = 0
- absmodname = self.relative_to_absolute_name(modname, level)
-
- try:
- return MANAGER.ast_from_module_name(absmodname)
- except exceptions.AstroidBuildingError:
- # we only want to import a sub module or package of this module,
- # skip here
- if relative_only:
- raise
- return MANAGER.ast_from_module_name(modname)
-
- def relative_to_absolute_name(self, modname, level):
- """Get the absolute module name for a relative import.
-
- The relative import can be implicit or explicit.
-
- :param modname: The module name to convert.
- :type modname: str
-
- :param level: The level of relative import.
- :type level: int
-
- :returns: The absolute module name.
- :rtype: str
-
- :raises TooManyLevelsError: When the relative import refers to a
- module too far above this one.
- """
- # XXX this returns non sens when called on an absolute import
- # like 'pylint.checkers.astroid.utils'
- # XXX doesn't return absolute name if self.name isn't absolute name
- if self.absolute_import_activated() and level is None:
- return modname
- if level:
- if self.package:
- level = level - 1
- if level and self.name.count('.') < level:
- raise exceptions.TooManyLevelsError(
- level=level, name=self.name)
-
- package_name = self.name.rsplit('.', level)[0]
- elif self.package:
- package_name = self.name
- else:
- package_name = self.name.rsplit('.', 1)[0]
-
- if package_name:
- if not modname:
- return package_name
- return '%s.%s' % (package_name, modname)
- return modname
-
- def wildcard_import_names(self):
- """The list of imported names when this module is 'wildcard imported'.
-
- It doesn't include the '__builtins__' name which is added by the
- current CPython implementation of wildcard imports.
-
- :returns: The list of imported names.
- :rtype: list(str)
- """
- # We separate the different steps of lookup in try/excepts
- # to avoid catching too many Exceptions
- default = [name for name in self.keys() if not name.startswith('_')]
- try:
- all_values = self['__all__']
- except KeyError:
- return default
-
- try:
- explicit = next(all_values.assigned_stmts())
- except exceptions.InferenceError:
- return default
- except AttributeError:
- # not an assignment node
- # XXX infer?
- return default
-
- # Try our best to detect the exported name.
- inferred = []
- try:
- explicit = next(explicit.infer())
- except exceptions.InferenceError:
- return default
- if not isinstance(explicit, (node_classes.Tuple, node_classes.List)):
- return default
-
- str_const = lambda node: (isinstance(node, node_classes.Const) and
- isinstance(node.value, six.string_types))
- for node in explicit.elts:
- if str_const(node):
- inferred.append(node.value)
- else:
- try:
- inferred_node = next(node.infer())
- except exceptions.InferenceError:
- continue
- if str_const(inferred_node):
- inferred.append(inferred_node.value)
- return inferred
-
- def public_names(self):
- """The list of the names that are publicly available in this module.
-
- :returns: The list of publc names.
- :rtype: list(str)
- """
- return [name for name in self.keys() if not name.startswith('_')]
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`Module` this is always ``True``.
- :rtype: bool
- """
- return True
-
-
- class ComprehensionScope(LocalsDictNodeNG):
- """Scoping for different types of comprehensions."""
- def frame(self):
- """The first parent frame node.
-
- A frame node is a :class:`Module`, :class:`FunctionDef`,
- or :class:`ClassDef`.
-
- :returns: The first parent frame node.
- :rtype: Module or FunctionDef or ClassDef
- """
- return self.parent.frame()
-
- scope_lookup = LocalsDictNodeNG._scope_lookup
-
-
- class GeneratorExp(ComprehensionScope):
- """Class representing an :class:`ast.GeneratorExp` node.
-
- >>> node = astroid.extract_node('(thing for thing in things if thing)')
- >>> node
- <GeneratorExp l.1 at 0x7f23b2e4e400>
- """
- _astroid_fields = ('elt', 'generators')
- _other_other_fields = ('locals',)
- elt = None
- """The element that forms the output of the expression.
-
- :type: NodeNG or None
- """
- generators = None
- """The generators that are looped through.
-
- :type: list(Comprehension) or None
- """
-
- def __init__(self, lineno=None, col_offset=None, parent=None):
- """
- :param lineno: The line that this node appears on in the source code.
- :type lineno: int or None
-
- :param col_offset: The column that this node appears on in the
- source code.
- :type col_offset: int or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
- """
- self.locals = {}
- """A map of the name of a local variable to the node defining the local.
-
- :type: dict(str, NodeNG)
- """
-
- super(GeneratorExp, self).__init__(lineno, col_offset, parent)
-
- def postinit(self, elt=None, generators=None):
- """Do some setup after initialisation.
-
- :param elt: The element that forms the output of the expression.
- :type elt: NodeNG or None
-
- :param generators: The generators that are looped through.
- :type generators: list(Comprehension) or None
- """
- self.elt = elt
- if generators is None:
- self.generators = []
- else:
- self.generators = generators
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`GeneratorExp` this is always ``True``.
- :rtype: bool
- """
- return True
-
-
- class DictComp(ComprehensionScope):
- """Class representing an :class:`ast.DictComp` node.
-
- >>> node = astroid.extract_node('{k:v for k, v in things if k > v}')
- >>> node
- <DictComp l.1 at 0x7f23b2e41d68>
- """
- _astroid_fields = ('key', 'value', 'generators')
- _other_other_fields = ('locals',)
- key = None
- """What produces the keys.
-
- :type: NodeNG or None
- """
- value = None
- """What produces the values.
-
- :type: NodeNG or None
- """
- generators = None
- """The generators that are looped through.
-
- :type: list(Comprehension) or None
- """
-
- def __init__(self, lineno=None, col_offset=None, parent=None):
- """
- :param lineno: The line that this node appears on in the source code.
- :type lineno: int or None
-
- :param col_offset: The column that this node appears on in the
- source code.
- :type col_offset: int or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
- """
- self.locals = {}
- """A map of the name of a local variable to the node defining the local.
-
- :type: dict(str, NodeNG)
- """
-
- super(DictComp, self).__init__(lineno, col_offset, parent)
-
- def postinit(self, key=None, value=None, generators=None):
- """Do some setup after initialisation.
-
- :param key: What produces the keys.
- :type key: NodeNG or None
-
- :param value: What produces the values.
- :type value: NodeNG or None
-
- :param generators: The generators that are looped through.
- :type generators: list(Comprehension) or None
- """
- self.key = key
- self.value = value
- if generators is None:
- self.generators = []
- else:
- self.generators = generators
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`DictComp` this is always :class:`Uninferable`.
- :rtype: Uninferable
- """
- return util.Uninferable
-
-
- class SetComp(ComprehensionScope):
- """Class representing an :class:`ast.SetComp` node.
-
- >>> node = astroid.extract_node('{thing for thing in things if thing}')
- >>> node
- <SetComp l.1 at 0x7f23b2e41898>
- """
- _astroid_fields = ('elt', 'generators')
- _other_other_fields = ('locals',)
- elt = None
- """The element that forms the output of the expression.
-
- :type: NodeNG or None
- """
- generators = None
- """The generators that are looped through.
-
- :type: list(Comprehension) or None
- """
-
- def __init__(self, lineno=None, col_offset=None, parent=None):
- """
- :param lineno: The line that this node appears on in the source code.
- :type lineno: int or None
-
- :param col_offset: The column that this node appears on in the
- source code.
- :type col_offset: int or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
- """
- self.locals = {}
- """A map of the name of a local variable to the node defining the local.
-
- :type: dict(str, NodeNG)
- """
-
- super(SetComp, self).__init__(lineno, col_offset, parent)
-
- def postinit(self, elt=None, generators=None):
- """Do some setup after initialisation.
-
- :param elt: The element that forms the output of the expression.
- :type elt: NodeNG or None
-
- :param generators: The generators that are looped through.
- :type generators: list(Comprehension) or None
- """
- self.elt = elt
- if generators is None:
- self.generators = []
- else:
- self.generators = generators
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`SetComp` this is always :class:`Uninferable`.
- :rtype: Uninferable
- """
- return util.Uninferable
-
-
- class _ListComp(node_classes.NodeNG):
- """Class representing an :class:`ast.ListComp` node.
-
- >>> node = astroid.extract_node('[thing for thing in things if thing]')
- >>> node
- <ListComp l.1 at 0x7f23b2e418d0>
- """
- _astroid_fields = ('elt', 'generators')
- elt = None
- """The element that forms the output of the expression.
-
- :type: NodeNG or None
- """
- generators = None
- """The generators that are looped through.
-
- :type: list(Comprehension) or None
- """
-
- def postinit(self, elt=None, generators=None):
- """Do some setup after initialisation.
-
- :param elt: The element that forms the output of the expression.
- :type elt: NodeNG or None
-
- :param generators: The generators that are looped through.
- :type generators: list(Comprehension) or None
- """
- self.elt = elt
- self.generators = generators
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`ListComp` this is always :class:`Uninferable`.
- :rtype: Uninferable
- """
- return util.Uninferable
-
-
- if six.PY3:
- class ListComp(_ListComp, ComprehensionScope):
- """Class representing an :class:`ast.ListComp` node.
-
- >>> node = astroid.extract_node('[thing for thing in things if thing]')
- >>> node
- <ListComp l.1 at 0x7f23b2e418d0>
- """
- _other_other_fields = ('locals',)
-
- def __init__(self, lineno=None, col_offset=None, parent=None):
- self.locals = {}
- """A map of the name of a local variable to the node defining it.
-
- :type: dict(str, NodeNG)
- """
-
- super(ListComp, self).__init__(lineno, col_offset, parent)
- else:
- class ListComp(_ListComp):
- """Class representing an :class:`ast.ListComp` node.
-
- >>> node = astroid.extract_node('[thing for thing in things if thing]')
- >>> node
- <ListComp l.1 at 0x7f23b2e418d0>
- """
-
-
- def _infer_decorator_callchain(node):
- """Detect decorator call chaining and see if the end result is a
- static or a classmethod.
- """
- if not isinstance(node, FunctionDef):
- return None
- if not node.parent:
- return None
- try:
- # TODO: We don't handle multiple inference results right now,
- # because there's no flow to reason when the return
- # is what we are looking for, a static or a class method.
- result = next(node.infer_call_result(node.parent))
- except (StopIteration, exceptions.InferenceError):
- return None
- if isinstance(result, bases.Instance):
- result = result._proxied
- if isinstance(result, ClassDef):
- if result.is_subtype_of('%s.classmethod' % BUILTINS):
- return 'classmethod'
- if result.is_subtype_of('%s.staticmethod' % BUILTINS):
- return 'staticmethod'
- return None
-
-
- class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG):
- """Class representing an :class:`ast.Lambda` node.
-
- >>> node = astroid.extract_node('lambda arg: arg + 1')
- >>> node
- <Lambda.<lambda> l.1 at 0x7f23b2e41518>
- """
- _astroid_fields = ('args', 'body',)
- _other_other_fields = ('locals',)
- name = '<lambda>'
-
- # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod'
- @property
- def type(self):
- """Whether this is a method or function.
-
- :returns: 'method' if this is a method, 'function' otherwise.
- :rtype: str
- """
- # pylint: disable=no-member
- if self.args.args and self.args.args[0].name == 'self':
- if isinstance(self.parent.scope(), ClassDef):
- return 'method'
- return 'function'
-
- def __init__(self, lineno=None, col_offset=None, parent=None):
- """
- :param lineno: The line that this node appears on in the source code.
- :type lineno: int or None
-
- :param col_offset: The column that this node appears on in the
- source code.
- :type col_offset: int or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
- """
- self.locals = {}
- """A map of the name of a local variable to the node defining it.
-
- :type: dict(str, NodeNG)
- """
-
- self.args = []
- """The arguments that the function takes.
-
- :type: Arguments or list
- """
-
- self.body = []
- """The contents of the function body.
-
- :type: list(NodeNG)
- """
-
- super(Lambda, self).__init__(lineno, col_offset, parent)
-
- def postinit(self, args, body):
- """Do some setup after initialisation.
-
- :param args: The arguments that the function takes.
- :type args: Arguments
-
- :param body: The contents of the function body.
- :type body: list(NodeNG)
- """
- self.args = args
- self.body = body
-
- def pytype(self):
- """Get the name of the type that this node represents.
-
- :returns: The name of the type.
- :rtype: str
- """
- if 'method' in self.type:
- return '%s.instancemethod' % BUILTINS
- return '%s.function' % BUILTINS
-
- def display_type(self):
- """A human readable type of this node.
-
- :returns: The type of this node.
- :rtype: str
- """
- if 'method' in self.type:
- return 'Method'
- return 'Function'
-
- def callable(self):
- """Whether this node defines something that is callable.
-
- :returns: True if this defines something that is callable,
- False otherwise.
- For a :class:`Lambda` this is always ``True``.
- :rtype: bool
- """
- return True
-
- def argnames(self):
- """Get the names of each of the arguments.
-
- :returns: The names of the arguments.
- :rtype: list(str)
- """
- # pylint: disable=no-member; github.com/pycqa/astroid/issues/291
- # args is in fact redefined later on by postinit. Can't be changed
- # to None due to a strong interaction between Lambda and FunctionDef.
-
- if self.args.args: # maybe None with builtin functions
- names = _rec_get_names(self.args.args)
- else:
- names = []
- if self.args.vararg:
- names.append(self.args.vararg)
- if self.args.kwarg:
- names.append(self.args.kwarg)
- return names
-
- def infer_call_result(self, caller, context=None):
- """Infer what the function returns when called.
-
- :param caller: Unused
- :type caller: object
- """
- # pylint: disable=no-member; github.com/pycqa/astroid/issues/291
- # args is in fact redefined later on by postinit. Can't be changed
- # to None due to a strong interaction between Lambda and FunctionDef.
-
- return self.body.infer(context)
-
- def scope_lookup(self, node, name, offset=0):
- """Lookup where the given names is assigned.
-
- :param node: The node to look for assignments up to.
- Any assignments after the given node are ignored.
- :type node: NodeNG
-
- :param name: The name to find assignments for.
- :type name: str
-
- :param offset: The line offset to filter statements up to.
- :type offset: int
-
- :returns: This scope node and the list of assignments associated to the
- given name according to the scope where it has been found (locals,
- globals or builtin).
- :rtype: tuple(str, list(NodeNG))
- """
- # pylint: disable=no-member; github.com/pycqa/astroid/issues/291
- # args is in fact redefined later on by postinit. Can't be changed
- # to None due to a strong interaction between Lambda and FunctionDef.
-
- if node in self.args.defaults or node in self.args.kw_defaults:
- frame = self.parent.frame()
- # line offset to avoid that def func(f=func) resolve the default
- # value to the defined function
- offset = -1
- else:
- # check this is not used in function decorators
- frame = self
- return frame._scope_lookup(node, name, offset)
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`Lambda` this is always ``True``.
- :rtype: bool
- """
- return True
-
-
- class FunctionDef(node_classes.Statement, Lambda):
- """Class representing an :class:`ast.FunctionDef`.
-
- >>> node = astroid.extract_node('''
- ... def my_func(arg):
- ... return arg + 1
- ... ''')
- >>> node
- <FunctionDef.my_func l.2 at 0x7f23b2e71e10>
- """
- if six.PY3:
- _astroid_fields = ('decorators', 'args', 'returns', 'body')
- returns = None
- else:
- _astroid_fields = ('decorators', 'args', 'body')
- decorators = None
- """The decorators that are applied to this method or function.
-
- :type: Decorators or None
- """
- special_attributes = objectmodel.FunctionModel()
- """The names of special attributes that this function has.
-
- :type: objectmodel.FunctionModel
- """
- is_function = True
- """Whether this node indicates a function.
-
- For a :class:`FunctionDef` this is always ``True``.
-
- :type: bool
- """
- # attributes below are set by the builder module or by raw factories
- _other_fields = ('name', 'doc')
- _other_other_fields = ('locals', '_type')
- _type = None
-
- def __init__(self, name=None, doc=None, lineno=None,
- col_offset=None, parent=None):
- """
- :param name: The name of the function.
- :type name: str or None
-
- :param doc: The function's docstring.
- :type doc: str or None
-
- :param lineno: The line that this node appears on in the source code.
- :type lineno: int or None
-
- :param col_offset: The column that this node appears on in the
- source code.
- :type col_offset: int or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
- """
- self.name = name
- """The name of the function.
-
- :type name: str or None
- """
-
- self.doc = doc
- """The function's docstring.
-
- :type doc: str or None
- """
-
- self.instance_attrs = {}
- super(FunctionDef, self).__init__(lineno, col_offset, parent)
- if parent:
- frame = parent.frame()
- frame.set_local(name, self)
-
- # pylint: disable=arguments-differ; different than Lambdas
- def postinit(self, args, body, decorators=None, returns=None):
- """Do some setup after initialisation.
-
- :param args: The arguments that the function takes.
- :type args: Arguments or list
-
- :param body: The contents of the function body.
- :type body: list(NodeNG)
-
- :param decorators: The decorators that are applied to this
- method or function.
- :type decorators: Decorators or None
- """
- self.args = args
- self.body = body
- self.decorators = decorators
- self.returns = returns
-
- if six.PY3 and isinstance(self.parent.frame(), ClassDef):
- self.set_local('__class__', self.parent.frame())
-
- @decorators_mod.cachedproperty
- def extra_decorators(self):
- """The extra decorators that this function can have.
-
- Additional decorators are considered when they are used as
- assignments, as in ``method = staticmethod(method)``.
- The property will return all the callables that are used for
- decoration.
-
- :type: list(NodeNG)
- """
- frame = self.parent.frame()
- if not isinstance(frame, ClassDef):
- return []
-
- decorators = []
- for assign in frame.nodes_of_class(node_classes.Assign):
- if (isinstance(assign.value, node_classes.Call)
- and isinstance(assign.value.func, node_classes.Name)):
- for assign_node in assign.targets:
- if not isinstance(assign_node, node_classes.AssignName):
- # Support only `name = callable(name)`
- continue
-
- if assign_node.name != self.name:
- # Interested only in the assignment nodes that
- # decorates the current method.
- continue
- try:
- meth = frame[self.name]
- except KeyError:
- continue
- else:
- # Must be a function and in the same frame as the
- # original method.
- if (isinstance(meth, FunctionDef)
- and assign_node.frame() == frame):
- decorators.append(assign.value)
- return decorators
-
- @decorators_mod.cachedproperty
- def type(self):
- """The function type for this node.
-
- Possible values are: method, function, staticmethod, classmethod.
-
- :type: str
- """
- builtin_descriptors = {'classmethod', 'staticmethod'}
-
- for decorator in self.extra_decorators:
- if decorator.func.name in builtin_descriptors:
- return decorator.func.name
-
- frame = self.parent.frame()
- type_name = 'function'
- if isinstance(frame, ClassDef):
- if self.name == '__new__':
- return 'classmethod'
- elif sys.version_info >= (3, 6) and self.name == '__init_subclass__':
- return 'classmethod'
- else:
- type_name = 'method'
-
- if not self.decorators:
- return type_name
-
- for node in self.decorators.nodes:
- if isinstance(node, node_classes.Name):
- if node.name in builtin_descriptors:
- return node.name
-
- if isinstance(node, node_classes.Call):
- # Handle the following case:
- # @some_decorator(arg1, arg2)
- # def func(...)
- #
- try:
- current = next(node.func.infer())
- except exceptions.InferenceError:
- continue
- _type = _infer_decorator_callchain(current)
- if _type is not None:
- return _type
-
- try:
- for inferred in node.infer():
- # Check to see if this returns a static or a class method.
- _type = _infer_decorator_callchain(inferred)
- if _type is not None:
- return _type
-
- if not isinstance(inferred, ClassDef):
- continue
- for ancestor in inferred.ancestors():
- if not isinstance(ancestor, ClassDef):
- continue
- if ancestor.is_subtype_of('%s.classmethod' % BUILTINS):
- return 'classmethod'
- elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS):
- return 'staticmethod'
- except exceptions.InferenceError:
- pass
- return type_name
-
- @decorators_mod.cachedproperty
- def fromlineno(self):
- """The first line that this node appears on in the source code.
-
- :type: int or None
- """
- # lineno is the line number of the first decorator, we want the def
- # statement lineno
- lineno = self.lineno
- if self.decorators is not None:
- lineno += sum(node.tolineno - node.lineno + 1
- for node in self.decorators.nodes)
-
- return lineno
-
- @decorators_mod.cachedproperty
- def blockstart_tolineno(self):
- """The line on which the beginning of this block ends.
-
- :type: int
- """
- return self.args.tolineno
-
- def block_range(self, lineno):
- """Get a range from the given line number to where this node ends.
-
- :param lineno: Unused.
- :type lineno: int
-
- :returns: The range of line numbers that this node belongs to,
- :rtype: tuple(int, int)
- """
- return self.fromlineno, self.tolineno
-
- def getattr(self, name, context=None):
- """this method doesn't look in the instance_attrs dictionary since it's
- done by an Instance proxy at inference time.
- """
- if name in self.instance_attrs:
- return self.instance_attrs[name]
- if name in self.special_attributes:
- return [self.special_attributes.lookup(name)]
- raise exceptions.AttributeInferenceError(target=self, attribute=name)
-
- def igetattr(self, name, context=None):
- """Inferred getattr, which returns an iterator of inferred statements."""
- try:
- return bases._infer_stmts(self.getattr(name, context),
- context, frame=self)
- except exceptions.AttributeInferenceError as error:
- util.reraise(exceptions.InferenceError(
- error.message, target=self, attribute=name, context=context))
-
- def is_method(self):
- """Check if this function node represents a method.
-
- :returns: True if this is a method, False otherwise.
- :rtype: bool
- """
- # check we are defined in a ClassDef, because this is usually expected
- # (e.g. pylint...) when is_method() return True
- return self.type != 'function' and isinstance(self.parent.frame(), ClassDef)
-
- @decorators_mod.cached
- def decoratornames(self):
- """Get the qualified names of each of the decorators on this function.
-
- :returns: The names of the decorators.
- :rtype: set(str)
- """
- result = set()
- decoratornodes = []
- if self.decorators is not None:
- decoratornodes += self.decorators.nodes
- decoratornodes += self.extra_decorators
- for decnode in decoratornodes:
- try:
- for infnode in decnode.infer():
- result.add(infnode.qname())
- except exceptions.InferenceError:
- continue
- return result
-
- def is_bound(self):
- """Check if the function is bound to an instance or class.
-
- :returns: True if the function is bound to an instance or class,
- False otherwise.
- :rtype: bool
- """
- return self.type == 'classmethod'
-
- def is_abstract(self, pass_is_abstract=True):
- """Check if the method is abstract.
-
- A method is considered abstract if any of the following is true:
- * The only statement is 'raise NotImplementedError'
- * The only statement is 'pass' and pass_is_abstract is True
- * The method is annotated with abc.astractproperty/abc.abstractmethod
-
- :returns: True if the method is abstract, False otherwise.
- :rtype: bool
- """
- if self.decorators:
- for node in self.decorators.nodes:
- try:
- inferred = next(node.infer())
- except exceptions.InferenceError:
- continue
- if inferred and inferred.qname() in ('abc.abstractproperty',
- 'abc.abstractmethod'):
- return True
-
- for child_node in self.body:
- if isinstance(child_node, node_classes.Raise):
- if child_node.raises_not_implemented():
- return True
- return pass_is_abstract and isinstance(child_node, node_classes.Pass)
- # empty function is the same as function with a single "pass" statement
- if pass_is_abstract:
- return True
-
- def is_generator(self):
- """Check if this is a generator function.
-
- :returns: True is this is a generator function, False otherwise.
- :rtype: bool
- """
- yield_nodes = (node_classes.Yield, node_classes.YieldFrom)
- return next(self.nodes_of_class(yield_nodes,
- skip_klass=(FunctionDef, Lambda)), False)
-
- def infer_call_result(self, caller, context=None):
- """Infer what the function returns when called.
-
- :returns: What the function returns.
- :rtype: iterable(NodeNG or Uninferable) or None
- """
- if self.is_generator():
- result = bases.Generator(self)
- yield result
- return
- # This is really a gigantic hack to work around metaclass generators
- # that return transient class-generating functions. Pylint's AST structure
- # cannot handle a base class object that is only used for calling __new__,
- # but does not contribute to the inheritance structure itself. We inject
- # a fake class into the hierarchy here for several well-known metaclass
- # generators, and filter it out later.
- if (self.name == 'with_metaclass' and
- len(self.args.args) == 1 and
- self.args.vararg is not None):
- metaclass = next(caller.args[0].infer(context))
- if isinstance(metaclass, ClassDef):
- c = ClassDef('temporary_class', None)
- c.hide = True
- c.parent = self
- class_bases = [next(b.infer(context)) for b in caller.args[1:]]
- c.bases = [base for base in class_bases if base != util.Uninferable]
- c._metaclass = metaclass
- yield c
- return
- returns = self.nodes_of_class(node_classes.Return, skip_klass=FunctionDef)
- for returnnode in returns:
- if returnnode.value is None:
- yield node_classes.Const(None)
- else:
- try:
- for inferred in returnnode.value.infer(context):
- yield inferred
- except exceptions.InferenceError:
- yield util.Uninferable
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`FunctionDef` this is always ``True``.
- :rtype: bool
- """
- return True
-
-
- class AsyncFunctionDef(FunctionDef):
- """Class representing an :class:`ast.FunctionDef` node.
-
- A :class:`AsyncFunctionDef` is an asynchronous function
- created with the `async` keyword.
-
- >>> node = astroid.extract_node('''
- async def func(things):
- async for thing in things:
- print(thing)
- ''')
- >>> node
- <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
- >>> node.body[0]
- <AsyncFor l.3 at 0x7f23b2e417b8>
- """
-
-
- def _rec_get_names(args, names=None):
- """return a list of all argument names"""
- if names is None:
- names = []
- for arg in args:
- if isinstance(arg, node_classes.Tuple):
- _rec_get_names(arg.elts, names)
- else:
- names.append(arg.name)
- return names
-
-
- def _is_metaclass(klass, seen=None):
- """ Return if the given class can be
- used as a metaclass.
- """
- if klass.name == 'type':
- return True
- if seen is None:
- seen = set()
- for base in klass.bases:
- try:
- for baseobj in base.infer():
- baseobj_name = baseobj.qname()
- if baseobj_name in seen:
- continue
- else:
- seen.add(baseobj_name)
- if isinstance(baseobj, bases.Instance):
- # not abstract
- return False
- if baseobj is util.Uninferable:
- continue
- if baseobj is klass:
- continue
- if not isinstance(baseobj, ClassDef):
- continue
- if baseobj._type == 'metaclass':
- return True
- if _is_metaclass(baseobj, seen):
- return True
- except exceptions.InferenceError:
- continue
- return False
-
-
- def _class_type(klass, ancestors=None):
- """return a ClassDef node type to differ metaclass and exception
- from 'regular' classes
- """
- # XXX we have to store ancestors in case we have a ancestor loop
- if klass._type is not None:
- return klass._type
- if _is_metaclass(klass):
- klass._type = 'metaclass'
- elif klass.name.endswith('Exception'):
- klass._type = 'exception'
- else:
- if ancestors is None:
- ancestors = set()
- klass_name = klass.qname()
- if klass_name in ancestors:
- # XXX we are in loop ancestors, and have found no type
- klass._type = 'class'
- return 'class'
- ancestors.add(klass_name)
- for base in klass.ancestors(recurs=False):
- name = _class_type(base, ancestors)
- if name != 'class':
- if name == 'metaclass' and not _is_metaclass(klass):
- # don't propagate it if the current class
- # can't be a metaclass
- continue
- klass._type = base.type
- break
- if klass._type is None:
- klass._type = 'class'
- return klass._type
-
-
- def get_wrapping_class(node):
- """Get the class that wraps the given node.
-
- We consider that a class wraps a node if the class
- is a parent for the said node.
-
- :returns: The class that wraps the given node
- :rtype: ClassDef or None
- """
-
- klass = node.frame()
- while klass is not None and not isinstance(klass, ClassDef):
- if klass.parent is None:
- klass = None
- else:
- klass = klass.parent.frame()
- return klass
-
-
-
- class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
- node_classes.Statement):
- """Class representing an :class:`ast.ClassDef` node.
-
- >>> node = astroid.extract_node('''
- class Thing:
- def my_meth(self, arg):
- return arg + self.offset
- ''')
- >>> node
- <ClassDef.Thing l.2 at 0x7f23b2e9e748>
- """
-
- # some of the attributes below are set by the builder module or
- # by a raw factories
-
- # a dictionary of class instances attributes
- _astroid_fields = ('decorators', 'bases', 'body') # name
-
- decorators = None
- """The decorators that are applied to this class.
-
- :type: Decorators or None
- """
- special_attributes = objectmodel.ClassModel()
- """The names of special attributes that this class has.
-
- :type: objectmodel.ClassModel
- """
-
- _type = None
- _metaclass_hack = False
- hide = False
- type = property(_class_type,
- doc=("The class type for this node.\n\n"
- "Possible values are: class, metaclass, exception.\n\n"
- ":type: str"))
- _other_fields = ('name', 'doc')
- _other_other_fields = ('locals', '_newstyle')
- _newstyle = None
-
- def __init__(self, name=None, doc=None, lineno=None,
- col_offset=None, parent=None):
- """
- :param name: The name of the class.
- :type name: str or None
-
- :param doc: The function's docstring.
- :type doc: str or None
-
- :param lineno: The line that this node appears on in the source code.
- :type lineno: int or None
-
- :param col_offset: The column that this node appears on in the
- source code.
- :type col_offset: int or None
-
- :param parent: The parent node in the syntax tree.
- :type parent: NodeNG or None
- """
- self.instance_attrs = {}
- self.locals = {}
- """A map of the name of a local variable to the node defining it.
-
- :type: dict(str, NodeNG)
- """
-
- self.keywords = []
- """The keywords given to the class definition.
-
- This is usually for :pep:`3115` style metaclass declaration.
-
- :type: list(Keyword) or None
- """
-
- self.bases = []
- """What the class inherits from.
-
- :type: list(NodeNG)
- """
-
- self.body = []
- """The contents of the class body.
-
- :type: list(NodeNG)
- """
-
- self.name = name
- """The name of the class.
-
- :type name: str or None
- """
-
- self.doc = doc
- """The class' docstring.
-
- :type doc: str or None
- """
-
- super(ClassDef, self).__init__(lineno, col_offset, parent)
- if parent is not None:
- parent.frame().set_local(name, self)
-
- # pylint: disable=redefined-outer-name
- def postinit(self, bases, body, decorators, newstyle=None, metaclass=None, keywords=None):
- """Do some setup after initialisation.
-
- :param bases: What the class inherits from.
- :type bases: list(NodeNG)
-
- :param body: The contents of the class body.
- :type body: list(NodeNG)
-
- :param decorators: The decorators that are applied to this class.
- :type decorators: Decorators or None
-
- :param newstyle: Whether this is a new style class or not.
- :type newstyle: bool or None
-
- :param metaclass: The metaclass of this class.
- :type metaclass: NodeNG or None
-
- :param keywords: The keywords given to the class definition.
- :type keywords: list(Keyword) or None
- """
- self.keywords = keywords
- self.bases = bases
- self.body = body
- self.decorators = decorators
- if newstyle is not None:
- self._newstyle = newstyle
- if metaclass is not None:
- self._metaclass = metaclass
-
- def _newstyle_impl(self, context=None):
- if context is None:
- context = contextmod.InferenceContext()
- if self._newstyle is not None:
- return self._newstyle
- for base in self.ancestors(recurs=False, context=context):
- if base._newstyle_impl(context):
- self._newstyle = True
- break
- klass = self.declared_metaclass()
- # could be any callable, we'd need to infer the result of klass(name,
- # bases, dict). punt if it's not a class node.
- if klass is not None and isinstance(klass, ClassDef):
- self._newstyle = klass._newstyle_impl(context)
- if self._newstyle is None:
- self._newstyle = False
- return self._newstyle
-
- _newstyle = None
- newstyle = property(_newstyle_impl,
- doc=("Whether this is a new style class or not\n\n"
- ":type: bool or None"))
-
- @decorators_mod.cachedproperty
- def blockstart_tolineno(self):
- """The line on which the beginning of this block ends.
-
- :type: int
- """
- if self.bases:
- return self.bases[-1].tolineno
-
- return self.fromlineno
-
- def block_range(self, lineno):
- """Get a range from the given line number to where this node ends.
-
- :param lineno: Unused.
- :type lineno: int
-
- :returns: The range of line numbers that this node belongs to,
- :rtype: tuple(int, int)
- """
- return self.fromlineno, self.tolineno
-
- def pytype(self):
- """Get the name of the type that this node represents.
-
- :returns: The name of the type.
- :rtype: str
- """
- if self.newstyle:
- return '%s.type' % BUILTINS
- return '%s.classobj' % BUILTINS
-
- def display_type(self):
- """A human readable type of this node.
-
- :returns: The type of this node.
- :rtype: str
- """
- return 'Class'
-
- def callable(self):
- """Whether this node defines something that is callable.
-
- :returns: True if this defines something that is callable,
- False otherwise.
- For a :class:`ClassDef` this is always ``True``.
- :rtype: bool
- """
- return True
-
- def is_subtype_of(self, type_name, context=None):
- """Whether this class is a subtype of the given type.
-
- :param type_name: The name of the type of check against.
- :type type_name: str
-
- :returns: True if this class is a subtype of the given type,
- False otherwise.
- :rtype: bool
- """
- if self.qname() == type_name:
- return True
- for anc in self.ancestors(context=context):
- if anc.qname() == type_name:
- return True
- return False
-
- def _infer_type_call(self, caller, context):
- name_node = next(caller.args[0].infer(context))
- if (isinstance(name_node, node_classes.Const) and
- isinstance(name_node.value, six.string_types)):
- name = name_node.value
- else:
- return util.Uninferable
-
- result = ClassDef(name, None)
-
- # Get the bases of the class.
- class_bases = next(caller.args[1].infer(context))
- if isinstance(class_bases, (node_classes.Tuple, node_classes.List)):
- result.bases = class_bases.itered()
- else:
- # There is currently no AST node that can represent an 'unknown'
- # node (Uninferable is not an AST node), therefore we simply return Uninferable here
- # although we know at least the name of the class.
- return util.Uninferable
-
- # Get the members of the class
- try:
- members = next(caller.args[2].infer(context))
- except exceptions.InferenceError:
- members = None
-
- if members and isinstance(members, node_classes.Dict):
- for attr, value in members.items:
- if (isinstance(attr, node_classes.Const) and
- isinstance(attr.value, six.string_types)):
- result.locals[attr.value] = [value]
-
- result.parent = caller.parent
- return result
-
- def infer_call_result(self, caller, context=None):
- """infer what a class is returning when called"""
- if (self.is_subtype_of('%s.type' % (BUILTINS,), context)
- and len(caller.args) == 3):
- result = self._infer_type_call(caller, context)
- yield result
- else:
- yield bases.Instance(self)
-
- def scope_lookup(self, node, name, offset=0):
- """Lookup where the given name is assigned.
-
- :param node: The node to look for assignments up to.
- Any assignments after the given node are ignored.
- :type node: NodeNG
-
- :param name: The name to find assignments for.
- :type name: str
-
- :param offset: The line offset to filter statements up to.
- :type offset: int
-
- :returns: This scope node and the list of assignments associated to the
- given name according to the scope where it has been found (locals,
- globals or builtin).
- :rtype: tuple(str, list(NodeNG))
- """
- # If the name looks like a builtin name, just try to look
- # into the upper scope of this class. We might have a
- # decorator that it's poorly named after a builtin object
- # inside this class.
- lookup_upper_frame = (
- isinstance(node.parent, node_classes.Decorators) and
- name in MANAGER.astroid_cache[six.moves.builtins.__name__]
- )
- if any(node == base or base.parent_of(node)
- for base in self.bases) or lookup_upper_frame:
- # Handle the case where we have either a name
- # in the bases of a class, which exists before
- # the actual definition or the case where we have
- # a Getattr node, with that name.
- #
- # name = ...
- # class A(name):
- # def name(self): ...
- #
- # import name
- # class A(name.Name):
- # def name(self): ...
-
- frame = self.parent.frame()
- # line offset to avoid that class A(A) resolve the ancestor to
- # the defined class
- offset = -1
- else:
- frame = self
- return frame._scope_lookup(node, name, offset)
-
- @property
- def basenames(self):
- """The names of the parent classes
-
- Names are given in the order they appear in the class definition.
-
- :type: list(str)
- """
- return [bnode.as_string() for bnode in self.bases]
-
- def ancestors(self, recurs=True, context=None):
- """Iterate over the base classes in prefixed depth first order.
-
- :param recurs: Whether to recurse or return direct ancestors only.
- :type recurs: bool
-
- :returns: The base classes
- :rtype: iterable(NodeNG)
- """
- # FIXME: should be possible to choose the resolution order
- # FIXME: inference make infinite loops possible here
- yielded = set([self])
- if context is None:
- context = contextmod.InferenceContext()
- if six.PY3:
- if not self.bases and self.qname() != 'builtins.object':
- yield builtin_lookup("object")[1][0]
- return
-
- for stmt in self.bases:
- with context.restore_path():
- try:
- for baseobj in stmt.infer(context):
- if not isinstance(baseobj, ClassDef):
- if isinstance(baseobj, bases.Instance):
- baseobj = baseobj._proxied
- else:
- continue
- if not baseobj.hide:
- if baseobj in yielded:
- continue
- yielded.add(baseobj)
- yield baseobj
- if recurs:
- for grandpa in baseobj.ancestors(recurs=True,
- context=context):
- if grandpa is self:
- # This class is the ancestor of itself.
- break
- if grandpa in yielded:
- continue
- yielded.add(grandpa)
- yield grandpa
- except exceptions.InferenceError:
- continue
-
- def local_attr_ancestors(self, name, context=None):
- """Iterate over the parents that define the given name.
-
- :param name: The name to find definitions for.
- :type name: str
-
- :returns: The parents that define the given name.
- :rtype: iterable(NodeNG)
- """
- if self.newstyle and all(n.newstyle for n in self.ancestors(context)):
- # Look up in the mro if we can. This will result in the
- # attribute being looked up just as Python does it.
- try:
- ancestors = self.mro(context)[1:]
- except exceptions.MroError:
- # Fallback to use ancestors, we can't determine
- # a sane MRO.
- ancestors = self.ancestors(context=context)
- else:
- ancestors = self.ancestors(context=context)
- for astroid in ancestors:
- if name in astroid:
- yield astroid
-
- def instance_attr_ancestors(self, name, context=None):
- """Iterate over the parents that define the given name as an attribute.
-
- :param name: The name to find definitions for.
- :type name: str
-
- :returns: The parents that define the given name as
- an instance attribute.
- :rtype: iterable(NodeNG)
- """
- for astroid in self.ancestors(context=context):
- if name in astroid.instance_attrs:
- yield astroid
-
- def has_base(self, node):
- """Whether this class directly inherits from the given node.
-
- :param node: The node to check for.
- :type node: NodeNG
-
- :returns: True if this class directly inherits from the given node.
- :rtype: bool
- """
- return node in self.bases
-
- def local_attr(self, name, context=None):
- """Get the list of assign nodes associated to the given name.
-
- Assignments are looked for in both this class and in parents.
-
- :returns: The list of assignments to the given name.
- :rtype: list(NodeNG)
-
- :raises AttributeInferenceError: If no attribute with this name
- can be found in this class or parent classes.
- """
- result = []
- if name in self.locals:
- result = self.locals[name]
- else:
- class_node = next(self.local_attr_ancestors(name, context), ())
- if class_node:
- result = class_node.locals[name]
- result = [n for n in result if not isinstance(n, node_classes.DelAttr)]
- if result:
- return result
- raise exceptions.AttributeInferenceError(target=self, attribute=name,
- context=context)
-
- def instance_attr(self, name, context=None):
- """Get the list of nodes associated to the given attribute name.
-
- Assignments are looked for in both this class and in parents.
-
- :returns: The list of assignments to the given name.
- :rtype: list(NodeNG)
-
- :raises AttributeInferenceError: If no attribute with this name
- can be found in this class or parent classes.
- """
- # Return a copy, so we don't modify self.instance_attrs,
- # which could lead to infinite loop.
- values = list(self.instance_attrs.get(name, []))
- # get all values from parents
- for class_node in self.instance_attr_ancestors(name, context):
- values += class_node.instance_attrs[name]
- values = [n for n in values if not isinstance(n, node_classes.DelAttr)]
- if values:
- return values
- raise exceptions.AttributeInferenceError(target=self, attribute=name,
- context=context)
-
- def instantiate_class(self):
- """Get an :class:`Instance` of the :class:`ClassDef` node.
-
- :returns: An :class:`Instance` of the :class:`ClassDef` node,
- or self if this is not possible.
- :rtype: Instance or ClassDef
- """
- return bases.Instance(self)
-
- def instanciate_class(self):
- """A deprecated alias for :meth:`instanciate_class`.
-
- .. deprecated:: 1.5
-
- :returns: An :class:`Instance` of the :class:`ClassDef` node,
- or self if this is not possible.
- :rtype: Instance or ClassDef
- """
- warnings.warn('%s.instanciate_class() is deprecated and slated for '
- 'removal in astroid 2.0, use %s.instantiate_class() '
- 'instead.' % (type(self).__name__, type(self).__name__),
- PendingDeprecationWarning, stacklevel=2)
- return self.instantiate_class()
-
- def getattr(self, name, context=None, class_context=True):
- """Get an attribute from this class, using Python's attribute semantic.
-
- This method doesn't look in the :attr:`instance_attrs` dictionary
- since it is done by an :class:`Instance` proxy at inference time.
- It may return an :class:`Uninferable` object if
- the attribute has not been
- found, but a ``__getattr__`` or ``__getattribute__`` method is defined.
- If ``class_context`` is given, then it is considered that the
- attribute is accessed from a class context,
- e.g. ClassDef.attribute, otherwise it might have been accessed
- from an instance as well. If ``class_context`` is used in that
- case, then a lookup in the implicit metaclass and the explicit
- metaclass will be done.
-
- :param name: The attribute to look for.
- :type name: str
-
- :param class_context: Whether the attribute can be accessed statically.
- :type class_context: bool
-
- :returns: The attribute.
- :rtype: list(NodeNG)
-
- :raises AttributeInferenceError: If the attribute cannot be inferred.
- """
- values = self.locals.get(name, [])
- if name in self.special_attributes and class_context and not values:
- result = [self.special_attributes.lookup(name)]
- if name == '__bases__':
- # Need special treatment, since they are mutable
- # and we need to return all the values.
- result += values
- return result
-
- # don't modify the list in self.locals!
- values = list(values)
- for classnode in self.ancestors(recurs=True, context=context):
- values += classnode.locals.get(name, [])
-
- if class_context:
- values += self._metaclass_lookup_attribute(name, context)
-
- if not values:
- raise exceptions.AttributeInferenceError(target=self, attribute=name,
- context=context)
- return values
-
- def _metaclass_lookup_attribute(self, name, context):
- """Search the given name in the implicit and the explicit metaclass."""
- attrs = set()
- implicit_meta = self.implicit_metaclass()
- metaclass = self.metaclass()
- for cls in {implicit_meta, metaclass}:
- if cls and cls != self and isinstance(cls, ClassDef):
- cls_attributes = self._get_attribute_from_metaclass(
- cls, name, context)
- attrs.update(set(cls_attributes))
- return attrs
-
- def _get_attribute_from_metaclass(self, cls, name, context):
- try:
- attrs = cls.getattr(name, context=context,
- class_context=True)
- except exceptions.AttributeInferenceError:
- return
-
- for attr in bases._infer_stmts(attrs, context, frame=cls):
- if not isinstance(attr, FunctionDef):
- yield attr
- continue
-
- if bases._is_property(attr):
- # TODO(cpopa): don't use a private API.
- for inferred in attr.infer_call_result(self, context):
- yield inferred
- continue
- if attr.type == 'classmethod':
- # If the method is a classmethod, then it will
- # be bound to the metaclass, not to the class
- # from where the attribute is retrieved.
- # get_wrapping_class could return None, so just
- # default to the current class.
- frame = get_wrapping_class(attr) or self
- yield bases.BoundMethod(attr, frame)
- elif attr.type == 'staticmethod':
- yield attr
- else:
- yield bases.BoundMethod(attr, self)
-
- def igetattr(self, name, context=None, class_context=True):
- """Infer the possible values of the given variable.
-
- :param name: The name of the variable to infer.
- :type name: str
-
- :returns: The inferred possible values.
- :rtype: iterable(NodeNG or Uninferable)
- """
- # set lookup name since this is necessary to infer on import nodes for
- # instance
- context = contextmod.copy_context(context)
- context.lookupname = name
- try:
- attrs = self.getattr(name, context, class_context=class_context)
- for inferred in bases._infer_stmts(attrs, context, frame=self):
- # yield Uninferable object instead of descriptors when necessary
- if (not isinstance(inferred, node_classes.Const)
- and isinstance(inferred, bases.Instance)):
- try:
- inferred._proxied.getattr('__get__', context)
- except exceptions.AttributeInferenceError:
- yield inferred
- else:
- yield util.Uninferable
- else:
- yield function_to_method(inferred, self)
- except exceptions.AttributeInferenceError as error:
- if not name.startswith('__') and self.has_dynamic_getattr(context):
- # class handle some dynamic attributes, return a Uninferable object
- yield util.Uninferable
- else:
- util.reraise(exceptions.InferenceError(
- error.message, target=self, attribute=name, context=context))
-
- def has_dynamic_getattr(self, context=None):
- """Check if the class has a custom __getattr__ or __getattribute__.
-
- If any such method is found and it is not from
- builtins, nor from an extension module, then the function
- will return True.
-
- :returns: True if the class has a custom
- __getattr__ or __getattribute__, False otherwise.
- :rtype: bool
- """
- def _valid_getattr(node):
- root = node.root()
- return root.name != BUILTINS and getattr(root, 'pure_python', None)
-
- try:
- return _valid_getattr(self.getattr('__getattr__', context)[0])
- except exceptions.AttributeInferenceError:
- #if self.newstyle: XXX cause an infinite recursion error
- try:
- getattribute = self.getattr('__getattribute__', context)[0]
- return _valid_getattr(getattribute)
- except exceptions.AttributeInferenceError:
- pass
- return False
-
- def getitem(self, index, context=None):
- """Return the inference of a subscript.
-
- This is basically looking up the method in the metaclass and calling it.
-
- :returns: The inferred value of a subscript to this class.
- :rtype: NodeNG
-
- :raises AstroidTypeError: If this class does not define a
- ``__getitem__`` method.
- """
- try:
- methods = dunder_lookup.lookup(self, '__getitem__')
- except exceptions.AttributeInferenceError as exc:
- util.reraise(
- exceptions.AstroidTypeError(
- node=self, error=exc,
- context=context
- )
- )
-
- method = methods[0]
-
- # Create a new callcontext for providing index as an argument.
- if context:
- new_context = context.clone()
- else:
- new_context = contextmod.InferenceContext()
-
- new_context.callcontext = contextmod.CallContext(args=[index])
- new_context.boundnode = self
-
- return next(method.infer_call_result(self, new_context))
-
- def methods(self):
- """Iterate over all of the method defined in this class and its parents.
-
- :returns: The methods defined on the class.
- :rtype: iterable(FunctionDef)
- """
- done = {}
- for astroid in itertools.chain(iter((self,)), self.ancestors()):
- for meth in astroid.mymethods():
- if meth.name in done:
- continue
- done[meth.name] = None
- yield meth
-
- def mymethods(self):
- """Iterate over all of the method defined in this class only.
-
- :returns: The methods defined on the class.
- :rtype: iterable(FunctionDef)
- """
- for member in self.values():
- if isinstance(member, FunctionDef):
- yield member
-
- def implicit_metaclass(self):
- """Get the implicit metaclass of the current class.
-
- For newstyle classes, this will return an instance of builtins.type.
- For oldstyle classes, it will simply return None, since there's
- no implicit metaclass there.
-
- :returns: The metaclass.
- :rtype: builtins.type or None
- """
- if self.newstyle:
- return builtin_lookup('type')[1][0]
- return None
-
- _metaclass = None
- def declared_metaclass(self):
- """Return the explicit declared metaclass for the current class.
-
- An explicit declared metaclass is defined
- either by passing the ``metaclass`` keyword argument
- in the class definition line (Python 3) or (Python 2) by
- having a ``__metaclass__`` class attribute, or if there are
- no explicit bases but there is a global ``__metaclass__`` variable.
-
- :returns: The metaclass of this class,
- or None if one could not be found.
- :rtype: NodeNG or None
- """
- for base in self.bases:
- try:
- for baseobj in base.infer():
- if isinstance(baseobj, ClassDef) and baseobj.hide:
- self._metaclass = baseobj._metaclass
- self._metaclass_hack = True
- break
- except exceptions.InferenceError:
- pass
-
- if self._metaclass:
- # Expects this from Py3k TreeRebuilder
- try:
- return next(node for node in self._metaclass.infer()
- if node is not util.Uninferable)
- except (exceptions.InferenceError, StopIteration):
- return None
- if six.PY3:
- return None
-
- if '__metaclass__' in self.locals:
- assignment = self.locals['__metaclass__'][-1]
- elif self.bases:
- return None
- elif '__metaclass__' in self.root().locals:
- assignments = [ass for ass in self.root().locals['__metaclass__']
- if ass.lineno < self.lineno]
- if not assignments:
- return None
- assignment = assignments[-1]
- else:
- return None
-
- try:
- inferred = next(assignment.infer())
- except exceptions.InferenceError:
- return None
- if inferred is util.Uninferable: # don't expose this
- return None
- return inferred
-
- def _find_metaclass(self, seen=None):
- if seen is None:
- seen = set()
- seen.add(self)
-
- klass = self.declared_metaclass()
- if klass is None:
- for parent in self.ancestors():
- if parent not in seen:
- klass = parent._find_metaclass(seen)
- if klass is not None:
- break
- return klass
-
- def metaclass(self):
- """Get the metaclass of this class.
-
- If this class does not define explicitly a metaclass,
- then the first defined metaclass in ancestors will be used
- instead.
-
- :returns: The metaclass of this class.
- :rtype: NodeNG or None
- """
- return self._find_metaclass()
-
- def has_metaclass_hack(self):
- return self._metaclass_hack
-
- def _islots(self):
- """ Return an iterator with the inferred slots. """
- if '__slots__' not in self.locals:
- return
- for slots in self.igetattr('__slots__'):
- # check if __slots__ is a valid type
- for meth in ITER_METHODS:
- try:
- slots.getattr(meth)
- break
- except exceptions.AttributeInferenceError:
- continue
- else:
- continue
-
- if isinstance(slots, node_classes.Const):
- # a string. Ignore the following checks,
- # but yield the node, only if it has a value
- if slots.value:
- yield slots
- continue
- if not hasattr(slots, 'itered'):
- # we can't obtain the values, maybe a .deque?
- continue
-
- if isinstance(slots, node_classes.Dict):
- values = [item[0] for item in slots.items]
- else:
- values = slots.itered()
- if values is util.Uninferable:
- continue
- if not values:
- # Stop the iteration, because the class
- # has an empty list of slots.
- raise StopIteration(values)
-
- for elt in values:
- try:
- for inferred in elt.infer():
- if inferred is util.Uninferable:
- continue
- if (not isinstance(inferred, node_classes.Const) or
- not isinstance(inferred.value,
- six.string_types)):
- continue
- if not inferred.value:
- continue
- yield inferred
- except exceptions.InferenceError:
- continue
-
- def _slots(self):
- if not self.newstyle:
- raise NotImplementedError(
- "The concept of slots is undefined for old-style classes.")
-
- slots = self._islots()
- try:
- first = next(slots)
- except StopIteration as exc:
- # The class doesn't have a __slots__ definition or empty slots.
- if exc.args and exc.args[0] not in ('', None):
- return exc.args[0]
- return None
- return [first] + list(slots)
-
- # Cached, because inferring them all the time is expensive
- @decorators_mod.cached
- def slots(self):
- """Get all the slots for this node.
-
- :returns: The names of slots for this class.
- If the class doesn't define any slot, through the ``__slots__``
- variable, then this function will return a None.
- Also, it will return None in the case the slots were not inferred.
- :rtype: list(str) or None
- """
- def grouped_slots():
- # Not interested in object, since it can't have slots.
- for cls in self.mro()[:-1]:
- try:
- cls_slots = cls._slots()
- except NotImplementedError:
- continue
- if cls_slots is not None:
- for slot in cls_slots:
- yield slot
- else:
- yield None
-
- if not self.newstyle:
- raise NotImplementedError(
- "The concept of slots is undefined for old-style classes.")
-
- slots = list(grouped_slots())
- if not all(slot is not None for slot in slots):
- return None
-
- return sorted(slots, key=lambda item: item.value)
-
- def _inferred_bases(self, context=None):
- # TODO(cpopa): really similar with .ancestors,
- # but the difference is when one base is inferred,
- # only the first object is wanted. That's because
- # we aren't interested in superclasses, as in the following
- # example:
- #
- # class SomeSuperClass(object): pass
- # class SomeClass(SomeSuperClass): pass
- # class Test(SomeClass): pass
- #
- # Inferring SomeClass from the Test's bases will give
- # us both SomeClass and SomeSuperClass, but we are interested
- # only in SomeClass.
-
- if context is None:
- context = contextmod.InferenceContext()
- if six.PY3:
- if not self.bases and self.qname() != 'builtins.object':
- yield builtin_lookup("object")[1][0]
- return
-
- for stmt in self.bases:
- try:
- baseobj = next(stmt.infer(context=context))
- except exceptions.InferenceError:
- continue
- if isinstance(baseobj, bases.Instance):
- baseobj = baseobj._proxied
- if not isinstance(baseobj, ClassDef):
- continue
- if not baseobj.hide:
- yield baseobj
- else:
- for base in baseobj.bases:
- yield base
-
- def _compute_mro(self, context=None):
- inferred_bases = list(self._inferred_bases(context=context))
- bases_mro = []
- for base in inferred_bases:
- if base is self:
- continue
-
- try:
- mro = base._compute_mro(context=context)
- bases_mro.append(mro)
- except NotImplementedError:
- # Some classes have in their ancestors both newstyle and
- # old style classes. For these we can't retrieve the .mro,
- # although in Python it's possible, since the class we are
- # currently working is in fact new style.
- # So, we fallback to ancestors here.
- ancestors = list(base.ancestors(context=context))
- bases_mro.append(ancestors)
-
- unmerged_mro = ([[self]] + bases_mro + [inferred_bases])
- _verify_duplicates_mro(unmerged_mro, self, context)
- return _c3_merge(unmerged_mro, self, context)
-
- def mro(self, context=None):
- """Get the method resolution order, using C3 linearization.
-
- :returns: The list of ancestors, sorted by the mro.
- :rtype: list(NodeNG)
-
- :raises NotImplementedError: If this is an old style class,
- since they don't have the concept of an MRO.
- """
-
- if not self.newstyle:
- raise NotImplementedError(
- "Could not obtain mro for old-style classes.")
-
- return self._compute_mro(context=context)
-
- def bool_value(self):
- """Determine the boolean value of this node.
-
- :returns: The boolean value of this node.
- For a :class:`ClassDef` this is always ``True``.
- :rtype: bool
- """
- return True
-
-
- # Backwards-compatibility aliases
- Class = util.proxy_alias('Class', ClassDef)
- Function = util.proxy_alias('Function', FunctionDef)
- GenExpr = util.proxy_alias('GenExpr', GeneratorExp)
|