From a81cdc4f4db86821b20930107d3c214a1b685fb2 Mon Sep 17 00:00:00 2001 From: monteiro Date: Fri, 18 Nov 2016 08:23:25 -0200 Subject: [PATCH 001/269] improved folding in various cases --- autoload/pymode/folding.vim | 120 +++++++++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 16 deletions(-) diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim index 3ed61bc5..b8362722 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -1,16 +1,25 @@ " Python-mode folding functions +" Notice that folding is based on single line so complex regular expressions +" that take previous line into consideration are not fit for the job. +" Regex definitions for correct folding let s:def_regex = g:pymode_folding_regex let s:blank_regex = '^\s*$' -let s:decorator_regex = '^\s*@' -let s:doc_begin_regex = '^\s*\%("""\|''''''\)' +" Spyder, a very popular IDE for python has a template which includes +" '@author:' ; thus the regex below. +let s:decorator_regex = '^\s*@\(author:\)\@!' +let s:doc_begin_regex = '^\s*[uU]\=\%("""\|''''''\)' let s:doc_end_regex = '\%("""\|''''''\)\s*$' -let s:doc_line_regex = '^\s*\("""\|''''''\).\+\1\s*$' +" This one is needed for the while loop to count for opening and closing +" docstrings. +let s:doc_general_regex = '\%("""\|''''''\)' +let s:doc_line_regex = '^\s*[uU]\=\("""\|''''''\).\+\1\s*$' let s:symbol = matchstr(&fillchars, 'fold:\zs.') " handles multibyte characters if s:symbol == '' let s:symbol = ' ' endif +" '''''''' fun! pymode#folding#text() " {{{ @@ -33,24 +42,29 @@ fun! pymode#folding#text() " {{{ let line = substitute(line, '\t', onetab, 'g') let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount)) - let line = substitute(line, '\%("""\|''''''\)', '', '') + let line = substitute(line, '[uU]\=\%("""\|''''''\)', '', '') let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + 1 return line . ' ' . repeat(s:symbol, fillcharcount) . ' ' . foldedlinecount endfunction "}}} - fun! pymode#folding#expr(lnum) "{{{ let line = getline(a:lnum) let indent = indent(a:lnum) let prev_line = getline(a:lnum - 1) + let next_line = getline(a:lnum + 1) + " Decorators {{{ if line =~ s:decorator_regex return ">".(indent / &shiftwidth + 1) - endif + endif "}}} + " Definition {{{ if line =~ s:def_regex - " single line def + " If indent of this line is greater or equal than line below + " and previous non blank line does not end with : (that is, is not a + " definition) + " Keep the same indentation if indent(a:lnum) >= indent(a:lnum+1) && getline(prevnonblank(a:lnum)) !~ ':\s*$' return '=' endif @@ -71,16 +85,35 @@ fun! pymode#folding#expr(lnum) "{{{ else return ">".(indent / &shiftwidth + 1) endif - endif + endif "}}} - if line =~ s:doc_begin_regex && line !~ s:doc_line_regex && prev_line =~ s:def_regex - return ">".(indent / &shiftwidth + 1) + " Docstrings {{{ + + " TODO: A while loop now counts the number of open and closed folding in + " order to determine if it is a closing or opening folding. + " It is working but looks like it is an overkill. + + " Notice that an effect of this is that other docstring matches will not + " be one liners. + if line =~ s:doc_line_regex + return "=" endif - if line =~ s:doc_end_regex && line !~ s:doc_line_regex - return "<".(indent / &shiftwidth + 1) + if line =~ s:doc_begin_regex + " echom 'just entering' + if s:Is_opening_folding(a:lnum) + " echom 'entering at line ' . a:lnum + return ">".(indent / &shiftwidth + 1) + endif endif + if line =~ s:doc_end_regex + if !s:Is_opening_folding(a:lnum) + " echom 'leaving at line ' . a:lnum + return "<".(indent / &shiftwidth + 1) + endif + endif "}}} + " Nested Definitions {{{ " Handle nested defs but only for files shorter than " g:pymode_folding_nest_limit lines due to performance concerns if line('$') < g:pymode_folding_nest_limit && indent(prevnonblank(a:lnum)) @@ -125,18 +158,25 @@ fun! pymode#folding#expr(lnum) "{{{ finally call setpos('.', curpos) endtry - endif + endif " }}} + " Blank Line {{{ if line =~ s:blank_regex if prev_line =~ s:blank_regex - if indent(a:lnum + 1) == 0 && getline(a:lnum + 1) !~ s:blank_regex - return 0 + if indent(a:lnum + 1) == 0 && next_line !~ s:blank_regex && next_line !~ s:doc_general_regex + if s:Is_opening_folding(a:lnum) + " echom a:lnum + return "=" + else + " echom "not " . a:lnum + return 0 + endif endif return -1 else return '=' endif - endif + endif " }}} return '=' @@ -174,4 +214,52 @@ fun! s:BlockEnd(lnum) "{{{ return searchpos('\v^\s{,'.indent('.').'}\S', 'nW')[0] - 1 endfunction "}}} +function! s:Is_opening_folding(lnum) "{{{ + " Helper function to see if docstring is opening or closing + let number_of_folding = 0 " To be analized if odd/even to inform if it is opening or closing. + let has_open_docstring = 0 " To inform is already has an open docstring. + let extra_docstrings = 0 " To help skipping ''' and """ which are not docstrings + + " The idea of this part of the function is to identify real docstrings and + " not just triple quotes (that could be a regular string). + " + " Iterater over all lines from the start until current line (inclusive) + for i in range(1, a:lnum) + let i_line = getline(i) + + if i_line =~ s:doc_line_regex + " echom "case 00 on line " . i + continue + endif + + if i_line =~ s:doc_begin_regex && ! has_open_docstring + " echom "case 01 on line " . i + " This causes the loop to continue if there is a triple quote which + " is not a docstring. + if extra_docstrings > 0 + let extra_docstrings = extra_docstrings - 1 + continue + else + let has_open_docstring = 1 + let number_of_folding = number_of_folding + 1 + endif + " If it is an end doc and has an open docstring. + elseif i_line =~ s:doc_end_regex && has_open_docstring + " echom "case 02 on line " . i + let has_open_docstring = 0 + let number_of_folding = number_of_folding + 1 + + elseif i_line =~ s:doc_general_regex + " echom "extra docstrings on line " . i + let extra_docstrings = extra_docstrings + 1 + endif + endfor + + if fmod(number_of_folding, 2) == 1 "If odd then it is an opening + return 1 + else + return 0 + endif +endfunction "}}} + " vim: fdm=marker:fdl=0 From 2c6e8981fdf051800d6d86f2491e6ecd22a1de42 Mon Sep 17 00:00:00 2001 From: monteiro Date: Fri, 18 Nov 2016 08:53:25 -0200 Subject: [PATCH 002/269] commiting to trigger travis support --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e10ed9f1..7097078e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,4 @@ rvm: - 1.9.3 script: - make travis + From 1b30f1f546b0eeb1ae8d6b1b6d36ebfca4cb8a70 Mon Sep 17 00:00:00 2001 From: Semyon Maryasin Date: Mon, 23 Jan 2017 04:50:44 +0300 Subject: [PATCH 003/269] Do support underscore in numbers, pep515 (py3.6) --- syntax/python.vim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/syntax/python.vim b/syntax/python.vim index aebb15ad..608d887f 100644 --- a/syntax/python.vim +++ b/syntax/python.vim @@ -250,16 +250,16 @@ endif " Numbers {{{ " =========== - syn match pythonHexError "\<0[xX]\x*[g-zG-Z]\x*[lL]\=\>" display - syn match pythonHexNumber "\<0[xX]\x\+[lL]\=\>" display - syn match pythonOctNumber "\<0[oO]\o\+[lL]\=\>" display - syn match pythonBinNumber "\<0[bB][01]\+[lL]\=\>" display - syn match pythonNumber "\<\d\+[lLjJ]\=\>" display - syn match pythonFloat "\.\d\+\([eE][+-]\=\d\+\)\=[jJ]\=\>" display - syn match pythonFloat "\<\d\+[eE][+-]\=\d\+[jJ]\=\>" display - syn match pythonFloat "\<\d\+\.\d*\([eE][+-]\=\d\+\)\=[jJ]\=" display - syn match pythonOctError "\<0[oO]\=\o*[8-9]\d*[lL]\=\>" display - syn match pythonBinError "\<0[bB][01]*[2-9]\d*[lL]\=\>" display + syn match pythonHexError "\<0[xX][0-9a-fA-F_]*[g-zG-Z][0-9a-fA-F_]*[lL]\=\>" display + syn match pythonHexNumber "\<0[xX][0-9a-fA-F_]\+[lL]\=\>" display + syn match pythonOctNumber "\<0[oO][0-7_]\+[lL]\=\>" display + syn match pythonBinNumber "\<0[bB][01_]\+[lL]\=\>" display + syn match pythonNumber "\<[0-9_]\+[lLjJ]\=\>" display + syn match pythonFloat "\.[0-9_]\+\([eE][+-]\=[0-9_]\+\)\=[jJ]\=\>" display + syn match pythonFloat "\<[0-9_]\+[eE][+-]\=[0-9_]\+[jJ]\=\>" display + syn match pythonFloat "\<[0-9_]\+\.[0-9_]*\([eE][+-]\=[0-9_]\+\)\=[jJ]\=" display + syn match pythonOctError "\<0[oO]\=[0-7_]*[8-9][0-9_]*[lL]\=\>" display + syn match pythonBinError "\<0[bB][01_]*[2-9][0-9_]*[lL]\=\>" display " }}} From b44f3642373536978d693c0ee77b52e5c3d24a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=BC=D1=91=D0=BD=20=D0=9C=D0=B0=D1=80=D1=8C?= =?UTF-8?q?=D1=8F=D1=81=D0=B8=D0=BD?= Date: Mon, 23 Jan 2017 06:14:01 +0400 Subject: [PATCH 004/269] Update owner name in links in readme After migrating this repo from `klen` to `python-mode` account, Travis build status indicator ceased to work. This commit fixes that, together with streamlining other links. --- README.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index 867029d5..0706f0c6 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,8 @@ |logo| Python-mode, Python in VIM ################################# -.. image:: https://travis-ci.org/klen/python-mode.png?branch=develop - :target: https://travis-ci.org/klen/python-mode +.. image:: https://travis-ci.org/python-mode/python-mode.png?branch=develop + :target: https://travis-ci.org/python-mode/python-mode ----- @@ -13,9 +13,9 @@ ----- | -| Src: https://github.com/klen/python-mode +| Src: https://github.com/python-mode/python-mode | Homepage: https://klen.github.io/python-mode/ -| Docs: https://github.com/klen/python-mode/blob/develop/doc/pymode.txt +| Docs: https://github.com/python-mode/python-mode/blob/develop/doc/pymode.txt | Python-mode is a vim plugin that helps you to create python code very quickly @@ -74,7 +74,7 @@ Using pathogen (recommended) % cd ~/.vim % mkdir -p bundle && cd bundle - % git clone https://github.com/klen/python-mode.git + % git clone https://github.com/python-mode/python-mode.git - Enable `pathogen `_ in your ``~/.vimrc``: :: @@ -93,7 +93,7 @@ Manually -------- :: - % git clone https://github.com/klen/python-mode.git + % git clone https://github.com/python-mode/python-mode.git % cd python-mode % cp -R * ~/.vim @@ -144,7 +144,7 @@ plugin. 2. Type `:PymodeTroubleshooting` And fix any warnings or copy the output and send it to me. (For example, by -creating a `new github issue `_ +creating a `new github issue `_ if one does not already exist for the problem). @@ -190,7 +190,7 @@ Bugtracker If you have any suggestions, bug reports or annoyances please report them to the issue tracker -at https://github.com/klen/python-mode/issues +at https://github.com/python-mode/python-mode/issues Contributing @@ -202,7 +202,7 @@ Contributing Also see the `AUTHORS` file. Development of python-mode happens at github: -https://github.com/klen/python-mode +https://github.com/python-mode/python-mode Please make a pull request to `development` branch and add yourself to `AUTHORS`. @@ -210,25 +210,25 @@ Please make a pull request to `development` branch and add yourself to Source Links =================== - `doc/pymode.txt - `__ + `__ -- ``:help pymode`` - `plugin/pymode.vim - `__ + `__ -- python-mode VIM plugin - `syntax/python.vim - `__ + `__ -- python-mode ``python.vim`` VIM syntax - `syntax/pyrex.vim - `__ + `__ -- ``pyrex.vim`` VIM syntax (pyrex, Cython) - `t/ - `__ + `__ -- ``*.vim`` more python-mode VIM configuration - `pymode/ - `__ + `__ -- ``*.py`` -- python-mode Python module - `pymode/libs/ - `__ + `__ -- ``*.py`` -- `Python Libraries <#python-libraries>`__ @@ -236,7 +236,7 @@ Python Libraries ------------------ Vendored Python modules are located mostly in -`pymode/libs/ `__. +`pymode/libs/ `__. ====== @@ -364,4 +364,4 @@ My address is here: "Russia, 143500, MO, Istra, pos. Severny 8-3" to "Kirill Kle .. _pythonvim: http://www.hlabs.spb.ru/vim/python.vim .. _pep8_: http://github.com/jcrocholl/pep8 .. _pep8indent: http://github.com/hynek/vim-python-pep8-indent -.. |logo| image:: https://raw.github.com/klen/python-mode/develop/logo.png +.. |logo| image:: https://raw.github.com/python-mode/python-mode/develop/logo.png From 72bbe35974b170c62887f7b7a767e8620dc624fa Mon Sep 17 00:00:00 2001 From: monteiro Date: Mon, 6 Feb 2017 12:19:24 -0200 Subject: [PATCH 005/269] fixed folding for inline def statements --- autoload/pymode/folding.vim | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim index b8362722..5a692ab6 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -8,7 +8,7 @@ let s:def_regex = g:pymode_folding_regex let s:blank_regex = '^\s*$' " Spyder, a very popular IDE for python has a template which includes " '@author:' ; thus the regex below. -let s:decorator_regex = '^\s*@\(author:\)\@!' +let s:decorator_regex = '^\s*@\(author:\)\@!' let s:doc_begin_regex = '^\s*[uU]\=\%("""\|''''''\)' let s:doc_end_regex = '\%("""\|''''''\)\s*$' " This one is needed for the while loop to count for opening and closing @@ -200,7 +200,7 @@ fun! s:BlockStart(lnum) "{{{ " Now find the class/def one shiftwidth lower than the start of the " aforementioned indent block. - if next_stmt_at_def_indent && next_stmt_at_def_indent < a:lnum + if next_stmt_at_def_indent && a:lnum <= next_stmt_at_def_indent let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0]) else let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0]) @@ -211,6 +211,14 @@ endfunction "}}} fun! s:BlockEnd(lnum) "{{{ " Note: Make sure to reset cursor position after using this function. call cursor(a:lnum, 0) + " Regex translation: + " \v: very magic + " \s: any space char + " {...}: zero to more as many as possible + " \S: non whitespace + " index [0]: gets the line returned by searchpos + " flag 'n': do not move cursor + " flag 'W': don't wrap around the end of the file return searchpos('\v^\s{,'.indent('.').'}\S', 'nW')[0] - 1 endfunction "}}} @@ -227,7 +235,7 @@ function! s:Is_opening_folding(lnum) "{{{ for i in range(1, a:lnum) let i_line = getline(i) - if i_line =~ s:doc_line_regex + if i_line =~ s:doc_line_regex " echom "case 00 on line " . i continue endif @@ -252,7 +260,7 @@ function! s:Is_opening_folding(lnum) "{{{ elseif i_line =~ s:doc_general_regex " echom "extra docstrings on line " . i let extra_docstrings = extra_docstrings + 1 - endif + endif endfor if fmod(number_of_folding, 2) == 1 "If odd then it is an opening From a8dcbc8805bf743455809df1f7c17d11e92fe38a Mon Sep 17 00:00:00 2001 From: monteiro Date: Mon, 6 Feb 2017 12:32:59 -0200 Subject: [PATCH 006/269] improved folding of various cases --- autoload/pymode/folding.vim | 10 +--------- plugin/pymode.vim | 6 +++--- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim index 5a692ab6..218520f0 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -200,7 +200,7 @@ fun! s:BlockStart(lnum) "{{{ " Now find the class/def one shiftwidth lower than the start of the " aforementioned indent block. - if next_stmt_at_def_indent && a:lnum <= next_stmt_at_def_indent + if next_stmt_at_def_indent && next_stmt_at_def_indent < a:lnum let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0]) else let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0]) @@ -211,14 +211,6 @@ endfunction "}}} fun! s:BlockEnd(lnum) "{{{ " Note: Make sure to reset cursor position after using this function. call cursor(a:lnum, 0) - " Regex translation: - " \v: very magic - " \s: any space char - " {...}: zero to more as many as possible - " \S: non whitespace - " index [0]: gets the line returned by searchpos - " flag 'n': do not move cursor - " flag 'W': don't wrap around the end of the file return searchpos('\v^\s{,'.indent('.').'}\S', 'nW')[0] - 1 endfunction "}}} diff --git a/plugin/pymode.vim b/plugin/pymode.vim index 26541ae2..3ac97388 100644 --- a/plugin/pymode.vim +++ b/plugin/pymode.vim @@ -1,5 +1,5 @@ " vi: fdl=1 -let g:pymode_version = "0.8.1" +let g:pymode_version = "0.9.0" com! PymodeVersion echomsg "Current python-mode version: " . g:pymode_version com! PymodeTroubleshooting call pymode#troubleshooting#test() @@ -39,7 +39,8 @@ call pymode#default("g:pymode_folding", 1) " Maximum file length to check for nested class/def statements call pymode#default("g:pymode_folding_nest_limit", 1000) " Change for folding customization (by example enable fold for 'if', 'for') -call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\) \w\+') +call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\|async\s\+def\) .\+\(:\s\+\w\)\@!') + " Enable/disable python motion operators call pymode#default("g:pymode_motion", 1) @@ -314,4 +315,3 @@ endif command! PymodeVersion echomsg "Pymode version: " . g:pymode_version . " interpreter: " . g:pymode_python . " lint: " . g:pymode_lint . " rope: " . g:pymode_rope augroup pymode - From 12d0d60e44c9e3adef61e1553980ef730c4096ee Mon Sep 17 00:00:00 2001 From: Semyon Maryasin Date: Sat, 11 Feb 2017 20:04:35 +0300 Subject: [PATCH 007/269] Don't treat lone _ as a digit --- syntax/python.vim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/syntax/python.vim b/syntax/python.vim index 608d887f..317ef1d9 100644 --- a/syntax/python.vim +++ b/syntax/python.vim @@ -251,13 +251,13 @@ endif " =========== syn match pythonHexError "\<0[xX][0-9a-fA-F_]*[g-zG-Z][0-9a-fA-F_]*[lL]\=\>" display - syn match pythonHexNumber "\<0[xX][0-9a-fA-F_]\+[lL]\=\>" display - syn match pythonOctNumber "\<0[oO][0-7_]\+[lL]\=\>" display - syn match pythonBinNumber "\<0[bB][01_]\+[lL]\=\>" display - syn match pythonNumber "\<[0-9_]\+[lLjJ]\=\>" display - syn match pythonFloat "\.[0-9_]\+\([eE][+-]\=[0-9_]\+\)\=[jJ]\=\>" display - syn match pythonFloat "\<[0-9_]\+[eE][+-]\=[0-9_]\+[jJ]\=\>" display - syn match pythonFloat "\<[0-9_]\+\.[0-9_]*\([eE][+-]\=[0-9_]\+\)\=[jJ]\=" display + syn match pythonHexNumber "\<0[xX][0-9a-fA-F_]*[0-9a-fA-F][0-9a-fA-F_]*[lL]\=\>" display + syn match pythonOctNumber "\<0[oO][0-7_]*[0-7][0-7_]*[lL]\=\>" display + syn match pythonBinNumber "\<0[bB][01_]*[01][01_]*[lL]\=\>" display + syn match pythonNumber "\<[0-9_]*[0-9][0-9_]*[lLjJ]\=\>" display + syn match pythonFloat "\.[0-9_]*[0-9][0-9_]*\([eE][+-]\=[0-9_]*[0-9][0-9_]*\)\=[jJ]\=\>" display + syn match pythonFloat "\<[0-9_]*[0-9][0-9_]*[eE][+-]\=[0-9_]\+[jJ]\=\>" display + syn match pythonFloat "\<[0-9_]*[0-9][0-9_]*\.[0-9_]*\([eE][+-]\=[0-9_]*[0-9][0-9_]*\)\=[jJ]\=" display syn match pythonOctError "\<0[oO]\=[0-7_]*[8-9][0-9_]*[lL]\=\>" display syn match pythonBinError "\<0[bB][01_]*[2-9][0-9_]*[lL]\=\>" display From 9bd0e40d272cca1562db8643234268263fc325f2 Mon Sep 17 00:00:00 2001 From: Semyon Maryasin Date: Sat, 11 Feb 2017 20:05:54 +0300 Subject: [PATCH 008/269] Number may not start with underscore --- syntax/python.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syntax/python.vim b/syntax/python.vim index 317ef1d9..090e9be1 100644 --- a/syntax/python.vim +++ b/syntax/python.vim @@ -254,10 +254,10 @@ endif syn match pythonHexNumber "\<0[xX][0-9a-fA-F_]*[0-9a-fA-F][0-9a-fA-F_]*[lL]\=\>" display syn match pythonOctNumber "\<0[oO][0-7_]*[0-7][0-7_]*[lL]\=\>" display syn match pythonBinNumber "\<0[bB][01_]*[01][01_]*[lL]\=\>" display - syn match pythonNumber "\<[0-9_]*[0-9][0-9_]*[lLjJ]\=\>" display + syn match pythonNumber "\<[0-9][0-9_]*[lLjJ]\=\>" display syn match pythonFloat "\.[0-9_]*[0-9][0-9_]*\([eE][+-]\=[0-9_]*[0-9][0-9_]*\)\=[jJ]\=\>" display - syn match pythonFloat "\<[0-9_]*[0-9][0-9_]*[eE][+-]\=[0-9_]\+[jJ]\=\>" display - syn match pythonFloat "\<[0-9_]*[0-9][0-9_]*\.[0-9_]*\([eE][+-]\=[0-9_]*[0-9][0-9_]*\)\=[jJ]\=" display + syn match pythonFloat "\<[0-9][0-9_]*[eE][+-]\=[0-9_]\+[jJ]\=\>" display + syn match pythonFloat "\<[0-9][0-9_]*\.[0-9_]*\([eE][+-]\=[0-9_]*[0-9][0-9_]*\)\=[jJ]\=" display syn match pythonOctError "\<0[oO]\=[0-7_]*[8-9][0-9_]*[lL]\=\>" display syn match pythonBinError "\<0[bB][01_]*[2-9][0-9_]*[lL]\=\>" display From 8587789fe828a22641b79a56e465fea1d4f35ede Mon Sep 17 00:00:00 2001 From: Jacob Niehus Date: Tue, 28 Feb 2017 20:23:39 -0700 Subject: [PATCH 009/269] Make folding much faster in some cases Cache the results of Is_opening_folding once per change so it doesn't loop over the same lines multiple times unnecessarily. --- autoload/pymode/folding.vim | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim index 218520f0..399748e3 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -216,6 +216,15 @@ endfunction "}}} function! s:Is_opening_folding(lnum) "{{{ " Helper function to see if docstring is opening or closing + + " Cache the result so the loop runs only once per change + if get(b:, 'fold_changenr', -1) == changenr() + return b:fold_cache[a:lnum] "If odd then it is an opening + else + let b:fold_changenr = changenr() + let b:fold_cache = [] + endif + let number_of_folding = 0 " To be analized if odd/even to inform if it is opening or closing. let has_open_docstring = 0 " To inform is already has an open docstring. let extra_docstrings = 0 " To help skipping ''' and """ which are not docstrings @@ -224,7 +233,9 @@ function! s:Is_opening_folding(lnum) "{{{ " not just triple quotes (that could be a regular string). " " Iterater over all lines from the start until current line (inclusive) - for i in range(1, a:lnum) + for i in range(1, line('$')) + call add(b:fold_cache, number_of_folding % 2) + let i_line = getline(i) if i_line =~ s:doc_line_regex @@ -255,11 +266,9 @@ function! s:Is_opening_folding(lnum) "{{{ endif endfor - if fmod(number_of_folding, 2) == 1 "If odd then it is an opening - return 1 - else - return 0 - endif + call add(b:fold_cache, number_of_folding % 2) + + return b:fold_cache[a:lnum] endfunction "}}} " vim: fdm=marker:fdl=0 From 8abc4363b0e6f073a9bc29af17752ea72a8cdf92 Mon Sep 17 00:00:00 2001 From: Colin Kennedy Date: Thu, 18 May 2017 13:29:00 -0800 Subject: [PATCH 010/269] Updated docstring regex to allow folding of raw python strings --- autoload/pymode/folding.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autoload/pymode/folding.vim b/autoload/pymode/folding.vim index 399748e3..3b29aebb 100644 --- a/autoload/pymode/folding.vim +++ b/autoload/pymode/folding.vim @@ -9,12 +9,12 @@ let s:blank_regex = '^\s*$' " Spyder, a very popular IDE for python has a template which includes " '@author:' ; thus the regex below. let s:decorator_regex = '^\s*@\(author:\)\@!' -let s:doc_begin_regex = '^\s*[uU]\=\%("""\|''''''\)' +let s:doc_begin_regex = '^\s*[uUrR]\=\%("""\|''''''\)' let s:doc_end_regex = '\%("""\|''''''\)\s*$' " This one is needed for the while loop to count for opening and closing " docstrings. let s:doc_general_regex = '\%("""\|''''''\)' -let s:doc_line_regex = '^\s*[uU]\=\("""\|''''''\).\+\1\s*$' +let s:doc_line_regex = '^\s*[uUrR]\=\("""\|''''''\).\+\1\s*$' let s:symbol = matchstr(&fillchars, 'fold:\zs.') " handles multibyte characters if s:symbol == '' let s:symbol = ' ' @@ -42,7 +42,7 @@ fun! pymode#folding#text() " {{{ let line = substitute(line, '\t', onetab, 'g') let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount)) - let line = substitute(line, '[uU]\=\%("""\|''''''\)', '', '') + let line = substitute(line, '[uUrR]\=\%("""\|''''''\)', '', '') let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + 1 return line . ' ' . repeat(s:symbol, fillcharcount) . ' ' . foldedlinecount endfunction "}}} From a8b1fe6887fa3ac875524ce907c4c6396135deb5 Mon Sep 17 00:00:00 2001 From: Kirill Klenov Date: Fri, 23 Dec 2016 15:21:09 +0300 Subject: [PATCH 011/269] Update libs. --- pylama.ini | 1 + pymode/libs/_markerlib/__init__.py | 16 - pymode/libs/_markerlib/markers.py | 119 - .../astroid-1.4.9.dist-info/DESCRIPTION.rst | 66 + pymode/libs/astroid-1.4.9.dist-info/INSTALLER | 1 + pymode/libs/astroid-1.4.9.dist-info/METADATA | 84 + pymode/libs/astroid-1.4.9.dist-info/RECORD | 272 ++ .../WHEEL | 3 +- .../astroid-1.4.9.dist-info/metadata.json | 1 + .../astroid-1.4.9.dist-info/top_level.txt | 1 + pymode/libs/astroid/__init__.py | 15 +- pymode/libs/astroid/__pkginfo__.py | 10 +- pymode/libs/astroid/arguments.py | 233 ++ pymode/libs/astroid/as_string.py | 115 +- pymode/libs/astroid/bases.py | 290 +- ...nference.py => brain_builtin_inference.py} | 143 +- pymode/libs/astroid/brain/brain_dateutil.py | 15 + .../astroid/brain/{py2gi.py => brain_gi.py} | 56 +- .../{py2mechanize.py => brain_mechanize.py} | 0 .../brain/{pynose.py => brain_nose.py} | 5 +- pymode/libs/astroid/brain/brain_numpy.py | 62 + pymode/libs/astroid/brain/brain_pytest.py | 76 + pymode/libs/astroid/brain/brain_qt.py | 44 + .../brain/{pysix_moves.py => brain_six.py} | 29 +- pymode/libs/astroid/brain/brain_ssl.py | 65 + pymode/libs/astroid/brain/brain_stdlib.py | 473 +++ pymode/libs/astroid/brain/py2pytest.py | 31 - pymode/libs/astroid/brain/py2qt4.py | 22 - pymode/libs/astroid/brain/py2stdlib.py | 334 -- pymode/libs/astroid/builder.py | 185 +- pymode/libs/astroid/context.py | 81 + pymode/libs/astroid/decorators.py | 75 + pymode/libs/astroid/exceptions.py | 20 + pymode/libs/astroid/inference.py | 320 +- pymode/libs/astroid/inspector.py | 273 -- pymode/libs/astroid/manager.py | 214 +- pymode/libs/astroid/mixins.py | 51 +- pymode/libs/astroid/modutils.py | 153 +- pymode/libs/astroid/node_classes.py | 399 ++- pymode/libs/astroid/nodes.py | 57 +- pymode/libs/astroid/objects.py | 186 + pymode/libs/astroid/protocols.py | 259 +- pymode/libs/astroid/raw_building.py | 40 +- pymode/libs/astroid/rebuilder.py | 681 ++-- pymode/libs/astroid/scoped_nodes.py | 1072 +++--- pymode/libs/astroid/test_utils.py | 25 +- .../_vendor => astroid/tests}/__init__.py | 0 pymode/libs/astroid/tests/resources.py | 72 + .../python2/data/MyPyPa-0.1.0-py2.5.egg | Bin 0 -> 1222 bytes .../python2/data/MyPyPa-0.1.0-py2.5.zip | Bin 0 -> 1222 bytes .../testdata/python2/data/SSL1/Connection1.py | 14 + .../testdata/python2/data/SSL1/__init__.py | 1 + .../tests/testdata/python2/data/__init__.py | 1 + .../testdata/python2/data/absimp/__init__.py | 5 + .../data/absimp/sidepackage/__init__.py | 3 + .../testdata/python2/data/absimp/string.py | 3 + .../tests/testdata/python2/data/absimport.py | 3 + .../tests/testdata/python2/data/all.py | 9 + .../testdata/python2/data/appl/__init__.py | 3 + .../python2/data/appl/myConnection.py | 12 + .../python2/data/clientmodule_test.py | 32 + .../testdata/python2/data/descriptor_crash.py | 11 + .../tests/testdata/python2/data/email.py | 1 + .../python2/data/find_test/__init__.py | 0 .../testdata/python2/data/find_test/module.py | 0 .../python2/data/find_test/module2.py | 0 .../python2/data/find_test/noendingnewline.py | 0 .../python2/data/find_test/nonregr.py | 0 .../tests/testdata/python2/data/format.py | 34 + .../testdata/python2/data/joined_strings.py | 1051 ++++++ .../testdata/python2/data/lmfp/__init__.py | 2 + .../tests/testdata/python2/data/lmfp/foo.py | 6 + .../tests/testdata/python2/data/module.py | 89 + .../python2/data/module1abs/__init__.py | 4 + .../testdata/python2/data/module1abs/core.py | 1 + .../tests/testdata/python2/data/module2.py | 143 + .../testdata/python2/data/noendingnewline.py | 36 + .../tests/testdata/python2/data/nonregr.py | 57 + .../tests/testdata/python2/data/notall.py | 7 + .../testdata/python2/data/package/__init__.py | 4 + .../python2/data/package/absimport.py | 6 + .../testdata/python2/data/package/hello.py | 2 + .../import_package_subpackage_module.py | 49 + .../data/package/subpackage/__init__.py | 1 + .../python2/data/package/subpackage/module.py | 1 + .../tests/testdata/python2/data/recursion.py | 3 + .../python2/data/suppliermodule_test.py | 13 + .../python2/data/unicode_package/__init__.py | 1 + .../data/unicode_package/core/__init__.py | 0 .../python3/data/MyPyPa-0.1.0-py2.5.egg | Bin 0 -> 1222 bytes .../python3/data/MyPyPa-0.1.0-py2.5.zip | Bin 0 -> 1222 bytes .../testdata/python3/data/SSL1/Connection1.py | 14 + .../testdata/python3/data/SSL1/__init__.py | 1 + .../tests/testdata/python3/data/__init__.py | 1 + .../testdata/python3/data/absimp/__init__.py | 5 + .../data/absimp/sidepackage/__init__.py | 3 + .../testdata/python3/data/absimp/string.py | 3 + .../tests/testdata/python3/data/absimport.py | 3 + .../tests/testdata/python3/data/all.py | 9 + .../testdata/python3/data/appl/__init__.py | 3 + .../python3/data/appl/myConnection.py | 11 + .../python3/data/clientmodule_test.py | 32 + .../testdata/python3/data/descriptor_crash.py | 11 + .../tests/testdata/python3/data/email.py | 1 + .../python3/data/find_test/__init__.py | 0 .../testdata/python3/data/find_test/module.py | 0 .../python3/data/find_test/module2.py | 0 .../python3/data/find_test/noendingnewline.py | 0 .../python3/data/find_test/nonregr.py | 0 .../tests/testdata/python3/data/format.py | 34 + .../testdata/python3/data/joined_strings.py | 1051 ++++++ .../testdata/python3/data/lmfp/__init__.py | 2 + .../tests/testdata/python3/data/lmfp/foo.py | 6 + .../tests/testdata/python3/data/module.py | 88 + .../python3/data/module1abs/__init__.py | 4 + .../testdata/python3/data/module1abs/core.py | 1 + .../tests/testdata/python3/data/module2.py | 143 + .../testdata/python3/data/noendingnewline.py | 36 + .../tests/testdata/python3/data/nonregr.py | 57 + .../tests/testdata/python3/data/notall.py | 8 + .../testdata/python3/data/package/__init__.py | 4 + .../python3/data/package/absimport.py | 6 + .../testdata/python3/data/package/hello.py | 2 + .../import_package_subpackage_module.py | 49 + .../data/package/subpackage/__init__.py | 1 + .../python3/data/package/subpackage/module.py | 1 + .../tests/testdata/python3/data/recursion.py | 3 + .../python3/data/suppliermodule_test.py | 13 + .../python3/data/unicode_package/__init__.py | 1 + .../data/unicode_package/core/__init__.py | 0 pymode/libs/astroid/tests/unittest_brain.py | 506 +++ pymode/libs/astroid/tests/unittest_builder.py | 774 ++++ .../libs/astroid/tests/unittest_inference.py | 2130 +++++++++++ pymode/libs/astroid/tests/unittest_lookup.py | 352 ++ pymode/libs/astroid/tests/unittest_manager.py | 216 ++ .../libs/astroid/tests/unittest_modutils.py | 269 ++ pymode/libs/astroid/tests/unittest_nodes.py | 764 ++++ pymode/libs/astroid/tests/unittest_objects.py | 530 +++ .../libs/astroid/tests/unittest_peephole.py | 121 + .../libs/astroid/tests/unittest_protocols.py | 176 + pymode/libs/astroid/tests/unittest_python3.py | 254 ++ .../astroid/tests/unittest_raw_building.py | 85 + .../libs/astroid/tests/unittest_regrtest.py | 364 ++ .../astroid/tests/unittest_scoped_nodes.py | 1583 +++++++++ .../libs/astroid/tests/unittest_transforms.py | 245 ++ pymode/libs/astroid/tests/unittest_utils.py | 124 + pymode/libs/astroid/transforms.py | 96 + pymode/libs/astroid/util.py | 89 + pymode/libs/astroid/utils.py | 239 -- ...ts.functools_lru_cache-1.3-py3.5-nspkg.pth | 1 + .../DESCRIPTION.rst | 28 + .../INSTALLER | 1 + .../METADATA | 44 + .../RECORD | 11 + .../WHEEL | 6 + .../metadata.json | 1 + .../namespace_packages.txt | 1 + .../top_level.txt | 1 + .../libs/backports/configparser/__init__.py | 1390 ++++++++ pymode/libs/backports/configparser/helpers.py | 171 + pymode/libs/backports/functools_lru_cache.py | 184 + .../libs/configparser-3.5.0-py2.7-nspkg.pth | 1 + .../DESCRIPTION.rst | 305 ++ .../configparser-3.5.0.dist-info/INSTALLER | 1 + .../configparser-3.5.0.dist-info/METADATA | 330 ++ .../libs/configparser-3.5.0.dist-info/RECORD | 15 + .../libs/configparser-3.5.0.dist-info/WHEEL | 5 + .../metadata.json | 1 + .../namespace_packages.txt | 1 + .../top_level.txt | 2 + pymode/libs/configparser.py | 52 + pymode/libs/easy_install.py | 5 - .../isort-4.2.5.dist-info/DESCRIPTION.rst | 606 ++++ pymode/libs/isort-4.2.5.dist-info/INSTALLER | 1 + pymode/libs/isort-4.2.5.dist-info/METADATA | 636 ++++ pymode/libs/isort-4.2.5.dist-info/RECORD | 25 + pymode/libs/isort-4.2.5.dist-info/WHEEL | 6 + .../isort-4.2.5.dist-info/entry_points.txt | 9 + .../libs/isort-4.2.5.dist-info/metadata.json | 1 + .../libs/isort-4.2.5.dist-info/top_level.txt | 1 + pymode/libs/isort/__init__.py | 28 + pymode/libs/isort/hooks.py | 82 + pymode/libs/isort/isort.py | 878 +++++ pymode/libs/isort/main.py | 287 ++ pymode/libs/isort/natural.py | 47 + pymode/libs/isort/pie_slice.py | 528 +++ pymode/libs/isort/pylama_isort.py | 29 + pymode/libs/isort/settings.py | 221 ++ .../DESCRIPTION.rst | 70 + .../INSTALLER | 1 + .../METADATA | 96 + .../lazy_object_proxy-1.2.2.dist-info/RECORD | 19 + .../lazy_object_proxy-1.2.2.dist-info/WHEEL | 5 + .../metadata.json | 1 + .../top_level.txt | 1 + pymode/libs/lazy_object_proxy/__init__.py | 20 + pymode/libs/lazy_object_proxy/cext.c | 1421 ++++++++ pymode/libs/lazy_object_proxy/cext.so | Bin 0 -> 36084 bytes pymode/libs/lazy_object_proxy/compat.py | 9 + pymode/libs/lazy_object_proxy/simple.py | 246 ++ pymode/libs/lazy_object_proxy/slots.py | 414 +++ pymode/libs/lazy_object_proxy/utils.py | 13 + pymode/libs/logilab/common/__init__.py | 184 - pymode/libs/logilab/common/cache.py | 114 - pymode/libs/logilab/common/changelog.py | 238 -- pymode/libs/logilab/common/clcommands.py | 334 -- pymode/libs/logilab/common/compat.py | 78 - pymode/libs/logilab/common/configuration.py | 1105 ------ pymode/libs/logilab/common/daemon.py | 101 - pymode/libs/logilab/common/date.py | 335 -- pymode/libs/logilab/common/debugger.py | 214 -- pymode/libs/logilab/common/decorators.py | 281 -- pymode/libs/logilab/common/deprecation.py | 189 - pymode/libs/logilab/common/fileutils.py | 404 --- pymode/libs/logilab/common/graph.py | 282 -- pymode/libs/logilab/common/interface.py | 71 - pymode/libs/logilab/common/logging_ext.py | 195 -- pymode/libs/logilab/common/modutils.py | 713 ---- pymode/libs/logilab/common/optik_ext.py | 392 --- pymode/libs/logilab/common/optparser.py | 92 - pymode/libs/logilab/common/proc.py | 277 -- pymode/libs/logilab/common/pytest.py | 1202 ------- pymode/libs/logilab/common/registry.py | 1125 ------ pymode/libs/logilab/common/shellutils.py | 462 --- pymode/libs/logilab/common/sphinx_ext.py | 87 - pymode/libs/logilab/common/sphinxutils.py | 122 - pymode/libs/logilab/common/table.py | 929 ----- pymode/libs/logilab/common/tasksqueue.py | 101 - pymode/libs/logilab/common/testlib.py | 1338 ------- pymode/libs/logilab/common/textutils.py | 537 --- pymode/libs/logilab/common/tree.py | 369 -- pymode/libs/logilab/common/umessage.py | 194 - .../libs/logilab/common/ureports/__init__.py | 172 - .../logilab/common/ureports/docbook_writer.py | 140 - .../logilab/common/ureports/html_writer.py | 133 - .../logilab/common/ureports/text_writer.py | 145 - pymode/libs/logilab/common/urllib2ext.py | 89 - pymode/libs/logilab/common/visitor.py | 109 - pymode/libs/logilab/common/xmlutils.py | 61 - .../libs/logilab_common-1.0.2-py2.7-nspkg.pth | 1 - .../DESCRIPTION.rst | 153 - .../logilab_common-1.0.2.dist-info/METADATA | 169 - .../logilab_common-1.0.2.dist-info/RECORD | 87 - .../metadata.json | 1 - .../namespace_packages.txt | 1 - .../top_level.txt | 1 - .../mccabe-0.5.3.dist-info/DESCRIPTION.rst | 139 + pymode/libs/mccabe-0.5.3.dist-info/INSTALLER | 1 + pymode/libs/mccabe-0.5.3.dist-info/METADATA | 164 + pymode/libs/mccabe-0.5.3.dist-info/RECORD | 10 + pymode/libs/mccabe-0.5.3.dist-info/WHEEL | 6 + .../mccabe-0.5.3.dist-info/entry_points.txt | 3 + .../libs/mccabe-0.5.3.dist-info/metadata.json | 1 + .../libs/mccabe-0.5.3.dist-info/top_level.txt | 1 + pymode/libs/mccabe.py | 65 +- pymode/libs/pkg_resources/__init__.py | 3113 ----------------- .../_vendor/packaging/__about__.py | 31 - .../_vendor/packaging/__init__.py | 24 - .../_vendor/packaging/_compat.py | 40 - .../_vendor/packaging/_structures.py | 78 - .../_vendor/packaging/specifiers.py | 784 ----- .../_vendor/packaging/version.py | 403 --- .../DESCRIPTION.rst | 870 +++++ .../pycodestyle-2.2.0.dist-info/INSTALLER | 1 + .../libs/pycodestyle-2.2.0.dist-info/METADATA | 890 +++++ .../libs/pycodestyle-2.2.0.dist-info/RECORD | 12 + pymode/libs/pycodestyle-2.2.0.dist-info/WHEEL | 6 + .../entry_points.txt | 3 + .../pycodestyle-2.2.0.dist-info/metadata.json | 1 + .../namespace_packages.txt} | 0 .../pycodestyle-2.2.0.dist-info/top_level.txt | 1 + pymode/libs/{pep8.py => pycodestyle.py} | 289 +- .../DESCRIPTION.rst | 57 + .../libs/pydocstyle-1.1.1.dist-info/INSTALLER | 1 + .../libs/pydocstyle-1.1.1.dist-info/METADATA | 74 + pymode/libs/pydocstyle-1.1.1.dist-info/RECORD | 28 + pymode/libs/pydocstyle-1.1.1.dist-info/WHEEL | 6 + .../entry_points.txt | 4 + .../pydocstyle-1.1.1.dist-info/metadata.json | 1 + .../pydocstyle-1.1.1.dist-info/top_level.txt | 1 + pymode/libs/{pep257.py => pydocstyle.py} | 1060 ++++-- pymode/libs/pydocstyle/__init__.py | 7 + pymode/libs/pydocstyle/__main__.py | 19 + pymode/libs/pydocstyle/checker.py | 450 +++ pymode/libs/pydocstyle/cli.py | 105 + pymode/libs/pydocstyle/config.py | 538 +++ pymode/libs/pydocstyle/parser.py | 562 +++ pymode/libs/pydocstyle/utils.py | 11 + pymode/libs/pydocstyle/violations.py | 221 ++ .../pyflakes-1.3.0.dist-info/DESCRIPTION.rst | 86 + .../libs/pyflakes-1.3.0.dist-info/INSTALLER | 1 + pymode/libs/pyflakes-1.3.0.dist-info/METADATA | 105 + pymode/libs/pyflakes-1.3.0.dist-info/RECORD | 43 + pymode/libs/pyflakes-1.3.0.dist-info/WHEEL | 6 + .../pyflakes-1.3.0.dist-info/entry_points.txt | 3 + .../pyflakes-1.3.0.dist-info/metadata.json | 1 + .../pyflakes-1.3.0.dist-info/top_level.txt | 1 + pymode/libs/pyflakes/__init__.py | 2 +- pymode/libs/pyflakes/api.py | 16 +- pymode/libs/pyflakes/checker.py | 532 ++- pymode/libs/pyflakes/messages.py | 105 +- pymode/libs/pyflakes/reporter.py | 2 +- pymode/libs/pyflakes/scripts/__init__.py | 0 pymode/libs/pyflakes/scripts/pyflakes.py | 8 + pymode/libs/pyflakes/test/__init__.py | 0 pymode/libs/pyflakes/test/harness.py | 72 + pymode/libs/pyflakes/test/test_api.py | 744 ++++ pymode/libs/pyflakes/test/test_dict.py | 217 ++ pymode/libs/pyflakes/test/test_doctests.py | 442 +++ pymode/libs/pyflakes/test/test_imports.py | 1180 +++++++ pymode/libs/pyflakes/test/test_other.py | 1802 ++++++++++ ..._return_with_arguments_inside_generator.py | 34 + .../pyflakes/test/test_undefined_names.py | 806 +++++ pymode/libs/pylama/__init__.py | 5 +- pymode/libs/pylama/async.py | 13 +- pymode/libs/pylama/config.py | 41 +- pymode/libs/pylama/core.py | 31 +- pymode/libs/pylama/errors.py | 24 +- pymode/libs/pylama/hook.py | 15 +- pymode/libs/pylama/lint/extensions.py | 16 +- pymode/libs/pylama/lint/pylama_mccabe.py | 5 +- pymode/libs/pylama/lint/pylama_pep257.py | 21 - .../{pylama_pep8.py => pylama_pycodestyle.py} | 18 +- pymode/libs/pylama/lint/pylama_pydocstyle.py | 25 + pymode/libs/pylama/lint/pylama_pyflakes.py | 12 +- .../pylama/lint/pylama_pylint/__init__.py | 2 +- pymode/libs/pylama/lint/pylama_pylint/main.py | 25 +- pymode/libs/pylama/lint/pylama_radon.py | 31 + pymode/libs/pylama/main.py | 10 +- pymode/libs/pylama/pytest.py | 2 +- .../pylint-1.6.4.dist-info/DESCRIPTION.rst | 3 + pymode/libs/pylint-1.6.4.dist-info/INSTALLER | 1 + pymode/libs/pylint-1.6.4.dist-info/METADATA | 31 + pymode/libs/pylint-1.6.4.dist-info/RECORD | 1236 +++++++ pymode/libs/pylint-1.6.4.dist-info/WHEEL | 6 + .../pylint-1.6.4.dist-info/entry_points.txt | 7 + .../libs/pylint-1.6.4.dist-info/metadata.json | 1 + .../libs/pylint-1.6.4.dist-info/top_level.txt | 1 + pymode/libs/pylint/__init__.py | 18 +- pymode/libs/pylint/__main__.py | 5 + pymode/libs/pylint/__pkginfo__.py | 46 +- pymode/libs/pylint/checkers/__init__.py | 23 +- pymode/libs/pylint/checkers/async.py | 72 + pymode/libs/pylint/checkers/base.py | 1101 +++++- pymode/libs/pylint/checkers/classes.py | 588 ++-- .../libs/pylint/checkers/design_analysis.py | 103 +- pymode/libs/pylint/checkers/exceptions.py | 148 +- pymode/libs/pylint/checkers/format.py | 131 +- pymode/libs/pylint/checkers/imports.py | 498 ++- pymode/libs/pylint/checkers/logging.py | 56 +- pymode/libs/pylint/checkers/misc.py | 28 +- pymode/libs/pylint/checkers/newstyle.py | 111 +- pymode/libs/pylint/checkers/python3.py | 69 +- pymode/libs/pylint/checkers/raw_metrics.py | 25 +- pymode/libs/pylint/checkers/similar.py | 30 +- pymode/libs/pylint/checkers/spelling.py | 57 +- pymode/libs/pylint/checkers/stdlib.py | 195 +- pymode/libs/pylint/checkers/strings.py | 97 +- pymode/libs/pylint/checkers/typecheck.py | 651 +++- pymode/libs/pylint/checkers/utils.py | 522 ++- pymode/libs/pylint/checkers/variables.py | 709 ++-- pymode/libs/pylint/config.py | 844 ++++- pymode/libs/pylint/epylint.py | 38 +- pymode/libs/pylint/extensions/__init__.py | 0 .../pylint/extensions/_check_docs_utils.py | 418 +++ pymode/libs/pylint/extensions/bad_builtin.py | 67 + pymode/libs/pylint/extensions/check_docs.py | 19 + pymode/libs/pylint/extensions/check_elif.py | 67 + pymode/libs/pylint/extensions/docparams.py | 297 ++ pymode/libs/pylint/extensions/docstyle.py | 74 + pymode/libs/pylint/extensions/mccabe.py | 170 + pymode/libs/pylint/graph.py | 169 + pymode/libs/pylint/gui.py | 22 +- pymode/libs/pylint/interfaces.py | 49 +- pymode/libs/pylint/lint.py | 302 +- pymode/libs/pylint/pyreverse/__init__.py | 5 + pymode/libs/pylint/pyreverse/diadefslib.py | 49 +- pymode/libs/pylint/pyreverse/diagrams.py | 37 +- pymode/libs/pylint/pyreverse/inspector.py | 361 ++ pymode/libs/pylint/pyreverse/main.py | 55 +- pymode/libs/pylint/pyreverse/utils.py | 100 +- .../common => pylint/pyreverse}/vcgutils.py | 50 +- pymode/libs/pylint/pyreverse/writer.py | 26 +- pymode/libs/pylint/reporters/__init__.py | 76 +- pymode/libs/pylint/reporters/guireporter.py | 5 +- pymode/libs/pylint/reporters/html.py | 33 +- pymode/libs/pylint/reporters/json.py | 32 +- pymode/libs/pylint/reporters/text.py | 115 +- .../pylint/reporters/ureports/__init__.py | 94 + .../pylint/reporters/ureports/html_writer.py | 81 + .../reporters}/ureports/nodes.py | 138 +- .../pylint/reporters/ureports/text_writer.py | 87 + pymode/libs/pylint/testutils.py | 76 +- pymode/libs/pylint/utils.py | 349 +- .../libs/six-1.10.0.dist-info/DESCRIPTION.rst | 18 + pymode/libs/six-1.10.0.dist-info/INSTALLER | 1 + pymode/libs/six-1.10.0.dist-info/METADATA | 34 + pymode/libs/six-1.10.0.dist-info/RECORD | 9 + pymode/libs/six-1.10.0.dist-info/WHEEL | 6 + .../libs/six-1.10.0.dist-info/metadata.json | 1 + .../libs/six-1.10.0.dist-info/top_level.txt | 1 + pymode/libs/six.py | 74 +- .../wrapt-1.10.8.dist-info/DESCRIPTION.rst | 19 + pymode/libs/wrapt-1.10.8.dist-info/INSTALLER | 1 + pymode/libs/wrapt-1.10.8.dist-info/METADATA | 29 + pymode/libs/wrapt-1.10.8.dist-info/RECORD | 18 + pymode/libs/wrapt-1.10.8.dist-info/WHEEL | 5 + .../libs/wrapt-1.10.8.dist-info/metadata.json | 1 + .../libs/wrapt-1.10.8.dist-info/top_level.txt | 1 + pymode/libs/wrapt/__init__.py | 19 + pymode/libs/wrapt/_wrappers.so | Bin 0 -> 47640 bytes pymode/libs/wrapt/arguments.py | 95 + pymode/libs/wrapt/decorators.py | 512 +++ pymode/libs/wrapt/importer.py | 228 ++ pymode/libs/wrapt/wrappers.py | 899 +++++ 415 files changed, 46433 insertions(+), 23459 deletions(-) delete mode 100644 pymode/libs/_markerlib/__init__.py delete mode 100644 pymode/libs/_markerlib/markers.py create mode 100644 pymode/libs/astroid-1.4.9.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/astroid-1.4.9.dist-info/INSTALLER create mode 100644 pymode/libs/astroid-1.4.9.dist-info/METADATA create mode 100644 pymode/libs/astroid-1.4.9.dist-info/RECORD rename pymode/libs/{logilab_common-1.0.2.dist-info => astroid-1.4.9.dist-info}/WHEEL (54%) create mode 100644 pymode/libs/astroid-1.4.9.dist-info/metadata.json create mode 100644 pymode/libs/astroid-1.4.9.dist-info/top_level.txt create mode 100644 pymode/libs/astroid/arguments.py rename pymode/libs/astroid/brain/{builtin_inference.py => brain_builtin_inference.py} (63%) create mode 100644 pymode/libs/astroid/brain/brain_dateutil.py rename pymode/libs/astroid/brain/{py2gi.py => brain_gi.py} (72%) rename pymode/libs/astroid/brain/{py2mechanize.py => brain_mechanize.py} (100%) rename pymode/libs/astroid/brain/{pynose.py => brain_nose.py} (92%) create mode 100644 pymode/libs/astroid/brain/brain_numpy.py create mode 100644 pymode/libs/astroid/brain/brain_pytest.py create mode 100644 pymode/libs/astroid/brain/brain_qt.py rename pymode/libs/astroid/brain/{pysix_moves.py => brain_six.py} (91%) create mode 100644 pymode/libs/astroid/brain/brain_ssl.py create mode 100644 pymode/libs/astroid/brain/brain_stdlib.py delete mode 100644 pymode/libs/astroid/brain/py2pytest.py delete mode 100644 pymode/libs/astroid/brain/py2qt4.py delete mode 100644 pymode/libs/astroid/brain/py2stdlib.py create mode 100644 pymode/libs/astroid/context.py create mode 100644 pymode/libs/astroid/decorators.py delete mode 100644 pymode/libs/astroid/inspector.py create mode 100644 pymode/libs/astroid/objects.py rename pymode/libs/{pkg_resources/_vendor => astroid/tests}/__init__.py (100%) create mode 100644 pymode/libs/astroid/tests/resources.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/MyPyPa-0.1.0-py2.5.egg create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/MyPyPa-0.1.0-py2.5.zip create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/SSL1/Connection1.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/SSL1/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/absimp/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/absimp/sidepackage/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/absimp/string.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/absimport.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/all.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/appl/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/appl/myConnection.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/clientmodule_test.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/descriptor_crash.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/email.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/find_test/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/find_test/module.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/find_test/module2.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/find_test/noendingnewline.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/find_test/nonregr.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/format.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/joined_strings.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/lmfp/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/lmfp/foo.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/module.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/module1abs/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/module1abs/core.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/module2.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/noendingnewline.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/nonregr.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/notall.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/package/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/package/absimport.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/package/hello.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/package/import_package_subpackage_module.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/module.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/recursion.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/suppliermodule_test.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/unicode_package/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python2/data/unicode_package/core/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/MyPyPa-0.1.0-py2.5.egg create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/MyPyPa-0.1.0-py2.5.zip create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/SSL1/Connection1.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/SSL1/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/absimp/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/absimp/sidepackage/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/absimp/string.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/absimport.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/all.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/appl/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/appl/myConnection.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/clientmodule_test.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/descriptor_crash.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/email.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/find_test/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/find_test/module.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/find_test/module2.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/find_test/noendingnewline.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/find_test/nonregr.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/format.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/joined_strings.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/lmfp/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/lmfp/foo.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/module.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/module1abs/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/module1abs/core.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/module2.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/noendingnewline.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/nonregr.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/notall.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/package/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/package/absimport.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/package/hello.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/package/import_package_subpackage_module.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/module.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/recursion.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/suppliermodule_test.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/unicode_package/__init__.py create mode 100644 pymode/libs/astroid/tests/testdata/python3/data/unicode_package/core/__init__.py create mode 100644 pymode/libs/astroid/tests/unittest_brain.py create mode 100644 pymode/libs/astroid/tests/unittest_builder.py create mode 100644 pymode/libs/astroid/tests/unittest_inference.py create mode 100644 pymode/libs/astroid/tests/unittest_lookup.py create mode 100644 pymode/libs/astroid/tests/unittest_manager.py create mode 100644 pymode/libs/astroid/tests/unittest_modutils.py create mode 100644 pymode/libs/astroid/tests/unittest_nodes.py create mode 100644 pymode/libs/astroid/tests/unittest_objects.py create mode 100644 pymode/libs/astroid/tests/unittest_peephole.py create mode 100644 pymode/libs/astroid/tests/unittest_protocols.py create mode 100644 pymode/libs/astroid/tests/unittest_python3.py create mode 100644 pymode/libs/astroid/tests/unittest_raw_building.py create mode 100644 pymode/libs/astroid/tests/unittest_regrtest.py create mode 100644 pymode/libs/astroid/tests/unittest_scoped_nodes.py create mode 100644 pymode/libs/astroid/tests/unittest_transforms.py create mode 100644 pymode/libs/astroid/tests/unittest_utils.py create mode 100644 pymode/libs/astroid/transforms.py create mode 100644 pymode/libs/astroid/util.py delete mode 100644 pymode/libs/astroid/utils.py create mode 100644 pymode/libs/backports.functools_lru_cache-1.3-py3.5-nspkg.pth create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/INSTALLER create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/METADATA create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/RECORD create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/WHEEL create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/metadata.json create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/namespace_packages.txt create mode 100644 pymode/libs/backports.functools_lru_cache-1.3.dist-info/top_level.txt create mode 100644 pymode/libs/backports/configparser/__init__.py create mode 100644 pymode/libs/backports/configparser/helpers.py create mode 100644 pymode/libs/backports/functools_lru_cache.py create mode 100644 pymode/libs/configparser-3.5.0-py2.7-nspkg.pth create mode 100644 pymode/libs/configparser-3.5.0.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/configparser-3.5.0.dist-info/INSTALLER create mode 100644 pymode/libs/configparser-3.5.0.dist-info/METADATA create mode 100644 pymode/libs/configparser-3.5.0.dist-info/RECORD create mode 100644 pymode/libs/configparser-3.5.0.dist-info/WHEEL create mode 100644 pymode/libs/configparser-3.5.0.dist-info/metadata.json create mode 100644 pymode/libs/configparser-3.5.0.dist-info/namespace_packages.txt create mode 100644 pymode/libs/configparser-3.5.0.dist-info/top_level.txt create mode 100644 pymode/libs/configparser.py delete mode 100644 pymode/libs/easy_install.py create mode 100644 pymode/libs/isort-4.2.5.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/isort-4.2.5.dist-info/INSTALLER create mode 100644 pymode/libs/isort-4.2.5.dist-info/METADATA create mode 100644 pymode/libs/isort-4.2.5.dist-info/RECORD create mode 100644 pymode/libs/isort-4.2.5.dist-info/WHEEL create mode 100644 pymode/libs/isort-4.2.5.dist-info/entry_points.txt create mode 100644 pymode/libs/isort-4.2.5.dist-info/metadata.json create mode 100644 pymode/libs/isort-4.2.5.dist-info/top_level.txt create mode 100644 pymode/libs/isort/__init__.py create mode 100644 pymode/libs/isort/hooks.py create mode 100644 pymode/libs/isort/isort.py create mode 100644 pymode/libs/isort/main.py create mode 100644 pymode/libs/isort/natural.py create mode 100644 pymode/libs/isort/pie_slice.py create mode 100644 pymode/libs/isort/pylama_isort.py create mode 100644 pymode/libs/isort/settings.py create mode 100644 pymode/libs/lazy_object_proxy-1.2.2.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/lazy_object_proxy-1.2.2.dist-info/INSTALLER create mode 100644 pymode/libs/lazy_object_proxy-1.2.2.dist-info/METADATA create mode 100644 pymode/libs/lazy_object_proxy-1.2.2.dist-info/RECORD create mode 100644 pymode/libs/lazy_object_proxy-1.2.2.dist-info/WHEEL create mode 100644 pymode/libs/lazy_object_proxy-1.2.2.dist-info/metadata.json create mode 100644 pymode/libs/lazy_object_proxy-1.2.2.dist-info/top_level.txt create mode 100644 pymode/libs/lazy_object_proxy/__init__.py create mode 100644 pymode/libs/lazy_object_proxy/cext.c create mode 100755 pymode/libs/lazy_object_proxy/cext.so create mode 100644 pymode/libs/lazy_object_proxy/compat.py create mode 100644 pymode/libs/lazy_object_proxy/simple.py create mode 100644 pymode/libs/lazy_object_proxy/slots.py create mode 100644 pymode/libs/lazy_object_proxy/utils.py delete mode 100644 pymode/libs/logilab/common/__init__.py delete mode 100644 pymode/libs/logilab/common/cache.py delete mode 100644 pymode/libs/logilab/common/changelog.py delete mode 100644 pymode/libs/logilab/common/clcommands.py delete mode 100644 pymode/libs/logilab/common/compat.py delete mode 100644 pymode/libs/logilab/common/configuration.py delete mode 100644 pymode/libs/logilab/common/daemon.py delete mode 100644 pymode/libs/logilab/common/date.py delete mode 100644 pymode/libs/logilab/common/debugger.py delete mode 100644 pymode/libs/logilab/common/decorators.py delete mode 100644 pymode/libs/logilab/common/deprecation.py delete mode 100644 pymode/libs/logilab/common/fileutils.py delete mode 100644 pymode/libs/logilab/common/graph.py delete mode 100644 pymode/libs/logilab/common/interface.py delete mode 100644 pymode/libs/logilab/common/logging_ext.py delete mode 100644 pymode/libs/logilab/common/modutils.py delete mode 100644 pymode/libs/logilab/common/optik_ext.py delete mode 100644 pymode/libs/logilab/common/optparser.py delete mode 100644 pymode/libs/logilab/common/proc.py delete mode 100644 pymode/libs/logilab/common/pytest.py delete mode 100644 pymode/libs/logilab/common/registry.py delete mode 100644 pymode/libs/logilab/common/shellutils.py delete mode 100644 pymode/libs/logilab/common/sphinx_ext.py delete mode 100644 pymode/libs/logilab/common/sphinxutils.py delete mode 100644 pymode/libs/logilab/common/table.py delete mode 100644 pymode/libs/logilab/common/tasksqueue.py delete mode 100644 pymode/libs/logilab/common/testlib.py delete mode 100644 pymode/libs/logilab/common/textutils.py delete mode 100644 pymode/libs/logilab/common/tree.py delete mode 100644 pymode/libs/logilab/common/umessage.py delete mode 100644 pymode/libs/logilab/common/ureports/__init__.py delete mode 100644 pymode/libs/logilab/common/ureports/docbook_writer.py delete mode 100644 pymode/libs/logilab/common/ureports/html_writer.py delete mode 100644 pymode/libs/logilab/common/ureports/text_writer.py delete mode 100644 pymode/libs/logilab/common/urllib2ext.py delete mode 100644 pymode/libs/logilab/common/visitor.py delete mode 100644 pymode/libs/logilab/common/xmlutils.py delete mode 100644 pymode/libs/logilab_common-1.0.2-py2.7-nspkg.pth delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/DESCRIPTION.rst delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/METADATA delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/RECORD delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/metadata.json delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/namespace_packages.txt delete mode 100644 pymode/libs/logilab_common-1.0.2.dist-info/top_level.txt create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/INSTALLER create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/METADATA create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/RECORD create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/WHEEL create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/entry_points.txt create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/metadata.json create mode 100644 pymode/libs/mccabe-0.5.3.dist-info/top_level.txt delete mode 100644 pymode/libs/pkg_resources/__init__.py delete mode 100644 pymode/libs/pkg_resources/_vendor/packaging/__about__.py delete mode 100644 pymode/libs/pkg_resources/_vendor/packaging/__init__.py delete mode 100644 pymode/libs/pkg_resources/_vendor/packaging/_compat.py delete mode 100644 pymode/libs/pkg_resources/_vendor/packaging/_structures.py delete mode 100644 pymode/libs/pkg_resources/_vendor/packaging/specifiers.py delete mode 100644 pymode/libs/pkg_resources/_vendor/packaging/version.py create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/INSTALLER create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/METADATA create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/RECORD create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/WHEEL create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/entry_points.txt create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/metadata.json rename pymode/libs/{logilab/__init__.py => pycodestyle-2.2.0.dist-info/namespace_packages.txt} (100%) create mode 100644 pymode/libs/pycodestyle-2.2.0.dist-info/top_level.txt rename pymode/libs/{pep8.py => pycodestyle.py} (90%) create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/INSTALLER create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/METADATA create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/RECORD create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/WHEEL create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/entry_points.txt create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/metadata.json create mode 100644 pymode/libs/pydocstyle-1.1.1.dist-info/top_level.txt rename pymode/libs/{pep257.py => pydocstyle.py} (50%) create mode 100644 pymode/libs/pydocstyle/__init__.py create mode 100644 pymode/libs/pydocstyle/__main__.py create mode 100644 pymode/libs/pydocstyle/checker.py create mode 100644 pymode/libs/pydocstyle/cli.py create mode 100644 pymode/libs/pydocstyle/config.py create mode 100644 pymode/libs/pydocstyle/parser.py create mode 100644 pymode/libs/pydocstyle/utils.py create mode 100644 pymode/libs/pydocstyle/violations.py create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/INSTALLER create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/METADATA create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/RECORD create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/WHEEL create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/entry_points.txt create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/metadata.json create mode 100644 pymode/libs/pyflakes-1.3.0.dist-info/top_level.txt create mode 100644 pymode/libs/pyflakes/scripts/__init__.py create mode 100644 pymode/libs/pyflakes/scripts/pyflakes.py create mode 100644 pymode/libs/pyflakes/test/__init__.py create mode 100644 pymode/libs/pyflakes/test/harness.py create mode 100644 pymode/libs/pyflakes/test/test_api.py create mode 100644 pymode/libs/pyflakes/test/test_dict.py create mode 100644 pymode/libs/pyflakes/test/test_doctests.py create mode 100644 pymode/libs/pyflakes/test/test_imports.py create mode 100644 pymode/libs/pyflakes/test/test_other.py create mode 100644 pymode/libs/pyflakes/test/test_return_with_arguments_inside_generator.py create mode 100644 pymode/libs/pyflakes/test/test_undefined_names.py delete mode 100644 pymode/libs/pylama/lint/pylama_pep257.py rename pymode/libs/pylama/lint/{pylama_pep8.py => pylama_pycodestyle.py} (76%) create mode 100644 pymode/libs/pylama/lint/pylama_pydocstyle.py create mode 100644 pymode/libs/pylama/lint/pylama_radon.py create mode 100644 pymode/libs/pylint-1.6.4.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/pylint-1.6.4.dist-info/INSTALLER create mode 100644 pymode/libs/pylint-1.6.4.dist-info/METADATA create mode 100644 pymode/libs/pylint-1.6.4.dist-info/RECORD create mode 100644 pymode/libs/pylint-1.6.4.dist-info/WHEEL create mode 100644 pymode/libs/pylint-1.6.4.dist-info/entry_points.txt create mode 100644 pymode/libs/pylint-1.6.4.dist-info/metadata.json create mode 100644 pymode/libs/pylint-1.6.4.dist-info/top_level.txt create mode 100644 pymode/libs/pylint/checkers/async.py create mode 100644 pymode/libs/pylint/extensions/__init__.py create mode 100644 pymode/libs/pylint/extensions/_check_docs_utils.py create mode 100644 pymode/libs/pylint/extensions/bad_builtin.py create mode 100644 pymode/libs/pylint/extensions/check_docs.py create mode 100644 pymode/libs/pylint/extensions/check_elif.py create mode 100644 pymode/libs/pylint/extensions/docparams.py create mode 100644 pymode/libs/pylint/extensions/docstyle.py create mode 100644 pymode/libs/pylint/extensions/mccabe.py create mode 100644 pymode/libs/pylint/graph.py create mode 100644 pymode/libs/pylint/pyreverse/inspector.py rename pymode/libs/{logilab/common => pylint/pyreverse}/vcgutils.py (82%) create mode 100644 pymode/libs/pylint/reporters/ureports/__init__.py create mode 100644 pymode/libs/pylint/reporters/ureports/html_writer.py rename pymode/libs/{logilab/common => pylint/reporters}/ureports/nodes.py (53%) create mode 100644 pymode/libs/pylint/reporters/ureports/text_writer.py create mode 100644 pymode/libs/six-1.10.0.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/six-1.10.0.dist-info/INSTALLER create mode 100644 pymode/libs/six-1.10.0.dist-info/METADATA create mode 100644 pymode/libs/six-1.10.0.dist-info/RECORD create mode 100644 pymode/libs/six-1.10.0.dist-info/WHEEL create mode 100644 pymode/libs/six-1.10.0.dist-info/metadata.json create mode 100644 pymode/libs/six-1.10.0.dist-info/top_level.txt create mode 100644 pymode/libs/wrapt-1.10.8.dist-info/DESCRIPTION.rst create mode 100644 pymode/libs/wrapt-1.10.8.dist-info/INSTALLER create mode 100644 pymode/libs/wrapt-1.10.8.dist-info/METADATA create mode 100644 pymode/libs/wrapt-1.10.8.dist-info/RECORD create mode 100644 pymode/libs/wrapt-1.10.8.dist-info/WHEEL create mode 100644 pymode/libs/wrapt-1.10.8.dist-info/metadata.json create mode 100644 pymode/libs/wrapt-1.10.8.dist-info/top_level.txt create mode 100644 pymode/libs/wrapt/__init__.py create mode 100755 pymode/libs/wrapt/_wrappers.so create mode 100644 pymode/libs/wrapt/arguments.py create mode 100644 pymode/libs/wrapt/decorators.py create mode 100644 pymode/libs/wrapt/importer.py create mode 100644 pymode/libs/wrapt/wrappers.py diff --git a/pylama.ini b/pylama.ini index 0394772f..9579796e 100644 --- a/pylama.ini +++ b/pylama.ini @@ -1,4 +1,5 @@ [pylama] +ignore=D213 linters=pep8,pyflakes,pylint [pylama:pymode/libs*] diff --git a/pymode/libs/_markerlib/__init__.py b/pymode/libs/_markerlib/__init__.py deleted file mode 100644 index e2b237b1..00000000 --- a/pymode/libs/_markerlib/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -try: - import ast - from _markerlib.markers import default_environment, compile, interpret -except ImportError: - if 'ast' in globals(): - raise - def default_environment(): - return {} - def compile(marker): - def marker_fn(environment=None, override=None): - # 'empty markers are True' heuristic won't install extra deps. - return not marker.strip() - marker_fn.__doc__ = marker - return marker_fn - def interpret(marker, environment=None, override=None): - return compile(marker)() diff --git a/pymode/libs/_markerlib/markers.py b/pymode/libs/_markerlib/markers.py deleted file mode 100644 index fa837061..00000000 --- a/pymode/libs/_markerlib/markers.py +++ /dev/null @@ -1,119 +0,0 @@ -# -*- coding: utf-8 -*- -"""Interpret PEP 345 environment markers. - -EXPR [in|==|!=|not in] EXPR [or|and] ... - -where EXPR belongs to any of those: - - python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1]) - python_full_version = sys.version.split()[0] - os.name = os.name - sys.platform = sys.platform - platform.version = platform.version() - platform.machine = platform.machine() - platform.python_implementation = platform.python_implementation() - a free string, like '2.6', or 'win32' -""" - -__all__ = ['default_environment', 'compile', 'interpret'] - -import ast -import os -import platform -import sys -import weakref - -_builtin_compile = compile - -try: - from platform import python_implementation -except ImportError: - if os.name == "java": - # Jython 2.5 has ast module, but not platform.python_implementation() function. - def python_implementation(): - return "Jython" - else: - raise - - -# restricted set of variables -_VARS = {'sys.platform': sys.platform, - 'python_version': '%s.%s' % sys.version_info[:2], - # FIXME parsing sys.platform is not reliable, but there is no other - # way to get e.g. 2.7.2+, and the PEP is defined with sys.version - 'python_full_version': sys.version.split(' ', 1)[0], - 'os.name': os.name, - 'platform.version': platform.version(), - 'platform.machine': platform.machine(), - 'platform.python_implementation': python_implementation(), - 'extra': None # wheel extension - } - -for var in list(_VARS.keys()): - if '.' in var: - _VARS[var.replace('.', '_')] = _VARS[var] - -def default_environment(): - """Return copy of default PEP 385 globals dictionary.""" - return dict(_VARS) - -class ASTWhitelist(ast.NodeTransformer): - def __init__(self, statement): - self.statement = statement # for error messages - - ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str) - # Bool operations - ALLOWED += (ast.And, ast.Or) - # Comparison operations - ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn) - - def visit(self, node): - """Ensure statement only contains allowed nodes.""" - if not isinstance(node, self.ALLOWED): - raise SyntaxError('Not allowed in environment markers.\n%s\n%s' % - (self.statement, - (' ' * node.col_offset) + '^')) - return ast.NodeTransformer.visit(self, node) - - def visit_Attribute(self, node): - """Flatten one level of attribute access.""" - new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx) - return ast.copy_location(new_node, node) - -def parse_marker(marker): - tree = ast.parse(marker, mode='eval') - new_tree = ASTWhitelist(marker).generic_visit(tree) - return new_tree - -def compile_marker(parsed_marker): - return _builtin_compile(parsed_marker, '', 'eval', - dont_inherit=True) - -_cache = weakref.WeakValueDictionary() - -def compile(marker): - """Return compiled marker as a function accepting an environment dict.""" - try: - return _cache[marker] - except KeyError: - pass - if not marker.strip(): - def marker_fn(environment=None, override=None): - """""" - return True - else: - compiled_marker = compile_marker(parse_marker(marker)) - def marker_fn(environment=None, override=None): - """override updates environment""" - if override is None: - override = {} - if environment is None: - environment = default_environment() - environment.update(override) - return eval(compiled_marker, environment) - marker_fn.__doc__ = marker - _cache[marker] = marker_fn - return _cache[marker] - -def interpret(marker, environment=None): - return compile(marker)(environment) diff --git a/pymode/libs/astroid-1.4.9.dist-info/DESCRIPTION.rst b/pymode/libs/astroid-1.4.9.dist-info/DESCRIPTION.rst new file mode 100644 index 00000000..09162a6e --- /dev/null +++ b/pymode/libs/astroid-1.4.9.dist-info/DESCRIPTION.rst @@ -0,0 +1,66 @@ +.. image:: https://drone.io/bitbucket.org/logilab/astroid/status.png + :alt: drone.io Build Status + :target: https://drone.io/bitbucket.org/logilab/astroid + +Astroid +======= + +What's this? +------------ + +The aim of this module is to provide a common base representation of +python source code for projects such as pychecker, pyreverse, +pylint... Well, actually the development of this library is essentially +governed by pylint's needs. It used to be called logilab-astng. + +It provides a compatible representation which comes from the `_ast` +module. It rebuilds the tree generated by the builtin _ast module by +recursively walking down the AST and building an extended ast. The new +node classes have additional methods and attributes for different +usages. They include some support for static inference and local name +scopes. Furthermore, astroid builds partial trees by inspecting living +objects. + +Main modules are: + +* `bases`, `node_classses` and `scoped_nodes` contain the classes for the + different type of nodes of the tree. + +* the `manager` contains a high level object to get astroid trees from + source files and living objects. It maintains a cache of previously + constructed tree for quick access. + + +Installation +------------ + +Extract the tarball, jump into the created directory and run:: + + python setup.py install + +For installation options, see:: + + python setup.py install --help + + +If you have any questions, please mail the code-quality@python.org +mailing list for support. See +http://mail.python.org/mailman/listinfo/code-quality for subscription +information and archives. You may find older archives at +http://lists.logilab.org/mailman/listinfo/python-projects . + +Python Versions +--------------- + +astroid is compatible with Python 2.7 as well as 3.3 and later. astroid uses +the same code base for both Python versions, using six. + +Test +---- + +Tests are in the 'test' subdirectory. To launch the whole tests suite +at once, you can use unittest discover:: + + python -m unittest discover -p "unittest*.py" + + diff --git a/pymode/libs/astroid-1.4.9.dist-info/INSTALLER b/pymode/libs/astroid-1.4.9.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/pymode/libs/astroid-1.4.9.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/pymode/libs/astroid-1.4.9.dist-info/METADATA b/pymode/libs/astroid-1.4.9.dist-info/METADATA new file mode 100644 index 00000000..9512196b --- /dev/null +++ b/pymode/libs/astroid-1.4.9.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.0 +Name: astroid +Version: 1.4.9 +Summary: A abstract syntax tree for Python with inference support. +Home-page: https://github.com/PyCQA/astroid +Author: Python Code Quality Authority +Author-email: code-quality@python.org +License: LGPL +Platform: UNKNOWN +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Requires-Dist: lazy-object-proxy +Requires-Dist: six +Requires-Dist: wrapt + +.. image:: https://drone.io/bitbucket.org/logilab/astroid/status.png + :alt: drone.io Build Status + :target: https://drone.io/bitbucket.org/logilab/astroid + +Astroid +======= + +What's this? +------------ + +The aim of this module is to provide a common base representation of +python source code for projects such as pychecker, pyreverse, +pylint... Well, actually the development of this library is essentially +governed by pylint's needs. It used to be called logilab-astng. + +It provides a compatible representation which comes from the `_ast` +module. It rebuilds the tree generated by the builtin _ast module by +recursively walking down the AST and building an extended ast. The new +node classes have additional methods and attributes for different +usages. They include some support for static inference and local name +scopes. Furthermore, astroid builds partial trees by inspecting living +objects. + +Main modules are: + +* `bases`, `node_classses` and `scoped_nodes` contain the classes for the + different type of nodes of the tree. + +* the `manager` contains a high level object to get astroid trees from + source files and living objects. It maintains a cache of previously + constructed tree for quick access. + + +Installation +------------ + +Extract the tarball, jump into the created directory and run:: + + python setup.py install + +For installation options, see:: + + python setup.py install --help + + +If you have any questions, please mail the code-quality@python.org +mailing list for support. See +http://mail.python.org/mailman/listinfo/code-quality for subscription +information and archives. You may find older archives at +http://lists.logilab.org/mailman/listinfo/python-projects . + +Python Versions +--------------- + +astroid is compatible with Python 2.7 as well as 3.3 and later. astroid uses +the same code base for both Python versions, using six. + +Test +---- + +Tests are in the 'test' subdirectory. To launch the whole tests suite +at once, you can use unittest discover:: + + python -m unittest discover -p "unittest*.py" + + diff --git a/pymode/libs/astroid-1.4.9.dist-info/RECORD b/pymode/libs/astroid-1.4.9.dist-info/RECORD new file mode 100644 index 00000000..c6842648 --- /dev/null +++ b/pymode/libs/astroid-1.4.9.dist-info/RECORD @@ -0,0 +1,272 @@ +astroid/__init__.py,sha256=uX2A4l2glYHVDxjeQENRAOv0GucqEvFx5LdzlI5Xq3s,4985 +astroid/__pkginfo__.py,sha256=S4rpTRFZpkqeiyW_M-pn6sLLmTEJGA4Uyvn96RrPkAw,1607 +astroid/arguments.py,sha256=wLUt8FfHOHSsupWRasZdRjNn7h2LAu39yK1Xg0hN33Y,9508 +astroid/as_string.py,sha256=SaaordMAHt_6a7mHLzUqOfFs3QCICWnkyDwNCEQmI-o,20789 +astroid/astpeephole.py,sha256=LjUBUYoxXSz7yBrTfaSTL9SEHOAwcdTjHE0BEaMJUyI,2952 +astroid/bases.py,sha256=oFxrjU-ueu3fTZBKmq5PMbCHYEew_cCBf0wHJMr4gkU,22197 +astroid/builder.py,sha256=c6F5rAbmntA-IM9O7KFGlec7gwnj8s2mVcShDODQcYM,10469 +astroid/context.py,sha256=1IBKkxJdmFXnjxYNoi2yJNSL-rXK77R-GT6aXejTzO8,2475 +astroid/decorators.py,sha256=FHYnm0Xkoo2SwdrztHdoBPW4auP2ZKxV3qbkeu14LkI,2494 +astroid/exceptions.py,sha256=C93us5nqsz6_Stt9buMl7JjR1xHjGnePt_hSNSzYNS4,2350 +astroid/inference.py,sha256=_sqh_TyGlJd1-zHFnK-mr0wEfk7kH5pfFzNkxV5I-7s,12086 +astroid/manager.py,sha256=6iyEeKjlml6Nde3dmuiwyE5hweVhwGHq2GB2lAJwg1U,11120 +astroid/mixins.py,sha256=8RhbWYZfmQVVBJRCHJyTbcrLwKKUxkjKRipi1lm35wo,5508 +astroid/modutils.py,sha256=jgaUFy79e6iF4Re8xYcvYQhbVSp7kSaVKeRU2dcHPAI,25506 +astroid/node_classes.py,sha256=urnKe8NcYLmZ2I31A6O_ATbPXsu4xxtEM-udTemswIM,32957 +astroid/nodes.py,sha256=xXopyFJ8fRhyZg-ETmQgGwaIW2-7jowz0z0HEARG494,3248 +astroid/objects.py,sha256=cipb8qZAaAKAbL3BQGmFH38UDhzM1ZeXXhUNOhhmzNE,6403 +astroid/protocols.py,sha256=gnIsx24PkeKLPUxVQ4lc8GZt0mnfichSTwVsfB9AFt8,16984 +astroid/raw_building.py,sha256=t-3LdclM8PcQCIzh5OaS6c1cYynGgnbErd8vwoQ9w-4,14884 +astroid/rebuilder.py,sha256=n_SwvjWLxC7NM_gOx0BYywX3SujJSL2vM_3PVaaXxXs,41977 +astroid/scoped_nodes.py,sha256=YWzxkW_wlq1f1O5cJEKoFzYMBnRLvB-Rafl9zsrqwac,62402 +astroid/test_utils.py,sha256=lPKQV3_iiIswfKFOT6p9YDsEbULH29fShK2JzS8NJfc,6989 +astroid/transforms.py,sha256=7b9eOnHrC4RxJdoZOcyiWbwBR2nbFu7PeuUnR6IBVPE,3830 +astroid/util.py,sha256=T9YHxUo-xy8jn05MPS7hVm2HZC7WG8TPVKD1zqiNZ2g,3041 +astroid/brain/brain_builtin_inference.py,sha256=eQkCn1SgjKiws_6NzEPo1JgRYgOvAEiYw73D0na_t-M,11120 +astroid/brain/brain_dateutil.py,sha256=RACrraY_cnkgkgwCWnDD4VicXLiaobRn6OGecBdPFEs,448 +astroid/brain/brain_gi.py,sha256=kDXzvmLkF8-ze53bzaajHDs9nX-vx-VCH-MrqiKyrJI,6029 +astroid/brain/brain_mechanize.py,sha256=H-PiUMsKUfqK-N8Kc6Ub_9MLb4J4TYNhh7IZQgSz7cE,486 +astroid/brain/brain_nose.py,sha256=psOQtV0UJFXzeVz7tqBZjTGIhsgE2KQS20-oSu9EufQ,2766 +astroid/brain/brain_numpy.py,sha256=jn99dLQwjCjwLEReXRv1DEx-4hANj2JnFNnErpnttDo,2210 +astroid/brain/brain_pytest.py,sha256=p5tyi9jzkxQqYJzR-63JWJy3zoeLzIbdlNNJkk29SEw,1943 +astroid/brain/brain_qt.py,sha256=a7ncWD4Iu4Nxh4YPiHqx6fXaxsrNcSnv8QZyM1g7POM,1275 +astroid/brain/brain_six.py,sha256=_LxHuomOfTNYjnOniwDBFbhpbD-AapTwO_af7md1Oxg,10175 +astroid/brain/brain_ssl.py,sha256=yeh-dwWUDqkU0IZ00Q-jVp6J7kCfEWm2m0IKmnON-Yg,3271 +astroid/brain/brain_stdlib.py,sha256=CYn_f-Sl5t7ZX61CdtS5iuNXXUZWXm1kJSTT71hzUaA,16349 +astroid/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/resources.py,sha256=Kpw_3IcQ_s5Rspo1VO_nIHN3-4PUbCjFlGrG-vIwodc,2372 +astroid/tests/unittest_brain.py,sha256=qwKuOsGv6Xd6r0WUr1YgLrJTk8gLf1h_dvdAPxhmfXM,18406 +astroid/tests/unittest_builder.py,sha256=IojJ8O81wdumy-ygiYSKYqnETAotGpTUmSf3ftR8FcU,28890 +astroid/tests/unittest_inference.py,sha256=qqYW7V5in6ULEbgdAYlmg2TJV1i9KVCFmUGNcgmau-E,74714 +astroid/tests/unittest_lookup.py,sha256=fPeBIHxEkHJ2wIrqljX5BElnUbYuAsYLl8Zd5d6qGXs,12870 +astroid/tests/unittest_manager.py,sha256=Ch7RCT-dHoQis8FuaPFS2bGUwLyxVCA56LCEgD-l6GQ,8780 +astroid/tests/unittest_modutils.py,sha256=UUw-v9XBQF9r46pO2Nxpi97xQkAInbyjJt5s5Foa0FA,10958 +astroid/tests/unittest_nodes.py,sha256=6yzHjb5W_DCgzv5KgJe-gQT65fNjtif2GLD0et9XMZc,26681 +astroid/tests/unittest_objects.py,sha256=I1_2FAbOz9bQm2DwYe820iU28Fhbft_zBEYZhmxyR7o,20666 +astroid/tests/unittest_peephole.py,sha256=XbzmRIaGgmLjpX-mM6ocdaEkk4hPB_zuGKFdzV4u8D0,3862 +astroid/tests/unittest_protocols.py,sha256=l6czE8jGJjSRIa93N2p5g4UvpkgbrFKcoPBfspuro-Y,6748 +astroid/tests/unittest_python3.py,sha256=irRwDy_crq8F_lIiTnAeqW9YXLwxPFZj7MzMNpJoFj4,9030 +astroid/tests/unittest_raw_building.py,sha256=g5CzqavC4x5y89uuZZTThfbd6GCuPVoyxyu4BLzqO3w,3075 +astroid/tests/unittest_regrtest.py,sha256=EOI6ekqzeXTCDlkB_4KWVP7kt9HEjAOYEVWP9ggnMzc,12203 +astroid/tests/unittest_scoped_nodes.py,sha256=n2cteqr-jB5GvwIXFnrkIhnPjTTBntTMBnRCWFBj-j4,58717 +astroid/tests/unittest_transforms.py,sha256=lc9JbwoO3Hul6jDukK48Fz_KO7bS9L4Q6LwOx85M-dI,8870 +astroid/tests/unittest_utils.py,sha256=Ni7cT0RCSMTbZa-3b-h-2FYlx3SAszyeUmitJ7iS69U,4441 +astroid/tests/testdata/python2/data/MyPyPa-0.1.0-py2.5.egg,sha256=hZPSxlunnE-5kTg-yII7E10WG-nsV3DELfKv_xYnBeI,1222 +astroid/tests/testdata/python2/data/MyPyPa-0.1.0-py2.5.zip,sha256=hZPSxlunnE-5kTg-yII7E10WG-nsV3DELfKv_xYnBeI,1222 +astroid/tests/testdata/python2/data/__init__.py,sha256=UUgQFilI5GXd3tVo42wvC99xr-OWdiFwd4AToVWMKJg,68 +astroid/tests/testdata/python2/data/absimport.py,sha256=Dl1v3sCTUuy5NjWsvk6xfXDGqG8dJxYky66oH_16y1U,78 +astroid/tests/testdata/python2/data/all.py,sha256=9hzh93N-w2OoWmuWFFPe4NfLPtN0CcQUWyJU9G2kki8,106 +astroid/tests/testdata/python2/data/clientmodule_test.py,sha256=jiqYWxRNJPU8xPNNVDcb5OwHzq73cRevErmzX5q_pVY,800 +astroid/tests/testdata/python2/data/descriptor_crash.py,sha256=c9dmcN0XSB1WiDINWLjfA0SYY87UzMIpETXHBdcgJ0Y,217 +astroid/tests/testdata/python2/data/email.py,sha256=bA18WU0kAWGxsPlWJjD6LgXj9NK4RDLjqaN5-EievLw,70 +astroid/tests/testdata/python2/data/format.py,sha256=Se18tU4br95nCnBg7DIYonoRIXAZi3u2RvyoKwupAXk,421 +astroid/tests/testdata/python2/data/joined_strings.py,sha256=5nO3HMS9TAB0jZml1cSBv_b-1m4GTJ_12hD8WYMugBw,72168 +astroid/tests/testdata/python2/data/module.py,sha256=jaS47E_rOtpGIECwWYYl3ZBzBUZt0fvyCs7tG99SxgU,1804 +astroid/tests/testdata/python2/data/module2.py,sha256=gNaybt93hMTRFCnOh3gjW0niEDP5nVO8TrpixkHWW5o,1960 +astroid/tests/testdata/python2/data/noendingnewline.py,sha256=cVu_K7C5NnjnEvmMUxVGeeguyFcHBuNFEO3ueF9X9LI,503 +astroid/tests/testdata/python2/data/nonregr.py,sha256=0M3kW2tiTQdfuIUU9CNZHDBd1qC6Sxms6b_QZLLGtro,1150 +astroid/tests/testdata/python2/data/notall.py,sha256=Jg0X_GfNZyAnDxHLeGUEa4f9m761kCAUySpqbCeUweM,74 +astroid/tests/testdata/python2/data/recursion.py,sha256=ZuYyd9K4DyZxXg3L-B1Dl7k9q8OpIfVDwN9kJ52xLDk,52 +astroid/tests/testdata/python2/data/suppliermodule_test.py,sha256=t_C4IIivrAtXzdpRdxWxPB4Ii74r0gK-xFoyXhk_ikg,236 +astroid/tests/testdata/python2/data/SSL1/Connection1.py,sha256=rOKmOG_JTouiVawzB5kty493I64pBM9WJDinQn-_Y5c,343 +astroid/tests/testdata/python2/data/SSL1/__init__.py,sha256=ZlvNUty1pEZy7wHMAM83YwYcdE4ypNHh0W2ijB3mqO8,35 +astroid/tests/testdata/python2/data/absimp/__init__.py,sha256=CTlFm8G4kKecaa0NpFb4X25NNZ9FNorigSG65GAvvYA,89 +astroid/tests/testdata/python2/data/absimp/string.py,sha256=liyEyorFV0OJFr-HcECPPRfVmLd0lO4YrGFnZz0_T0M,83 +astroid/tests/testdata/python2/data/absimp/sidepackage/__init__.py,sha256=9E8Vj_jbaQ7tm80sIxyruqZPjzlVLNbd3qQxbvj39rI,42 +astroid/tests/testdata/python2/data/appl/__init__.py,sha256=9OoDa7y4MPXKZenN5CA2wmwsG7vUqiO4ImtTjsNs6YY,13 +astroid/tests/testdata/python2/data/appl/myConnection.py,sha256=Zc3RQ_GjoZ91k3LkaIfV4_1SePpwKUU2cOFAzN5Iq6Y,303 +astroid/tests/testdata/python2/data/find_test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python2/data/find_test/module.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python2/data/find_test/module2.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python2/data/find_test/noendingnewline.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python2/data/find_test/nonregr.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python2/data/lmfp/__init__.py,sha256=JmYecBTypWpPdKNy76pDWYliy-gWg3PPOOMcUdMAvzo,51 +astroid/tests/testdata/python2/data/lmfp/foo.py,sha256=ePynel7303gG6wq8wb6kRmaV75Q7mR9A_X7SZVP0YWM,170 +astroid/tests/testdata/python2/data/module1abs/__init__.py,sha256=RTMiBz8OgkD3dy2Sehwv6am35Xzlf6X8SQJcfo-m2sA,113 +astroid/tests/testdata/python2/data/module1abs/core.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11 +astroid/tests/testdata/python2/data/package/__init__.py,sha256=U50oVo2CraRtPYheia534Z0iPVQMDT2C6Qwj2ZWAmO0,57 +astroid/tests/testdata/python2/data/package/absimport.py,sha256=cTkLoSR4oIJtQ8yVLAgdopJXro0qFsehlMGYLCfiPvo,172 +astroid/tests/testdata/python2/data/package/hello.py,sha256=sTddKXRfLNAysty0r625S8QysSDOmtF8oXDvbl3Cywk,20 +astroid/tests/testdata/python2/data/package/import_package_subpackage_module.py,sha256=U6BsMb_ygFb8RqImsTrWEGJihU7nJgELPH6AvWM-zaU,2242 +astroid/tests/testdata/python2/data/package/subpackage/__init__.py,sha256=XtKilaAqziUI-ImaSw4V6Aic40domt4v_If7lAZYhSE,25 +astroid/tests/testdata/python2/data/package/subpackage/module.py,sha256=WAtPIk13pW6tYI6rSgNHcCgTu0EXhX6i5CugdHPH8N0,32 +astroid/tests/testdata/python2/data/unicode_package/__init__.py,sha256=Qq8Rv1-47xfh9UMnDqtU6MYCoZbK2DF1zxAvxlkhCNU,17 +astroid/tests/testdata/python2/data/unicode_package/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python3/data/MyPyPa-0.1.0-py2.5.egg,sha256=hZPSxlunnE-5kTg-yII7E10WG-nsV3DELfKv_xYnBeI,1222 +astroid/tests/testdata/python3/data/MyPyPa-0.1.0-py2.5.zip,sha256=hZPSxlunnE-5kTg-yII7E10WG-nsV3DELfKv_xYnBeI,1222 +astroid/tests/testdata/python3/data/__init__.py,sha256=UUgQFilI5GXd3tVo42wvC99xr-OWdiFwd4AToVWMKJg,68 +astroid/tests/testdata/python3/data/absimport.py,sha256=-CKa6uxNJwTox5JoeWFe_hnxPcp1BT_vgPrXjsk4c-w,40 +astroid/tests/testdata/python3/data/all.py,sha256=96OFTf0wN5cad6Zt4WvJ6OxHTUncQyPyghPMRxGV9B8,107 +astroid/tests/testdata/python3/data/clientmodule_test.py,sha256=jiqYWxRNJPU8xPNNVDcb5OwHzq73cRevErmzX5q_pVY,800 +astroid/tests/testdata/python3/data/descriptor_crash.py,sha256=c9dmcN0XSB1WiDINWLjfA0SYY87UzMIpETXHBdcgJ0Y,217 +astroid/tests/testdata/python3/data/email.py,sha256=bA18WU0kAWGxsPlWJjD6LgXj9NK4RDLjqaN5-EievLw,70 +astroid/tests/testdata/python3/data/format.py,sha256=Se18tU4br95nCnBg7DIYonoRIXAZi3u2RvyoKwupAXk,421 +astroid/tests/testdata/python3/data/joined_strings.py,sha256=5nO3HMS9TAB0jZml1cSBv_b-1m4GTJ_12hD8WYMugBw,72168 +astroid/tests/testdata/python3/data/module.py,sha256=gmtEr1dRdtYP5oyUwvl-Bmk498D3q9fpPSMcEGeoPPc,1799 +astroid/tests/testdata/python3/data/module2.py,sha256=A3c7169M8pPIBi8U6mnLjNQnTOPg_en9qVW18yEGNCs,1978 +astroid/tests/testdata/python3/data/noendingnewline.py,sha256=PaqOTMH1fn703GRn8_lZox2ByExWci0LiXfEKZjKgGU,506 +astroid/tests/testdata/python3/data/nonregr.py,sha256=oCCrE6UTcDUmFcLnde2N34Fxv1PQ8Ck3WqE0or1Jqqk,1101 +astroid/tests/testdata/python3/data/notall.py,sha256=DftFceOP1cQfe2imrwTWcsbuxugJx9mDFFM57cCPUnA,75 +astroid/tests/testdata/python3/data/recursion.py,sha256=ZuYyd9K4DyZxXg3L-B1Dl7k9q8OpIfVDwN9kJ52xLDk,52 +astroid/tests/testdata/python3/data/suppliermodule_test.py,sha256=t_C4IIivrAtXzdpRdxWxPB4Ii74r0gK-xFoyXhk_ikg,236 +astroid/tests/testdata/python3/data/SSL1/Connection1.py,sha256=bvnJLQ3Ey3FzNDCR2mEeU8G44-c4iw9vOHBKOXHuGJM,306 +astroid/tests/testdata/python3/data/SSL1/__init__.py,sha256=3Flw6M01FPCVMhiVC_yk-NQbOaQW6K4H_H9wqx6c1do,36 +astroid/tests/testdata/python3/data/absimp/__init__.py,sha256=CTlFm8G4kKecaa0NpFb4X25NNZ9FNorigSG65GAvvYA,89 +astroid/tests/testdata/python3/data/absimp/string.py,sha256=liyEyorFV0OJFr-HcECPPRfVmLd0lO4YrGFnZz0_T0M,83 +astroid/tests/testdata/python3/data/absimp/sidepackage/__init__.py,sha256=9E8Vj_jbaQ7tm80sIxyruqZPjzlVLNbd3qQxbvj39rI,42 +astroid/tests/testdata/python3/data/appl/__init__.py,sha256=9OoDa7y4MPXKZenN5CA2wmwsG7vUqiO4ImtTjsNs6YY,13 +astroid/tests/testdata/python3/data/appl/myConnection.py,sha256=mWi72c6yYuIXoyRXo-uKFwY7NSj-lok_NRlNc9N2hfM,261 +astroid/tests/testdata/python3/data/find_test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python3/data/find_test/module.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python3/data/find_test/module2.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python3/data/find_test/noendingnewline.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python3/data/find_test/nonregr.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/tests/testdata/python3/data/lmfp/__init__.py,sha256=JmYecBTypWpPdKNy76pDWYliy-gWg3PPOOMcUdMAvzo,51 +astroid/tests/testdata/python3/data/lmfp/foo.py,sha256=ePynel7303gG6wq8wb6kRmaV75Q7mR9A_X7SZVP0YWM,170 +astroid/tests/testdata/python3/data/module1abs/__init__.py,sha256=qeBmkE-gZ07oAuq_fgcaMP8217AdA-FGOR73iB5lltg,59 +astroid/tests/testdata/python3/data/module1abs/core.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11 +astroid/tests/testdata/python3/data/package/__init__.py,sha256=U50oVo2CraRtPYheia534Z0iPVQMDT2C6Qwj2ZWAmO0,57 +astroid/tests/testdata/python3/data/package/absimport.py,sha256=cTkLoSR4oIJtQ8yVLAgdopJXro0qFsehlMGYLCfiPvo,172 +astroid/tests/testdata/python3/data/package/hello.py,sha256=sTddKXRfLNAysty0r625S8QysSDOmtF8oXDvbl3Cywk,20 +astroid/tests/testdata/python3/data/package/import_package_subpackage_module.py,sha256=U6BsMb_ygFb8RqImsTrWEGJihU7nJgELPH6AvWM-zaU,2242 +astroid/tests/testdata/python3/data/package/subpackage/__init__.py,sha256=XtKilaAqziUI-ImaSw4V6Aic40domt4v_If7lAZYhSE,25 +astroid/tests/testdata/python3/data/package/subpackage/module.py,sha256=WAtPIk13pW6tYI6rSgNHcCgTu0EXhX6i5CugdHPH8N0,32 +astroid/tests/testdata/python3/data/unicode_package/__init__.py,sha256=Qq8Rv1-47xfh9UMnDqtU6MYCoZbK2DF1zxAvxlkhCNU,17 +astroid/tests/testdata/python3/data/unicode_package/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid-1.4.9.dist-info/DESCRIPTION.rst,sha256=xc_zc3CC4NAakMv6bAqAlAk6NC7ScAC-G4wxGLuOq7k,2024 +astroid-1.4.9.dist-info/METADATA,sha256=52pYWwlyD3d1oGcKbpa8T0JsYWgdF-7uYkm-uV64bvQ,2643 +astroid-1.4.9.dist-info/RECORD,, +astroid-1.4.9.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +astroid-1.4.9.dist-info/metadata.json,sha256=dpnAZsv48TonGcqaiI1OL1LfpzJ1p-wG1IPQD-4T-QA,789 +astroid-1.4.9.dist-info/top_level.txt,sha256=HsdW4O2x7ZXRj6k-agi3RaQybGLobI3VSE-jt4vQUXM,8 +astroid-1.4.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +astroid/brain/brain_qt.pyc,, +astroid/tests/testdata/python3/data/format.pyc,, +astroid/tests/unittest_manager.pyc,, +astroid/tests/testdata/python3/data/descriptor_crash.pyc,, +astroid/tests/testdata/python3/data/find_test/__init__.pyc,, +astroid/modutils.pyc,, +astroid/tests/unittest_builder.pyc,, +astroid/tests/testdata/python2/data/absimp/__init__.pyc,, +astroid/tests/testdata/python2/data/appl/__init__.pyc,, +astroid/brain/brain_six.pyc,, +astroid/tests/testdata/python2/data/find_test/nonregr.pyc,, +astroid/tests/testdata/python2/data/descriptor_crash.pyc,, +astroid/tests/unittest_inference.pyc,, +astroid/objects.pyc,, +astroid/tests/testdata/python2/data/format.pyc,, +astroid/tests/unittest_utils.pyc,, +astroid/tests/testdata/python3/data/package/__init__.pyc,, +astroid/tests/testdata/python3/data/suppliermodule_test.pyc,, +astroid/tests/testdata/python3/data/noendingnewline.pyc,, +astroid/tests/testdata/python3/data/lmfp/__init__.pyc,, +astroid/tests/testdata/python2/data/lmfp/__init__.pyc,, +astroid/__pkginfo__.pyc,, +astroid/tests/testdata/python3/data/module1abs/__init__.pyc,, +astroid/transforms.pyc,, +astroid/tests/testdata/python3/data/email.pyc,, +astroid/brain/brain_builtin_inference.pyc,, +astroid/tests/testdata/python2/data/lmfp/foo.pyc,, +astroid/tests/testdata/python3/data/package/hello.pyc,, +astroid/tests/testdata/python2/data/find_test/noendingnewline.pyc,, +astroid/tests/testdata/python3/data/absimport.pyc,, +astroid/tests/testdata/python2/data/module1abs/__init__.pyc,, +astroid/tests/unittest_scoped_nodes.pyc,, +astroid/tests/testdata/python2/data/unicode_package/core/__init__.pyc,, +astroid/mixins.pyc,, +astroid/tests/testdata/python3/data/SSL1/__init__.pyc,, +astroid/tests/unittest_transforms.pyc,, +astroid/tests/__init__.pyc,, +astroid/decorators.pyc,, +astroid/tests/testdata/python3/data/find_test/module.pyc,, +astroid/tests/testdata/python2/data/module2.pyc,, +astroid/tests/unittest_modutils.pyc,, +astroid/tests/testdata/python3/data/absimp/string.pyc,, +astroid/brain/brain_pytest.pyc,, +astroid/tests/testdata/python2/data/module1abs/core.pyc,, +astroid/tests/testdata/python3/data/module.pyc,, +astroid/tests/testdata/python2/data/recursion.pyc,, +astroid/tests/testdata/python2/data/noendingnewline.pyc,, +astroid/tests/testdata/python3/data/absimp/sidepackage/__init__.pyc,, +astroid/tests/testdata/python2/data/package/hello.pyc,, +astroid/tests/testdata/python3/data/package/subpackage/__init__.pyc,, +astroid/brain/brain_dateutil.pyc,, +astroid/tests/testdata/python3/data/all.pyc,, +astroid/inference.pyc,, +astroid/tests/testdata/python2/data/find_test/module2.pyc,, +astroid/brain/brain_stdlib.pyc,, +astroid/tests/testdata/python2/data/appl/myConnection.pyc,, +astroid/tests/testdata/python3/data/recursion.pyc,, +astroid/util.pyc,, +astroid/tests/testdata/python2/data/module.pyc,, +astroid/tests/testdata/python2/data/SSL1/__init__.pyc,, +astroid/tests/unittest_brain.pyc,, +astroid/test_utils.pyc,, +astroid/node_classes.pyc,, +astroid/tests/testdata/python2/data/clientmodule_test.pyc,, +astroid/tests/testdata/python3/data/unicode_package/__init__.pyc,, +astroid/tests/unittest_peephole.pyc,, +astroid/tests/testdata/python2/data/package/import_package_subpackage_module.pyc,, +astroid/tests/testdata/python3/data/package/subpackage/module.pyc,, +astroid/context.pyc,, +astroid/brain/brain_mechanize.pyc,, +astroid/bases.pyc,, +astroid/__init__.pyc,, +astroid/tests/testdata/python3/data/nonregr.pyc,, +astroid/tests/testdata/python3/data/find_test/nonregr.pyc,, +astroid/tests/testdata/python3/data/lmfp/foo.pyc,, +astroid/tests/testdata/python2/data/unicode_package/__init__.pyc,, +astroid/rebuilder.pyc,, +astroid/tests/testdata/python2/data/all.pyc,, +astroid/brain/brain_numpy.pyc,, +astroid/tests/testdata/python2/data/absimport.pyc,, +astroid/tests/testdata/python2/data/SSL1/Connection1.pyc,, +astroid/brain/brain_nose.pyc,, +astroid/scoped_nodes.pyc,, +astroid/manager.pyc,, +astroid/tests/testdata/python3/data/find_test/module2.pyc,, +astroid/tests/testdata/python2/data/absimp/sidepackage/__init__.pyc,, +astroid/tests/testdata/python2/data/package/absimport.pyc,, +astroid/tests/testdata/python3/data/unicode_package/core/__init__.pyc,, +astroid/tests/resources.pyc,, +astroid/tests/testdata/python3/data/absimp/__init__.pyc,, +astroid/tests/testdata/python3/data/module1abs/core.pyc,, +astroid/tests/testdata/python2/data/find_test/__init__.pyc,, +astroid/astpeephole.pyc,, +astroid/tests/testdata/python2/data/package/subpackage/__init__.pyc,, +astroid/raw_building.pyc,, +astroid/brain/brain_gi.pyc,, +astroid/brain/brain_ssl.pyc,, +astroid/tests/unittest_raw_building.pyc,, +astroid/tests/testdata/python3/data/notall.pyc,, +astroid/nodes.pyc,, +astroid/builder.pyc,, +astroid/tests/testdata/python3/data/joined_strings.pyc,, +astroid/tests/testdata/python3/data/appl/myConnection.pyc,, +astroid/tests/testdata/python3/data/package/import_package_subpackage_module.pyc,, +astroid/tests/testdata/python2/data/package/subpackage/module.pyc,, +astroid/tests/testdata/python2/data/absimp/string.pyc,, +astroid/tests/unittest_regrtest.pyc,, +astroid/tests/testdata/python2/data/nonregr.pyc,, +astroid/as_string.pyc,, +astroid/tests/unittest_objects.pyc,, +astroid/tests/unittest_protocols.pyc,, +astroid/tests/testdata/python2/data/suppliermodule_test.pyc,, +astroid/tests/testdata/python2/data/__init__.pyc,, +astroid/protocols.pyc,, +astroid/tests/unittest_nodes.pyc,, +astroid/tests/testdata/python2/data/joined_strings.pyc,, +astroid/exceptions.pyc,, +astroid/tests/unittest_python3.pyc,, +astroid/tests/testdata/python3/data/clientmodule_test.pyc,, +astroid/tests/testdata/python3/data/__init__.pyc,, +astroid/tests/testdata/python3/data/appl/__init__.pyc,, +astroid/arguments.pyc,, +astroid/tests/testdata/python3/data/find_test/noendingnewline.pyc,, +astroid/tests/testdata/python3/data/SSL1/Connection1.pyc,, +astroid/tests/testdata/python2/data/find_test/module.pyc,, +astroid/tests/testdata/python2/data/notall.pyc,, +astroid/tests/unittest_lookup.pyc,, +astroid/tests/testdata/python3/data/package/absimport.pyc,, +astroid/tests/testdata/python2/data/package/__init__.pyc,, +astroid/tests/testdata/python2/data/email.pyc,, diff --git a/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL b/pymode/libs/astroid-1.4.9.dist-info/WHEEL similarity index 54% rename from pymode/libs/logilab_common-1.0.2.dist-info/WHEEL rename to pymode/libs/astroid-1.4.9.dist-info/WHEEL index 45a0cd88..8b6dd1b5 100644 --- a/pymode/libs/logilab_common-1.0.2.dist-info/WHEEL +++ b/pymode/libs/astroid-1.4.9.dist-info/WHEEL @@ -1,5 +1,6 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) +Generator: bdist_wheel (0.29.0) Root-Is-Purelib: true Tag: py2-none-any +Tag: py3-none-any diff --git a/pymode/libs/astroid-1.4.9.dist-info/metadata.json b/pymode/libs/astroid-1.4.9.dist-info/metadata.json new file mode 100644 index 00000000..ae263d92 --- /dev/null +++ b/pymode/libs/astroid-1.4.9.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "code-quality@python.org", "name": "Python Code Quality Authority", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/PyCQA/astroid"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "license": "LGPL", "metadata_version": "2.0", "name": "astroid", "run_requires": [{"requires": ["lazy-object-proxy", "six", "wrapt"]}], "summary": "A abstract syntax tree for Python with inference support.", "version": "1.4.9"} \ No newline at end of file diff --git a/pymode/libs/astroid-1.4.9.dist-info/top_level.txt b/pymode/libs/astroid-1.4.9.dist-info/top_level.txt new file mode 100644 index 00000000..450d4fe9 --- /dev/null +++ b/pymode/libs/astroid-1.4.9.dist-info/top_level.txt @@ -0,0 +1 @@ +astroid diff --git a/pymode/libs/astroid/__init__.py b/pymode/libs/astroid/__init__.py index d4fd12c5..175dcb5e 100644 --- a/pymode/libs/astroid/__init__.py +++ b/pymode/libs/astroid/__init__.py @@ -58,13 +58,15 @@ # more stuff available from astroid import raw_building -from astroid.bases import YES, Instance, BoundMethod, UnboundMethod +from astroid.bases import Instance, BoundMethod, UnboundMethod from astroid.node_classes import are_exclusive, unpack_infer from astroid.scoped_nodes import builtin_lookup +from astroid.builder import parse +from astroid.util import YES # make a manager instance (borg) as well as Project and Package classes # accessible from astroid package -from astroid.manager import AstroidManager, Project +from astroid.manager import AstroidManager MANAGER = AstroidManager() del AstroidManager @@ -100,7 +102,7 @@ def inference_tip(infer_function): .. sourcecode:: python - MANAGER.register_transform(CallFunc, inference_tip(infer_named_tuple), + MANAGER.register_transform(Call, inference_tip(infer_named_tuple), predicate) """ def transform(node, infer_function=infer_function): @@ -112,8 +114,11 @@ def transform(node, infer_function=infer_function): def register_module_extender(manager, module_name, get_extension_mod): def transform(node): extension_module = get_extension_mod() - for name, obj in extension_module.locals.items(): - node.locals[name] = obj + for name, objs in extension_module._locals.items(): + node._locals[name] = objs + for obj in objs: + if obj.parent is extension_module: + obj.parent = node manager.register_transform(Module, transform, lambda n: n.name == module_name) diff --git a/pymode/libs/astroid/__pkginfo__.py b/pymode/libs/astroid/__pkginfo__.py index 3fb45aa4..7a5acfa5 100644 --- a/pymode/libs/astroid/__pkginfo__.py +++ b/pymode/libs/astroid/__pkginfo__.py @@ -20,17 +20,17 @@ modname = 'astroid' -numversion = (1, 3, 8) +numversion = (1, 4, 9) version = '.'.join([str(num) for num in numversion]) -install_requires = ['logilab-common>=0.63.0', 'six'] +install_requires = ['six', 'lazy_object_proxy', 'wrapt'] license = 'LGPL' -author = 'Logilab' -author_email = 'pylint-dev@lists.logilab.org' +author = 'Python Code Quality Authority' +author_email = 'code-quality@python.org' mailinglist = "mailto://%s" % author_email -web = 'http://bitbucket.org/logilab/astroid' +web = 'https://github.com/PyCQA/astroid' description = "A abstract syntax tree for Python with inference support." diff --git a/pymode/libs/astroid/arguments.py b/pymode/libs/astroid/arguments.py new file mode 100644 index 00000000..f05d48a3 --- /dev/null +++ b/pymode/libs/astroid/arguments.py @@ -0,0 +1,233 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import nodes +from astroid import util + +import six + + +class CallSite(object): + """Class for understanding arguments passed into a call site + + It needs a call context, which contains the arguments and the + keyword arguments that were passed into a given call site. + In order to infer what an argument represents, call + :meth:`infer_argument` with the corresponding function node + and the argument name. + """ + + def __init__(self, callcontext): + args = callcontext.args + keywords = callcontext.keywords + self.duplicated_keywords = set() + self._unpacked_args = self._unpack_args(args) + self._unpacked_kwargs = self._unpack_keywords(keywords) + + self.positional_arguments = [ + arg for arg in self._unpacked_args + if arg is not util.YES + ] + self.keyword_arguments = { + key: value for key, value in self._unpacked_kwargs.items() + if value is not util.YES + } + + @classmethod + def from_call(cls, call_node): + """Get a CallSite object from the given Call node.""" + callcontext = contextmod.CallContext(call_node.args, + call_node.keywords) + return cls(callcontext) + + def has_invalid_arguments(self): + """Check if in the current CallSite were passed *invalid* arguments + + This can mean multiple things. For instance, if an unpacking + of an invalid object was passed, then this method will return True. + Other cases can be when the arguments can't be inferred by astroid, + for example, by passing objects which aren't known statically. + """ + return len(self.positional_arguments) != len(self._unpacked_args) + + def has_invalid_keywords(self): + """Check if in the current CallSite were passed *invalid* keyword arguments + + For instance, unpacking a dictionary with integer keys is invalid + (**{1:2}), because the keys must be strings, which will make this + method to return True. Other cases where this might return True if + objects which can't be inferred were passed. + """ + return len(self.keyword_arguments) != len(self._unpacked_kwargs) + + def _unpack_keywords(self, keywords): + values = {} + context = contextmod.InferenceContext() + for name, value in keywords: + if name is None: + # Then it's an unpacking operation (**) + try: + inferred = next(value.infer(context=context)) + except exceptions.InferenceError: + values[name] = util.YES + continue + + if not isinstance(inferred, nodes.Dict): + # Not something we can work with. + values[name] = util.YES + continue + + for dict_key, dict_value in inferred.items: + try: + dict_key = next(dict_key.infer(context=context)) + except exceptions.InferenceError: + values[name] = util.YES + continue + if not isinstance(dict_key, nodes.Const): + values[name] = util.YES + continue + if not isinstance(dict_key.value, six.string_types): + values[name] = util.YES + continue + if dict_key.value in values: + # The name is already in the dictionary + values[dict_key.value] = util.YES + self.duplicated_keywords.add(dict_key.value) + continue + values[dict_key.value] = dict_value + else: + values[name] = value + return values + + @staticmethod + def _unpack_args(args): + values = [] + context = contextmod.InferenceContext() + for arg in args: + if isinstance(arg, nodes.Starred): + try: + inferred = next(arg.value.infer(context=context)) + except exceptions.InferenceError: + values.append(util.YES) + continue + + if inferred is util.YES: + values.append(util.YES) + continue + if not hasattr(inferred, 'elts'): + values.append(util.YES) + continue + values.extend(inferred.elts) + else: + values.append(arg) + return values + + def infer_argument(self, funcnode, name, context): + """infer a function argument value according to the call context""" + if name in self.duplicated_keywords: + raise exceptions.InferenceError(name) + + # Look into the keywords first, maybe it's already there. + try: + return self.keyword_arguments[name].infer(context) + except KeyError: + pass + + # Too many arguments given and no variable arguments. + if len(self.positional_arguments) > len(funcnode.args.args): + if not funcnode.args.vararg: + raise exceptions.InferenceError(name) + + positional = self.positional_arguments[:len(funcnode.args.args)] + vararg = self.positional_arguments[len(funcnode.args.args):] + argindex = funcnode.args.find_argname(name)[0] + kwonlyargs = set(arg.name for arg in funcnode.args.kwonlyargs) + kwargs = { + key: value for key, value in self.keyword_arguments.items() + if key not in kwonlyargs + } + # If there are too few positionals compared to + # what the function expects to receive, check to see + # if the missing positional arguments were passed + # as keyword arguments and if so, place them into the + # positional args list. + if len(positional) < len(funcnode.args.args): + for func_arg in funcnode.args.args: + if func_arg.name in kwargs: + arg = kwargs.pop(func_arg.name) + positional.append(arg) + + if argindex is not None: + # 2. first argument of instance/class method + if argindex == 0 and funcnode.type in ('method', 'classmethod'): + if context.boundnode is not None: + boundnode = context.boundnode + else: + # XXX can do better ? + boundnode = funcnode.parent.frame() + if funcnode.type == 'method': + if not isinstance(boundnode, bases.Instance): + boundnode = bases.Instance(boundnode) + return iter((boundnode,)) + if funcnode.type == 'classmethod': + return iter((boundnode,)) + # if we have a method, extract one position + # from the index, so we'll take in account + # the extra parameter represented by `self` or `cls` + if funcnode.type in ('method', 'classmethod'): + argindex -= 1 + # 2. search arg index + try: + return self.positional_arguments[argindex].infer(context) + except IndexError: + pass + + if funcnode.args.kwarg == name: + # It wants all the keywords that were passed into + # the call site. + if self.has_invalid_keywords(): + raise exceptions.InferenceError + kwarg = nodes.Dict() + kwarg.lineno = funcnode.args.lineno + kwarg.col_offset = funcnode.args.col_offset + kwarg.parent = funcnode.args + items = [(nodes.const_factory(key), value) + for key, value in kwargs.items()] + kwarg.items = items + return iter((kwarg, )) + elif funcnode.args.vararg == name: + # It wants all the args that were passed into + # the call site. + if self.has_invalid_arguments(): + raise exceptions.InferenceError + args = nodes.Tuple() + args.lineno = funcnode.args.lineno + args.col_offset = funcnode.args.col_offset + args.parent = funcnode.args + args.elts = vararg + return iter((args, )) + + # Check if it's a default parameter. + try: + return funcnode.args.default_value(name).infer(context) + except exceptions.NoDefault: + pass + raise exceptions.InferenceError(name) diff --git a/pymode/libs/astroid/as_string.py b/pymode/libs/astroid/as_string.py index f627f9e8..2b07200c 100644 --- a/pymode/libs/astroid/as_string.py +++ b/pymode/libs/astroid/as_string.py @@ -22,9 +22,10 @@ * :func:`dump` function return an internal representation of nodes found in the tree, useful for debugging or understanding the tree structure """ - import sys +import six + INDENT = ' ' # 4 spaces ; keep indentation variable @@ -89,9 +90,9 @@ def visit_arguments(self, node): """return an astroid.Function node as string""" return node.format_args() - def visit_assattr(self, node): + def visit_assignattr(self, node): """return an astroid.AssAttr node as string""" - return self.visit_getattr(node) + return self.visit_attribute(node) def visit_assert(self, node): """return an astroid.Assert node as string""" @@ -100,7 +101,7 @@ def visit_assert(self, node): node.fail.accept(self)) return 'assert %s' % node.test.accept(self) - def visit_assname(self, node): + def visit_assignname(self, node): """return an astroid.AssName node as string""" return node.name @@ -113,8 +114,8 @@ def visit_augassign(self, node): """return an astroid.AugAssign node as string""" return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) - def visit_backquote(self, node): - """return an astroid.Backquote node as string""" + def visit_repr(self, node): + """return an astroid.Repr node as string""" return '`%s`' % node.value.accept(self) def visit_binop(self, node): @@ -130,18 +131,20 @@ def visit_break(self, node): """return an astroid.Break node as string""" return 'break' - def visit_callfunc(self, node): - """return an astroid.CallFunc node as string""" + def visit_call(self, node): + """return an astroid.Call node as string""" expr_str = node.func.accept(self) args = [arg.accept(self) for arg in node.args] - if node.starargs: - args.append('*' + node.starargs.accept(self)) - if node.kwargs: - args.append('**' + node.kwargs.accept(self)) + if node.keywords: + keywords = [kwarg.accept(self) for kwarg in node.keywords] + else: + keywords = [] + + args.extend(keywords) return '%s(%s)' % (expr_str, ', '.join(args)) - def visit_class(self, node): - """return an astroid.Class node as string""" + def visit_classdef(self, node): + """return an astroid.ClassDef node as string""" decorate = node.decorators and node.decorators.accept(self) or '' bases = ', '.join([n.accept(self) for n in node.bases]) if sys.version_info[0] == 2: @@ -186,7 +189,7 @@ def visit_delete(self, node): # XXX check if correct def visit_delattr(self, node): """return an astroid.DelAttr node as string""" - return self.visit_getattr(node) + return self.visit_attribute(node) def visit_delname(self, node): """return an astroid.DelName node as string""" @@ -198,16 +201,27 @@ def visit_decorators(self, node): def visit_dict(self, node): """return an astroid.Dict node as string""" - return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), - value.accept(self)) - for key, value in node.items]) + return '{%s}' % ', '.join(self._visit_dict(node)) + + def _visit_dict(self, node): + for key, value in node.items: + key = key.accept(self) + value = value.accept(self) + if key == '**': + # It can only be a DictUnpack node. + yield key + value + else: + yield '%s: %s' % (key, value) + + def visit_dictunpack(self, node): + return '**' def visit_dictcomp(self, node): """return an astroid.DictComp node as string""" return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), ' '.join([n.accept(self) for n in node.generators])) - def visit_discard(self, node): + def visit_expr(self, node): """return an astroid.Discard node as string""" return node.value.accept(self) @@ -258,24 +272,33 @@ def visit_for(self, node): fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) return fors - def visit_from(self, node): - """return an astroid.From node as string""" + def visit_importfrom(self, node): + """return an astroid.ImportFrom node as string""" return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, _import_string(node.names)) - def visit_function(self, node): + def visit_functiondef(self, node): """return an astroid.Function node as string""" decorate = node.decorators and node.decorators.accept(self) or '' docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self), - docs, self._stmt_list(node.body)) - - def visit_genexpr(self, node): - """return an astroid.GenExpr node as string""" + return_annotation = '' + if six.PY3 and node.returns: + return_annotation = '->' + node.returns.as_string() + trailer = return_annotation + ":" + else: + trailer = ":" + def_format = "\n%sdef %s(%s)%s%s\n%s" + return def_format % (decorate, node.name, + node.args.accept(self), + trailer, docs, + self._stmt_list(node.body)) + + def visit_generatorexp(self, node): + """return an astroid.GeneratorExp node as string""" return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self) for n in node.generators])) - def visit_getattr(self, node): + def visit_attribute(self, node): """return an astroid.Getattr node as string""" return '%s.%s' % (node.expr.accept(self), node.attrname) @@ -302,6 +325,8 @@ def visit_import(self, node): def visit_keyword(self, node): """return an astroid.Keyword node as string""" + if node.arg is None: + return '**%s' % node.value.accept(self) return '%s=%s' % (node.arg, node.value.accept(self)) def visit_lambda(self, node): @@ -438,6 +463,22 @@ def visit_yield(self, node): else: return "(%s)" % (expr,) + def visit_starred(self, node): + """return Starred node as string""" + return "*" + node.value.accept(self) + + + # These aren't for real AST nodes, but for inference objects. + + def visit_frozenset(self, node): + return node.parent.accept(self) + + def visit_super(self, node): + return node.parent.accept(self) + + def visit_yes(self, node): + return "Uninferable" + class AsStringVisitor3k(AsStringVisitor): """AsStringVisitor3k overwrites some AsStringVisitor methods""" @@ -466,10 +507,6 @@ def visit_raise(self, node): return 'raise %s' % node.exc.accept(self) return 'raise' - def visit_starred(self, node): - """return Starred node as string""" - return "*" + node.value.accept(self) - def visit_yieldfrom(self, node): """ Return an astroid.YieldFrom node as string. """ yi_val = node.value and (" " + node.value.accept(self)) or "" @@ -479,6 +516,19 @@ def visit_yieldfrom(self, node): else: return "(%s)" % (expr,) + def visit_asyncfunctiondef(self, node): + function = super(AsStringVisitor3k, self).visit_functiondef(node) + return 'async ' + function.strip() + + def visit_await(self, node): + return 'await %s' % node.value.accept(self) + + def visit_asyncwith(self, node): + return 'async %s' % self.visit_with(node) + + def visit_asyncfor(self, node): + return 'async %s' % self.visit_for(node) + def _import_string(names): """return a list of (name, asname) formatted as a string""" @@ -496,4 +546,3 @@ def _import_string(names): # this visitor is stateless, thus it can be reused to_code = AsStringVisitor() - diff --git a/pymode/libs/astroid/bases.py b/pymode/libs/astroid/bases.py index ee8ee1c3..8dfa8126 100644 --- a/pymode/libs/astroid/bases.py +++ b/pymode/libs/astroid/bases.py @@ -18,22 +18,44 @@ """This module contains base classes and functions for the nodes and some inference utils. """ - -__docformat__ = "restructuredtext en" - +import functools import sys -from contextlib import contextmanager +import warnings -from logilab.common.decorators import cachedproperty +import wrapt -from astroid.exceptions import (InferenceError, AstroidError, NotFoundError, - UnresolvableName, UseInferenceDefault) +from astroid import context as contextmod +from astroid import decorators as decoratorsmod +from astroid import exceptions +from astroid import util if sys.version_info >= (3, 0): BUILTINS = 'builtins' else: BUILTINS = '__builtin__' +PROPERTIES = {BUILTINS + '.property', 'abc.abstractproperty'} +# List of possible property names. We use this list in order +# to see if a method is a property or not. This should be +# pretty reliable and fast, the alternative being to check each +# decorator to see if its a real property-like descriptor, which +# can be too complicated. +# Also, these aren't qualified, because each project can +# define them, we shouldn't expect to know every possible +# property-like decorator! +# TODO(cpopa): just implement descriptors already. +POSSIBLE_PROPERTIES = {"cached_property", "cachedproperty", + "lazyproperty", "lazy_property", "reify", + "lazyattribute", "lazy_attribute", + "LazyProperty", "lazy"} + + +def _is_property(meth): + if PROPERTIES.intersection(meth.decoratornames()): + return True + stripped = {name.split(".")[-1] for name in meth.decoratornames() + if name is not util.YES} + return any(name in stripped for name in POSSIBLE_PROPERTIES) class Proxy(object): @@ -56,101 +78,34 @@ def infer(self, context=None): yield self -# Inference ################################################################## - -class InferenceContext(object): - __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'infered') - - def __init__(self, path=None, infered=None): - self.path = path or set() - self.lookupname = None - self.callcontext = None - self.boundnode = None - self.infered = infered or {} - - def push(self, node): - name = self.lookupname - if (node, name) in self.path: - raise StopIteration() - self.path.add((node, name)) - - def clone(self): - # XXX copy lookupname/callcontext ? - clone = InferenceContext(self.path, infered=self.infered) - clone.callcontext = self.callcontext - clone.boundnode = self.boundnode - return clone - - def cache_generator(self, key, generator): - results = [] - for result in generator: - results.append(result) - yield result - - self.infered[key] = tuple(results) - return - - @contextmanager - def restore_path(self): - path = set(self.path) - yield - self.path = path - -def copy_context(context): - if context is not None: - return context.clone() - else: - return InferenceContext() - - def _infer_stmts(stmts, context, frame=None): - """return an iterator on statements inferred by each statement in - """ + """Return an iterator on statements inferred by each statement in *stmts*.""" stmt = None - infered = False + inferred = False if context is not None: name = context.lookupname context = context.clone() else: name = None - context = InferenceContext() + context = contextmod.InferenceContext() + for stmt in stmts: - if stmt is YES: + if stmt is util.YES: yield stmt - infered = True + inferred = True continue context.lookupname = stmt._infer_name(frame, name) try: - for infered in stmt.infer(context): - yield infered - infered = True - except UnresolvableName: + for inferred in stmt.infer(context=context): + yield inferred + inferred = True + except exceptions.UnresolvableName: continue - except InferenceError: - yield YES - infered = True - if not infered: - raise InferenceError(str(stmt)) - - -# special inference objects (e.g. may be returned as nodes by .infer()) ####### - -class _Yes(object): - """a yes object""" - def __repr__(self): - return 'YES' - def __getattribute__(self, name): - if name == 'next': - raise AttributeError('next method should not be called') - if name.startswith('__') and name.endswith('__'): - # to avoid inspection pb - return super(_Yes, self).__getattribute__(name) - return self - def __call__(self, *args, **kwargs): - return self - - -YES = _Yes() + except exceptions.InferenceError: + yield util.YES + inferred = True + if not inferred: + raise exceptions.InferenceError(str(stmt)) class Instance(Proxy): @@ -158,7 +113,7 @@ class Instance(Proxy): def getattr(self, name, context=None, lookupclass=True): try: values = self._proxied.instance_attr(name, context) - except NotFoundError: + except exceptions.NotFoundError: if name == '__class__': return [self._proxied] if lookupclass: @@ -167,23 +122,22 @@ def getattr(self, name, context=None, lookupclass=True): if name in ('__name__', '__bases__', '__mro__', '__subclasses__'): return self._proxied.local_attr(name) return self._proxied.getattr(name, context) - raise NotFoundError(name) + raise exceptions.NotFoundError(name) # since we've no context information, return matching class members as # well if lookupclass: try: return values + self._proxied.getattr(name, context) - except NotFoundError: + except exceptions.NotFoundError: pass return values def igetattr(self, name, context=None): """inferred getattr""" if not context: - context = InferenceContext() + context = contextmod.InferenceContext() try: # avoid recursively inferring the same attr on the same class - context.push((self._proxied, name)) # XXX frame should be self._proxied, or not ? get_attr = self.getattr(name, context, lookupclass=False) @@ -192,38 +146,49 @@ def igetattr(self, name, context=None): context, frame=self, ) - except NotFoundError: + except exceptions.NotFoundError: try: # fallback to class'igetattr since it has some logic to handle # descriptors return self._wrap_attr(self._proxied.igetattr(name, context), context) - except NotFoundError: - raise InferenceError(name) + except exceptions.NotFoundError: + raise exceptions.InferenceError(name) def _wrap_attr(self, attrs, context=None): """wrap bound methods of attrs in a InstanceMethod proxies""" for attr in attrs: if isinstance(attr, UnboundMethod): - if BUILTINS + '.property' in attr.decoratornames(): - for infered in attr.infer_call_result(self, context): - yield infered + if _is_property(attr): + for inferred in attr.infer_call_result(self, context): + yield inferred else: yield BoundMethod(attr, self) + elif hasattr(attr, 'name') and attr.name == '': + # This is a lambda function defined at class level, + # since its scope is the underlying _proxied class. + # Unfortunately, we can't do an isinstance check here, + # because of the circular dependency between astroid.bases + # and astroid.scoped_nodes. + if attr.statement().scope() == self._proxied: + if attr.args.args and attr.args.args[0].name == 'self': + yield BoundMethod(attr, self) + continue + yield attr else: yield attr def infer_call_result(self, caller, context=None): """infer what a class instance is returning when called""" - infered = False + inferred = False for node in self._proxied.igetattr('__call__', context): - if node is YES: + if node is util.YES or not node.callable(): continue for res in node.infer_call_result(caller, context): - infered = True + inferred = True yield res - if not infered: - raise InferenceError() + if not inferred: + raise exceptions.InferenceError() def __repr__(self): return '' % (self._proxied.root().name, @@ -237,7 +202,7 @@ def callable(self): try: self._proxied.getattr('__call__') return True - except NotFoundError: + except exceptions.NotFoundError: return False def pytype(self): @@ -247,6 +212,12 @@ def display_type(self): return 'Instance of' + # TODO(cpopa): this is set in inference.py + # The circular dependency hell goes deeper and deeper. + # pylint: disable=unused-argument + def getitem(self, index, context=None): + pass + class UnboundMethod(Proxy): """a special node representing a method not bound to an instance""" def __repr__(self): @@ -261,12 +232,12 @@ def is_bound(self): def getattr(self, name, context=None): if name == 'im_func': return [self._proxied] - return super(UnboundMethod, self).getattr(name, context) + return self._proxied.getattr(name, context) def igetattr(self, name, context=None): if name == 'im_func': return iter((self._proxied,)) - return super(UnboundMethod, self).igetattr(name, context) + return self._proxied.igetattr(name, context) def infer_call_result(self, caller, context): # If we're unbound method __new__ of builtin object, the result is an @@ -274,7 +245,7 @@ def infer_call_result(self, caller, context): if (self._proxied.name == '__new__' and self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): infer = caller.args[0].infer() if caller.args else [] - return ((x is YES and x or Instance(x)) for x in infer) + return ((x is util.YES and x or Instance(x)) for x in infer) return self._proxied.infer_call_result(caller, context) @@ -287,10 +258,13 @@ def __init__(self, proxy, bound): def is_bound(self): return True - def infer_call_result(self, caller, context): + def infer_call_result(self, caller, context=None): + + if context is None: + context = contextmod.InferenceContext() context = context.clone() context.boundnode = self.bound - return self._proxied.infer_call_result(caller, context) + return super(BoundMethod, self).infer_call_result(caller, context) class Generator(Instance): @@ -318,10 +292,11 @@ def __str__(self): def path_wrapper(func): """return the given infer function wrapped to handle the path""" + @functools.wraps(func) def wrapped(node, context=None, _func=func, **kwargs): """wrapper function handling context""" if context is None: - context = InferenceContext() + context = contextmod.InferenceContext() context.push(node) yielded = set() for res in _func(node, context, **kwargs): @@ -330,30 +305,28 @@ def wrapped(node, context=None, _func=func, **kwargs): ares = res._proxied else: ares = res - if not ares in yielded: + if ares not in yielded: yield res yielded.add(ares) return wrapped -def yes_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - yield YES - return wrapper - -def raise_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - raise InferenceError() - return wrapper +@wrapt.decorator +def yes_if_nothing_inferred(func, instance, args, kwargs): + inferred = False + for node in func(*args, **kwargs): + inferred = True + yield node + if not inferred: + yield util.YES + +@wrapt.decorator +def raise_if_nothing_inferred(func, instance, args, kwargs): + inferred = False + for node in func(*args, **kwargs): + inferred = True + yield node + if not inferred: + raise exceptions.InferenceError() # Node ###################################################################### @@ -364,8 +337,8 @@ class NodeNG(object): It represents a node of the new abstract syntax tree. """ is_statement = False - optional_assign = False # True for For (and for Comprehension if py <3.0) - is_function = False # True for Function nodes + optional_assign = False # True for For (and for Comprehension if py <3.0) + is_function = False # True for FunctionDef nodes # attributes below are set by the builder module or by raw factories lineno = None fromlineno = None @@ -389,7 +362,7 @@ def infer(self, context=None, **kwargs): # explicit_inference is not bound, give it self explicitly try: return self._explicit_inference(self, context, **kwargs) - except UseInferenceDefault: + except exceptions.UseInferenceDefault: pass if not context: @@ -397,8 +370,8 @@ def infer(self, context=None, **kwargs): key = (self, context.lookupname, context.callcontext, context.boundnode) - if key in context.infered: - return iter(context.infered[key]) + if key in context.inferred: + return iter(context.inferred[key]) return context.cache_generator(key, self._infer(context, **kwargs)) @@ -438,7 +411,7 @@ def last_child(self): attr = getattr(self, field) if not attr: # None or empty listy / tuple continue - if attr.__class__ in (list, tuple): + if isinstance(attr, (list, tuple)): return attr[-1] else: return attr @@ -460,13 +433,16 @@ def statement(self): return self.parent.statement() def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) + """return the first parent frame node (i.e. Module, FunctionDef or + ClassDef) + """ return self.parent.frame() def scope(self): - """return the first node defining a new scope (i.e. Module, Function, - Class, Lambda but also GenExpr) + """return the first node defining a new scope (i.e. Module, + FunctionDef, ClassDef, Lambda but also GenExpr) + """ return self.parent.scope() @@ -483,11 +459,12 @@ def child_sequence(self, child): if node_or_sequence is child: return [node_or_sequence] # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: + if (isinstance(node_or_sequence, (tuple, list)) + and child in node_or_sequence): return node_or_sequence - else: - msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) + + msg = 'Could not find %s in %s\'s children' + raise exceptions.AstroidError(msg % (repr(child), repr(self))) def locate_child(self, child): """return a 2-uple (child attribute name, sequence or node)""" @@ -499,7 +476,7 @@ def locate_child(self, child): if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: return field, node_or_sequence msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) + raise exceptions.AstroidError(msg % (repr(child), repr(self))) # FIXME : should we merge child_sequence and locate_child ? locate_child # is only used in are_exclusive, child_sequence one time in pylint. @@ -532,14 +509,14 @@ def nearest(self, nodes): # these are lazy because they're relatively expensive to compute for every # single node, and they rarely get looked at - @cachedproperty + @decoratorsmod.cachedproperty def fromlineno(self): if self.lineno is None: return self._fixed_source_line() else: return self.lineno - @cachedproperty + @decoratorsmod.cachedproperty def tolineno(self): if not self._astroid_fields: # can't have children @@ -597,20 +574,27 @@ def nodes_of_class(self, klass, skip_klass=None): yield matching def _infer_name(self, frame, name): - # overridden for From, Import, Global, TryExcept and Arguments + # overridden for ImportFrom, Import, Global, TryExcept and Arguments return None def _infer(self, context=None): """we don't know how to resolve a statement by default""" # this method is overridden by most concrete classes - raise InferenceError(self.__class__.__name__) + raise exceptions.InferenceError(self.__class__.__name__) - def infered(self): - '''return list of infered values for a more simple inference usage''' + def inferred(self): + '''return list of inferred values for a more simple inference usage''' return list(self.infer()) + def infered(self): + warnings.warn('%s.infered() is deprecated and slated for removal ' + 'in astroid 2.0, use %s.inferred() instead.' + % (type(self).__name__, type(self).__name__), + PendingDeprecationWarning, stacklevel=2) + return self.inferred() + def instanciate_class(self): - """instanciate a node if it is a Class node, else return self""" + """instanciate a node if it is a ClassDef node, else return self""" return self def has_base(self, node): diff --git a/pymode/libs/astroid/brain/builtin_inference.py b/pymode/libs/astroid/brain/brain_builtin_inference.py similarity index 63% rename from pymode/libs/astroid/brain/builtin_inference.py rename to pymode/libs/astroid/brain/brain_builtin_inference.py index f60e7913..ed78111f 100644 --- a/pymode/libs/astroid/brain/builtin_inference.py +++ b/pymode/libs/astroid/brain/brain_builtin_inference.py @@ -7,9 +7,11 @@ import six from astroid import (MANAGER, UseInferenceDefault, inference_tip, YES, InferenceError, UnresolvableName) +from astroid import arguments from astroid import nodes +from astroid import objects from astroid.builder import AstroidBuilder - +from astroid import util def _extend_str(class_node, rvalue): """function to extend builtin str/unicode class""" @@ -51,7 +53,7 @@ def lstrip(self, chars=None): def rstrip(self, chars=None): return {rvalue} def rjust(self, width, fillchar=None): - return {rvalue} + return {rvalue} def center(self, width, fillchar=None): return {rvalue} def ljust(self, width, fillchar=None): @@ -60,7 +62,7 @@ def ljust(self, width, fillchar=None): code = code.format(rvalue=rvalue) fake = AstroidBuilder(MANAGER).string_build(code)['whatever'] for method in fake.mymethods(): - class_node.locals[method.name] = [method] + class_node._locals[method.name] = [method] method.parent = class_node def extend_builtins(class_transforms): @@ -86,12 +88,17 @@ def register_builtin_transform(transform, builtin_name): def _transform_wrapper(node, context=None): result = transform(node, context=context) if result: - result.parent = node + if not result.parent: + # Let the transformation function determine + # the parent for its result. Otherwise, + # we set it to be the node we transformed from. + result.parent = node + result.lineno = node.lineno result.col_offset = node.col_offset return iter([result]) - MANAGER.register_transform(nodes.CallFunc, + MANAGER.register_transform(nodes.Call, inference_tip(_transform_wrapper), lambda n: (isinstance(n.func, nodes.Name) and n.func.name == builtin_name)) @@ -108,13 +115,13 @@ def _generic_inference(node, context, node_type, transform): transformed = transform(arg) if not transformed: try: - infered = next(arg.infer(context=context)) + inferred = next(arg.infer(context=context)) except (InferenceError, StopIteration): raise UseInferenceDefault() - if infered is YES: + if inferred is util.YES: raise UseInferenceDefault() - transformed = transform(infered) - if not transformed or transformed is YES: + transformed = transform(inferred) + if not transformed or transformed is util.YES: raise UseInferenceDefault() return transformed @@ -172,19 +179,25 @@ def _infer_builtin(node, context, iterables=(nodes.List, nodes.Tuple), build_elts=set) +infer_frozenset = partial( + _infer_builtin, + klass=objects.FrozenSet, + iterables=(nodes.List, nodes.Tuple, nodes.Set), + build_elts=frozenset) + def _get_elts(arg, context): is_iterable = lambda n: isinstance(n, (nodes.List, nodes.Tuple, nodes.Set)) try: - infered = next(arg.infer(context)) + inferred = next(arg.infer(context)) except (InferenceError, UnresolvableName): raise UseInferenceDefault() - if isinstance(infered, nodes.Dict): - items = infered.items - elif is_iterable(infered): + if isinstance(inferred, nodes.Dict): + items = inferred.items + elif is_iterable(inferred): items = [] - for elt in infered.elts: + for elt in inferred.elts: # If an item is not a pair of two items, # then fallback to the default inference. # Also, take in consideration only hashable items, @@ -213,24 +226,28 @@ def infer_dict(node, context=None): * dict(mapping, **kwargs) * dict(**kwargs) - If a case can't be infered, we'll fallback to default inference. + If a case can't be inferred, we'll fallback to default inference. """ - has_keywords = lambda args: all(isinstance(arg, nodes.Keyword) - for arg in args) - if not node.args and not node.kwargs: + call = arguments.CallSite.from_call(node) + if call.has_invalid_arguments() or call.has_invalid_keywords(): + raise UseInferenceDefault + + args = call.positional_arguments + kwargs = list(call.keyword_arguments.items()) + + if not args and not kwargs: # dict() return nodes.Dict() - elif has_keywords(node.args) and node.args: + elif kwargs and not args: # dict(a=1, b=2, c=4) - items = [(nodes.Const(arg.arg), arg.value) for arg in node.args] - elif (len(node.args) >= 2 and - has_keywords(node.args[1:])): + items = [(nodes.Const(key), value) for key, value in kwargs] + elif len(args) == 1 and kwargs: # dict(some_iterable, b=2, c=4) - elts = _get_elts(node.args[0], context) - keys = [(nodes.Const(arg.arg), arg.value) for arg in node.args[1:]] + elts = _get_elts(args[0], context) + keys = [(nodes.Const(key), value) for key, value in kwargs] items = elts + keys - elif len(node.args) == 1: - items = _get_elts(node.args[0], context) + elif len(args) == 1: + items = _get_elts(args[0], context) else: raise UseInferenceDefault() @@ -238,8 +255,82 @@ def infer_dict(node, context=None): empty.items = items return empty + +def _node_class(node): + klass = node.frame() + while klass is not None and not isinstance(klass, nodes.ClassDef): + if klass.parent is None: + klass = None + else: + klass = klass.parent.frame() + return klass + + +def infer_super(node, context=None): + """Understand super calls. + + There are some restrictions for what can be understood: + + * unbounded super (one argument form) is not understood. + + * if the super call is not inside a function (classmethod or method), + then the default inference will be used. + + * if the super arguments can't be infered, the default inference + will be used. + """ + if len(node.args) == 1: + # Ignore unbounded super. + raise UseInferenceDefault + + scope = node.scope() + if not isinstance(scope, nodes.FunctionDef): + # Ignore non-method uses of super. + raise UseInferenceDefault + if scope.type not in ('classmethod', 'method'): + # Not interested in staticmethods. + raise UseInferenceDefault + + cls = _node_class(scope) + if not len(node.args): + mro_pointer = cls + # In we are in a classmethod, the interpreter will fill + # automatically the class as the second argument, not an instance. + if scope.type == 'classmethod': + mro_type = cls + else: + mro_type = cls.instantiate_class() + else: + # TODO(cpopa): support flow control (multiple inference values). + try: + mro_pointer = next(node.args[0].infer(context=context)) + except InferenceError: + raise UseInferenceDefault + try: + mro_type = next(node.args[1].infer(context=context)) + except InferenceError: + raise UseInferenceDefault + + if mro_pointer is YES or mro_type is YES: + # No way we could understand this. + raise UseInferenceDefault + + super_obj = objects.Super(mro_pointer=mro_pointer, + mro_type=mro_type, + self_class=cls, + scope=scope) + super_obj.parent = node + return iter([super_obj]) + + # Builtins inference +MANAGER.register_transform(nodes.Call, + inference_tip(infer_super), + lambda n: (isinstance(n.func, nodes.Name) and + n.func.name == 'super')) + register_builtin_transform(infer_tuple, 'tuple') register_builtin_transform(infer_set, 'set') register_builtin_transform(infer_list, 'list') register_builtin_transform(infer_dict, 'dict') +register_builtin_transform(infer_frozenset, 'frozenset') diff --git a/pymode/libs/astroid/brain/brain_dateutil.py b/pymode/libs/astroid/brain/brain_dateutil.py new file mode 100644 index 00000000..d077327b --- /dev/null +++ b/pymode/libs/astroid/brain/brain_dateutil.py @@ -0,0 +1,15 @@ +"""Astroid hooks for dateutil""" + +import textwrap + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder + +def dateutil_transform(): + return AstroidBuilder(MANAGER).string_build(textwrap.dedent(''' + import datetime + def parse(timestr, parserinfo=None, **kwargs): + return datetime.datetime() + ''')) + +register_module_extender(MANAGER, 'dateutil.parser', dateutil_transform) diff --git a/pymode/libs/astroid/brain/py2gi.py b/pymode/libs/astroid/brain/brain_gi.py similarity index 72% rename from pymode/libs/astroid/brain/py2gi.py rename to pymode/libs/astroid/brain/brain_gi.py index 6747898d..d9fc1b45 100644 --- a/pymode/libs/astroid/brain/py2gi.py +++ b/pymode/libs/astroid/brain/brain_gi.py @@ -7,8 +7,9 @@ import itertools import sys import re +import warnings -from astroid import MANAGER, AstroidBuildingException +from astroid import MANAGER, AstroidBuildingException, nodes from astroid.builder import AstroidBuilder @@ -46,13 +47,13 @@ def _gi_build_stub(parent): elif (inspect.ismethod(obj) or inspect.ismethoddescriptor(obj)): methods[name] = obj - elif type(obj) in [int, str]: - constants[name] = obj elif (str(obj).startswith(", ) + # Only accept function calls with two constant arguments + if len(node.args) != 2: + return False -MANAGER.register_failed_import_hook(_import_gi_module) + if not all(isinstance(arg, nodes.Const) for arg in node.args): + return False + + func = node.func + if isinstance(func, nodes.Attribute): + if func.attrname != 'require_version': + return False + if isinstance(func.expr, nodes.Name) and func.expr.name == 'gi': + return True + + return False + if isinstance(func, nodes.Name): + return func.name == 'require_version' + + return False + +def _register_require_version(node): + # Load the gi.require_version locally + try: + import gi + gi.require_version(node.args[0].value, node.args[1].value) + except Exception: + pass + + return node + +MANAGER.register_failed_import_hook(_import_gi_module) +MANAGER.register_transform(nodes.Call, _register_require_version, _looks_like_require_version) diff --git a/pymode/libs/astroid/brain/py2mechanize.py b/pymode/libs/astroid/brain/brain_mechanize.py similarity index 100% rename from pymode/libs/astroid/brain/py2mechanize.py rename to pymode/libs/astroid/brain/brain_mechanize.py diff --git a/pymode/libs/astroid/brain/pynose.py b/pymode/libs/astroid/brain/brain_nose.py similarity index 92% rename from pymode/libs/astroid/brain/pynose.py rename to pymode/libs/astroid/brain/brain_nose.py index 67a6fb8f..4b077843 100644 --- a/pymode/libs/astroid/brain/pynose.py +++ b/pymode/libs/astroid/brain/brain_nose.py @@ -48,11 +48,14 @@ class Test(unittest.TestCase): if method.name.startswith('assert') and '_' not in method.name: pep8_name = _pep8(method.name) yield pep8_name, astroid.BoundMethod(method, case) + if method.name == 'assertEqual': + # nose also exports assert_equals. + yield 'assert_equals', astroid.BoundMethod(method, case) def _nose_tools_transform(node): for method_name, method in _nose_tools_functions(): - node.locals[method_name] = [method] + node._locals[method_name] = [method] def _nose_tools_trivial_transform(): diff --git a/pymode/libs/astroid/brain/brain_numpy.py b/pymode/libs/astroid/brain/brain_numpy.py new file mode 100644 index 00000000..75f4f18f --- /dev/null +++ b/pymode/libs/astroid/brain/brain_numpy.py @@ -0,0 +1,62 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# astroid is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +"""Astroid hooks for numpy.""" + +import astroid + + +# TODO(cpopa): drop when understanding augmented assignments + +def numpy_core_transform(): + return astroid.parse(''' + from numpy.core import numeric + from numpy.core import fromnumeric + from numpy.core import defchararray + from numpy.core import records + from numpy.core import function_base + from numpy.core import machar + from numpy.core import getlimits + from numpy.core import shape_base + __all__ = (['char', 'rec', 'memmap', 'chararray'] + numeric.__all__ + + fromnumeric.__all__ + + records.__all__ + + function_base.__all__ + + machar.__all__ + + getlimits.__all__ + + shape_base.__all__) + ''') + + +def numpy_transform(): + return astroid.parse(''' + from numpy import core + from numpy import matrixlib as _mat + from numpy import lib + __all__ = ['add_newdocs', + 'ModuleDeprecationWarning', + 'VisibleDeprecationWarning', 'linalg', 'fft', 'random', + 'ctypeslib', 'ma', + '__version__', 'pkgload', 'PackageLoader', + 'show_config'] + core.__all__ + _mat.__all__ + lib.__all__ + + ''') + + +astroid.register_module_extender(astroid.MANAGER, 'numpy.core', numpy_core_transform) +astroid.register_module_extender(astroid.MANAGER, 'numpy', numpy_transform) diff --git a/pymode/libs/astroid/brain/brain_pytest.py b/pymode/libs/astroid/brain/brain_pytest.py new file mode 100644 index 00000000..1859b985 --- /dev/null +++ b/pymode/libs/astroid/brain/brain_pytest.py @@ -0,0 +1,76 @@ +"""Astroid hooks for pytest.""" +from __future__ import absolute_import +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder + + +def pytest_transform(): + return AstroidBuilder(MANAGER).string_build(''' + +try: + import _pytest.mark + import _pytest.recwarn + import _pytest.runner + import _pytest.python + import _pytest.skipping + import _pytest.assertion +except ImportError: + pass +else: + deprecated_call = _pytest.recwarn.deprecated_call + warns = _pytest.recwarn.warns + + exit = _pytest.runner.exit + fail = _pytest.runner.fail + skip = _pytest.runner.skip + importorskip = _pytest.runner.importorskip + + xfail = _pytest.skipping.xfail + mark = _pytest.mark.MarkGenerator() + raises = _pytest.python.raises + + # New in pytest 3.0 + try: + approx = _pytest.python.approx + register_assert_rewrite = _pytest.assertion.register_assert_rewrite + except AttributeError: + pass + + +# Moved in pytest 3.0 + +try: + import _pytest.freeze_support + freeze_includes = _pytest.freeze_support.freeze_includes +except ImportError: + try: + import _pytest.genscript + freeze_includes = _pytest.genscript.freeze_includes + except ImportError: + pass + +try: + import _pytest.debugging + set_trace = _pytest.debugging.pytestPDB().set_trace +except ImportError: + try: + import _pytest.pdb + set_trace = _pytest.pdb.pytestPDB().set_trace + except ImportError: + pass + +try: + import _pytest.fixtures + fixture = _pytest.fixtures.fixture + yield_fixture = _pytest.fixtures.yield_fixture +except ImportError: + try: + import _pytest.python + fixture = _pytest.python.fixture + yield_fixture = _pytest.python.yield_fixture + except ImportError: + pass +''') + +register_module_extender(MANAGER, 'pytest', pytest_transform) +register_module_extender(MANAGER, 'py.test', pytest_transform) diff --git a/pymode/libs/astroid/brain/brain_qt.py b/pymode/libs/astroid/brain/brain_qt.py new file mode 100644 index 00000000..1a03b2be --- /dev/null +++ b/pymode/libs/astroid/brain/brain_qt.py @@ -0,0 +1,44 @@ +"""Astroid hooks for the PyQT library.""" + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder +from astroid import nodes +from astroid import parse + + +def _looks_like_signal(node, signal_name='pyqtSignal'): + if '__class__' in node._instance_attrs: + cls = node._instance_attrs['__class__'][0] + return cls.name == signal_name + return False + + +def transform_pyqt_signal(node): + module = parse(''' + class pyqtSignal(object): + def connect(self, slot, type=None, no_receiver_check=False): + pass + def disconnect(self, slot): + pass + def emit(self, *args): + pass + ''') + signal_cls = module['pyqtSignal'] + node._instance_attrs['emit'] = signal_cls['emit'] + node._instance_attrs['disconnect'] = signal_cls['disconnect'] + node._instance_attrs['connect'] = signal_cls['connect'] + + +def pyqt4_qtcore_transform(): + return AstroidBuilder(MANAGER).string_build(''' + +def SIGNAL(signal_name): pass + +class QObject(object): + def emit(self, signal): pass +''') + + +register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform) +MANAGER.register_transform(nodes.FunctionDef, transform_pyqt_signal, + _looks_like_signal) \ No newline at end of file diff --git a/pymode/libs/astroid/brain/pysix_moves.py b/pymode/libs/astroid/brain/brain_six.py similarity index 91% rename from pymode/libs/astroid/brain/pysix_moves.py rename to pymode/libs/astroid/brain/brain_six.py index 548d9761..9596a6c8 100644 --- a/pymode/libs/astroid/brain/pysix_moves.py +++ b/pymode/libs/astroid/brain/brain_six.py @@ -23,7 +23,12 @@ from astroid import MANAGER, register_module_extender from astroid.builder import AstroidBuilder -from astroid.exceptions import AstroidBuildingException +from astroid.exceptions import AstroidBuildingException, InferenceError +from astroid import nodes + + +SIX_ADD_METACLASS = 'six.add_metaclass' + def _indent(text, prefix, predicate=None): """Adds 'prefix' to the beginning of selected lines in 'text'. @@ -254,8 +259,30 @@ def _six_fail_hook(modname): module.name = 'six.moves' return module +def transform_six_add_metaclass(node): + """Check if the given class node is decorated with *six.add_metaclass* + + If so, inject its argument as the metaclass of the underlying class. + """ + if not node.decorators: + return + + for decorator in node.decorators.nodes: + if not isinstance(decorator, nodes.Call): + continue + + try: + func = next(decorator.func.infer()) + except InferenceError: + continue + if func.qname() == SIX_ADD_METACLASS and decorator.args: + metaclass = decorator.args[0] + node._metaclass = metaclass + return node + register_module_extender(MANAGER, 'six', six_moves_transform) register_module_extender(MANAGER, 'requests.packages.urllib3.packages.six', six_moves_transform) MANAGER.register_failed_import_hook(_six_fail_hook) +MANAGER.register_transform(nodes.ClassDef, transform_six_add_metaclass) diff --git a/pymode/libs/astroid/brain/brain_ssl.py b/pymode/libs/astroid/brain/brain_ssl.py new file mode 100644 index 00000000..1cf8d1b8 --- /dev/null +++ b/pymode/libs/astroid/brain/brain_ssl.py @@ -0,0 +1,65 @@ +"""Astroid hooks for the ssl library.""" + +from astroid import MANAGER, register_module_extender +from astroid.builder import AstroidBuilder +from astroid import nodes +from astroid import parse + + +def ssl_transform(): + return parse(''' + from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION + from _ssl import _SSLContext, MemoryBIO + from _ssl import ( + SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, + SSLSyscallError, SSLEOFError, + ) + from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED + from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj + from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes + try: + from _ssl import RAND_egd + except ImportError: + # LibreSSL does not provide RAND_egd + pass + from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE, + OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3, + OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2, + OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE) + + from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE, + ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE, + ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE, + ALERT_DESCRIPTION_BAD_RECORD_MAC, + ALERT_DESCRIPTION_CERTIFICATE_EXPIRED, + ALERT_DESCRIPTION_CERTIFICATE_REVOKED, + ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN, + ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE, + ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR, + ALERT_DESCRIPTION_DECOMPRESSION_FAILURE, + ALERT_DESCRIPTION_DECRYPT_ERROR, + ALERT_DESCRIPTION_HANDSHAKE_FAILURE, + ALERT_DESCRIPTION_ILLEGAL_PARAMETER, + ALERT_DESCRIPTION_INSUFFICIENT_SECURITY, + ALERT_DESCRIPTION_INTERNAL_ERROR, + ALERT_DESCRIPTION_NO_RENEGOTIATION, + ALERT_DESCRIPTION_PROTOCOL_VERSION, + ALERT_DESCRIPTION_RECORD_OVERFLOW, + ALERT_DESCRIPTION_UNEXPECTED_MESSAGE, + ALERT_DESCRIPTION_UNKNOWN_CA, + ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY, + ALERT_DESCRIPTION_UNRECOGNIZED_NAME, + ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE, + ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION, + ALERT_DESCRIPTION_USER_CANCELLED) + from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL, + SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ, + SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN) + from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT + from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN + from _ssl import _OPENSSL_API_VERSION + from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 + ''') + + +register_module_extender(MANAGER, 'ssl', ssl_transform) diff --git a/pymode/libs/astroid/brain/brain_stdlib.py b/pymode/libs/astroid/brain/brain_stdlib.py new file mode 100644 index 00000000..ad395a27 --- /dev/null +++ b/pymode/libs/astroid/brain/brain_stdlib.py @@ -0,0 +1,473 @@ + +"""Astroid hooks for the Python 2 standard library. + +Currently help understanding of : + +* hashlib.md5 and hashlib.sha1 +""" + +import functools +import sys +from textwrap import dedent + +from astroid import ( + MANAGER, UseInferenceDefault, inference_tip, BoundMethod, + InferenceError, register_module_extender) +from astroid import exceptions +from astroid import nodes +from astroid.builder import AstroidBuilder +from astroid import util +from astroid import test_utils + +PY3K = sys.version_info > (3, 0) +PY33 = sys.version_info >= (3, 3) +PY34 = sys.version_info >= (3, 4) + +# general function + +def infer_func_form(node, base_type, context=None, enum=False): + """Specific inference function for namedtuple or Python 3 enum. """ + def infer_first(node): + if node is util.YES: + raise UseInferenceDefault + try: + value = next(node.infer(context=context)) + if value is util.YES: + raise UseInferenceDefault() + else: + return value + except StopIteration: + raise InferenceError() + + # node is a Call node, class name as first argument and generated class + # attributes as second argument + if len(node.args) != 2: + # something weird here, go back to class implementation + raise UseInferenceDefault() + # namedtuple or enums list of attributes can be a list of strings or a + # whitespace-separate string + try: + name = infer_first(node.args[0]).value + names = infer_first(node.args[1]) + try: + attributes = names.value.replace(',', ' ').split() + except AttributeError: + if not enum: + attributes = [infer_first(const).value for const in names.elts] + else: + # Enums supports either iterator of (name, value) pairs + # or mappings. + # TODO: support only list, tuples and mappings. + if hasattr(names, 'items') and isinstance(names.items, list): + attributes = [infer_first(const[0]).value + for const in names.items + if isinstance(const[0], nodes.Const)] + elif hasattr(names, 'elts'): + # Enums can support either ["a", "b", "c"] + # or [("a", 1), ("b", 2), ...], but they can't + # be mixed. + if all(isinstance(const, nodes.Tuple) + for const in names.elts): + attributes = [infer_first(const.elts[0]).value + for const in names.elts + if isinstance(const, nodes.Tuple)] + else: + attributes = [infer_first(const).value + for const in names.elts] + else: + raise AttributeError + if not attributes: + raise AttributeError + except (AttributeError, exceptions.InferenceError): + raise UseInferenceDefault() + + # If we can't iner the name of the class, don't crash, up to this point + # we know it is a namedtuple anyway. + name = name or 'Uninferable' + # we want to return a Class node instance with proper attributes set + class_node = nodes.ClassDef(name, 'docstring') + class_node.parent = node.parent + # set base class=tuple + class_node.bases.append(base_type) + # XXX add __init__(*attributes) method + for attr in attributes: + fake_node = nodes.EmptyNode() + fake_node.parent = class_node + fake_node.attrname = attr + class_node._instance_attrs[attr] = [fake_node] + return class_node, name, attributes + + +# module specific transformation functions ##################################### + +def hashlib_transform(): + template = ''' + +class %(name)s(object): + def __init__(self, value=''): pass + def digest(self): + return %(digest)s + def copy(self): + return self + def update(self, value): pass + def hexdigest(self): + return '' + @property + def name(self): + return %(name)r + @property + def block_size(self): + return 1 + @property + def digest_size(self): + return 1 +''' + algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') + classes = "".join( + template % {'name': hashfunc, 'digest': 'b""' if PY3K else '""'} + for hashfunc in algorithms) + return AstroidBuilder(MANAGER).string_build(classes) + + +def collections_transform(): + return AstroidBuilder(MANAGER).string_build(''' + +class defaultdict(dict): + default_factory = None + def __missing__(self, key): pass + +class deque(object): + maxlen = 0 + def __init__(self, iterable=None, maxlen=None): + self.iterable = iterable + def append(self, x): pass + def appendleft(self, x): pass + def clear(self): pass + def count(self, x): return 0 + def extend(self, iterable): pass + def extendleft(self, iterable): pass + def pop(self): pass + def popleft(self): pass + def remove(self, value): pass + def reverse(self): pass + def rotate(self, n): pass + def __iter__(self): return self + def __reversed__(self): return self.iterable[::-1] + def __getitem__(self, index): pass + def __setitem__(self, index, value): pass + def __delitem__(self, index): pass +''') + + +def pkg_resources_transform(): + return AstroidBuilder(MANAGER).string_build(''' +def require(*requirements): + return pkg_resources.working_set.require(*requirements) + +def run_script(requires, script_name): + return pkg_resources.working_set.run_script(requires, script_name) + +def iter_entry_points(group, name=None): + return pkg_resources.working_set.iter_entry_points(group, name) + +def resource_exists(package_or_requirement, resource_name): + return get_provider(package_or_requirement).has_resource(resource_name) + +def resource_isdir(package_or_requirement, resource_name): + return get_provider(package_or_requirement).resource_isdir( + resource_name) + +def resource_filename(package_or_requirement, resource_name): + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name) + +def resource_stream(package_or_requirement, resource_name): + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name) + +def resource_string(package_or_requirement, resource_name): + return get_provider(package_or_requirement).get_resource_string( + self, resource_name) + +def resource_listdir(package_or_requirement, resource_name): + return get_provider(package_or_requirement).resource_listdir( + resource_name) + +def extraction_error(): + pass + +def get_cache_path(archive_name, names=()): + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name+'-tmp', *names) + return target_path + +def postprocess(tempname, filename): + pass + +def set_extraction_path(path): + pass + +def cleanup_resources(force=False): + pass + +''') + + +def subprocess_transform(): + if PY3K: + communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) + communicate_signature = 'def communicate(self, input=None, timeout=None)' + init = """ + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0, restore_signals=True, + start_new_session=False, pass_fds=()): + pass + """ + else: + communicate = ('string', 'string') + communicate_signature = 'def communicate(self, input=None)' + init = """ + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0): + pass + """ + if PY33: + wait_signature = 'def wait(self, timeout=None)' + else: + wait_signature = 'def wait(self)' + if PY3K: + ctx_manager = ''' + def __enter__(self): return self + def __exit__(self, *args): pass + ''' + else: + ctx_manager = '' + code = dedent(''' + + class Popen(object): + returncode = pid = 0 + stdin = stdout = stderr = file() + + %(init)s + + %(communicate_signature)s: + return %(communicate)r + %(wait_signature)s: + return self.returncode + def poll(self): + return self.returncode + def send_signal(self, signal): + pass + def terminate(self): + pass + def kill(self): + pass + %(ctx_manager)s + ''' % {'init': init, + 'communicate': communicate, + 'communicate_signature': communicate_signature, + 'wait_signature': wait_signature, + 'ctx_manager': ctx_manager}) + return AstroidBuilder(MANAGER).string_build(code) + + +# namedtuple support ########################################################### + +def _looks_like(node, name): + func = node.func + if isinstance(func, nodes.Attribute): + return func.attrname == name + if isinstance(func, nodes.Name): + return func.name == name + return False + +_looks_like_namedtuple = functools.partial(_looks_like, name='namedtuple') +_looks_like_enum = functools.partial(_looks_like, name='Enum') + + +def infer_named_tuple(node, context=None): + """Specific inference function for namedtuple Call node""" + class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, + context=context) + fake = AstroidBuilder(MANAGER).string_build(''' +class %(name)s(tuple): + _fields = %(fields)r + def _asdict(self): + return self.__dict__ + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + return new(cls, iterable) + def _replace(self, **kwds): + return self + ''' % {'name': name, 'fields': attributes}) + class_node._locals['_asdict'] = fake.body[0]._locals['_asdict'] + class_node._locals['_make'] = fake.body[0]._locals['_make'] + class_node._locals['_replace'] = fake.body[0]._locals['_replace'] + class_node._locals['_fields'] = fake.body[0]._locals['_fields'] + # we use UseInferenceDefault, we can't be a generator so return an iterator + return iter([class_node]) + + +def infer_enum(node, context=None): + """ Specific inference function for enum Call node. """ + enum_meta = test_utils.extract_node(''' + class EnumMeta(object): + 'docstring' + def __call__(self, node): + class EnumAttribute(object): + name = '' + value = 0 + return EnumAttribute() + ''') + class_node = infer_func_form(node, enum_meta, + context=context, enum=True)[0] + return iter([class_node.instantiate_class()]) + + +def infer_enum_class(node): + """ Specific inference for enums. """ + names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) + for basename in node.basenames: + # TODO: doesn't handle subclasses yet. This implementation + # is a hack to support enums. + if basename not in names: + continue + if node.root().name == 'enum': + # Skip if the class is directly from enum module. + break + for local, values in node._locals.items(): + if any(not isinstance(value, nodes.AssignName) + for value in values): + continue + + stmt = values[0].statement() + if isinstance(stmt.targets[0], nodes.Tuple): + targets = stmt.targets[0].itered() + else: + targets = stmt.targets + + new_targets = [] + for target in targets: + # Replace all the assignments with our mocked class. + classdef = dedent(''' + class %(name)s(%(types)s): + @property + def value(self): + # Not the best return. + return None + @property + def name(self): + return %(name)r + ''' % {'name': target.name, 'types': ', '.join(node.basenames)}) + fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] + fake.parent = target.parent + for method in node.mymethods(): + fake._locals[method.name] = [method] + new_targets.append(fake.instantiate_class()) + node._locals[local] = new_targets + break + return node + +def multiprocessing_transform(): + module = AstroidBuilder(MANAGER).string_build(dedent(''' + from multiprocessing.managers import SyncManager + def Manager(): + return SyncManager() + ''')) + if not PY34: + return module + + # On Python 3.4, multiprocessing uses a getattr lookup inside contexts, + # in order to get the attributes they need. Since it's extremely + # dynamic, we use this approach to fake it. + node = AstroidBuilder(MANAGER).string_build(dedent(''' + from multiprocessing.context import DefaultContext, BaseContext + default = DefaultContext() + base = BaseContext() + ''')) + try: + context = next(node['default'].infer()) + base = next(node['base'].infer()) + except InferenceError: + return module + + for node in (context, base): + for key, value in node._locals.items(): + if key.startswith("_"): + continue + + value = value[0] + if isinstance(value, nodes.FunctionDef): + # We need to rebound this, since otherwise + # it will have an extra argument (self). + value = BoundMethod(value, node) + module[key] = value + return module + +def multiprocessing_managers_transform(): + return AstroidBuilder(MANAGER).string_build(dedent(''' + import array + import threading + import multiprocessing.pool as pool + + import six + + class Namespace(object): + pass + + class Value(object): + def __init__(self, typecode, value, lock=True): + self._typecode = typecode + self._value = value + def get(self): + return self._value + def set(self, value): + self._value = value + def __repr__(self): + return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value) + value = property(get, set) + + def Array(typecode, sequence, lock=True): + return array.array(typecode, sequence) + + class SyncManager(object): + Queue = JoinableQueue = six.moves.queue.Queue + Event = threading.Event + RLock = threading.RLock + BoundedSemaphore = threading.BoundedSemaphore + Condition = threading.Condition + Barrier = threading.Barrier + Pool = pool.Pool + list = list + dict = dict + Value = Value + Array = Array + Namespace = Namespace + __enter__ = lambda self: self + __exit__ = lambda *args: args + + def start(self, initializer=None, initargs=None): + pass + def shutdown(self): + pass + ''')) + + +MANAGER.register_transform(nodes.Call, inference_tip(infer_named_tuple), + _looks_like_namedtuple) +MANAGER.register_transform(nodes.Call, inference_tip(infer_enum), + _looks_like_enum) +MANAGER.register_transform(nodes.ClassDef, infer_enum_class) +register_module_extender(MANAGER, 'hashlib', hashlib_transform) +register_module_extender(MANAGER, 'collections', collections_transform) +register_module_extender(MANAGER, 'pkg_resources', pkg_resources_transform) +register_module_extender(MANAGER, 'subprocess', subprocess_transform) +register_module_extender(MANAGER, 'multiprocessing.managers', + multiprocessing_managers_transform) +register_module_extender(MANAGER, 'multiprocessing', multiprocessing_transform) diff --git a/pymode/libs/astroid/brain/py2pytest.py b/pymode/libs/astroid/brain/py2pytest.py deleted file mode 100644 index e24d449c..00000000 --- a/pymode/libs/astroid/brain/py2pytest.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Astroid hooks for pytest.""" - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - - -def pytest_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -try: - import _pytest.mark - import _pytest.recwarn - import _pytest.runner - import _pytest.python -except ImportError: - pass -else: - deprecated_call = _pytest.recwarn.deprecated_call - exit = _pytest.runner.exit - fail = _pytest.runner.fail - fixture = _pytest.python.fixture - importorskip = _pytest.runner.importorskip - mark = _pytest.mark.MarkGenerator() - raises = _pytest.python.raises - skip = _pytest.runner.skip - yield_fixture = _pytest.python.yield_fixture - -''') - -register_module_extender(MANAGER, 'pytest', pytest_transform) -register_module_extender(MANAGER, 'py.test', pytest_transform) diff --git a/pymode/libs/astroid/brain/py2qt4.py b/pymode/libs/astroid/brain/py2qt4.py deleted file mode 100644 index d5578097..00000000 --- a/pymode/libs/astroid/brain/py2qt4.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Astroid hooks for the Python 2 qt4 module. - -Currently help understanding of : - -* PyQT4.QtCore -""" - -from astroid import MANAGER, register_module_extender -from astroid.builder import AstroidBuilder - - -def pyqt4_qtcore_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -def SIGNAL(signal_name): pass - -class QObject(object): - def emit(self, signal): pass -''') - - -register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform) diff --git a/pymode/libs/astroid/brain/py2stdlib.py b/pymode/libs/astroid/brain/py2stdlib.py deleted file mode 100644 index 2bfcbcd3..00000000 --- a/pymode/libs/astroid/brain/py2stdlib.py +++ /dev/null @@ -1,334 +0,0 @@ - -"""Astroid hooks for the Python 2 standard library. - -Currently help understanding of : - -* hashlib.md5 and hashlib.sha1 -""" - -import sys -from functools import partial -from textwrap import dedent - -from astroid import ( - MANAGER, AsStringRegexpPredicate, - UseInferenceDefault, inference_tip, - YES, InferenceError, register_module_extender) -from astroid import exceptions -from astroid import nodes -from astroid.builder import AstroidBuilder - -PY3K = sys.version_info > (3, 0) -PY33 = sys.version_info >= (3, 3) - -# general function - -def infer_func_form(node, base_type, context=None, enum=False): - """Specific inference function for namedtuple or Python 3 enum. """ - def infer_first(node): - try: - value = next(node.infer(context=context)) - if value is YES: - raise UseInferenceDefault() - else: - return value - except StopIteration: - raise InferenceError() - - # node is a CallFunc node, class name as first argument and generated class - # attributes as second argument - if len(node.args) != 2: - # something weird here, go back to class implementation - raise UseInferenceDefault() - # namedtuple or enums list of attributes can be a list of strings or a - # whitespace-separate string - try: - name = infer_first(node.args[0]).value - names = infer_first(node.args[1]) - try: - attributes = names.value.replace(',', ' ').split() - except AttributeError: - if not enum: - attributes = [infer_first(const).value for const in names.elts] - else: - # Enums supports either iterator of (name, value) pairs - # or mappings. - # TODO: support only list, tuples and mappings. - if hasattr(names, 'items') and isinstance(names.items, list): - attributes = [infer_first(const[0]).value - for const in names.items - if isinstance(const[0], nodes.Const)] - elif hasattr(names, 'elts'): - # Enums can support either ["a", "b", "c"] - # or [("a", 1), ("b", 2), ...], but they can't - # be mixed. - if all(isinstance(const, nodes.Tuple) - for const in names.elts): - attributes = [infer_first(const.elts[0]).value - for const in names.elts - if isinstance(const, nodes.Tuple)] - else: - attributes = [infer_first(const).value - for const in names.elts] - else: - raise AttributeError - if not attributes: - raise AttributeError - except (AttributeError, exceptions.InferenceError) as exc: - raise UseInferenceDefault() - # we want to return a Class node instance with proper attributes set - class_node = nodes.Class(name, 'docstring') - class_node.parent = node.parent - # set base class=tuple - class_node.bases.append(base_type) - # XXX add __init__(*attributes) method - for attr in attributes: - fake_node = nodes.EmptyNode() - fake_node.parent = class_node - class_node.instance_attrs[attr] = [fake_node] - return class_node, name, attributes - - -# module specific transformation functions ##################################### - -def hashlib_transform(): - template = ''' - -class %(name)s(object): - def __init__(self, value=''): pass - def digest(self): - return %(digest)s - def copy(self): - return self - def update(self, value): pass - def hexdigest(self): - return '' - @property - def name(self): - return %(name)r - @property - def block_size(self): - return 1 - @property - def digest_size(self): - return 1 -''' - algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') - classes = "".join( - template % {'name': hashfunc, 'digest': 'b""' if PY3K else '""'} - for hashfunc in algorithms) - return AstroidBuilder(MANAGER).string_build(classes) - - -def collections_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -class defaultdict(dict): - default_factory = None - def __missing__(self, key): pass - -class deque(object): - maxlen = 0 - def __init__(self, iterable=None, maxlen=None): pass - def append(self, x): pass - def appendleft(self, x): pass - def clear(self): pass - def count(self, x): return 0 - def extend(self, iterable): pass - def extendleft(self, iterable): pass - def pop(self): pass - def popleft(self): pass - def remove(self, value): pass - def reverse(self): pass - def rotate(self, n): pass - def __iter__(self): return self - -''') - - -def pkg_resources_transform(): - return AstroidBuilder(MANAGER).string_build(''' - -def resource_exists(package_or_requirement, resource_name): - pass - -def resource_isdir(package_or_requirement, resource_name): - pass - -def resource_filename(package_or_requirement, resource_name): - pass - -def resource_stream(package_or_requirement, resource_name): - pass - -def resource_string(package_or_requirement, resource_name): - pass - -def resource_listdir(package_or_requirement, resource_name): - pass - -def extraction_error(): - pass - -def get_cache_path(archive_name, names=()): - pass - -def postprocess(tempname, filename): - pass - -def set_extraction_path(path): - pass - -def cleanup_resources(force=False): - pass - -''') - - -def subprocess_transform(): - if PY3K: - communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=()): - pass - """ - else: - communicate = ('string', 'string') - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - pass - """ - if PY33: - wait_signature = 'def wait(self, timeout=None)' - else: - wait_signature = 'def wait(self)' - return AstroidBuilder(MANAGER).string_build(''' - -class Popen(object): - returncode = pid = 0 - stdin = stdout = stderr = file() - - %(init)s - - def communicate(self, input=None): - return %(communicate)r - %(wait_signature)s: - return self.returncode - def poll(self): - return self.returncode - def send_signal(self, signal): - pass - def terminate(self): - pass - def kill(self): - pass - ''' % {'init': init, - 'communicate': communicate, - 'wait_signature': wait_signature}) - - -# namedtuple support ########################################################### - -def looks_like_namedtuple(node): - func = node.func - if type(func) is nodes.Getattr: - return func.attrname == 'namedtuple' - if type(func) is nodes.Name: - return func.name == 'namedtuple' - return False - -def infer_named_tuple(node, context=None): - """Specific inference function for namedtuple CallFunc node""" - class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, - context=context) - fake = AstroidBuilder(MANAGER).string_build(''' -class %(name)s(tuple): - _fields = %(fields)r - def _asdict(self): - return self.__dict__ - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - return new(cls, iterable) - def _replace(_self, **kwds): - result = _self._make(map(kwds.pop, %(fields)r, _self)) - if kwds: - raise ValueError('Got unexpected field names: %%r' %% list(kwds)) - return result - ''' % {'name': name, 'fields': attributes}) - class_node.locals['_asdict'] = fake.body[0].locals['_asdict'] - class_node.locals['_make'] = fake.body[0].locals['_make'] - class_node.locals['_replace'] = fake.body[0].locals['_replace'] - class_node.locals['_fields'] = fake.body[0].locals['_fields'] - # we use UseInferenceDefault, we can't be a generator so return an iterator - return iter([class_node]) - -def infer_enum(node, context=None): - """ Specific inference function for enum CallFunc node. """ - enum_meta = nodes.Class("EnumMeta", 'docstring') - class_node = infer_func_form(node, enum_meta, - context=context, enum=True)[0] - return iter([class_node.instanciate_class()]) - -def infer_enum_class(node): - """ Specific inference for enums. """ - names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) - for basename in node.basenames: - # TODO: doesn't handle subclasses yet. This implementation - # is a hack to support enums. - if basename not in names: - continue - if node.root().name == 'enum': - # Skip if the class is directly from enum module. - break - for local, values in node.locals.items(): - if any(not isinstance(value, nodes.AssName) - for value in values): - continue - - stmt = values[0].statement() - if isinstance(stmt.targets[0], nodes.Tuple): - targets = stmt.targets[0].itered() - else: - targets = stmt.targets - - new_targets = [] - for target in targets: - # Replace all the assignments with our mocked class. - classdef = dedent(''' - class %(name)s(object): - @property - def value(self): - # Not the best return. - return None - @property - def name(self): - return %(name)r - ''' % {'name': target.name}) - fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] - fake.parent = target.parent - for method in node.mymethods(): - fake.locals[method.name] = [method] - new_targets.append(fake.instanciate_class()) - node.locals[local] = new_targets - break - return node - - -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_named_tuple), - looks_like_namedtuple) -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_enum), - AsStringRegexpPredicate('Enum', 'func')) -MANAGER.register_transform(nodes.Class, infer_enum_class) -register_module_extender(MANAGER, 'hashlib', hashlib_transform) -register_module_extender(MANAGER, 'collections', collections_transform) -register_module_extender(MANAGER, 'pkg_resources', pkg_resources_transform) -register_module_extender(MANAGER, 'subprocess', subprocess_transform) diff --git a/pymode/libs/astroid/builder.py b/pymode/libs/astroid/builder.py index 1fe7a36d..63c156a1 100644 --- a/pymode/libs/astroid/builder.py +++ b/pymode/libs/astroid/builder.py @@ -22,23 +22,26 @@ """ from __future__ import with_statement -__docformat__ = "restructuredtext en" - +import _ast +import os import sys -from os.path import splitext, basename, exists, abspath +import textwrap + +from astroid import bases +from astroid import exceptions +from astroid import manager +from astroid import modutils +from astroid import raw_building +from astroid import rebuilder +from astroid import util -from astroid.exceptions import AstroidBuildingException, InferenceError -from astroid.raw_building import InspectBuilder -from astroid.rebuilder import TreeRebuilder -from astroid.manager import AstroidManager -from astroid.bases import YES, Instance -from astroid.modutils import modpath_from_file -from _ast import PyCF_ONLY_AST -def parse(string): - return compile(string, "", 'exec', PyCF_ONLY_AST) +def _parse(string): + return compile(string, "", 'exec', _ast.PyCF_ONLY_AST) + if sys.version_info >= (3, 0): + # pylint: disable=no-name-in-module; We don't understand flows yet. from tokenize import detect_encoding def open_source_file(filename): @@ -47,10 +50,10 @@ def open_source_file(filename): stream = open(filename, 'r', newline=None, encoding=encoding) try: data = stream.read() - except UnicodeError: # wrong encodingg + except UnicodeError: # wrong encoding # detect_encoding returns utf-8 if no encoding specified msg = 'Wrong (%s) or no encoding specified' % encoding - raise AstroidBuildingException(msg) + raise exceptions.AstroidBuildingException(msg) return stream, encoding, data else: @@ -59,8 +62,7 @@ def open_source_file(filename): _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)") def _guess_encoding(string): - """get encoding from a python file as string or return None if not found - """ + """get encoding from a python file as string or return None if not found""" # check for UTF-8 byte-order mark if string.startswith('\xef\xbb\xbf'): return 'UTF-8' @@ -77,95 +79,101 @@ def open_source_file(filename): encoding = _guess_encoding(data) return stream, encoding, data -# ast NG builder ############################################################## -MANAGER = AstroidManager() +MANAGER = manager.AstroidManager() -class AstroidBuilder(InspectBuilder): - """provide astroid building methods""" - def __init__(self, manager=None): - InspectBuilder.__init__(self) +class AstroidBuilder(raw_building.InspectBuilder): + """Class for building an astroid tree from source code or from a live module. + + The param *manager* specifies the manager class which should be used. + If no manager is given, then the default one will be used. The + param *apply_transforms* determines if the transforms should be + applied after the tree was built from source or from a live object, + by default being True. + """ + + def __init__(self, manager=None, apply_transforms=True): + super(AstroidBuilder, self).__init__() self._manager = manager or MANAGER + self._apply_transforms = apply_transforms def module_build(self, module, modname=None): - """build an astroid from a living module instance - """ + """Build an astroid from a living module instance.""" node = None path = getattr(module, '__file__', None) if path is not None: - path_, ext = splitext(module.__file__) - if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'): + path_, ext = os.path.splitext(modutils._path_from_filename(path)) + if ext in ('.py', '.pyc', '.pyo') and os.path.exists(path_ + '.py'): node = self.file_build(path_ + '.py', modname) if node is None: # this is a built-in module # get a partial representation by introspection node = self.inspect_build(module, modname=modname, path=path) - # we have to handle transformation by ourselves since the rebuilder - # isn't called for builtin nodes - # - # XXX it's then only called for Module nodes, not for underlying - # nodes - node = self._manager.transform(node) + if self._apply_transforms: + # We have to handle transformation by ourselves since the + # rebuilder isn't called for builtin nodes + node = self._manager.visit_transforms(node) return node def file_build(self, path, modname=None): - """build astroid from a source code file (i.e. from an ast) + """Build astroid from a source code file (i.e. from an ast) - path is expected to be a python source file + *path* is expected to be a python source file """ try: stream, encoding, data = open_source_file(path) except IOError as exc: msg = 'Unable to load file %r (%s)' % (path, exc) - raise AstroidBuildingException(msg) - except SyntaxError as exc: # py3k encoding specification error - raise AstroidBuildingException(exc) - except LookupError as exc: # unknown encoding - raise AstroidBuildingException(exc) + raise exceptions.AstroidBuildingException(msg) + except SyntaxError as exc: # py3k encoding specification error + raise exceptions.AstroidBuildingException(exc) + except LookupError as exc: # unknown encoding + raise exceptions.AstroidBuildingException(exc) with stream: # get module name if necessary if modname is None: try: - modname = '.'.join(modpath_from_file(path)) + modname = '.'.join(modutils.modpath_from_file(path)) except ImportError: - modname = splitext(basename(path))[0] + modname = os.path.splitext(os.path.basename(path))[0] # build astroid representation module = self._data_build(data, modname, path) return self._post_build(module, encoding) def string_build(self, data, modname='', path=None): - """build astroid from source code string and return rebuilded astroid""" + """Build astroid from source code string.""" module = self._data_build(data, modname, path) - module.file_bytes = data.encode('utf-8') + module.source_code = data.encode('utf-8') return self._post_build(module, 'utf-8') def _post_build(self, module, encoding): - """handles encoding and delayed nodes - after a module has been built - """ + """Handles encoding and delayed nodes after a module has been built""" module.file_encoding = encoding self._manager.cache_module(module) # post tree building steps after we stored the module in the cache: - for from_node in module._from_nodes: + for from_node in module._import_from_nodes: if from_node.modname == '__future__': for symbol, _ in from_node.names: - module.future_imports.add(symbol) + module._future_imports.add(symbol) self.add_from_names_to_locals(from_node) # handle delayed assattr nodes for delayed in module._delayed_assattr: self.delayed_assattr(delayed) + + # Visit the transforms + if self._apply_transforms: + module = self._manager.visit_transforms(module) return module def _data_build(self, data, modname, path): - """build tree node from data and add some informations""" - # this method could be wrapped with a pickle/cache function + """Build tree node from data and add some informations""" try: - node = parse(data + '\n') - except TypeError as exc: - raise AstroidBuildingException(exc) + node = _parse(data + '\n') + except (TypeError, ValueError, SyntaxError) as exc: + raise exceptions.AstroidBuildingException(exc) if path is not None: - node_file = abspath(path) + node_file = os.path.abspath(path) else: node_file = '' if modname.endswith('.__init__'): @@ -173,68 +181,83 @@ def _data_build(self, data, modname, path): package = True else: package = path and path.find('__init__.py') > -1 or False - rebuilder = TreeRebuilder(self._manager) - module = rebuilder.visit_module(node, modname, node_file, package) - module._from_nodes = rebuilder._from_nodes - module._delayed_assattr = rebuilder._delayed_assattr + builder = rebuilder.TreeRebuilder(self._manager) + module = builder.visit_module(node, modname, node_file, package) + module._import_from_nodes = builder._import_from_nodes + module._delayed_assattr = builder._delayed_assattr return module def add_from_names_to_locals(self, node): - """store imported names to the locals; - resort the locals if coming from a delayed node - """ + """Store imported names to the locals + Resort the locals if coming from a delayed node + """ _key_func = lambda node: node.fromlineno def sort_locals(my_list): my_list.sort(key=_key_func) + for (name, asname) in node.names: if name == '*': try: imported = node.do_import_module() - except InferenceError: + except exceptions.InferenceError: continue - for name in imported.wildcard_import_names(): + for name in imported._public_names(): node.parent.set_local(name, node) - sort_locals(node.parent.scope().locals[name]) + sort_locals(node.parent.scope()._locals[name]) else: node.parent.set_local(asname or name, node) - sort_locals(node.parent.scope().locals[asname or name]) + sort_locals(node.parent.scope()._locals[asname or name]) def delayed_assattr(self, node): - """visit a AssAttr node -> add name to locals, handle members - definition + """Visit a AssAttr node + + This adds name to locals and handle members definition. """ try: frame = node.frame() - for infered in node.expr.infer(): - if infered is YES: + for inferred in node.expr.infer(): + if inferred is util.YES: continue try: - if infered.__class__ is Instance: - infered = infered._proxied - iattrs = infered.instance_attrs - elif isinstance(infered, Instance): + if inferred.__class__ is bases.Instance: + inferred = inferred._proxied + iattrs = inferred._instance_attrs + elif isinstance(inferred, bases.Instance): # Const, Tuple, ... we may be wrong, may be not, but # anyway we don't want to pollute builtin's namespace continue - elif infered.is_function: - iattrs = infered.instance_attrs + elif inferred.is_function: + iattrs = inferred._instance_attrs else: - iattrs = infered.locals + iattrs = inferred._locals except AttributeError: # XXX log error - #import traceback - #traceback.print_exc() continue values = iattrs.setdefault(node.attrname, []) if node in values: continue # get assign in __init__ first XXX useful ? - if frame.name == '__init__' and values and not \ - values[0].frame().name == '__init__': + if (frame.name == '__init__' and values and + not values[0].frame().name == '__init__'): values.insert(0, node) else: values.append(node) - except InferenceError: + except exceptions.InferenceError: pass + +def parse(code, module_name='', path=None, apply_transforms=True): + """Parses a source string in order to obtain an astroid AST from it + + :param str code: The code for the module. + :param str module_name: The name for the module, if any + :param str path: The path for the module + :param bool apply_transforms: + Apply the transforms for the give code. Use it if you + don't want the default transforms to be applied. + """ + code = textwrap.dedent(code) + builder = AstroidBuilder(manager=MANAGER, + apply_transforms=apply_transforms) + return builder.string_build(code, modname=module_name, path=path) diff --git a/pymode/libs/astroid/context.py b/pymode/libs/astroid/context.py new file mode 100644 index 00000000..284dfa18 --- /dev/null +++ b/pymode/libs/astroid/context.py @@ -0,0 +1,81 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +"""Various context related utilities, including inference and call contexts.""" + +import contextlib + + +class InferenceContext(object): + __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'inferred') + + def __init__(self, path=None, inferred=None): + self.path = path or set() + self.lookupname = None + self.callcontext = None + self.boundnode = None + self.inferred = inferred or {} + + def push(self, node): + name = self.lookupname + if (node, name) in self.path: + raise StopIteration() + self.path.add((node, name)) + + def clone(self): + # XXX copy lookupname/callcontext ? + clone = InferenceContext(self.path, inferred=self.inferred) + clone.callcontext = self.callcontext + clone.boundnode = self.boundnode + return clone + + def cache_generator(self, key, generator): + results = [] + for result in generator: + results.append(result) + yield result + + self.inferred[key] = tuple(results) + return + + @contextlib.contextmanager + def restore_path(self): + path = set(self.path) + yield + self.path = path + + +class CallContext(object): + """Holds information for a call site.""" + + __slots__ = ('args', 'keywords') + + def __init__(self, args, keywords=None): + self.args = args + if keywords: + keywords = [(arg.arg, arg.value) for arg in keywords] + else: + keywords = [] + self.keywords = keywords + + +def copy_context(context): + if context is not None: + return context.clone() + else: + return InferenceContext() diff --git a/pymode/libs/astroid/decorators.py b/pymode/libs/astroid/decorators.py new file mode 100644 index 00000000..a446536c --- /dev/null +++ b/pymode/libs/astroid/decorators.py @@ -0,0 +1,75 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +# +# The code in this file was originally part of logilab-common, licensed under +# the same license. + +""" A few useful function/method decorators.""" + +import wrapt + + +@wrapt.decorator +def cached(func, instance, args, kwargs): + """Simple decorator to cache result of method calls without args.""" + cache = getattr(instance, '__cache', None) + if cache is None: + instance.__cache = cache = {} + try: + return cache[func] + except KeyError: + cache[func] = result = func(*args, **kwargs) + return result + + +class cachedproperty(object): + """ Provides a cached property equivalent to the stacking of + @cached and @property, but more efficient. + + After first usage, the becomes part of the object's + __dict__. Doing: + + del obj. empties the cache. + + Idea taken from the pyramid_ framework and the mercurial_ project. + + .. _pyramid: http://pypi.python.org/pypi/pyramid + .. _mercurial: http://pypi.python.org/pypi/Mercurial + """ + __slots__ = ('wrapped',) + + def __init__(self, wrapped): + try: + wrapped.__name__ + except AttributeError: + raise TypeError('%s must have a __name__ attribute' % + wrapped) + self.wrapped = wrapped + + @property + def __doc__(self): + doc = getattr(self.wrapped, '__doc__', None) + return ('%s' + % ('\n%s' % doc if doc else '')) + + def __get__(self, inst, objtype=None): + if inst is None: + return self + val = self.wrapped(inst) + setattr(inst, self.wrapped.__name__, val) + return val diff --git a/pymode/libs/astroid/exceptions.py b/pymode/libs/astroid/exceptions.py index 3889e2e7..47f2fe50 100644 --- a/pymode/libs/astroid/exceptions.py +++ b/pymode/libs/astroid/exceptions.py @@ -30,6 +30,26 @@ class AstroidBuildingException(AstroidError): class ResolveError(AstroidError): """base class of astroid resolution/inference error""" +class MroError(ResolveError): + """Error raised when there is a problem with method resolution of a class.""" + + +class DuplicateBasesError(MroError): + """Error raised when there are duplicate bases in the same class bases.""" + + +class InconsistentMroError(MroError): + """Error raised when a class's MRO is inconsistent.""" + + +class SuperError(ResolveError): + """Error raised when there is a problem with a super call.""" + + +class SuperArgumentTypeError(SuperError): + """Error raised when the super arguments are invalid.""" + + class NotFoundError(ResolveError): """raised when we are unable to resolve a name""" diff --git a/pymode/libs/astroid/inference.py b/pymode/libs/astroid/inference.py index 22807049..ddd43561 100644 --- a/pymode/libs/astroid/inference.py +++ b/pymode/libs/astroid/inference.py @@ -18,125 +18,32 @@ """this module contains a set of functions to handle inference on astroid trees """ -__doctype__ = "restructuredtext en" - -from itertools import chain +from __future__ import print_function +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import manager from astroid import nodes +from astroid import protocols +from astroid import util -from astroid.manager import AstroidManager -from astroid.exceptions import (AstroidError, InferenceError, NoDefault, - NotFoundError, UnresolvableName) -from astroid.bases import (YES, Instance, InferenceContext, - _infer_stmts, copy_context, path_wrapper, - raise_if_nothing_infered) -from astroid.protocols import ( - _arguments_infer_argname, - BIN_OP_METHOD, UNARY_OP_METHOD) - -MANAGER = AstroidManager() - - -class CallContext(object): - """when inferring a function call, this class is used to remember values - given as argument - """ - def __init__(self, args, starargs, dstarargs): - self.args = [] - self.nargs = {} - for arg in args: - if isinstance(arg, nodes.Keyword): - self.nargs[arg.arg] = arg.value - else: - self.args.append(arg) - self.starargs = starargs - self.dstarargs = dstarargs - def infer_argument(self, funcnode, name, context): - """infer a function argument value according to the call context""" - # 1. search in named keywords - try: - return self.nargs[name].infer(context) - except KeyError: - # Function.args.args can be None in astroid (means that we don't have - # information on argnames) - argindex = funcnode.args.find_argname(name)[0] - if argindex is not None: - # 2. first argument of instance/class method - if argindex == 0 and funcnode.type in ('method', 'classmethod'): - if context.boundnode is not None: - boundnode = context.boundnode - else: - # XXX can do better ? - boundnode = funcnode.parent.frame() - if funcnode.type == 'method': - if not isinstance(boundnode, Instance): - boundnode = Instance(boundnode) - return iter((boundnode,)) - if funcnode.type == 'classmethod': - return iter((boundnode,)) - # if we have a method, extract one position - # from the index, so we'll take in account - # the extra parameter represented by `self` or `cls` - if funcnode.type in ('method', 'classmethod'): - argindex -= 1 - # 2. search arg index - try: - return self.args[argindex].infer(context) - except IndexError: - pass - # 3. search in *args (.starargs) - if self.starargs is not None: - its = [] - for infered in self.starargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(argindex, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 4. XXX search in **kwargs (.dstarargs) - if self.dstarargs is not None: - its = [] - for infered in self.dstarargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(name, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 5. */** argument, (Tuple or Dict) - if name == funcnode.args.vararg: - return iter((nodes.const_factory(()))) - if name == funcnode.args.kwarg: - return iter((nodes.const_factory({}))) - # 6. return default value if any - try: - return funcnode.args.default_value(name).infer(context) - except NoDefault: - raise InferenceError(name) +MANAGER = manager.AstroidManager() # .infer method ############################################################### def infer_end(self, context=None): - """inference's end for node such as Module, Class, Function, Const... + """inference's end for node such as Module, ClassDef, FunctionDef, + Const... + """ yield self nodes.Module._infer = infer_end -nodes.Class._infer = infer_end -nodes.Function._infer = infer_end +nodes.ClassDef._infer = infer_end +nodes.FunctionDef._infer = infer_end nodes.Lambda._infer = infer_end nodes.Const._infer = infer_end nodes.List._infer = infer_end @@ -157,7 +64,7 @@ def _higher_function_scope(node): which encloses the given node. """ current = node - while current.parent and not isinstance(current.parent, nodes.Function): + while current.parent and not isinstance(current.parent, nodes.FunctionDef): current = current.parent if current and current.parent: return current.parent @@ -174,72 +81,80 @@ def infer_name(self, context=None): _, stmts = parent_function.lookup(self.name) if not stmts: - raise UnresolvableName(self.name) + raise exceptions.UnresolvableName(self.name) context = context.clone() context.lookupname = self.name - return _infer_stmts(stmts, context, frame) -nodes.Name._infer = path_wrapper(infer_name) -nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper + return bases._infer_stmts(stmts, context, frame) +nodes.Name._infer = bases.path_wrapper(infer_name) +nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper -def infer_callfunc(self, context=None): - """infer a CallFunc node by trying to guess what the function returns""" +@bases.path_wrapper +@bases.raise_if_nothing_inferred +def infer_call(self, context=None): + """infer a Call node by trying to guess what the function returns""" callcontext = context.clone() - callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs) + callcontext.callcontext = contextmod.CallContext(args=self.args, + keywords=self.keywords) callcontext.boundnode = None for callee in self.func.infer(context): - if callee is YES: + if callee is util.YES: yield callee continue try: if hasattr(callee, 'infer_call_result'): - for infered in callee.infer_call_result(self, callcontext): - yield infered - except InferenceError: + for inferred in callee.infer_call_result(self, callcontext): + yield inferred + except exceptions.InferenceError: ## XXX log error ? continue -nodes.CallFunc._infer = path_wrapper(raise_if_nothing_infered(infer_callfunc)) +nodes.Call._infer = infer_call +@bases.path_wrapper def infer_import(self, context=None, asname=True): """infer an Import node: return the imported module/object""" name = context.lookupname if name is None: - raise InferenceError() + raise exceptions.InferenceError() if asname: yield self.do_import_module(self.real_name(name)) else: yield self.do_import_module(name) -nodes.Import._infer = path_wrapper(infer_import) +nodes.Import._infer = infer_import + def infer_name_module(self, name): - context = InferenceContext() + context = contextmod.InferenceContext() context.lookupname = name return self.infer(context, asname=False) nodes.Import.infer_name_module = infer_name_module -def infer_from(self, context=None, asname=True): - """infer a From nodes: return the imported module/object""" +@bases.path_wrapper +def infer_import_from(self, context=None, asname=True): + """infer a ImportFrom node: return the imported module/object""" name = context.lookupname if name is None: - raise InferenceError() + raise exceptions.InferenceError() if asname: name = self.real_name(name) module = self.do_import_module() try: - context = copy_context(context) + context = contextmod.copy_context(context) context.lookupname = name - return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context) - except NotFoundError: - raise InferenceError(name) -nodes.From._infer = path_wrapper(infer_from) + stmts = module.getattr(name, ignore_locals=module is self.root()) + return bases._infer_stmts(stmts, context) + except exceptions.NotFoundError: + raise exceptions.InferenceError(name) +nodes.ImportFrom._infer = infer_import_from -def infer_getattr(self, context=None): - """infer a Getattr node by using getattr on the associated object""" +@bases.raise_if_nothing_inferred +def infer_attribute(self, context=None): + """infer an Attribute node by using getattr on the associated object""" for owner in self.expr.infer(context): - if owner is YES: + if owner is util.YES: yield owner continue try: @@ -247,58 +162,69 @@ def infer_getattr(self, context=None): for obj in owner.igetattr(self.attrname, context): yield obj context.boundnode = None - except (NotFoundError, InferenceError): + except (exceptions.NotFoundError, exceptions.InferenceError): context.boundnode = None except AttributeError: # XXX method / function context.boundnode = None -nodes.Getattr._infer = path_wrapper(raise_if_nothing_infered(infer_getattr)) -nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper +nodes.Attribute._infer = bases.path_wrapper(infer_attribute) +nodes.AssignAttr.infer_lhs = infer_attribute # # won't work with a path wrapper +@bases.path_wrapper def infer_global(self, context=None): if context.lookupname is None: - raise InferenceError() + raise exceptions.InferenceError() try: - return _infer_stmts(self.root().getattr(context.lookupname), context) - except NotFoundError: - raise InferenceError() -nodes.Global._infer = path_wrapper(infer_global) + return bases._infer_stmts(self.root().getattr(context.lookupname), + context) + except exceptions.NotFoundError: + raise exceptions.InferenceError() +nodes.Global._infer = infer_global +@bases.raise_if_nothing_inferred def infer_subscript(self, context=None): - """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]""" + """Inference for subscripts + + We're understanding if the index is a Const + or a slice, passing the result of inference + to the value's `getitem` method, which should + handle each supported index type accordingly. + """ + value = next(self.value.infer(context)) - if value is YES: - yield YES + if value is util.YES: + yield util.YES return index = next(self.slice.infer(context)) - if index is YES: - yield YES + if index is util.YES: + yield util.YES return if isinstance(index, nodes.Const): try: assigned = value.getitem(index.value, context) except AttributeError: - raise InferenceError() + raise exceptions.InferenceError() except (IndexError, TypeError): - yield YES + yield util.YES return # Prevent inferring if the infered subscript # is the same as the original subscripted object. - if self is assigned: - yield YES + if self is assigned or assigned is util.YES: + yield util.YES return for infered in assigned.infer(context): yield infered else: - raise InferenceError() -nodes.Subscript._infer = path_wrapper(infer_subscript) -nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript) + raise exceptions.InferenceError() +nodes.Subscript._infer = bases.path_wrapper(infer_subscript) +nodes.Subscript.infer_lhs = infer_subscript +@bases.raise_if_nothing_inferred def infer_unaryop(self, context=None): for operand in self.operand.infer(context): try: @@ -306,9 +232,9 @@ def infer_unaryop(self, context=None): except TypeError: continue except AttributeError: - meth = UNARY_OP_METHOD[self.op] + meth = protocols.UNARY_OP_METHOD[self.op] if meth is None: - yield YES + yield util.YES else: try: # XXX just suppose if the type implement meth, returned type @@ -318,88 +244,116 @@ def infer_unaryop(self, context=None): except GeneratorExit: raise except: - yield YES -nodes.UnaryOp._infer = path_wrapper(infer_unaryop) + yield util.YES +nodes.UnaryOp._infer = bases.path_wrapper(infer_unaryop) -def _infer_binop(operator, operand1, operand2, context, failures=None): - if operand1 is YES: +def _infer_binop(binop, operand1, operand2, context, failures=None): + if operand1 is util.YES: yield operand1 return try: - for valnode in operand1.infer_binary_op(operator, operand2, context): + for valnode in operand1.infer_binary_op(binop, operand2, context): yield valnode except AttributeError: try: # XXX just suppose if the type implement meth, returned type # will be the same - operand1.getattr(BIN_OP_METHOD[operator]) + operand1.getattr(protocols.BIN_OP_METHOD[operator]) yield operand1 except: if failures is None: - yield YES + yield util.YES else: failures.append(operand1) +@bases.yes_if_nothing_inferred def infer_binop(self, context=None): failures = [] for lhs in self.left.infer(context): - for val in _infer_binop(self.op, lhs, self.right, context, failures): + for val in _infer_binop(self, lhs, self.right, context, failures): yield val for lhs in failures: for rhs in self.right.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): + for val in _infer_binop(self, rhs, lhs, context): yield val -nodes.BinOp._infer = path_wrapper(infer_binop) +nodes.BinOp._infer = bases.path_wrapper(infer_binop) def infer_arguments(self, context=None): name = context.lookupname if name is None: - raise InferenceError() - return _arguments_infer_argname(self, name, context) + raise exceptions.InferenceError() + return protocols._arguments_infer_argname(self, name, context) nodes.Arguments._infer = infer_arguments -def infer_ass(self, context=None): - """infer a AssName/AssAttr: need to inspect the RHS part of the +@bases.path_wrapper +def infer_assign(self, context=None): + """infer a AssignName/AssignAttr: need to inspect the RHS part of the assign node """ stmt = self.statement() if isinstance(stmt, nodes.AugAssign): return stmt.infer(context) + stmts = list(self.assigned_stmts(context=context)) - return _infer_stmts(stmts, context) -nodes.AssName._infer = path_wrapper(infer_ass) -nodes.AssAttr._infer = path_wrapper(infer_ass) + return bases._infer_stmts(stmts, context) +nodes.AssignName._infer = infer_assign +nodes.AssignAttr._infer = infer_assign def infer_augassign(self, context=None): failures = [] for lhs in self.target.infer_lhs(context): - for val in _infer_binop(self.op, lhs, self.value, context, failures): + for val in _infer_binop(self, lhs, self.value, context, failures): yield val for lhs in failures: for rhs in self.value.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): + for val in _infer_binop(self, rhs, lhs, context): yield val -nodes.AugAssign._infer = path_wrapper(infer_augassign) +nodes.AugAssign._infer = bases.path_wrapper(infer_augassign) # no infer method on DelName and DelAttr (expected InferenceError) - +@bases.path_wrapper def infer_empty_node(self, context=None): if not self.has_underlying_object(): - yield YES + yield util.YES else: try: - for infered in MANAGER.infer_ast_from_something(self.object, - context=context): - yield infered - except AstroidError: - yield YES -nodes.EmptyNode._infer = path_wrapper(infer_empty_node) + for inferred in MANAGER.infer_ast_from_something(self.object, + context=context): + yield inferred + except exceptions.AstroidError: + yield util.YES +nodes.EmptyNode._infer = infer_empty_node def infer_index(self, context=None): return self.value.infer(context) nodes.Index._infer = infer_index + +# TODO: move directly into bases.Instance when the dependency hell +# will be solved. +def instance_getitem(self, index, context=None): + # Rewrap index to Const for this case + index = nodes.Const(index) + if context: + new_context = context.clone() + else: + context = new_context = contextmod.InferenceContext() + + # Create a new callcontext for providing index as an argument. + new_context.callcontext = contextmod.CallContext(args=[index]) + new_context.boundnode = self + + method = next(self.igetattr('__getitem__', context=context)) + if not isinstance(method, bases.BoundMethod): + raise exceptions.InferenceError + + try: + return next(method.infer_call_result(self, new_context)) + except StopIteration: + raise exceptions.InferenceError + +bases.Instance.getitem = instance_getitem diff --git a/pymode/libs/astroid/inspector.py b/pymode/libs/astroid/inspector.py deleted file mode 100644 index 1fc31926..00000000 --- a/pymode/libs/astroid/inspector.py +++ /dev/null @@ -1,273 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""visitor doing some postprocessing on the astroid tree. -Try to resolve definitions (namespace) dictionary, relationship... - -This module has been imported from pyreverse -""" - -__docformat__ = "restructuredtext en" - -from os.path import dirname - -import astroid -from astroid.exceptions import InferenceError -from astroid.utils import LocalsVisitor -from astroid.modutils import get_module_part, is_relative, is_standard_module - -class IdGeneratorMixIn(object): - """ - Mixin adding the ability to generate integer uid - """ - def __init__(self, start_value=0): - self.id_count = start_value - - def init_counter(self, start_value=0): - """init the id counter - """ - self.id_count = start_value - - def generate_id(self): - """generate a new identifier - """ - self.id_count += 1 - return self.id_count - - -class Linker(IdGeneratorMixIn, LocalsVisitor): - """ - walk on the project tree and resolve relationships. - - According to options the following attributes may be added to visited nodes: - - * uid, - a unique identifier for the node (on astroid.Project, astroid.Module, - astroid.Class and astroid.locals_type). Only if the linker has been instantiated - with tag=True parameter (False by default). - - * Function - a mapping from locals names to their bounded value, which may be a - constant like a string or an integer, or an astroid node (on astroid.Module, - astroid.Class and astroid.Function). - - * instance_attrs_type - as locals_type but for klass member attributes (only on astroid.Class) - - * implements, - list of implemented interface _objects_ (only on astroid.Class nodes) - """ - - def __init__(self, project, inherited_interfaces=0, tag=False): - IdGeneratorMixIn.__init__(self) - LocalsVisitor.__init__(self) - # take inherited interface in consideration or not - self.inherited_interfaces = inherited_interfaces - # tag nodes or not - self.tag = tag - # visited project - self.project = project - - - def visit_project(self, node): - """visit an astroid.Project node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for module in node.modules: - self.visit(module) - - def visit_package(self, node): - """visit an astroid.Package node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for subelmt in node.values(): - self.visit(subelmt) - - def visit_module(self, node): - """visit an astroid.Module node - - * set the locals_type mapping - * set the depends mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - node.depends = [] - if self.tag: - node.uid = self.generate_id() - - def visit_class(self, node): - """visit an astroid.Class node - - * set the locals_type and instance_attrs_type mappings - * set the implements list and build it - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - # resolve ancestors - for baseobj in node.ancestors(recurs=False): - specializations = getattr(baseobj, 'specializations', []) - specializations.append(node) - baseobj.specializations = specializations - # resolve instance attributes - node.instance_attrs_type = {} - for assattrs in node.instance_attrs.values(): - for assattr in assattrs: - self.handle_assattr_type(assattr, node) - # resolve implemented interface - try: - node.implements = list(node.interfaces(self.inherited_interfaces)) - except InferenceError: - node.implements = () - - def visit_function(self, node): - """visit an astroid.Function node - - * set the locals_type mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - - link_project = visit_project - link_module = visit_module - link_class = visit_class - link_function = visit_function - - def visit_assname(self, node): - """visit an astroid.AssName node - - handle locals_type - """ - # avoid double parsing done by different Linkers.visit - # running over the same project: - if hasattr(node, '_handled'): - return - node._handled = True - if node.name in node.frame(): - frame = node.frame() - else: - # the name has been defined as 'global' in the frame and belongs - # there. Btw the frame is not yet visited as the name is in the - # root locals; the frame hence has no locals_type attribute - frame = node.root() - try: - values = node.infered() - try: - already_infered = frame.locals_type[node.name] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - frame.locals_type[node.name] = values - except astroid.InferenceError: - pass - - def handle_assattr_type(self, node, parent): - """handle an astroid.AssAttr node - - handle instance_attrs_type - """ - try: - values = list(node.infer()) - try: - already_infered = parent.instance_attrs_type[node.attrname] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - parent.instance_attrs_type[node.attrname] = values - except astroid.InferenceError: - pass - - def visit_import(self, node): - """visit an astroid.Import node - - resolve module dependencies - """ - context_file = node.root().file - for name in node.names: - relative = is_relative(name[0], context_file) - self._imported_module(node, name[0], relative) - - - def visit_from(self, node): - """visit an astroid.From node - - resolve module dependencies - """ - basename = node.modname - context_file = node.root().file - if context_file is not None: - relative = is_relative(basename, context_file) - else: - relative = False - for name in node.names: - if name[0] == '*': - continue - # analyze dependencies - fullname = '%s.%s' % (basename, name[0]) - if fullname.find('.') > -1: - try: - # XXX: don't use get_module_part, missing package precedence - fullname = get_module_part(fullname, context_file) - except ImportError: - continue - if fullname != basename: - self._imported_module(node, fullname, relative) - - - def compute_module(self, context_name, mod_path): - """return true if the module should be added to dependencies""" - package_dir = dirname(self.project.path) - if context_name == mod_path: - return 0 - elif is_standard_module(mod_path, (package_dir,)): - return 1 - return 0 - - # protected methods ######################################################## - - def _imported_module(self, node, mod_path, relative): - """notify an imported module, used to analyze dependencies - """ - module = node.root() - context_name = module.name - if relative: - mod_path = '%s.%s' % ('.'.join(context_name.split('.')[:-1]), - mod_path) - if self.compute_module(context_name, mod_path): - # handle dependencies - if not hasattr(module, 'depends'): - module.depends = [] - mod_paths = module.depends - if not mod_path in mod_paths: - mod_paths.append(mod_path) diff --git a/pymode/libs/astroid/manager.py b/pymode/libs/astroid/manager.py index b1fb3058..d08adc29 100644 --- a/pymode/libs/astroid/manager.py +++ b/pymode/libs/astroid/manager.py @@ -21,45 +21,23 @@ """ from __future__ import print_function -__docformat__ = "restructuredtext en" - -import collections import imp import os -from os.path import dirname, join, isdir, exists -from warnings import warn import zipimport -from logilab.common.configuration import OptionsProviderMixIn - -from astroid.exceptions import AstroidBuildingException +from astroid import exceptions from astroid import modutils +from astroid import transforms -def astroid_wrapper(func, modname): - """wrapper to give to AstroidManager.project_from_files""" - print('parsing %s...' % modname) - try: - return func(modname) - except AstroidBuildingException as exc: - print(exc) - except Exception as exc: - import traceback - traceback.print_exc() - -def _silent_no_wrap(func, modname): - """silent wrapper that doesn't do anything; can be used for tests""" - return func(modname) - def safe_repr(obj): try: return repr(obj) - except: + except Exception: # pylint: disable=broad-except return '???' - -class AstroidManager(OptionsProviderMixIn): +class AstroidManager(object): """the astroid manager, responsible to build astroid from files or modules. @@ -67,31 +45,27 @@ class AstroidManager(OptionsProviderMixIn): """ name = 'astroid loader' - options = (("ignore", - {'type' : "csv", 'metavar' : "", - 'dest' : "black_list", "default" : ('CVS',), - 'help' : "add (may be a directory) to the black list\ -. It should be a base name, not a path. You may set this option multiple times\ -."}), - ("project", - {'default': "No Name", 'type' : 'string', 'short': 'p', - 'metavar' : '', - 'help' : 'set the project name.'}), - ) brain = {} + def __init__(self): self.__dict__ = AstroidManager.brain if not self.__dict__: - OptionsProviderMixIn.__init__(self) - self.load_defaults() # NOTE: cache entries are added by the [re]builder self.astroid_cache = {} self._mod_file_cache = {} - self.transforms = collections.defaultdict(list) self._failed_import_hooks = [] self.always_load_extensions = False self.optimize_ast = False self.extension_package_whitelist = set() + self._transform = transforms.TransformVisitor() + + # Export these APIs for convenience + self.register_transform = self._transform.register_transform + self.unregister_transform = self._transform.unregister_transform + + def visit_transforms(self, node): + """Visit the transforms and apply them to the given *node*.""" + return self._transform.visit(node) def ast_from_file(self, filepath, modname=None, fallback=True, source=False): """given a module name, return the astroid object""" @@ -105,15 +79,15 @@ def ast_from_file(self, filepath, modname=None, fallback=True, source=False): modname = '.'.join(modutils.modpath_from_file(filepath)) except ImportError: modname = filepath - if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: + if modname in self.astroid_cache and self.astroid_cache[modname].source_file == filepath: return self.astroid_cache[modname] if source: from astroid.builder import AstroidBuilder return AstroidBuilder(self).file_build(filepath, modname) elif fallback and modname: return self.ast_from_module_name(modname) - raise AstroidBuildingException('unable to get astroid for file %s' % - filepath) + raise exceptions.AstroidBuildingException( + 'unable to get astroid for file %s' % filepath) def _build_stub_module(self, modname): from astroid.builder import AstroidBuilder @@ -137,7 +111,7 @@ def ast_from_module_name(self, modname, context_file=None): return self._build_stub_module(modname) old_cwd = os.getcwd() if context_file: - os.chdir(dirname(context_file)) + os.chdir(os.path.dirname(context_file)) try: filepath, mp_type = self.file_from_module_name(modname, context_file) if mp_type == modutils.PY_ZIPMODULE: @@ -151,18 +125,20 @@ def ast_from_module_name(self, modname, context_file=None): module = modutils.load_module_from_name(modname) except Exception as ex: msg = 'Unable to load module %s (%s)' % (modname, ex) - raise AstroidBuildingException(msg) + raise exceptions.AstroidBuildingException(msg) return self.ast_from_module(module, modname) elif mp_type == imp.PY_COMPILED: - raise AstroidBuildingException("Unable to load compiled module %s" % (modname,)) + msg = "Unable to load compiled module %s" % (modname,) + raise exceptions.AstroidBuildingException(msg) if filepath is None: - raise AstroidBuildingException("Unable to load module %s" % (modname,)) + msg = "Unable to load module %s" % (modname,) + raise exceptions.AstroidBuildingException(msg) return self.ast_from_file(filepath, modname, fallback=False) - except AstroidBuildingException as e: + except exceptions.AstroidBuildingException as e: for hook in self._failed_import_hooks: try: return hook(modname) - except AstroidBuildingException: + except exceptions.AstroidBuildingException: pass raise e finally: @@ -186,11 +162,12 @@ def zip_import_data(self, filepath): module = builder.string_build(importer.get_source(resource), zmodname, filepath) return module - except: + except Exception: # pylint: disable=broad-except continue return None def file_from_module_name(self, modname, contextfile): + # pylint: disable=redefined-variable-type try: value = self._mod_file_cache[(modname, contextfile)] except KeyError: @@ -199,9 +176,9 @@ def file_from_module_name(self, modname, contextfile): modname.split('.'), context_file=contextfile) except ImportError as ex: msg = 'Unable to load module %s (%s)' % (modname, ex) - value = AstroidBuildingException(msg) + value = exceptions.AstroidBuildingException(msg) self._mod_file_cache[(modname, contextfile)] = value - if isinstance(value, AstroidBuildingException): + if isinstance(value, exceptions.AstroidBuildingException): raise value return value @@ -226,12 +203,11 @@ def ast_from_class(self, klass, modname=None): try: modname = klass.__module__ except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for class %s' % safe_repr(klass)) + msg = 'Unable to get module for class %s' % safe_repr(klass) + raise exceptions.AstroidBuildingException(msg) modastroid = self.ast_from_module_name(modname) return modastroid.getattr(klass.__name__)[0] # XXX - def infer_ast_from_something(self, obj, context=None): """infer astroid for the given class""" if hasattr(obj, '__class__') and not isinstance(obj, type): @@ -241,75 +217,29 @@ def infer_ast_from_something(self, obj, context=None): try: modname = klass.__module__ except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for %s' % safe_repr(klass)) + msg = 'Unable to get module for %s' % safe_repr(klass) + raise exceptions.AstroidBuildingException(msg) except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving module for %s: %s' - % (safe_repr(klass), ex)) + msg = ('Unexpected error while retrieving module for %s: %s' + % (safe_repr(klass), ex)) + raise exceptions.AstroidBuildingException(msg) try: name = klass.__name__ except AttributeError: - raise AstroidBuildingException( - 'Unable to get name for %s' % safe_repr(klass)) + msg = 'Unable to get name for %s' % safe_repr(klass) + raise exceptions.AstroidBuildingException(msg) except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving name for %s: %s' - % (safe_repr(klass), ex)) + exc = ('Unexpected error while retrieving name for %s: %s' + % (safe_repr(klass), ex)) + raise exceptions.AstroidBuildingException(exc) # take care, on living object __module__ is regularly wrong :( modastroid = self.ast_from_module_name(modname) if klass is obj: - for infered in modastroid.igetattr(name, context): - yield infered + for inferred in modastroid.igetattr(name, context): + yield inferred else: - for infered in modastroid.igetattr(name, context): - yield infered.instanciate_class() - - def project_from_files(self, files, func_wrapper=astroid_wrapper, - project_name=None, black_list=None): - """return a Project from a list of files or modules""" - # build the project representation - project_name = project_name or self.config.project - black_list = black_list or self.config.black_list - project = Project(project_name) - for something in files: - if not exists(something): - fpath = modutils.file_from_modpath(something.split('.')) - elif isdir(something): - fpath = join(something, '__init__.py') - else: - fpath = something - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None: - continue - # XXX why is first file defining the project.path ? - project.path = project.path or astroid.file - project.add_module(astroid) - base_name = astroid.name - # recurse in package except if __init__ was explicitly given - if astroid.package and something.find('__init__') == -1: - # recurse on others packages / modules if this is a package - for fpath in modutils.get_module_files(dirname(astroid.file), - black_list): - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None or astroid.name == base_name: - continue - project.add_module(astroid) - return project - - def register_transform(self, node_class, transform, predicate=None): - """Register `transform(node)` function to be applied on the given - Astroid's `node_class` if `predicate` is None or returns true - when called with the node as argument. - - The transform function may return a value which is then used to - substitute the original node in the tree. - """ - self.transforms[node_class].append((transform, predicate)) - - def unregister_transform(self, node_class, transform, predicate=None): - """Unregister the given transform.""" - self.transforms[node_class].remove((transform, predicate)) + for inferred in modastroid.igetattr(name, context): + yield inferred.instantiate_class() def register_failed_import_hook(self, hook): """Registers a hook to resolve imports that cannot be found otherwise. @@ -321,30 +251,6 @@ def register_failed_import_hook(self, hook): """ self._failed_import_hooks.append(hook) - def transform(self, node): - """Call matching transforms for the given node if any and return the - transformed node. - """ - cls = node.__class__ - if cls not in self.transforms: - # no transform registered for this class of node - return node - - transforms = self.transforms[cls] - orig_node = node # copy the reference - for transform_func, predicate in transforms: - if predicate is None or predicate(node): - ret = transform_func(node) - # if the transformation function returns something, it's - # expected to be a replacement for the node - if ret is not None: - if node is not orig_node: - # node has already be modified by some previous - # transformation, warn about it - warn('node %s substituted multiple times' % node) - node = ret - return node - def cache_module(self, module): """Cache a module if no module with the same name is known yet.""" self.astroid_cache.setdefault(module.name, module) @@ -359,33 +265,3 @@ def clear_cache(self, astroid_builtin=None): import astroid.raw_building astroid.raw_building._astroid_bootstrapping( astroid_builtin=astroid_builtin) - - -class Project(object): - """a project handle a set of modules / packages""" - def __init__(self, name=''): - self.name = name - self.path = None - self.modules = [] - self.locals = {} - self.__getitem__ = self.locals.__getitem__ - self.__iter__ = self.locals.__iter__ - self.values = self.locals.values - self.keys = self.locals.keys - self.items = self.locals.items - - def add_module(self, node): - self.locals[node.name] = node - self.modules.append(node) - - def get_module(self, name): - return self.locals[name] - - def get_children(self): - return self.modules - - def __repr__(self): - return '' % (self.name, id(self), - len(self.modules)) - - diff --git a/pymode/libs/astroid/mixins.py b/pymode/libs/astroid/mixins.py index dbf1673a..57082f0f 100644 --- a/pymode/libs/astroid/mixins.py +++ b/pymode/libs/astroid/mixins.py @@ -18,16 +18,16 @@ """This module contains some mixins for the different nodes. """ -from logilab.common.decorators import cachedproperty +import warnings -from astroid.exceptions import (AstroidBuildingException, InferenceError, - NotFoundError) +from astroid import decorators +from astroid import exceptions class BlockRangeMixIn(object): """override block range """ - @cachedproperty + @decorators.cachedproperty def blockstart_tolineno(self): return self.lineno @@ -55,15 +55,29 @@ def _get_filtered_stmts(self, _, node, _stmts, mystmt): return [node], True return _stmts, False - def ass_type(self): + def assign_type(self): return self + def ass_type(self): + warnings.warn('%s.ass_type() is deprecated and slated for removal ' + 'in astroid 2.0, use %s.assign_type() instead.' + % (type(self).__name__, type(self).__name__), + PendingDeprecationWarning, stacklevel=2) + return self.assign_type() + class AssignTypeMixin(object): - def ass_type(self): + def assign_type(self): return self + def ass_type(self): + warnings.warn('%s.ass_type() is deprecated and slated for removal ' + 'in astroid 2.0, use %s.assign_type() instead.' + % (type(self).__name__, type(self).__name__), + PendingDeprecationWarning, stacklevel=2) + return self.assign_type() + def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): """method used in filter_stmts""" if self is mystmt: @@ -77,11 +91,18 @@ def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): class ParentAssignTypeMixin(AssignTypeMixin): + def assign_type(self): + return self.parent.assign_type() + def ass_type(self): - return self.parent.ass_type() + warnings.warn('%s.ass_type() is deprecated and slated for removal ' + 'in astroid 2.0, use %s.assign_type() instead.' + % (type(self).__name__, type(self).__name__), + PendingDeprecationWarning, stacklevel=2) + return self.assign_type() -class FromImportMixIn(FilterStmtsMixin): +class ImportFromMixin(FilterStmtsMixin): """MixIn for From and Import Nodes""" def _infer_name(self, frame, name): @@ -104,11 +125,14 @@ def do_import_module(self, modname=None): # FIXME: we used to raise InferenceError here, but why ? return mymodule try: - return mymodule.import_module(modname, level=level) - except AstroidBuildingException: - raise InferenceError(modname) + return mymodule.import_module(modname, level=level, + relative_only=level and level >= 1) + except exceptions.AstroidBuildingException as ex: + if isinstance(ex.args[0], SyntaxError): + raise exceptions.InferenceError(str(ex)) + raise exceptions.InferenceError(modname) except SyntaxError as ex: - raise InferenceError(str(ex)) + raise exceptions.InferenceError(str(ex)) def real_name(self, asname): """get name from 'as' name""" @@ -120,5 +144,4 @@ def real_name(self, asname): _asname = name if asname == _asname: return name - raise NotFoundError(asname) - + raise exceptions.NotFoundError(asname) diff --git a/pymode/libs/astroid/modutils.py b/pymode/libs/astroid/modutils.py index c547f3e6..31104cb5 100644 --- a/pymode/libs/astroid/modutils.py +++ b/pymode/libs/astroid/modutils.py @@ -28,10 +28,9 @@ """ from __future__ import with_statement -__docformat__ = "restructuredtext en" - import imp import os +import platform import sys from distutils.sysconfig import get_python_lib from distutils.errors import DistutilsPlatformError @@ -42,8 +41,6 @@ except ImportError: pkg_resources = None -from logilab.common import _handle_blacklist - PY_ZIPMODULE = object() if sys.platform.startswith('win'): @@ -53,12 +50,7 @@ PY_SOURCE_EXTS = ('py',) PY_COMPILED_EXTS = ('so',) -# Notes about STD_LIB_DIRS -# Consider arch-specific installation for STD_LIB_DIRS definition -# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on -# -# :see: `Problems with /usr/lib64 builds `_ -# :see: `FHS `_ + try: # The explicit sys.prefix is to work around a patch in virtualenv that # replaces the 'real' sys.prefix (i.e. the location of the binary) @@ -70,22 +62,53 @@ # Take care of installations where exec_prefix != prefix. get_python_lib(standard_lib=True, prefix=sys.exec_prefix), get_python_lib(standard_lib=True)]) - if os.name == 'nt': - STD_LIB_DIRS.add(os.path.join(sys.prefix, 'dlls')) - try: - # real_prefix is defined when running inside virtualenv. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'dlls')) - except AttributeError: - pass # get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to # non-valid path, see https://bugs.pypy.org/issue1164 except DistutilsPlatformError: STD_LIB_DIRS = set() -EXT_LIB_DIR = get_python_lib() +if os.name == 'nt': + STD_LIB_DIRS.add(os.path.join(sys.prefix, 'dlls')) + try: + # real_prefix is defined when running inside virtualenv. + STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'dlls')) + except AttributeError: + pass +if platform.python_implementation() == 'PyPy': + _root = os.path.join(sys.prefix, 'lib_pypy') + STD_LIB_DIRS.add(_root) + try: + # real_prefix is defined when running inside virtualenv. + STD_LIB_DIRS.add(os.path.join(sys.real_prefix, 'lib_pypy')) + except AttributeError: + pass + del _root +if os.name == 'posix': + # Need the real prefix is we're under a virtualenv, otherwise + # the usual one will do. + try: + prefix = sys.real_prefix + except AttributeError: + prefix = sys.prefix + + def _posix_path(path): + base_python = 'python%d.%d' % sys.version_info[:2] + return os.path.join(prefix, path, base_python) + + STD_LIB_DIRS.add(_posix_path('lib')) + if sys.maxsize > 2**32: + # This tries to fix a problem with /usr/lib64 builds, + # where systems are running both 32-bit and 64-bit code + # on the same machine, which reflects into the places where + # standard library could be found. More details can be found + # here http://bugs.python.org/issue1294959. + # An easy reproducing case would be + # https://github.com/PyCQA/pylint/issues/712#issuecomment-163178753 + STD_LIB_DIRS.add(_posix_path('lib64')) -BUILTIN_MODULES = dict(zip(sys.builtin_module_names, - [1]*len(sys.builtin_module_names))) +EXT_LIB_DIR = get_python_lib() +IS_JYTHON = platform.python_implementation() == 'Jython' +BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True) class NoSourceFile(Exception): @@ -97,6 +120,32 @@ def _normalize_path(path): return os.path.normcase(os.path.abspath(path)) +def _path_from_filename(filename, is_jython=IS_JYTHON): + if not is_jython: + if sys.version_info > (3, 0): + return filename + else: + if filename.endswith(".pyc"): + return filename[:-1] + return filename + head, has_pyclass, _ = filename.partition("$py.class") + if has_pyclass: + return head + ".py" + return filename + + +def _handle_blacklist(blacklist, dirnames, filenames): + """remove files/directories in the black list + + dirnames/filenames are usually from os.walk + """ + for norecurs in blacklist: + if norecurs in dirnames: + dirnames.remove(norecurs) + elif norecurs in filenames: + filenames.remove(norecurs) + + _NORM_PATH_CACHE = {} def _cache_normalize_path(path): @@ -112,7 +161,7 @@ def _cache_normalize_path(path): result = _NORM_PATH_CACHE[path] = _normalize_path(path) return result -def load_module_from_name(dotted_name, path=None, use_sys=1): +def load_module_from_name(dotted_name, path=None, use_sys=True): """Load a Python module from its name. :type dotted_name: str @@ -184,14 +233,16 @@ def load_module_from_modpath(parts, path=None, use_sys=1): if prevmodule: setattr(prevmodule, part, module) _file = getattr(module, '__file__', '') + prevmodule = module + if not _file and _is_namespace(curname): + continue if not _file and len(modpath) != len(parts): raise ImportError('no module in %s' % '.'.join(parts[len(modpath):])) path = [os.path.dirname(_file)] - prevmodule = module return module -def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None): +def load_module_from_file(filepath, path=None, use_sys=True, extrapath=None): """Load a Python module from it's path. :type filepath: str @@ -219,9 +270,11 @@ def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None): def _check_init(path, mod_path): """check there are some __init__.py all along the way""" + modpath = [] for part in mod_path: + modpath.append(part) path = os.path.join(path, part) - if not _has_init(path): + if not _is_namespace('.'.join(modpath)) and not _has_init(path): return False return True @@ -246,7 +299,9 @@ def modpath_from_file(filename, extrapath=None): :rtype: list(str) :return: the corresponding splitted module's name """ - base = os.path.splitext(os.path.abspath(filename))[0] + filename = _path_from_filename(filename) + filename = os.path.abspath(filename) + base = os.path.splitext(filename)[0] if extrapath is not None: for path_ in extrapath: path = os.path.abspath(path_) @@ -317,8 +372,8 @@ def file_info_from_modpath(modpath, path=None, context_file=None): def get_module_part(dotted_name, context_file=None): """given a dotted name return the module part of the name : - >>> get_module_part('logilab.common.modutils.get_module_part') - 'logilab.common.modutils' + >>> get_module_part('astroid.as_string.dump') + 'astroid.as_string' :type dotted_name: str :param dotted_name: full name of the identifier we are interested in @@ -382,9 +437,8 @@ def get_module_files(src_directory, blacklist): path of the directory corresponding to the package :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` + :param blacklist: iterable + list of files or directories to ignore. :rtype: list :return: @@ -419,7 +473,8 @@ def get_source_file(filename, include_no_ext=False): :rtype: str :return: the absolute path of the source file if it exists """ - base, orig_ext = os.path.splitext(os.path.abspath(filename)) + filename = os.path.abspath(_path_from_filename(filename)) + base, orig_ext = os.path.splitext(filename) for ext in PY_SOURCE_EXTS: source_path = '%s.%s' % (base, ext) if os.path.exists(source_path): @@ -464,7 +519,8 @@ def is_standard_module(modname, std_path=None): # modules which are not living in a file are considered standard # (sys and __builtin__ for instance) if filename is None: - return True + # we assume there are no namespaces in stdlib + return not _is_namespace(modname) filename = _normalize_path(filename) if filename.startswith(_cache_normalize_path(EXT_LIB_DIR)): return False @@ -538,15 +594,27 @@ def _file_from_modpath(modpath, path=None, context=None): return mp_filename, mtype def _search_zip(modpath, pic): - for filepath, importer in pic.items(): + for filepath, importer in list(pic.items()): if importer is not None: if importer.find_module(modpath[0]): if not importer.find_module(os.path.sep.join(modpath)): raise ImportError('No module named %s in %s/%s' % ( '.'.join(modpath[1:]), filepath, modpath)) - return PY_ZIPMODULE, os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), filepath + return (PY_ZIPMODULE, + os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), + filepath) raise ImportError('No module named %s' % '.'.join(modpath)) +try: + import pkg_resources +except ImportError: + pkg_resources = None + + +def _is_namespace(modname): + return (pkg_resources is not None + and modname in pkg_resources._namespace_packages) + def _module_file(modpath, path=None): """get a module type / file path @@ -579,14 +647,13 @@ def _module_file(modpath, path=None): except AttributeError: checkeggs = False # pkg_resources support (aka setuptools namespace packages) - if (pkg_resources is not None - and modpath[0] in pkg_resources._namespace_packages - and modpath[0] in sys.modules - and len(modpath) > 1): + if _is_namespace(modpath[0]) and modpath[0] in sys.modules: # setuptools has added into sys.modules a module object with proper # __path__, get back information from there module = sys.modules[modpath.pop(0)] - path = module.__path__ + path = list(module.__path__) + if not modpath: + return imp.C_BUILTIN, None imported = [] while modpath: modname = modpath[0] @@ -609,7 +676,7 @@ def _module_file(modpath, path=None): # Don't forget to close the stream to avoid # spurious ResourceWarnings. if stream: - stream.close() + stream.close() if checkeggs and mp_filename: fullabspath = [_cache_normalize_path(x) for x in _path] @@ -639,7 +706,11 @@ def _module_file(modpath, path=None): except IOError: path = [mp_filename] else: - if b'pkgutil' in data and b'extend_path' in data: + extend_path = b'pkgutil' in data and b'extend_path' in data + declare_namespace = ( + b"pkg_resources" in data + and b"declare_namespace(__name__)" in data) + if extend_path or declare_namespace: # extend_path is called, search sys.path for module/packages # of this name see pkgutil.extend_path documentation path = [os.path.join(p, *imported) for p in sys.path diff --git a/pymode/libs/astroid/node_classes.py b/pymode/libs/astroid/node_classes.py index 4b413ef8..ca773c3a 100644 --- a/pymode/libs/astroid/node_classes.py +++ b/pymode/libs/astroid/node_classes.py @@ -18,40 +18,47 @@ """Module for some node classes. More nodes in scoped_nodes.py """ -import sys +import abc +import warnings +import lazy_object_proxy import six -from logilab.common.decorators import cachedproperty -from astroid.exceptions import NoDefault -from astroid.bases import (NodeNG, Statement, Instance, InferenceContext, - _infer_stmts, YES, BUILTINS) -from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin, - ParentAssignTypeMixin, FromImportMixIn) +from astroid import bases +from astroid import context as contextmod +from astroid import decorators +from astroid import exceptions +from astroid import mixins +from astroid import util -PY3K = sys.version_info >= (3, 0) +BUILTINS = six.moves.builtins.__name__ + +@bases.raise_if_nothing_inferred def unpack_infer(stmt, context=None): """recursively generate nodes inferred by the given statement. If the inferred value is a list or a tuple, recurse on the elements """ if isinstance(stmt, (List, Tuple)): for elt in stmt.elts: - for infered_elt in unpack_infer(elt, context): - yield infered_elt + if elt is util.YES: + yield elt + continue + for inferred_elt in unpack_infer(elt, context): + yield inferred_elt return - # if infered is a final node, return it and stop - infered = next(stmt.infer(context)) - if infered is stmt: - yield infered + # if inferred is a final node, return it and stop + inferred = next(stmt.infer(context)) + if inferred is stmt: + yield inferred return # else, infer recursivly, except YES object that should be returned as is - for infered in stmt.infer(context): - if infered is YES: - yield infered + for inferred in stmt.infer(context): + if inferred is util.YES: + yield inferred else: - for inf_inf in unpack_infer(infered, context): + for inf_inf in unpack_infer(inferred, context): yield inf_inf @@ -93,7 +100,9 @@ def are_exclusive(stmt1, stmt2, exceptions=None): c2attr, c2node = node.locate_child(previous) c1attr, c1node = node.locate_child(children[node]) if c1node is not c2node: - if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or + if ((c2attr == 'body' + and c1attr == 'handlers' + and children[node].catch(exceptions)) or (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or (c2attr == 'handlers' and c1attr == 'orelse') or (c2attr == 'orelse' and c1attr == 'handlers')): @@ -106,6 +115,31 @@ def are_exclusive(stmt1, stmt2, exceptions=None): return False +@six.add_metaclass(abc.ABCMeta) +class _BaseContainer(mixins.ParentAssignTypeMixin, + bases.NodeNG, + bases.Instance): + """Base class for Set, FrozenSet, Tuple and List.""" + + _astroid_fields = ('elts',) + + def __init__(self, elts=None): + if elts is None: + self.elts = [] + else: + self.elts = [const_factory(e) for e in elts] + + def itered(self): + return self.elts + + def bool_value(self): + return bool(self.elts) + + @abc.abstractmethod + def pytype(self): + pass + + class LookupMixIn(object): """Mixin looking up a name in the right scope """ @@ -124,14 +158,14 @@ def lookup(self, name): return self.scope().scope_lookup(self, name) def ilookup(self, name): - """infered lookup + """inferred lookup - return an iterator on infered values of the statements returned by + return an iterator on inferred values of the statements returned by the lookup method """ frame, stmts = self.lookup(name) - context = InferenceContext() - return _infer_stmts(stmts, context, frame) + context = contextmod.InferenceContext() + return bases._infer_stmts(stmts, context, frame) def _filter_stmts(self, stmts, frame, offset): """filter statements to remove ignorable statements. @@ -163,8 +197,7 @@ def _filter_stmts(self, stmts, frame, offset): if self.statement() is myframe and myframe.parent: myframe = myframe.parent.frame() - if not myframe is frame or self is frame: - return stmts + mystmt = self.statement() # line filtering if we are in the same frame # @@ -183,19 +216,18 @@ def _filter_stmts(self, stmts, frame, offset): # line filtering is on and we have reached our location, break if mylineno > 0 and stmt.fromlineno > mylineno: break - assert hasattr(node, 'ass_type'), (node, node.scope(), - node.scope().locals) - ass_type = node.ass_type() - + assert hasattr(node, 'assign_type'), (node, node.scope(), + node.scope().locals) + assign_type = node.assign_type() if node.has_base(self): break - _stmts, done = ass_type._get_filtered_stmts(self, node, _stmts, mystmt) + _stmts, done = assign_type._get_filtered_stmts(self, node, _stmts, mystmt) if done: break - optional_assign = ass_type.optional_assign - if optional_assign and ass_type.parent_of(self): + optional_assign = assign_type.optional_assign + if optional_assign and assign_type.parent_of(self): # we are inside a loop, loop var assigment is hidding previous # assigment _stmts = [node] @@ -210,7 +242,7 @@ def _filter_stmts(self, stmts, frame, offset): else: # we got a parent index, this means the currently visited node # is at the same block level as a previously visited node - if _stmts[pindex].ass_type().parent_of(ass_type): + if _stmts[pindex].assign_type().parent_of(assign_type): # both statements are not at the same block level continue # if currently visited node is following previously considered @@ -239,7 +271,7 @@ def _filter_stmts(self, stmts, frame, offset): if not (optional_assign or are_exclusive(_stmts[pindex], node)): del _stmt_parents[pindex] del _stmts[pindex] - if isinstance(node, AssName): + if isinstance(node, AssignName): if not optional_assign and stmt.parent is mystmt.parent: _stmts = [] _stmt_parents = [] @@ -252,27 +284,24 @@ def _filter_stmts(self, stmts, frame, offset): _stmt_parents.append(stmt.parent) return _stmts + # Name classes -class AssName(LookupMixIn, ParentAssignTypeMixin, NodeNG): +class AssignName(LookupMixIn, mixins.ParentAssignTypeMixin, bases.NodeNG): """class representing an AssName node""" -class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG): +class DelName(LookupMixIn, mixins.ParentAssignTypeMixin, bases.NodeNG): """class representing a DelName node""" -class Name(LookupMixIn, NodeNG): +class Name(LookupMixIn, bases.NodeNG): """class representing a Name node""" - - -##################### node classes ######################################## - -class Arguments(NodeNG, AssignTypeMixin): +class Arguments(mixins.AssignTypeMixin, bases.NodeNG): """class representing an Arguments node""" - if PY3K: + if six.PY3: # Python 3.4+ uses a different approach regarding annotations, # each argument is a new class, _ast.arg, which exposes an # 'annotation' attribute. In astroid though, arguments are exposed @@ -306,7 +335,7 @@ def _infer_name(self, frame, name): return name return None - @cachedproperty + @decorators.cachedproperty def fromlineno(self): lineno = super(Arguments, self).fromlineno return max(lineno, self.parent.fromlineno or 0) @@ -315,15 +344,18 @@ def format_args(self): """return arguments formatted as string""" result = [] if self.args: - result.append(_format_args(self.args, self.defaults)) + result.append( + _format_args(self.args, self.defaults, + getattr(self, 'annotations', None)) + ) if self.vararg: result.append('*%s' % self.vararg) - if self.kwarg: - result.append('**%s' % self.kwarg) if self.kwonlyargs: if not self.vararg: result.append('*') result.append(_format_args(self.kwonlyargs, self.kw_defaults)) + if self.kwarg: + result.append('**%s' % self.kwarg) return ', '.join(result) def default_value(self, argname): @@ -339,7 +371,7 @@ def default_value(self, argname): i = _find_arg(argname, self.kwonlyargs)[0] if i is not None and self.kw_defaults[i] is not None: return self.kw_defaults[i] - raise NoDefault() + raise exceptions.NoDefault() def is_argument(self, name): """return True if the name is defined in arguments""" @@ -374,79 +406,91 @@ def _find_arg(argname, args, rec=False): return None, None -def _format_args(args, defaults=None): +def _format_args(args, defaults=None, annotations=None): values = [] if args is None: return '' + if annotations is None: + annotations = [] if defaults is not None: default_offset = len(args) - len(defaults) - for i, arg in enumerate(args): + packed = six.moves.zip_longest(args, annotations) + for i, (arg, annotation) in enumerate(packed): if isinstance(arg, Tuple): values.append('(%s)' % _format_args(arg.elts)) else: - values.append(arg.name) + argname = arg.name + if annotation is not None: + argname += ':' + annotation.as_string() + values.append(argname) + if defaults is not None and i >= default_offset: if defaults[i-default_offset] is not None: values[-1] += '=' + defaults[i-default_offset].as_string() return ', '.join(values) -class AssAttr(NodeNG, ParentAssignTypeMixin): - """class representing an AssAttr node""" +class AssignAttr(mixins.ParentAssignTypeMixin, bases.NodeNG): + """class representing an AssignAttr node""" _astroid_fields = ('expr',) expr = None -class Assert(Statement): +class Assert(bases.Statement): """class representing an Assert node""" _astroid_fields = ('test', 'fail',) test = None fail = None -class Assign(Statement, AssignTypeMixin): +class Assign(bases.Statement, mixins.AssignTypeMixin): """class representing an Assign node""" _astroid_fields = ('targets', 'value',) targets = None value = None -class AugAssign(Statement, AssignTypeMixin): +class AugAssign(bases.Statement, mixins.AssignTypeMixin): """class representing an AugAssign node""" _astroid_fields = ('target', 'value',) target = None value = None -class Backquote(NodeNG): +class Repr(bases.NodeNG): """class representing a Backquote node""" _astroid_fields = ('value',) value = None -class BinOp(NodeNG): +class BinOp(bases.NodeNG): """class representing a BinOp node""" _astroid_fields = ('left', 'right',) left = None right = None -class BoolOp(NodeNG): +class BoolOp(bases.NodeNG): """class representing a BoolOp node""" _astroid_fields = ('values',) values = None -class Break(Statement): +class Break(bases.Statement): """class representing a Break node""" -class CallFunc(NodeNG): - """class representing a CallFunc node""" - _astroid_fields = ('func', 'args', 'starargs', 'kwargs') +class Call(bases.NodeNG): + """class representing a Call node""" + _astroid_fields = ('func', 'args', 'keywords') func = None args = None - starargs = None - kwargs = None + keywords = None + + @property + def starargs(self): + args = self.args or [] + return [arg for arg in args if isinstance(arg, Starred)] - def __init__(self): - self.starargs = None - self.kwargs = None + @property + def kwargs(self): + keywords = self.keywords or [] + return [keyword for keyword in keywords if keyword.arg is None] -class Compare(NodeNG): +class Compare(bases.NodeNG): """class representing a Compare node""" _astroid_fields = ('left', 'ops',) left = None @@ -464,7 +508,8 @@ def last_child(self): return self.ops[-1][1] #return self.left -class Comprehension(NodeNG): + +class Comprehension(bases.NodeNG): """class representing a Comprehension node""" _astroid_fields = ('target', 'iter', 'ifs') target = None @@ -472,9 +517,16 @@ class Comprehension(NodeNG): ifs = None optional_assign = True - def ass_type(self): + def assign_type(self): return self + def ass_type(self): + warnings.warn('%s.ass_type() is deprecated and slated for removal' + 'in astroid 2.0, use %s.assign_type() instead.' + % (type(self).__name__, type(self).__name__), + PendingDeprecationWarning, stacklevel=2) + return self.assign_type() + def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): """method used in filter_stmts""" if self is mystmt: @@ -490,7 +542,7 @@ def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): return stmts, False -class Const(NodeNG, Instance): +class Const(bases.NodeNG, bases.Instance): """represent a constant node like num, str, bool, None, bytes""" def __init__(self, value=None): @@ -499,6 +551,11 @@ def __init__(self, value=None): def getitem(self, index, context=None): if isinstance(self.value, six.string_types): return Const(self.value[index]) + if isinstance(self.value, bytes) and six.PY3: + # Bytes aren't instances of six.string_types + # on Python 3. Also, indexing them should return + # integers. + return Const(self.value[index]) raise TypeError('%r (value=%s)' % (self, self.value)) def has_dynamic_getattr(self): @@ -513,11 +570,11 @@ def pytype(self): return self._proxied.qname() -class Continue(Statement): +class Continue(bases.Statement): """class representing a Continue node""" -class Decorators(NodeNG): +class Decorators(bases.NodeNG): """class representing a Decorators node""" _astroid_fields = ('nodes',) nodes = None @@ -529,19 +586,21 @@ def scope(self): # skip the function node to go directly to the upper level scope return self.parent.parent.scope() -class DelAttr(NodeNG, ParentAssignTypeMixin): + +class DelAttr(mixins.ParentAssignTypeMixin, bases.NodeNG): """class representing a DelAttr node""" _astroid_fields = ('expr',) expr = None -class Delete(Statement, AssignTypeMixin): + +class Delete(mixins.AssignTypeMixin, bases.Statement): """class representing a Delete node""" _astroid_fields = ('targets',) targets = None -class Dict(NodeNG, Instance): +class Dict(bases.NodeNG, bases.Instance): """class representing a Dict node""" _astroid_fields = ('items',) @@ -550,7 +609,7 @@ def __init__(self, items=None): self.items = [] else: self.items = [(const_factory(k), const_factory(v)) - for k, v in items.items()] + for k, v in list(items.items())] def pytype(self): return '%s.dict' % BUILTINS @@ -573,39 +632,45 @@ def itered(self): def getitem(self, lookup_key, context=None): for key, value in self.items: - for inferedkey in key.infer(context): - if inferedkey is YES: + # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}. + if isinstance(key, DictUnpack): + try: + return value.getitem(lookup_key, context) + except IndexError: continue - if isinstance(inferedkey, Const) \ - and inferedkey.value == lookup_key: + for inferredkey in key.infer(context): + if inferredkey is util.YES: + continue + if isinstance(inferredkey, Const) \ + and inferredkey.value == lookup_key: return value # This should raise KeyError, but all call sites only catch # IndexError. Let's leave it like that for now. raise IndexError(lookup_key) -class Discard(Statement): - """class representing a Discard node""" +class Expr(bases.Statement): + """class representing a Expr node""" _astroid_fields = ('value',) value = None -class Ellipsis(NodeNG): +class Ellipsis(bases.NodeNG): # pylint: disable=redefined-builtin """class representing an Ellipsis node""" -class EmptyNode(NodeNG): +class EmptyNode(bases.NodeNG): """class representing an EmptyNode node""" -class ExceptHandler(Statement, AssignTypeMixin): +class ExceptHandler(mixins.AssignTypeMixin, bases.Statement): """class representing an ExceptHandler node""" _astroid_fields = ('type', 'name', 'body',) type = None name = None body = None - @cachedproperty + @decorators.cachedproperty def blockstart_tolineno(self): if self.name: return self.name.tolineno @@ -622,7 +687,7 @@ def catch(self, exceptions): return True -class Exec(Statement): +class Exec(bases.Statement): """class representing an Exec node""" _astroid_fields = ('expr', 'globals', 'locals',) expr = None @@ -630,12 +695,12 @@ class Exec(Statement): locals = None -class ExtSlice(NodeNG): +class ExtSlice(bases.NodeNG): """class representing an ExtSlice node""" _astroid_fields = ('dims',) dims = None -class For(BlockRangeMixIn, AssignTypeMixin, Statement): +class For(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, bases.Statement): """class representing a For node""" _astroid_fields = ('target', 'iter', 'body', 'orelse',) target = None @@ -644,12 +709,26 @@ class For(BlockRangeMixIn, AssignTypeMixin, Statement): orelse = None optional_assign = True - @cachedproperty + @decorators.cachedproperty def blockstart_tolineno(self): return self.iter.tolineno -class From(FromImportMixIn, Statement): +class AsyncFor(For): + """Asynchronous For built with `async` keyword.""" + + +class Await(bases.NodeNG): + """Await node for the `await` keyword.""" + + _astroid_fields = ('value', ) + value = None + + def postinit(self, value=None): + self.value = value + + +class ImportFrom(mixins.ImportFromMixin, bases.Statement): """class representing a From node""" def __init__(self, fromname, names, level=0): @@ -657,13 +736,13 @@ def __init__(self, fromname, names, level=0): self.names = names self.level = level -class Getattr(NodeNG): - """class representing a Getattr node""" +class Attribute(bases.NodeNG): + """class representing a Attribute node""" _astroid_fields = ('expr',) expr = None -class Global(Statement): +class Global(bases.Statement): """class representing a Global node""" def __init__(self, names): @@ -673,14 +752,14 @@ def _infer_name(self, frame, name): return name -class If(BlockRangeMixIn, Statement): +class If(mixins.BlockRangeMixIn, bases.Statement): """class representing an If node""" _astroid_fields = ('test', 'body', 'orelse') test = None body = None orelse = None - @cachedproperty + @decorators.cachedproperty def blockstart_tolineno(self): return self.test.tolineno @@ -694,7 +773,7 @@ def block_range(self, lineno): self.body[0].fromlineno - 1) -class IfExp(NodeNG): +class IfExp(bases.NodeNG): """class representing an IfExp node""" _astroid_fields = ('test', 'body', 'orelse') test = None @@ -702,31 +781,24 @@ class IfExp(NodeNG): orelse = None -class Import(FromImportMixIn, Statement): +class Import(mixins.ImportFromMixin, bases.Statement): """class representing an Import node""" -class Index(NodeNG): +class Index(bases.NodeNG): """class representing an Index node""" _astroid_fields = ('value',) value = None -class Keyword(NodeNG): +class Keyword(bases.NodeNG): """class representing a Keyword node""" _astroid_fields = ('value',) value = None -class List(NodeNG, Instance, ParentAssignTypeMixin): +class List(_BaseContainer): """class representing a List node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] def pytype(self): return '%s.list' % BUILTINS @@ -734,11 +806,8 @@ def pytype(self): def getitem(self, index, context=None): return self.elts[index] - def itered(self): - return self.elts - -class Nonlocal(Statement): +class Nonlocal(bases.Statement): """class representing a Nonlocal node""" def __init__(self, names): @@ -748,21 +817,21 @@ def _infer_name(self, frame, name): return name -class Pass(Statement): +class Pass(bases.Statement): """class representing a Pass node""" -class Print(Statement): +class Print(bases.Statement): """class representing a Print node""" _astroid_fields = ('dest', 'values',) dest = None values = None -class Raise(Statement): +class Raise(bases.Statement): """class representing a Raise node""" exc = None - if sys.version_info < (3, 0): + if six.PY2: _astroid_fields = ('exc', 'inst', 'tback') inst = None tback = None @@ -779,50 +848,40 @@ def raises_not_implemented(self): return True -class Return(Statement): +class Return(bases.Statement): """class representing a Return node""" _astroid_fields = ('value',) value = None -class Set(NodeNG, Instance, ParentAssignTypeMixin): +class Set(_BaseContainer): """class representing a Set node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] def pytype(self): return '%s.set' % BUILTINS - def itered(self): - return self.elts - -class Slice(NodeNG): +class Slice(bases.NodeNG): """class representing a Slice node""" _astroid_fields = ('lower', 'upper', 'step') lower = None upper = None step = None -class Starred(NodeNG, ParentAssignTypeMixin): +class Starred(mixins.ParentAssignTypeMixin, bases.NodeNG): """class representing a Starred node""" _astroid_fields = ('value',) value = None -class Subscript(NodeNG): +class Subscript(bases.NodeNG): """class representing a Subscript node""" _astroid_fields = ('value', 'slice') value = None slice = None -class TryExcept(BlockRangeMixIn, Statement): +class TryExcept(mixins.BlockRangeMixIn, bases.Statement): """class representing a TryExcept node""" _astroid_fields = ('body', 'handlers', 'orelse',) body = None @@ -845,7 +904,7 @@ def block_range(self, lineno): return self._elsed_block_range(lineno, self.orelse, last) -class TryFinally(BlockRangeMixIn, Statement): +class TryFinally(mixins.BlockRangeMixIn, bases.Statement): """class representing a TryFinally node""" _astroid_fields = ('body', 'finalbody',) body = None @@ -861,15 +920,8 @@ def block_range(self, lineno): return self._elsed_block_range(lineno, self.finalbody) -class Tuple(NodeNG, Instance, ParentAssignTypeMixin): +class Tuple(_BaseContainer): """class representing a Tuple node""" - _astroid_fields = ('elts',) - - def __init__(self, elts=None): - if elts is None: - self.elts = [] - else: - self.elts = [const_factory(e) for e in elts] def pytype(self): return '%s.tuple' % BUILTINS @@ -877,24 +929,21 @@ def pytype(self): def getitem(self, index, context=None): return self.elts[index] - def itered(self): - return self.elts - -class UnaryOp(NodeNG): +class UnaryOp(bases.NodeNG): """class representing an UnaryOp node""" _astroid_fields = ('operand',) operand = None -class While(BlockRangeMixIn, Statement): +class While(mixins.BlockRangeMixIn, bases.Statement): """class representing a While node""" _astroid_fields = ('test', 'body', 'orelse',) test = None body = None orelse = None - @cachedproperty + @decorators.cachedproperty def blockstart_tolineno(self): return self.test.tolineno @@ -903,13 +952,13 @@ def block_range(self, lineno): return self. _elsed_block_range(lineno, self.orelse) -class With(BlockRangeMixIn, AssignTypeMixin, Statement): +class With(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, bases.Statement): """class representing a With node""" _astroid_fields = ('items', 'body') items = None body = None - @cachedproperty + @decorators.cachedproperty def blockstart_tolineno(self): return self.items[-1][0].tolineno @@ -921,7 +970,12 @@ def get_children(self): for elt in self.body: yield elt -class Yield(NodeNG): + +class AsyncWith(With): + """Asynchronous `with` built with the `async` keyword.""" + + +class Yield(bases.NodeNG): """class representing a Yield node""" _astroid_fields = ('value',) value = None @@ -929,6 +983,11 @@ class Yield(NodeNG): class YieldFrom(Yield): """ Class representing a YieldFrom node. """ + +class DictUnpack(bases.NodeNG): + """Represents the unpacking of dicts into dicts using PEP 448.""" + + # constants ############################################################## CONST_CLS = { @@ -937,19 +996,20 @@ class YieldFrom(Yield): dict: Dict, set: Set, type(None): Const, + type(NotImplemented): Const, } def _update_const_classes(): """update constant classes, so the keys of CONST_CLS can be reused""" klasses = (bool, int, float, complex, str) - if sys.version_info < (3, 0): + if six.PY2: klasses += (unicode, long) - if sys.version_info >= (2, 6): - klasses += (bytes,) + klasses += (bytes,) for kls in klasses: CONST_CLS[kls] = Const _update_const_classes() + def const_factory(value): """return an astroid node for a python value""" # XXX we should probably be stricter here and only consider stuff in @@ -957,10 +1017,37 @@ def const_factory(value): # we should rather recall the builder on this value than returning an empty # node (another option being that const_factory shouldn't be called with something # not in CONST_CLS) - assert not isinstance(value, NodeNG) + assert not isinstance(value, bases.NodeNG) try: return CONST_CLS[value.__class__](value) except (KeyError, AttributeError): node = EmptyNode() node.object = value return node + + +# Backward-compatibility aliases +def instancecheck(cls, other): + wrapped = cls.__wrapped__ + other_cls = other.__class__ + is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped) + warnings.warn("%r is deprecated and slated for removal in astroid " + "2.0, use %r instead" % (cls.__class__.__name__, + wrapped.__name__), + PendingDeprecationWarning, stacklevel=2) + return is_instance_of + + +def proxy_alias(alias_name, node_type): + proxy = type(alias_name, (lazy_object_proxy.Proxy,), + {'__class__': object.__dict__['__class__'], + '__instancecheck__': instancecheck}) + return proxy(lambda: node_type) + +Backquote = proxy_alias('Backquote', Repr) +Discard = proxy_alias('Discard', Expr) +AssName = proxy_alias('AssName', AssignName) +AssAttr = proxy_alias('AssAttr', AssignAttr) +Getattr = proxy_alias('Getattr', Attribute) +CallFunc = proxy_alias('CallFunc', Call) +From = proxy_alias('From', ImportFrom) diff --git a/pymode/libs/astroid/nodes.py b/pymode/libs/astroid/nodes.py index 67c2f8e8..2fd6cb65 100644 --- a/pymode/libs/astroid/nodes.py +++ b/pymode/libs/astroid/nodes.py @@ -24,40 +24,54 @@ .next_sibling(), returning next sibling statement node .statement(), returning the first parent node marked as statement node .frame(), returning the first node defining a new local scope (i.e. - Module, Function or Class) + Module, FunctionDef or ClassDef) .set_local(name, node), define an identifier on the first parent frame, with the node defining it. This is used by the astroid builder and should not be used from out there. -on From and Import : +on ImportFrom and Import : .real_name(name), """ -# pylint: disable=unused-import +# pylint: disable=unused-import,redefined-builtin + +from astroid.node_classes import ( + Arguments, AssignAttr, Assert, Assign, + AssignName, AugAssign, Repr, BinOp, BoolOp, Break, Call, Compare, + Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, + Dict, Expr, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, + ImportFrom, Attribute, Global, If, IfExp, Import, Index, Keyword, + List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, + TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, + const_factory, + AsyncFor, Await, AsyncWith, + # Backwards-compatibility aliases + Backquote, Discard, AssName, AssAttr, Getattr, CallFunc, From, + # Node not present in the builtin ast module. + DictUnpack, +) +from astroid.scoped_nodes import ( + Module, GeneratorExp, Lambda, DictComp, + ListComp, SetComp, FunctionDef, ClassDef, + AsyncFunctionDef, + # Backwards-compatibility aliases + Class, Function, GenExpr, +) -__docformat__ = "restructuredtext en" -from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \ - AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \ - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \ - Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \ - From, Getattr, Global, If, IfExp, Import, Index, Keyword, \ - List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \ - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \ - const_factory -from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \ - ListComp, SetComp, Function, Class ALL_NODE_CLASSES = ( - Arguments, AssAttr, Assert, Assign, AssName, AugAssign, - Backquote, BinOp, BoolOp, Break, - CallFunc, Class, Compare, Comprehension, Const, Continue, + AsyncFunctionDef, AsyncFor, AsyncWith, Await, + + Arguments, AssignAttr, Assert, Assign, AssignName, AugAssign, + Repr, BinOp, BoolOp, Break, + Call, ClassDef, Compare, Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, - Dict, DictComp, Discard, + Dict, DictComp, DictUnpack, Expr, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, - For, From, Function, - Getattr, GenExpr, Global, + For, ImportFrom, FunctionDef, + Attribute, GeneratorExp, Global, If, IfExp, Import, Index, Keyword, Lambda, List, ListComp, @@ -69,6 +83,5 @@ TryExcept, TryFinally, Tuple, UnaryOp, While, With, - Yield, YieldFrom + Yield, YieldFrom, ) - diff --git a/pymode/libs/astroid/objects.py b/pymode/libs/astroid/objects.py new file mode 100644 index 00000000..d2f4270b --- /dev/null +++ b/pymode/libs/astroid/objects.py @@ -0,0 +1,186 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +""" +Inference objects are a way to represent composite AST nodes, +which are used only as inference results, so they can't be found in the +code tree. For instance, inferring the following frozenset use, leads to an +inferred FrozenSet: + + CallFunc(func=Name('frozenset'), args=Tuple(...)) + +""" + +import six + +from astroid import MANAGER +from astroid.bases import ( + BUILTINS, NodeNG, Instance, _infer_stmts, + BoundMethod, _is_property +) +from astroid.decorators import cachedproperty +from astroid.exceptions import ( + SuperError, SuperArgumentTypeError, + NotFoundError, MroError +) +from astroid.node_classes import const_factory +from astroid.scoped_nodes import ClassDef, FunctionDef +from astroid.mixins import ParentAssignTypeMixin + + +class FrozenSet(NodeNG, Instance, ParentAssignTypeMixin): + """class representing a FrozenSet composite node""" + + def __init__(self, elts=None): + if elts is None: + self.elts = [] + else: + self.elts = [const_factory(e) for e in elts] + + def pytype(self): + return '%s.frozenset' % BUILTINS + + def itered(self): + return self.elts + + def _infer(self, context=None): + yield self + + @cachedproperty + def _proxied(self): + builtins = MANAGER.astroid_cache[BUILTINS] + return builtins.getattr('frozenset')[0] + + +class Super(NodeNG): + """Proxy class over a super call. + + This class offers almost the same behaviour as Python's super, + which is MRO lookups for retrieving attributes from the parents. + + The *mro_pointer* is the place in the MRO from where we should + start looking, not counting it. *mro_type* is the object which + provides the MRO, it can be both a type or an instance. + *self_class* is the class where the super call is, while + *scope* is the function where the super call is. + """ + + def __init__(self, mro_pointer, mro_type, self_class, scope): + self.type = mro_type + self.mro_pointer = mro_pointer + self._class_based = False + self._self_class = self_class + self._scope = scope + self._model = { + '__thisclass__': self.mro_pointer, + '__self_class__': self._self_class, + '__self__': self.type, + '__class__': self._proxied, + } + + def _infer(self, context=None): + yield self + + def super_mro(self): + """Get the MRO which will be used to lookup attributes in this super.""" + if not isinstance(self.mro_pointer, ClassDef): + raise SuperArgumentTypeError("The first super argument must be type.") + + if isinstance(self.type, ClassDef): + # `super(type, type)`, most likely in a class method. + self._class_based = True + mro_type = self.type + else: + mro_type = getattr(self.type, '_proxied', None) + if not isinstance(mro_type, (Instance, ClassDef)): + raise SuperArgumentTypeError("super(type, obj): obj must be an " + "instance or subtype of type") + + if not mro_type.newstyle: + raise SuperError("Unable to call super on old-style classes.") + + mro = mro_type.mro() + if self.mro_pointer not in mro: + raise SuperArgumentTypeError("super(type, obj): obj must be an " + "instance or subtype of type") + + index = mro.index(self.mro_pointer) + return mro[index + 1:] + + @cachedproperty + def _proxied(self): + builtins = MANAGER.astroid_cache[BUILTINS] + return builtins.getattr('super')[0] + + def pytype(self): + return '%s.super' % BUILTINS + + def display_type(self): + return 'Super of' + + @property + def name(self): + """Get the name of the MRO pointer.""" + return self.mro_pointer.name + + def igetattr(self, name, context=None): + """Retrieve the inferred values of the given attribute name.""" + + local_name = self._model.get(name) + if local_name: + yield local_name + return + + try: + mro = self.super_mro() + except (MroError, SuperError) as exc: + # Don't let invalid MROs or invalid super calls + # to leak out as is from this function. + six.raise_from(NotFoundError, exc) + + found = False + for cls in mro: + if name not in cls._locals: + continue + + found = True + for infered in _infer_stmts([cls[name]], context, frame=self): + if not isinstance(infered, FunctionDef): + yield infered + continue + + # We can obtain different descriptors from a super depending + # on what we are accessing and where the super call is. + if infered.type == 'classmethod': + yield BoundMethod(infered, cls) + elif self._scope.type == 'classmethod' and infered.type == 'method': + yield infered + elif self._class_based or infered.type == 'staticmethod': + yield infered + elif _is_property(infered): + # TODO: support other descriptors as well. + for value in infered.infer_call_result(self, context): + yield value + else: + yield BoundMethod(infered, cls) + + if not found: + raise NotFoundError(name) + + def getattr(self, name, context=None): + return list(self.igetattr(name, context=context)) diff --git a/pymode/libs/astroid/protocols.py b/pymode/libs/astroid/protocols.py index 4c11f9cf..87a6d4d2 100644 --- a/pymode/libs/astroid/protocols.py +++ b/pymode/libs/astroid/protocols.py @@ -19,28 +19,31 @@ where it makes sense. """ -__doctype__ = "restructuredtext en" import collections - -from astroid.exceptions import InferenceError, NoDefault, NotFoundError -from astroid.node_classes import unpack_infer -from astroid.bases import InferenceContext, copy_context, \ - raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES -from astroid.nodes import const_factory +import operator +import sys + +from astroid import arguments +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import node_classes from astroid import nodes +from astroid import util BIN_OP_METHOD = {'+': '__add__', '-': '__sub__', '/': '__div__', '//': '__floordiv__', '*': '__mul__', - '**': '__power__', + '**': '__pow__', '%': '__mod__', '&': '__and__', '|': '__or__', '^': '__xor__', '<<': '__lshift__', '>>': '__rshift__', + '@': '__matmul__' } UNARY_OP_METHOD = {'+': '__pos__', @@ -53,7 +56,7 @@ def tl_infer_unary_op(self, operator): if operator == 'not': - return const_factory(not bool(self.elts)) + return node_classes.const_factory(not bool(self.elts)) raise TypeError() # XXX log unsupported operation nodes.Tuple.infer_unary_op = tl_infer_unary_op nodes.List.infer_unary_op = tl_infer_unary_op @@ -61,19 +64,19 @@ def tl_infer_unary_op(self, operator): def dict_infer_unary_op(self, operator): if operator == 'not': - return const_factory(not bool(self.items)) + return node_classes.const_factory(not bool(self.items)) raise TypeError() # XXX log unsupported operation nodes.Dict.infer_unary_op = dict_infer_unary_op def const_infer_unary_op(self, operator): if operator == 'not': - return const_factory(not self.value) + return node_classes.const_factory(not self.value) # XXX log potentially raised TypeError elif operator == '+': - return const_factory(+self.value) + return node_classes.const_factory(+self.value) else: # operator == '-': - return const_factory(-self.value) + return node_classes.const_factory(-self.value) nodes.Const.infer_unary_op = const_infer_unary_op @@ -92,17 +95,23 @@ def const_infer_unary_op(self, operator): '<<': lambda a, b: a << b, '>>': lambda a, b: a >> b, } + +if sys.version_info >= (3, 5): + # MatMult is available since Python 3.5+. + BIN_OP_IMPL['@'] = operator.matmul + for key, impl in list(BIN_OP_IMPL.items()): BIN_OP_IMPL[key+'='] = impl -def const_infer_binary_op(self, operator, other, context): +def const_infer_binary_op(self, binop, other, context): + operator = binop.op for other in other.infer(context): if isinstance(other, nodes.Const): try: impl = BIN_OP_IMPL[operator] try: - yield const_factory(impl(self.value, other.value)) + yield node_classes.const_factory(impl(self.value, other.value)) except Exception: # ArithmeticError is not enough: float >> float is a TypeError # TODO : let pylint know about the problem @@ -110,68 +119,88 @@ def const_infer_binary_op(self, operator, other, context): except TypeError: # XXX log TypeError continue - elif other is YES: + elif other is util.YES: yield other else: try: - for val in other.infer_binary_op(operator, self, context): + for val in other.infer_binary_op(binop, self, context): yield val except AttributeError: - yield YES -nodes.Const.infer_binary_op = yes_if_nothing_infered(const_infer_binary_op) + yield util.YES +nodes.Const.infer_binary_op = bases.yes_if_nothing_inferred(const_infer_binary_op) + + +def _multiply_seq_by_int(self, binop, other, context): + node = self.__class__() + node.parent = binop + elts = [] + for elt in self.elts: + infered = util.safe_infer(elt, context) + if infered is None: + infered = util.YES + elts.append(infered) + node.elts = elts * other.value + return node -def tl_infer_binary_op(self, operator, other, context): + +def _filter_uninferable_nodes(elts, context): + for elt in elts: + if elt is util.YES: + yield elt + else: + for inferred in elt.infer(context): + yield inferred + + +def tl_infer_binary_op(self, binop, other, context): + operator = binop.op for other in other.infer(context): if isinstance(other, self.__class__) and operator == '+': node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] - elts += [n for elt in other.elts for n in elt.infer(context) - if not n is YES] + node.parent = binop + elts = list(_filter_uninferable_nodes(self.elts, context)) + elts += list(_filter_uninferable_nodes(other.elts, context)) node.elts = elts yield node elif isinstance(other, nodes.Const) and operator == '*': if not isinstance(other.value, int): - yield YES + yield util.YES continue - node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] * other.value - node.elts = elts - yield node - elif isinstance(other, Instance) and not isinstance(other, nodes.Const): - yield YES + yield _multiply_seq_by_int(self, binop, other, context) + elif isinstance(other, bases.Instance) and not isinstance(other, nodes.Const): + yield util.YES # XXX else log TypeError -nodes.Tuple.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) -nodes.List.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) +nodes.Tuple.infer_binary_op = bases.yes_if_nothing_inferred(tl_infer_binary_op) +nodes.List.infer_binary_op = bases.yes_if_nothing_inferred(tl_infer_binary_op) -def dict_infer_binary_op(self, operator, other, context): +def dict_infer_binary_op(self, binop, other, context): for other in other.infer(context): - if isinstance(other, Instance) and isinstance(other._proxied, nodes.Class): - yield YES + if isinstance(other, bases.Instance) and isinstance(other._proxied, nodes.ClassDef): + yield util.YES # XXX else log TypeError -nodes.Dict.infer_binary_op = yes_if_nothing_infered(dict_infer_binary_op) +nodes.Dict.infer_binary_op = bases.yes_if_nothing_inferred(dict_infer_binary_op) -def instance_infer_binary_op(self, operator, other, context): +def instance_infer_binary_op(self, binop, other, context): + operator = binop.op try: methods = self.getattr(BIN_OP_METHOD[operator]) - except (NotFoundError, KeyError): + except (exceptions.NotFoundError, KeyError): # Unknown operator - yield YES + yield util.YES else: for method in methods: - if not isinstance(method, nodes.Function): + if not isinstance(method, nodes.FunctionDef): continue for result in method.infer_call_result(self, context): - if result is not YES: + if result is not util.YES: yield result # We are interested only in the first infered method, # don't go looking in the rest of the methods of the ancestors. break -Instance.infer_binary_op = yes_if_nothing_infered(instance_infer_binary_op) +bases.Instance.infer_binary_op = bases.yes_if_nothing_inferred(instance_infer_binary_op) # assignment ################################################################## @@ -192,7 +221,7 @@ def _resolve_looppart(parts, asspath, context): asspath = asspath[:] index = asspath.pop(0) for part in parts: - if part is YES: + if part is util.YES: continue # XXX handle __iter__ and log potentially detected errors if not hasattr(part, 'itered'): @@ -212,104 +241,125 @@ def _resolve_looppart(parts, asspath, context): # we achieved to resolved the assignment path, # don't infer the last part yield assigned - elif assigned is YES: + elif assigned is util.YES: break else: # we are not yet on the last part of the path # search on each possibly inferred value try: - for infered in _resolve_looppart(assigned.infer(context), + for inferred in _resolve_looppart(assigned.infer(context), asspath, context): - yield infered - except InferenceError: + yield inferred + except exceptions.InferenceError: break -def for_assigned_stmts(self, node, context=None, asspath=None): +@bases.raise_if_nothing_inferred +def for_assigned_stmts(self, node=None, context=None, asspath=None): if asspath is None: for lst in self.iter.infer(context): if isinstance(lst, (nodes.Tuple, nodes.List)): for item in lst.elts: yield item else: - for infered in _resolve_looppart(self.iter.infer(context), + for inferred in _resolve_looppart(self.iter.infer(context), asspath, context): - yield infered + yield inferred -nodes.For.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) -nodes.Comprehension.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) +nodes.For.assigned_stmts = for_assigned_stmts +nodes.Comprehension.assigned_stmts = for_assigned_stmts -def mulass_assigned_stmts(self, node, context=None, asspath=None): +def sequence_assigned_stmts(self, node=None, context=None, asspath=None): if asspath is None: asspath = [] - asspath.insert(0, self.elts.index(node)) - return self.parent.assigned_stmts(self, context, asspath) -nodes.Tuple.assigned_stmts = mulass_assigned_stmts -nodes.List.assigned_stmts = mulass_assigned_stmts + try: + index = self.elts.index(node) + except ValueError: + util.reraise(exceptions.InferenceError( + 'Tried to retrieve a node {node!r} which does not exist', + node=self, assign_path=asspath, context=context)) + + asspath.insert(0, index) + return self.parent.assigned_stmts(node=self, context=context, asspath=asspath) + +nodes.Tuple.assigned_stmts = sequence_assigned_stmts +nodes.List.assigned_stmts = sequence_assigned_stmts -def assend_assigned_stmts(self, context=None): - return self.parent.assigned_stmts(self, context=context) -nodes.AssName.assigned_stmts = assend_assigned_stmts -nodes.AssAttr.assigned_stmts = assend_assigned_stmts +def assend_assigned_stmts(self, node=None, context=None, asspath=None): + return self.parent.assigned_stmts(node=self, context=context) +nodes.AssignName.assigned_stmts = assend_assigned_stmts +nodes.AssignAttr.assigned_stmts = assend_assigned_stmts def _arguments_infer_argname(self, name, context): # arguments information may be missing, in which case we can't do anything # more if not (self.args or self.vararg or self.kwarg): - yield YES + yield util.YES return # first argument of instance/class method if self.args and getattr(self.args[0], 'name', None) == name: functype = self.parent.type if functype == 'method': - yield Instance(self.parent.parent.frame()) + yield bases.Instance(self.parent.parent.frame()) return if functype == 'classmethod': yield self.parent.parent.frame() return + + if context and context.callcontext: + call_site = arguments.CallSite(context.callcontext) + for value in call_site.infer_argument(self.parent, name, context): + yield value + return + + # TODO: just provide the type here, no need to have an empty Dict. if name == self.vararg: - vararg = const_factory(()) + vararg = node_classes.const_factory(()) vararg.parent = self yield vararg return if name == self.kwarg: - kwarg = const_factory({}) + kwarg = node_classes.const_factory({}) kwarg.parent = self yield kwarg return # if there is a default value, yield it. And then yield YES to reflect # we can't guess given argument value try: - context = copy_context(context) - for infered in self.default_value(name).infer(context): - yield infered - yield YES - except NoDefault: - yield YES + context = contextmod.copy_context(context) + for inferred in self.default_value(name).infer(context): + yield inferred + yield util.YES + except exceptions.NoDefault: + yield util.YES -def arguments_assigned_stmts(self, node, context, asspath=None): +def arguments_assigned_stmts(self, node=None, context=None, asspath=None): if context.callcontext: # reset call context/name callcontext = context.callcontext - context = copy_context(context) + context = contextmod.copy_context(context) context.callcontext = None - return callcontext.infer_argument(self.parent, node.name, context) + args = arguments.CallSite(callcontext) + return args.infer_argument(self.parent, node.name, context) return _arguments_infer_argname(self, node.name, context) + nodes.Arguments.assigned_stmts = arguments_assigned_stmts -def assign_assigned_stmts(self, node, context=None, asspath=None): +@bases.raise_if_nothing_inferred +def assign_assigned_stmts(self, node=None, context=None, asspath=None): if not asspath: yield self.value return - for infered in _resolve_asspart(self.value.infer(context), asspath, context): - yield infered -nodes.Assign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) -nodes.AugAssign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) + for inferred in _resolve_asspart(self.value.infer(context), asspath, context): + yield inferred + +nodes.Assign.assigned_stmts = assign_assigned_stmts +nodes.AugAssign.assigned_stmts = assign_assigned_stmts def _resolve_asspart(parts, asspath, context): @@ -328,28 +378,30 @@ def _resolve_asspart(parts, asspath, context): # we achieved to resolved the assignment path, don't infer the # last part yield assigned - elif assigned is YES: + elif assigned is util.YES: return else: # we are not yet on the last part of the path search on each # possibly inferred value try: - for infered in _resolve_asspart(assigned.infer(context), + for inferred in _resolve_asspart(assigned.infer(context), asspath, context): - yield infered - except InferenceError: + yield inferred + except exceptions.InferenceError: return -def excepthandler_assigned_stmts(self, node, context=None, asspath=None): - for assigned in unpack_infer(self.type): - if isinstance(assigned, nodes.Class): - assigned = Instance(assigned) +@bases.raise_if_nothing_inferred +def excepthandler_assigned_stmts(self, node=None, context=None, asspath=None): + for assigned in node_classes.unpack_infer(self.type): + if isinstance(assigned, nodes.ClassDef): + assigned = bases.Instance(assigned) yield assigned -nodes.ExceptHandler.assigned_stmts = raise_if_nothing_infered(excepthandler_assigned_stmts) +nodes.ExceptHandler.assigned_stmts = bases.raise_if_nothing_inferred(excepthandler_assigned_stmts) -def with_assigned_stmts(self, node, context=None, asspath=None): +@bases.raise_if_nothing_inferred +def with_assigned_stmts(self, node=None, context=None, asspath=None): if asspath is None: for _, vars in self.items: if vars is None: @@ -358,13 +410,14 @@ def with_assigned_stmts(self, node, context=None, asspath=None): if isinstance(lst, (nodes.Tuple, nodes.List)): for item in lst.nodes: yield item -nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts) +nodes.With.assigned_stmts = with_assigned_stmts +@bases.yes_if_nothing_inferred def starred_assigned_stmts(self, node=None, context=None, asspath=None): stmt = self.statement() if not isinstance(stmt, (nodes.Assign, nodes.For)): - raise InferenceError() + raise exceptions.InferenceError() if isinstance(stmt, nodes.Assign): value = stmt.value @@ -372,24 +425,24 @@ def starred_assigned_stmts(self, node=None, context=None, asspath=None): if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1: # Too many starred arguments in the expression. - raise InferenceError() + raise exceptions.InferenceError() if context is None: - context = InferenceContext() + context = contextmod.InferenceContext() try: rhs = next(value.infer(context)) - except InferenceError: - yield YES + except exceptions.InferenceError: + yield util.YES return - if rhs is YES or not hasattr(rhs, 'elts'): + if rhs is util.YES or not hasattr(rhs, 'elts'): # Not interested in inferred values without elts. - yield YES + yield util.YES return elts = collections.deque(rhs.elts[:]) if len(lhs.elts) > len(rhs.elts): # a, *b, c = (1, 2) - raise InferenceError() + raise exceptions.InferenceError() # Unpack iteratively the values from the rhs of the assignment, # until the find the starred node. What will remain will @@ -408,8 +461,10 @@ def starred_assigned_stmts(self, node=None, context=None, asspath=None): elts.pop() continue # We're done - for elt in elts: - yield elt + packed = nodes.List() + packed.elts = elts + packed.parent = self + yield packed break nodes.Starred.assigned_stmts = starred_assigned_stmts diff --git a/pymode/libs/astroid/raw_building.py b/pymode/libs/astroid/raw_building.py index 99a026a7..aaaf52f2 100644 --- a/pymode/libs/astroid/raw_building.py +++ b/pymode/libs/astroid/raw_building.py @@ -19,9 +19,8 @@ (build_* functions) or from living object (object_build_* functions) """ -__docformat__ = "restructuredtext en" - import sys +import os from os.path import abspath from inspect import (getargspec, isdatadescriptor, isfunction, ismethod, ismethoddescriptor, isclass, isbuiltin, ismodule) @@ -35,6 +34,8 @@ MANAGER = AstroidManager() _CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types +_JYTHON = os.name == 'java' +_BUILTINS = vars(six.moves.builtins) def _io_discrepancy(member): # _io module names itself `io`: http://bugs.python.org/issue18602 @@ -48,6 +49,18 @@ def _attach_local_node(parent, node, name): node.name = name # needed by add_local_node parent.add_local_node(node) + +def _add_dunder_class(func, member): + """Add a __class__ member to the given func node, if we can determine it.""" + python_cls = member.__class__ + cls_name = getattr(python_cls, '__name__', None) + if not cls_name: + return + bases = [ancestor.__name__ for ancestor in python_cls.__bases__] + ast_klass = build_class(cls_name, bases, python_cls.__doc__) + func._instance_attrs['__class__'] = [ast_klass] + + _marker = object() def attach_dummy_node(node, name, object=_marker): @@ -170,6 +183,7 @@ def object_build_methoddescriptor(node, member, localname): # and empty argument list func.args.args = None node.add_local_node(func, localname) + _add_dunder_class(func, member) def _base_class_object_build(node, member, basenames, name=None, localname=None): """create astroid for a living class object, with a given set of base names @@ -196,7 +210,7 @@ def _base_class_object_build(node, member, basenames, name=None, localname=None) valnode.object = obj valnode.parent = klass valnode.lineno = 1 - klass.instance_attrs[name] = [valnode] + klass._instance_attrs[name] = [valnode] return klass @@ -228,7 +242,7 @@ def inspect_build(self, module, modname=None, path=None): except AttributeError: # in jython, java modules have no __doc__ (see #109562) node = build_module(modname) - node.file = node.path = path and abspath(path) or path + node.source_file = path and abspath(path) or path node.name = modname MANAGER.cache_module(node) node.package = hasattr(module, '__path__') @@ -273,7 +287,7 @@ def object_build(self, node, obj): continue if member in self._done: class_node = self._done[member] - if not class_node in node.locals.get(name, ()): + if not class_node in node._locals.get(name, ()): node.add_local_node(class_node, name) else: class_node = object_build_class(node, member, name) @@ -307,7 +321,8 @@ def imported_member(self, node, member, name): traceback.print_exc() modname = None if modname is None: - if name in ('__new__', '__subclasshook__'): + if (name in ('__new__', '__subclasshook__') + or (name in _BUILTINS and _JYTHON)): # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14) # >>> print object.__new__.__module__ # None @@ -315,7 +330,13 @@ def imported_member(self, node, member, name): else: attach_dummy_node(node, name, member) return True - if {'gtk': 'gtk._gtk'}.get(modname, modname) != self._module.__name__: + + real_name = { + 'gtk': 'gtk_gtk', + '_io': 'io', + }.get(modname, modname) + + if real_name != self._module.__name__: # check if it sounds valid and then add an import node, else use a # dummy node try: @@ -337,13 +358,16 @@ def _astroid_bootstrapping(astroid_builtin=None): # this boot strapping is necessary since we need the Const nodes to # inspect_build builtins, and then we can proxy Const if astroid_builtin is None: - from logilab.common.compat import builtins + from six.moves import builtins astroid_builtin = Astroid_BUILDER.inspect_build(builtins) for cls, node_cls in CONST_CLS.items(): if cls is type(None): proxy = build_class('NoneType') proxy.parent = astroid_builtin + elif cls is type(NotImplemented): + proxy = build_class('NotImplementedType') + proxy.parent = astroid_builtin else: proxy = astroid_builtin.getattr(cls.__name__)[0] if cls in (dict, list, set, tuple): diff --git a/pymode/libs/astroid/rebuilder.py b/pymode/libs/astroid/rebuilder.py index 013479a8..859b8280 100644 --- a/pymode/libs/astroid/rebuilder.py +++ b/pymode/libs/astroid/rebuilder.py @@ -20,10 +20,10 @@ """ import sys +import _ast from _ast import ( - Expr as Discard, Str, # binary operators - Add, BinOp, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor, + Add, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor, LShift, RShift, # logical operators And, Or, @@ -50,6 +50,9 @@ LShift: '<<', RShift: '>>', } +if sys.version_info >= (3, 5): + from _ast import MatMult + _BIN_OP_CLASSES[MatMult] = '@' _BOOL_OP_CLASSES = {And: 'and', Or: 'or', @@ -79,19 +82,11 @@ } REDIRECT = {'arguments': 'Arguments', - 'Attribute': 'Getattr', 'comprehension': 'Comprehension', - 'Call': 'CallFunc', - 'ClassDef': 'Class', "ListCompFor": 'Comprehension', "GenExprFor": 'Comprehension', 'excepthandler': 'ExceptHandler', - 'Expr': 'Discard', - 'FunctionDef': 'Function', - 'GeneratorExp': 'GenExpr', - 'ImportFrom': 'From', 'keyword': 'Keyword', - 'Repr': 'Backquote', } PY3K = sys.version_info >= (3, 0) PY34 = sys.version_info >= (3, 4) @@ -99,7 +94,7 @@ def _init_set_doc(node, newnode): newnode.doc = None try: - if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str): + if isinstance(node.body[0], _ast.Expr) and isinstance(node.body[0].value, _ast.Str): newnode.doc = node.body[0].value.s node.body = node.body[1:] @@ -122,9 +117,21 @@ def _create_yield_node(node, parent, rebuilder, factory): newnode = factory() _lineno_parent(node, newnode, parent) if node.value is not None: - newnode.value = rebuilder.visit(node.value, newnode) + newnode.value = rebuilder.visit(node.value, newnode, None) return newnode +def _visit_or_none(node, attr, visitor, parent, assign_ctx, visit='visit', + **kws): + """If the given node has an attribute, visits the attribute, and + otherwise returns None. + + """ + value = getattr(node, attr, None) + if value: + return getattr(visitor, visit)(value, parent, assign_ctx, **kws) + else: + return None + class TreeRebuilder(object): """Rebuilds the _ast tree to become an Astroid tree""" @@ -133,10 +140,9 @@ def __init__(self, manager): self._manager = manager self.asscontext = None self._global_names = [] - self._from_nodes = [] + self._import_from_nodes = [] self._delayed_assattr = [] self._visit_meths = {} - self._transform = manager.transform self._peepholer = astpeephole.ASTPeepholeOptimizer() def visit_module(self, node, modname, modpath, package): @@ -146,10 +152,10 @@ def visit_module(self, node, modname, modpath, package): newnode.parent = None _init_set_doc(node, newnode) newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.file = newnode.path = modpath - return self._transform(newnode) + newnode.source_file = modpath + return newnode - def visit(self, node, parent): + def visit(self, node, parent, assign_ctx=None): cls = node.__class__ if cls in self._visit_meths: visit_method = self._visit_meths[cls] @@ -158,7 +164,7 @@ def visit(self, node, parent): visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower() visit_method = getattr(self, visit_name) self._visit_meths[cls] = visit_method - return self._transform(visit_method(node, parent)) + return visit_method(node, parent, assign_ctx) def _save_assignment(self, node, name=None): """save assignement situation since node.parent is not available yet""" @@ -167,15 +173,14 @@ def _save_assignment(self, node, name=None): else: node.parent.set_local(node.name, node) - - def visit_arguments(self, node, parent): + def visit_arguments(self, node, parent, assign_ctx=None): """visit a Arguments node by returning a fresh instance of it""" newnode = new.Arguments() newnode.parent = parent - self.asscontext = "Ass" - newnode.args = [self.visit(child, newnode) for child in node.args] - self.asscontext = None - newnode.defaults = [self.visit(child, newnode) for child in node.defaults] + newnode.args = [self.visit(child, newnode, "Assign") + for child in node.args] + newnode.defaults = [self.visit(child, newnode, assign_ctx) + for child in node.defaults] newnode.kwonlyargs = [] newnode.kw_defaults = [] vararg, kwarg = node.vararg, node.kwarg @@ -185,21 +190,21 @@ def visit_arguments(self, node, parent): if PY34: if vararg.annotation: newnode.varargannotation = self.visit(vararg.annotation, - newnode) + newnode, assign_ctx) vararg = vararg.arg elif PY3K and node.varargannotation: newnode.varargannotation = self.visit(node.varargannotation, - newnode) + newnode, assign_ctx) if kwarg: if PY34: if kwarg.annotation: newnode.kwargannotation = self.visit(kwarg.annotation, - newnode) + newnode, assign_ctx) kwarg = kwarg.arg elif PY3K: if node.kwargannotation: newnode.kwargannotation = self.visit(node.kwargannotation, - newnode) + newnode, assign_ctx) newnode.vararg = vararg newnode.kwarg = kwarg # save argument names in locals: @@ -209,81 +214,59 @@ def visit_arguments(self, node, parent): newnode.parent.set_local(kwarg, newnode) return newnode - def visit_assattr(self, node, parent): + def visit_assignattr(self, node, parent, assign_ctx=None): """visit a AssAttr node by returning a fresh instance of it""" - assc, self.asscontext = self.asscontext, None - newnode = new.AssAttr() + newnode = new.AssignAttr() _lineno_parent(node, newnode, parent) - newnode.expr = self.visit(node.expr, newnode) - self.asscontext = assc + newnode.expr = self.visit(node.expr, newnode, assign_ctx) self._delayed_assattr.append(newnode) return newnode - def visit_assert(self, node, parent): + def visit_assert(self, node, parent, assign_ctx=None): """visit a Assert node by returning a fresh instance of it""" newnode = new.Assert() _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) + newnode.test = self.visit(node.test, newnode, assign_ctx) if node.msg is not None: - newnode.fail = self.visit(node.msg, newnode) + newnode.fail = self.visit(node.msg, newnode, assign_ctx) return newnode - def visit_assign(self, node, parent): + def visit_assign(self, node, parent, assign_ctx=None): """visit a Assign node by returning a fresh instance of it""" newnode = new.Assign() _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None - newnode.value = self.visit(node.value, newnode) - # set some function or metaclass infos XXX explain ? - klass = newnode.parent.frame() - if (isinstance(klass, new.Class) - and isinstance(newnode.value, new.CallFunc) - and isinstance(newnode.value.func, new.Name)): - func_name = newnode.value.func.name - for ass_node in newnode.targets: - try: - meth = klass[ass_node.name] - if isinstance(meth, new.Function): - if func_name in ('classmethod', 'staticmethod'): - meth.type = func_name - elif func_name == 'classproperty': # see lgc.decorators - meth.type = 'classmethod' - meth.extra_decorators.append(newnode.value) - except (AttributeError, KeyError): - continue - return newnode - - def visit_assname(self, node, parent, node_name=None): + newnode.targets = [self.visit(child, newnode, "Assign") + for child in node.targets] + newnode.value = self.visit(node.value, newnode, None) + return newnode + + def visit_assignname(self, node, parent, assign_ctx=None, node_name=None): '''visit a node and return a AssName node''' - newnode = new.AssName() + newnode = new.AssignName() _set_infos(node, newnode, parent) newnode.name = node_name self._save_assignment(newnode) return newnode - def visit_augassign(self, node, parent): + def visit_augassign(self, node, parent, assign_ctx=None): """visit a AugAssign node by returning a fresh instance of it""" newnode = new.AugAssign() _lineno_parent(node, newnode, parent) newnode.op = _BIN_OP_CLASSES[node.op.__class__] + "=" - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.value = self.visit(node.value, newnode) + newnode.target = self.visit(node.target, newnode, "Assign") + newnode.value = self.visit(node.value, newnode, None) return newnode - def visit_backquote(self, node, parent): + def visit_repr(self, node, parent, assign_ctx=None): """visit a Backquote node by returning a fresh instance of it""" - newnode = new.Backquote() + newnode = new.Repr() _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) + newnode.value = self.visit(node.value, newnode, assign_ctx) return newnode - def visit_binop(self, node, parent): + def visit_binop(self, node, parent, assign_ctx=None): """visit a BinOp node by returning a fresh instance of it""" - if isinstance(node.left, BinOp) and self._manager.optimize_ast: + if isinstance(node.left, _ast.BinOp) and self._manager.optimize_ast: # Optimize BinOp operations in order to remove # redundant recursion. For instance, if the # following code is parsed in order to obtain @@ -296,264 +279,299 @@ def visit_binop(self, node, parent): # problem for the correctness of the program). # # ("a" + "b" + # one thousand more + "c") - newnode = self._peepholer.optimize_binop(node) - if newnode: - _lineno_parent(node, newnode, parent) - return newnode + optimized = self._peepholer.optimize_binop(node) + if optimized: + _lineno_parent(node, optimized, parent) + return optimized newnode = new.BinOp() _lineno_parent(node, newnode, parent) - newnode.left = self.visit(node.left, newnode) - newnode.right = self.visit(node.right, newnode) + newnode.left = self.visit(node.left, newnode, assign_ctx) + newnode.right = self.visit(node.right, newnode, assign_ctx) newnode.op = _BIN_OP_CLASSES[node.op.__class__] return newnode - def visit_boolop(self, node, parent): + def visit_boolop(self, node, parent, assign_ctx=None): """visit a BoolOp node by returning a fresh instance of it""" newnode = new.BoolOp() _lineno_parent(node, newnode, parent) - newnode.values = [self.visit(child, newnode) for child in node.values] + newnode.values = [self.visit(child, newnode, assign_ctx) + for child in node.values] newnode.op = _BOOL_OP_CLASSES[node.op.__class__] return newnode - def visit_break(self, node, parent): + def visit_break(self, node, parent, assign_ctx=None): """visit a Break node by returning a fresh instance of it""" newnode = new.Break() _set_infos(node, newnode, parent) return newnode - def visit_callfunc(self, node, parent): + def visit_call(self, node, parent, assign_ctx=None): """visit a CallFunc node by returning a fresh instance of it""" - newnode = new.CallFunc() - _lineno_parent(node, newnode, parent) - newnode.func = self.visit(node.func, newnode) - newnode.args = [self.visit(child, newnode) for child in node.args] - if node.starargs is not None: - newnode.starargs = self.visit(node.starargs, newnode) - if node.kwargs is not None: - newnode.kwargs = self.visit(node.kwargs, newnode) - for child in node.keywords: - newnode.args.append(self.visit(child, newnode)) + newnode = new.Call() + _lineno_parent(node, newnode, parent) + newnode.func = self.visit(node.func, newnode, assign_ctx) + args = [self.visit(child, newnode, assign_ctx) + for child in node.args] + + starargs = _visit_or_none(node, 'starargs', self, newnode, + assign_ctx) + kwargs = _visit_or_none(node, 'kwargs', self, newnode, + assign_ctx) + keywords = None + if node.keywords: + keywords = [self.visit(child, newnode, assign_ctx) + for child in node.keywords] + + if starargs: + new_starargs = new.Starred() + new_starargs.col_offset = starargs.col_offset + new_starargs.lineno = starargs.lineno + new_starargs.parent = starargs.parent + new_starargs.value = starargs + args.append(new_starargs) + if kwargs: + new_kwargs = new.Keyword() + new_kwargs.arg = None + new_kwargs.col_offset = kwargs.col_offset + new_kwargs.lineno = kwargs.lineno + new_kwargs.parent = kwargs.parent + new_kwargs.value = kwargs + if keywords: + keywords.append(new_kwargs) + else: + keywords = [new_kwargs] + + newnode.args = args + newnode.keywords = keywords return newnode - def visit_class(self, node, parent): + def visit_classdef(self, node, parent, assign_ctx=None): """visit a Class node to become astroid""" - newnode = new.Class(node.name, None) + newnode = new.ClassDef(node.name, None) _lineno_parent(node, newnode, parent) _init_set_doc(node, newnode) - newnode.bases = [self.visit(child, newnode) for child in node.bases] - newnode.body = [self.visit(child, newnode) for child in node.body] - if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6 - newnode.decorators = self.visit_decorators(node, newnode) + newnode.bases = [self.visit(child, newnode, assign_ctx) + for child in node.bases] + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] + if node.decorator_list: + newnode.decorators = self.visit_decorators(node, newnode, assign_ctx) newnode.parent.frame().set_local(newnode.name, newnode) return newnode - def visit_const(self, node, parent): + def visit_const(self, node, parent, assign_ctx=None): """visit a Const node by returning a fresh instance of it""" newnode = new.Const(node.value) _set_infos(node, newnode, parent) return newnode - def visit_continue(self, node, parent): + def visit_continue(self, node, parent, assign_ctx=None): """visit a Continue node by returning a fresh instance of it""" newnode = new.Continue() _set_infos(node, newnode, parent) return newnode - def visit_compare(self, node, parent): + def visit_compare(self, node, parent, assign_ctx=None): """visit a Compare node by returning a fresh instance of it""" newnode = new.Compare() _lineno_parent(node, newnode, parent) - newnode.left = self.visit(node.left, newnode) - newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode)) + newnode.left = self.visit(node.left, newnode, assign_ctx) + newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode, assign_ctx)) for (op, expr) in zip(node.ops, node.comparators)] return newnode - def visit_comprehension(self, node, parent): + def visit_comprehension(self, node, parent, assign_ctx=None): """visit a Comprehension node by returning a fresh instance of it""" newnode = new.Comprehension() newnode.parent = parent - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.ifs = [self.visit(child, newnode) for child in node.ifs] + newnode.target = self.visit(node.target, newnode, 'Assign') + newnode.iter = self.visit(node.iter, newnode, None) + newnode.ifs = [self.visit(child, newnode, None) + for child in node.ifs] return newnode - def visit_decorators(self, node, parent): + def visit_decorators(self, node, parent, assign_ctx=None): """visit a Decorators node by returning a fresh instance of it""" # /!\ node is actually a _ast.Function node while # parent is a astroid.nodes.Function node newnode = new.Decorators() _lineno_parent(node, newnode, parent) - if 'decorators' in node._fields: # py < 2.6, i.e. 2.5 - decorators = node.decorators - else: - decorators = node.decorator_list - newnode.nodes = [self.visit(child, newnode) for child in decorators] + decorators = node.decorator_list + newnode.nodes = [self.visit(child, newnode, assign_ctx) + for child in decorators] return newnode - def visit_delete(self, node, parent): + def visit_delete(self, node, parent, assign_ctx=None): """visit a Delete node by returning a fresh instance of it""" newnode = new.Delete() _lineno_parent(node, newnode, parent) - self.asscontext = "Del" - newnode.targets = [self.visit(child, newnode) for child in node.targets] - self.asscontext = None + newnode.targets = [self.visit(child, newnode, 'Del') + for child in node.targets] return newnode - def visit_dict(self, node, parent): + def _visit_dict_items(self, node, parent, newnode, assign_ctx): + for key, value in zip(node.keys, node.values): + rebuilt_value = self.visit(value, newnode, assign_ctx) + if not key: + # Python 3.5 and extended unpacking + rebuilt_key = new.DictUnpack() + rebuilt_key.lineno = rebuilt_value.lineno + rebuilt_key.col_offset = rebuilt_value.col_offset + rebuilt_key.parent = rebuilt_value.parent + else: + rebuilt_key = self.visit(key, newnode, assign_ctx) + yield rebuilt_key, rebuilt_value + + def visit_dict(self, node, parent, assign_ctx=None): """visit a Dict node by returning a fresh instance of it""" newnode = new.Dict() _lineno_parent(node, newnode, parent) - newnode.items = [(self.visit(key, newnode), self.visit(value, newnode)) - for key, value in zip(node.keys, node.values)] + newnode.items = list(self._visit_dict_items(node, parent, newnode, assign_ctx)) return newnode - def visit_dictcomp(self, node, parent): + def visit_dictcomp(self, node, parent, assign_ctx=None): """visit a DictComp node by returning a fresh instance of it""" newnode = new.DictComp() _lineno_parent(node, newnode, parent) - newnode.key = self.visit(node.key, newnode) - newnode.value = self.visit(node.value, newnode) - newnode.generators = [self.visit(child, newnode) + newnode.key = self.visit(node.key, newnode, assign_ctx) + newnode.value = self.visit(node.value, newnode, assign_ctx) + newnode.generators = [self.visit(child, newnode, assign_ctx) for child in node.generators] return newnode - def visit_discard(self, node, parent): + def visit_expr(self, node, parent, assign_ctx=None): """visit a Discard node by returning a fresh instance of it""" - newnode = new.Discard() + newnode = new.Expr() _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) + newnode.value = self.visit(node.value, newnode, assign_ctx) return newnode - def visit_ellipsis(self, node, parent): + def visit_ellipsis(self, node, parent, assign_ctx=None): """visit an Ellipsis node by returning a fresh instance of it""" newnode = new.Ellipsis() _set_infos(node, newnode, parent) return newnode - def visit_emptynode(self, node, parent): + def visit_emptynode(self, node, parent, assign_ctx=None): """visit an EmptyNode node by returning a fresh instance of it""" newnode = new.EmptyNode() _set_infos(node, newnode, parent) return newnode - def visit_excepthandler(self, node, parent): + def visit_excepthandler(self, node, parent, assign_ctx=None): """visit an ExceptHandler node by returning a fresh instance of it""" newnode = new.ExceptHandler() _lineno_parent(node, newnode, parent) if node.type is not None: - newnode.type = self.visit(node.type, newnode) + newnode.type = self.visit(node.type, newnode, assign_ctx) if node.name is not None: # /!\ node.name can be a tuple - self.asscontext = "Ass" - newnode.name = self.visit(node.name, newnode) - self.asscontext = None - newnode.body = [self.visit(child, newnode) for child in node.body] + newnode.name = self.visit(node.name, newnode, 'Assign') + newnode.body = [self.visit(child, newnode, None) + for child in node.body] return newnode - def visit_exec(self, node, parent): + def visit_exec(self, node, parent, assign_ctx=None): """visit an Exec node by returning a fresh instance of it""" newnode = new.Exec() _lineno_parent(node, newnode, parent) newnode.expr = self.visit(node.body, newnode) if node.globals is not None: - newnode.globals = self.visit(node.globals, newnode) + newnode.globals = self.visit(node.globals, newnode, + assign_ctx) if node.locals is not None: - newnode.locals = self.visit(node.locals, newnode) + newnode.locals = self.visit(node.locals, newnode, + assign_ctx) return newnode - def visit_extslice(self, node, parent): + def visit_extslice(self, node, parent, assign_ctx=None): """visit an ExtSlice node by returning a fresh instance of it""" newnode = new.ExtSlice() newnode.parent = parent - newnode.dims = [self.visit(dim, newnode) for dim in node.dims] + newnode.dims = [self.visit(dim, newnode, assign_ctx) + for dim in node.dims] return newnode - def visit_for(self, node, parent): + def _visit_for(self, cls, node, parent, assign_ctx=None): """visit a For node by returning a fresh instance of it""" - newnode = new.For() + newnode = cls() _lineno_parent(node, newnode, parent) - self.asscontext = "Ass" - newnode.target = self.visit(node.target, newnode) - self.asscontext = None - newnode.iter = self.visit(node.iter, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] + newnode.target = self.visit(node.target, newnode, "Assign") + newnode.iter = self.visit(node.iter, newnode, None) + newnode.body = [self.visit(child, newnode, None) + for child in node.body] + newnode.orelse = [self.visit(child, newnode, None) + for child in node.orelse] return newnode - def visit_from(self, node, parent): + def visit_for(self, node, parent, assign_ctx=None): + return self._visit_for(new.For, node, parent, + assign_ctx=assign_ctx) + def visit_importfrom(self, node, parent, assign_ctx=None): """visit a From node by returning a fresh instance of it""" names = [(alias.name, alias.asname) for alias in node.names] - newnode = new.From(node.module or '', names, node.level or None) + newnode = new.ImportFrom(node.module or '', names, node.level or None) _set_infos(node, newnode, parent) # store From names to add them to locals after building - self._from_nodes.append(newnode) + self._import_from_nodes.append(newnode) return newnode - def visit_function(self, node, parent): - """visit an Function node to become astroid""" + def _visit_functiondef(self, cls, node, parent, assign_ctx=None): + """visit an FunctionDef node to become astroid""" self._global_names.append({}) - newnode = new.Function(node.name, None) + newnode = cls(node.name, None) _lineno_parent(node, newnode, parent) _init_set_doc(node, newnode) - newnode.args = self.visit(node.args, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - if 'decorators' in node._fields: # py < 2.6 - attr = 'decorators' - else: - attr = 'decorator_list' - decorators = getattr(node, attr) + newnode.args = self.visit(node.args, newnode, assign_ctx) + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] + decorators = node.decorator_list if decorators: - newnode.decorators = self.visit_decorators(node, newnode) + newnode.decorators = self.visit_decorators( + node, newnode, assign_ctx) if PY3K and node.returns: - newnode.returns = self.visit(node.returns, newnode) + newnode.returns = self.visit(node.returns, newnode, + assign_ctx) self._global_names.pop() frame = newnode.parent.frame() - if isinstance(frame, new.Class): - if newnode.name == '__new__': - newnode._type = 'classmethod' - else: - newnode._type = 'method' - if newnode.decorators is not None: - for decorator_expr in newnode.decorators.nodes: - if isinstance(decorator_expr, new.Name): - if decorator_expr.name in ('classmethod', 'staticmethod'): - newnode._type = decorator_expr.name - elif decorator_expr.name == 'classproperty': - newnode._type = 'classmethod' frame.set_local(newnode.name, newnode) return newnode - def visit_genexpr(self, node, parent): + def visit_functiondef(self, node, parent, assign_ctx=None): + return self._visit_functiondef(new.FunctionDef, node, parent, + assign_ctx=assign_ctx) + + def visit_generatorexp(self, node, parent, assign_ctx=None): """visit a GenExpr node by returning a fresh instance of it""" - newnode = new.GenExpr() + newnode = new.GeneratorExp() _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) for child in node.generators] + newnode.elt = self.visit(node.elt, newnode, assign_ctx) + newnode.generators = [self.visit(child, newnode, assign_ctx) + for child in node.generators] return newnode - def visit_getattr(self, node, parent): + def visit_attribute(self, node, parent, assign_ctx=None): """visit a Getattr node by returning a fresh instance of it""" - if self.asscontext == "Del": + # pylint: disable=redefined-variable-type + if assign_ctx == "Del": # FIXME : maybe we should reintroduce and visit_delattr ? # for instance, deactivating asscontext newnode = new.DelAttr() - elif self.asscontext == "Ass": + elif assign_ctx == "Assign": # FIXME : maybe we should call visit_assattr ? - newnode = new.AssAttr() - self._delayed_assattr.append(newnode) + # Prohibit a local save if we are in an ExceptHandler. + newnode = new.AssignAttr() + if not isinstance(parent, new.ExceptHandler): + self._delayed_assattr.append(newnode) else: - newnode = new.Getattr() + newnode = new.Attribute() _lineno_parent(node, newnode, parent) - asscontext, self.asscontext = self.asscontext, None - newnode.expr = self.visit(node.value, newnode) - self.asscontext = asscontext + newnode.expr = self.visit(node.value, newnode, None) newnode.attrname = node.attr return newnode - def visit_global(self, node, parent): + def visit_global(self, node, parent, assign_ctx=None): """visit an Global node to become astroid""" newnode = new.Global(node.names) _set_infos(node, newnode, parent) @@ -562,25 +580,27 @@ def visit_global(self, node, parent): self._global_names[-1].setdefault(name, []).append(newnode) return newnode - def visit_if(self, node, parent): + def visit_if(self, node, parent, assign_ctx=None): """visit a If node by returning a fresh instance of it""" newnode = new.If() _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] + newnode.test = self.visit(node.test, newnode, assign_ctx) + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] + newnode.orelse = [self.visit(child, newnode, assign_ctx) + for child in node.orelse] return newnode - def visit_ifexp(self, node, parent): + def visit_ifexp(self, node, parent, assign_ctx=None): """visit a IfExp node by returning a fresh instance of it""" newnode = new.IfExp() _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = self.visit(node.body, newnode) - newnode.orelse = self.visit(node.orelse, newnode) + newnode.test = self.visit(node.test, newnode, assign_ctx) + newnode.body = self.visit(node.body, newnode, assign_ctx) + newnode.orelse = self.visit(node.orelse, newnode, assign_ctx) return newnode - def visit_import(self, node, parent): + def visit_import(self, node, parent, assign_ctx=None): """visit a Import node by returning a fresh instance of it""" newnode = new.Import() _set_infos(node, newnode, parent) @@ -591,53 +611,54 @@ def visit_import(self, node, parent): newnode.parent.set_local(name.split('.')[0], newnode) return newnode - def visit_index(self, node, parent): + def visit_index(self, node, parent, assign_ctx=None): """visit a Index node by returning a fresh instance of it""" newnode = new.Index() newnode.parent = parent - newnode.value = self.visit(node.value, newnode) + newnode.value = self.visit(node.value, newnode, assign_ctx) return newnode - def visit_keyword(self, node, parent): + def visit_keyword(self, node, parent, assign_ctx=None): """visit a Keyword node by returning a fresh instance of it""" newnode = new.Keyword() newnode.parent = parent newnode.arg = node.arg - newnode.value = self.visit(node.value, newnode) + newnode.value = self.visit(node.value, newnode, assign_ctx) return newnode - def visit_lambda(self, node, parent): + def visit_lambda(self, node, parent, assign_ctx=None): """visit a Lambda node by returning a fresh instance of it""" newnode = new.Lambda() _lineno_parent(node, newnode, parent) - newnode.args = self.visit(node.args, newnode) - newnode.body = self.visit(node.body, newnode) + newnode.args = self.visit(node.args, newnode, assign_ctx) + newnode.body = self.visit(node.body, newnode, assign_ctx) return newnode - def visit_list(self, node, parent): + def visit_list(self, node, parent, assign_ctx=None): """visit a List node by returning a fresh instance of it""" newnode = new.List() _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] + newnode.elts = [self.visit(child, newnode, assign_ctx) + for child in node.elts] return newnode - def visit_listcomp(self, node, parent): + def visit_listcomp(self, node, parent, assign_ctx=None): """visit a ListComp node by returning a fresh instance of it""" newnode = new.ListComp() _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) + newnode.elt = self.visit(node.elt, newnode, assign_ctx) + newnode.generators = [self.visit(child, newnode, assign_ctx) for child in node.generators] return newnode - def visit_name(self, node, parent): + def visit_name(self, node, parent, assign_ctx=None): """visit a Name node by returning a fresh instance of it""" # True and False can be assigned to something in py2x, so we have to # check first the asscontext - if self.asscontext == "Del": + # pylint: disable=redefined-variable-type + if assign_ctx == "Del": newnode = new.DelName() - elif self.asscontext is not None: # Ass - assert self.asscontext == "Ass" + elif assign_ctx is not None: # Ass newnode = new.AssName() elif node.id in CONST_NAME_TRANSFORMS: newnode = new.Const(CONST_NAME_TRANSFORMS[node.id]) @@ -648,279 +669,321 @@ def visit_name(self, node, parent): _lineno_parent(node, newnode, parent) newnode.name = node.id # XXX REMOVE me : - if self.asscontext in ('Del', 'Ass'): # 'Aug' ?? + if assign_ctx in ('Del', 'Assign'): # 'Aug' ?? self._save_assignment(newnode) return newnode - def visit_bytes(self, node, parent): + def visit_bytes(self, node, parent, assign_ctx=None): """visit a Bytes node by returning a fresh instance of Const""" newnode = new.Const(node.s) _set_infos(node, newnode, parent) return newnode - def visit_num(self, node, parent): + def visit_num(self, node, parent, assign_ctx=None): """visit a Num node by returning a fresh instance of Const""" newnode = new.Const(node.n) _set_infos(node, newnode, parent) return newnode - def visit_pass(self, node, parent): + def visit_pass(self, node, parent, assign_ctx=None): """visit a Pass node by returning a fresh instance of it""" newnode = new.Pass() _set_infos(node, newnode, parent) return newnode - def visit_str(self, node, parent): + def visit_str(self, node, parent, assign_ctx=None): """visit a Str node by returning a fresh instance of Const""" newnode = new.Const(node.s) _set_infos(node, newnode, parent) return newnode - def visit_print(self, node, parent): + def visit_print(self, node, parent, assign_ctx=None): """visit a Print node by returning a fresh instance of it""" newnode = new.Print() _lineno_parent(node, newnode, parent) newnode.nl = node.nl if node.dest is not None: - newnode.dest = self.visit(node.dest, newnode) - newnode.values = [self.visit(child, newnode) for child in node.values] + newnode.dest = self.visit(node.dest, newnode, assign_ctx) + newnode.values = [self.visit(child, newnode, assign_ctx) + for child in node.values] return newnode - def visit_raise(self, node, parent): + def visit_raise(self, node, parent, assign_ctx=None): """visit a Raise node by returning a fresh instance of it""" newnode = new.Raise() _lineno_parent(node, newnode, parent) if node.type is not None: - newnode.exc = self.visit(node.type, newnode) + newnode.exc = self.visit(node.type, newnode, assign_ctx) if node.inst is not None: - newnode.inst = self.visit(node.inst, newnode) + newnode.inst = self.visit(node.inst, newnode, assign_ctx) if node.tback is not None: - newnode.tback = self.visit(node.tback, newnode) + newnode.tback = self.visit(node.tback, newnode, assign_ctx) return newnode - def visit_return(self, node, parent): + def visit_return(self, node, parent, assign_ctx=None): """visit a Return node by returning a fresh instance of it""" newnode = new.Return() _lineno_parent(node, newnode, parent) if node.value is not None: - newnode.value = self.visit(node.value, newnode) + newnode.value = self.visit(node.value, newnode, assign_ctx) return newnode - def visit_set(self, node, parent): + def visit_set(self, node, parent, assign_ctx=None): """visit a Set node by returning a fresh instance of it""" newnode = new.Set() _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] + newnode.elts = [self.visit(child, newnode, assign_ctx) + for child in node.elts] return newnode - def visit_setcomp(self, node, parent): + def visit_setcomp(self, node, parent, assign_ctx=None): """visit a SetComp node by returning a fresh instance of it""" newnode = new.SetComp() _lineno_parent(node, newnode, parent) - newnode.elt = self.visit(node.elt, newnode) - newnode.generators = [self.visit(child, newnode) + newnode.elt = self.visit(node.elt, newnode, assign_ctx) + newnode.generators = [self.visit(child, newnode, assign_ctx) for child in node.generators] return newnode - def visit_slice(self, node, parent): + def visit_slice(self, node, parent, assign_ctx=None): """visit a Slice node by returning a fresh instance of it""" newnode = new.Slice() newnode.parent = parent if node.lower is not None: - newnode.lower = self.visit(node.lower, newnode) + newnode.lower = self.visit(node.lower, newnode, assign_ctx) if node.upper is not None: - newnode.upper = self.visit(node.upper, newnode) + newnode.upper = self.visit(node.upper, newnode, assign_ctx) if node.step is not None: - newnode.step = self.visit(node.step, newnode) + newnode.step = self.visit(node.step, newnode, assign_ctx) return newnode - def visit_subscript(self, node, parent): + def visit_subscript(self, node, parent, assign_ctx=None): """visit a Subscript node by returning a fresh instance of it""" newnode = new.Subscript() _lineno_parent(node, newnode, parent) - subcontext, self.asscontext = self.asscontext, None - newnode.value = self.visit(node.value, newnode) - newnode.slice = self.visit(node.slice, newnode) - self.asscontext = subcontext + newnode.value = self.visit(node.value, newnode, None) + newnode.slice = self.visit(node.slice, newnode, None) return newnode - def visit_tryexcept(self, node, parent): + def visit_tryexcept(self, node, parent, assign_ctx=None): """visit a TryExcept node by returning a fresh instance of it""" newnode = new.TryExcept() _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.handlers = [self.visit(child, newnode) for child in node.handlers] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] + newnode.handlers = [self.visit(child, newnode, assign_ctx) + for child in node.handlers] + newnode.orelse = [self.visit(child, newnode, assign_ctx) + for child in node.orelse] return newnode - def visit_tryfinally(self, node, parent): + def visit_tryfinally(self, node, parent, assign_ctx=None): """visit a TryFinally node by returning a fresh instance of it""" newnode = new.TryFinally() _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] + newnode.finalbody = [self.visit(n, newnode, assign_ctx) + for n in node.finalbody] return newnode - def visit_tuple(self, node, parent): + def visit_tuple(self, node, parent, assign_ctx=None): """visit a Tuple node by returning a fresh instance of it""" newnode = new.Tuple() _lineno_parent(node, newnode, parent) - newnode.elts = [self.visit(child, newnode) for child in node.elts] + newnode.elts = [self.visit(child, newnode, assign_ctx) + for child in node.elts] return newnode - def visit_unaryop(self, node, parent): + def visit_unaryop(self, node, parent, assign_ctx=None): """visit a UnaryOp node by returning a fresh instance of it""" newnode = new.UnaryOp() _lineno_parent(node, newnode, parent) - newnode.operand = self.visit(node.operand, newnode) + newnode.operand = self.visit(node.operand, newnode, assign_ctx) newnode.op = _UNARY_OP_CLASSES[node.op.__class__] return newnode - def visit_while(self, node, parent): + def visit_while(self, node, parent, assign_ctx=None): """visit a While node by returning a fresh instance of it""" newnode = new.While() _lineno_parent(node, newnode, parent) - newnode.test = self.visit(node.test, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] + newnode.test = self.visit(node.test, newnode, assign_ctx) + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] + newnode.orelse = [self.visit(child, newnode, assign_ctx) + for child in node.orelse] return newnode - def visit_with(self, node, parent): + def visit_with(self, node, parent, assign_ctx=None): newnode = new.With() _lineno_parent(node, newnode, parent) - expr = self.visit(node.context_expr, newnode) - self.asscontext = "Ass" + expr = self.visit(node.context_expr, newnode, assign_ctx) if node.optional_vars is not None: - vars = self.visit(node.optional_vars, newnode) + vars = self.visit(node.optional_vars, newnode, 'Assign') else: vars = None self.asscontext = None newnode.items = [(expr, vars)] - newnode.body = [self.visit(child, newnode) for child in node.body] + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] return newnode - def visit_yield(self, node, parent): + def visit_yield(self, node, parent, assign_ctx=None): """visit a Yield node by returning a fresh instance of it""" return _create_yield_node(node, parent, self, new.Yield) class TreeRebuilder3k(TreeRebuilder): """extend and overwrite TreeRebuilder for python3k""" - def visit_arg(self, node, parent): + def visit_arg(self, node, parent, assign_ctx=None): """visit a arg node by returning a fresh AssName instance""" - # the node is coming from py>=3.0, but we use AssName in py2.x - # XXX or we should instead introduce a Arg node in astroid ? - return self.visit_assname(node, parent, node.arg) + # TODO(cpopa): introduce an Arg node instead of using AssignName. + return self.visit_assignname(node, parent, assign_ctx, node.arg) - def visit_nameconstant(self, node, parent): + def visit_nameconstant(self, node, parent, assign_ctx=None): # in Python 3.4 we have NameConstant for True / False / None newnode = new.Const(node.value) _set_infos(node, newnode, parent) return newnode - def visit_arguments(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_arguments(node, parent) - self.asscontext = "Ass" - newnode.kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs] - self.asscontext = None - newnode.kw_defaults = [self.visit(child, newnode) if child else None for child in node.kw_defaults] + def visit_arguments(self, node, parent, assign_ctx=None): + newnode = super(TreeRebuilder3k, self).visit_arguments(node, parent, assign_ctx) + newnode.kwonlyargs = [self.visit(child, newnode, 'Assign') + for child in node.kwonlyargs] + newnode.kw_defaults = [self.visit(child, newnode, None) + if child else None for child in node.kw_defaults] newnode.annotations = [ - self.visit(arg.annotation, newnode) if arg.annotation else None + self.visit(arg.annotation, newnode, None) if arg.annotation else None for arg in node.args] return newnode - def visit_excepthandler(self, node, parent): + def visit_excepthandler(self, node, parent, assign_ctx=None): """visit an ExceptHandler node by returning a fresh instance of it""" newnode = new.ExceptHandler() _lineno_parent(node, newnode, parent) if node.type is not None: - newnode.type = self.visit(node.type, newnode) + newnode.type = self.visit(node.type, newnode, assign_ctx) if node.name is not None: - newnode.name = self.visit_assname(node, newnode, node.name) - newnode.body = [self.visit(child, newnode) for child in node.body] + newnode.name = self.visit_assignname(node, newnode, 'Assign', node.name) + newnode.body = [self.visit(child, newnode, None) + for child in node.body] return newnode - def visit_nonlocal(self, node, parent): + def visit_nonlocal(self, node, parent, assign_ctx=None): """visit a Nonlocal node and return a new instance of it""" newnode = new.Nonlocal(node.names) _set_infos(node, newnode, parent) return newnode - def visit_raise(self, node, parent): + def visit_raise(self, node, parent, assign_ctx=None): """visit a Raise node by returning a fresh instance of it""" newnode = new.Raise() _lineno_parent(node, newnode, parent) # no traceback; anyway it is not used in Pylint if node.exc is not None: - newnode.exc = self.visit(node.exc, newnode) + newnode.exc = self.visit(node.exc, newnode, assign_ctx) if node.cause is not None: - newnode.cause = self.visit(node.cause, newnode) + newnode.cause = self.visit(node.cause, newnode, assign_ctx) return newnode - def visit_starred(self, node, parent): + def visit_starred(self, node, parent, assign_ctx=None): """visit a Starred node and return a new instance of it""" newnode = new.Starred() _lineno_parent(node, newnode, parent) - newnode.value = self.visit(node.value, newnode) + newnode.value = self.visit(node.value, newnode, assign_ctx) return newnode - def visit_try(self, node, parent): + def visit_try(self, node, parent, assign_ctx=None): # python 3.3 introduce a new Try node replacing TryFinally/TryExcept nodes + # pylint: disable=redefined-variable-type if node.finalbody: newnode = new.TryFinally() _lineno_parent(node, newnode, parent) - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] + newnode.finalbody = [self.visit(n, newnode, assign_ctx) + for n in node.finalbody] if node.handlers: excnode = new.TryExcept() _lineno_parent(node, excnode, newnode) - excnode.body = [self.visit(child, excnode) for child in node.body] - excnode.handlers = [self.visit(child, excnode) for child in node.handlers] - excnode.orelse = [self.visit(child, excnode) for child in node.orelse] + excnode.body = [self.visit(child, excnode, assign_ctx) + for child in node.body] + excnode.handlers = [self.visit(child, excnode, assign_ctx) + for child in node.handlers] + excnode.orelse = [self.visit(child, excnode, assign_ctx) + for child in node.orelse] newnode.body = [excnode] else: - newnode.body = [self.visit(child, newnode) for child in node.body] + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] elif node.handlers: newnode = new.TryExcept() _lineno_parent(node, newnode, parent) - newnode.body = [self.visit(child, newnode) for child in node.body] - newnode.handlers = [self.visit(child, newnode) for child in node.handlers] - newnode.orelse = [self.visit(child, newnode) for child in node.orelse] + newnode.body = [self.visit(child, newnode, assign_ctx) + for child in node.body] + newnode.handlers = [self.visit(child, newnode, assign_ctx) + for child in node.handlers] + newnode.orelse = [self.visit(child, newnode, assign_ctx) + for child in node.orelse] return newnode - def visit_with(self, node, parent): + def _visit_with(self, cls, node, parent, assign_ctx=None): if 'items' not in node._fields: # python < 3.3 - return super(TreeRebuilder3k, self).visit_with(node, parent) + return super(TreeRebuilder3k, self).visit_with(node, parent, + assign_ctx) - newnode = new.With() + newnode = cls() _lineno_parent(node, newnode, parent) def visit_child(child): expr = self.visit(child.context_expr, newnode) - self.asscontext = 'Ass' if child.optional_vars: - var = self.visit(child.optional_vars, newnode) + var = self.visit(child.optional_vars, newnode, + 'Assign') else: var = None - self.asscontext = None return expr, var newnode.items = [visit_child(child) for child in node.items] - newnode.body = [self.visit(child, newnode) for child in node.body] + newnode.body = [self.visit(child, newnode, None) + for child in node.body] return newnode - def visit_yieldfrom(self, node, parent): + def visit_with(self, node, parent, assign_ctx=None): + return self._visit_with(new.With, node, parent, assign_ctx=assign_ctx) + + def visit_yieldfrom(self, node, parent, assign_ctx=None): return _create_yield_node(node, parent, self, new.YieldFrom) - def visit_class(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_class(node, parent) + def visit_classdef(self, node, parent, assign_ctx=None): + newnode = super(TreeRebuilder3k, self).visit_classdef(node, parent, assign_ctx) newnode._newstyle = True for keyword in node.keywords: if keyword.arg == 'metaclass': - newnode._metaclass = self.visit(keyword, newnode).value + newnode._metaclass = self.visit(keyword, newnode, assign_ctx).value break return newnode -if sys.version_info >= (3, 0): - TreeRebuilder = TreeRebuilder3k + # Async structs added in Python 3.5 + def visit_asyncfunctiondef(self, node, parent, assign_ctx=None): + return self._visit_functiondef(new.AsyncFunctionDef, node, parent, + assign_ctx=assign_ctx) + def visit_asyncfor(self, node, parent, assign_ctx=None): + return self._visit_for(new.AsyncFor, node, parent, + assign_ctx=assign_ctx) + + def visit_await(self, node, parent, assign_ctx=None): + newnode = new.Await() + newnode.lineno = node.lineno + newnode.col_offset = node.col_offset + newnode.parent = parent + newnode.value = self.visit(node.value, newnode, None) + return newnode + + def visit_asyncwith(self, node, parent, assign_ctx=None): + return self._visit_with(new.AsyncWith, node, parent, + assign_ctx=assign_ctx) + + +if sys.version_info >= (3, 0): + TreeRebuilder = TreeRebuilder3k diff --git a/pymode/libs/astroid/scoped_nodes.py b/pymode/libs/astroid/scoped_nodes.py index ac90f878..d78d1510 100644 --- a/pymode/libs/astroid/scoped_nodes.py +++ b/pymode/libs/astroid/scoped_nodes.py @@ -15,40 +15,33 @@ # # You should have received a copy of the GNU Lesser General Public License along # with astroid. If not, see . -"""This module contains the classes for "scoped" node, i.e. which are opening a -new local scope in the language definition : Module, Class, Function (and -Lambda, GenExpr, DictComp and SetComp to some extent). -""" -from __future__ import with_statement -__doctype__ = "restructuredtext en" +""" +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 -from itertools import chain -try: - from io import BytesIO -except ImportError: - from cStringIO import StringIO as BytesIO import six -from logilab.common.compat import builtins -from logilab.common.decorators import cached, cachedproperty - -from astroid.exceptions import NotFoundError, \ - AstroidBuildingException, InferenceError, ResolveError -from astroid.node_classes import Const, DelName, DelAttr, \ - Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ - LookupMixIn, const_factory as cf, unpack_infer, CallFunc -from astroid.bases import NodeNG, InferenceContext, Instance, copy_context, \ - YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, \ - BUILTINS -from astroid.mixins import FilterStmtsMixin -from astroid.bases import Statement -from astroid.manager import AstroidManager +import wrapt + +from astroid import bases +from astroid import context as contextmod +from astroid import exceptions +from astroid import manager +from astroid import mixins +from astroid import node_classes +from astroid import decorators as decorators_mod +from astroid import util + +BUILTINS = six.moves.builtins.__name__ ITER_METHODS = ('__iter__', '__getitem__') -PY3K = sys.version_info >= (3, 0) + def _c3_merge(sequences): """Merges MROs in *sequences* to a single MRO using the C3 algorithm. @@ -75,8 +68,10 @@ def _c3_merge(sequences): bases = ["({})".format(", ".join(base.name for base in subsequence)) for subsequence in sequences] - raise ResolveError("Cannot create a consistent method resolution " - "order for bases %s" % ", ".join(bases)) + raise exceptions.InconsistentMroError( + "Cannot create a consistent method resolution " + "order for bases %s" % ", ".join(bases)) + result.append(candidate) # remove the chosen candidate for seq in sequences: @@ -88,59 +83,62 @@ def _verify_duplicates_mro(sequences): for sequence in sequences: names = [node.qname() for node in sequence] if len(names) != len(set(names)): - raise ResolveError('Duplicates found in the mro.') + raise exceptions.DuplicateBasesError('Duplicates found in the mro.') -def remove_nodes(func, cls): - def wrapper(*args, **kwargs): +def remove_nodes(cls): + @wrapt.decorator + def decorator(func, instance, args, kwargs): nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)] if not nodes: - raise NotFoundError() + raise exceptions.NotFoundError() return nodes - return wrapper + return decorator def function_to_method(n, klass): - if isinstance(n, Function): + if isinstance(n, FunctionDef): if n.type == 'classmethod': - return BoundMethod(n, klass) + return bases.BoundMethod(n, klass) if n.type != 'staticmethod': - return UnboundMethod(n) + return bases.UnboundMethod(n) return n + def std_special_attributes(self, name, add_locals=True): if add_locals: - locals = self.locals + locals = self._locals else: locals = {} if name == '__name__': - return [cf(self.name)] + locals.get(name, []) + return [node_classes.const_factory(self.name)] + locals.get(name, []) if name == '__doc__': - return [cf(self.doc)] + locals.get(name, []) + return [node_classes.const_factory(self.doc)] + locals.get(name, []) if name == '__dict__': - return [Dict()] + locals.get(name, []) - raise NotFoundError(name) + return [node_classes.Dict()] + locals.get(name, []) + raise exceptions.NotFoundError(name) -MANAGER = AstroidManager() + +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(builtins) + builtin_astroid = MANAGER.ast_from_module(six.moves.builtins) if name == '__dict__': return builtin_astroid, () try: - stmts = builtin_astroid.locals[name] + stmts = builtin_astroid._locals[name] except KeyError: stmts = () return builtin_astroid, stmts -# TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup -class LocalsDictNodeNG(LookupMixIn, NodeNG): - """ this class provides locals handling common to Module, Function - and Class nodes, including a dict like interface for direct access +# TODO move this Mixin to mixins.py; problem: 'FunctionDef' in _scope_lookup +class LocalsDictNodeNG(node_classes.LookupMixIn, bases.NodeNG): + """ this class provides locals handling common to Module, FunctionDef + and ClassDef nodes, including a dict like interface for direct access to locals information """ @@ -148,6 +146,18 @@ class LocalsDictNodeNG(LookupMixIn, NodeNG): # dictionary of locals with name as key and node defining the local as # value + @property + def locals(self): + util.attribute_to_function_warning('locals', 2.0, 'get_locals') + return self._locals + @locals.setter + def locals(self, _locals): + util.attribute_to_function_warning('locals', 2.0, 'get_locals') + self._locals = _locals + @locals.deleter + def locals(self): + util.attribute_to_function_warning('locals', 2.0, 'get_locals') + del self._locals def qname(self): """return the 'qualified' name of the node, eg module.name, @@ -158,21 +168,20 @@ def qname(self): return '%s.%s' % (self.parent.frame().qname(), self.name) def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) + """return the first parent frame node (i.e. Module, FunctionDef or ClassDef) """ return self def scope(self): """return the first node defining a new scope (i.e. Module, - Function, Class, Lambda but also GenExpr, DictComp and SetComp) + FunctionDef, ClassDef, Lambda but also GeneratorExp, DictComp and SetComp) """ 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) + stmts = node._filter_stmts(self._locals[name], self, offset) except KeyError: stmts = () if stmts: @@ -186,8 +195,6 @@ def _scope_lookup(self, node, name, offset=0): return pscope.scope_lookup(node, name) return builtin_lookup(name) # Module - - def set_local(self, name, stmt): """define in locals ( is the node defining the name) if the node is a Module node (i.e. has globals), add the name to @@ -195,8 +202,8 @@ def set_local(self, name, stmt): if the name is already defined, ignore it """ - #assert not stmt in self.locals.get(name, ()), (self, stmt) - self.locals.setdefault(name, []).append(stmt) + #assert not stmt in self._locals.get(name, ()), (self, stmt) + self._locals.setdefault(name, []).append(stmt) __setitem__ = set_local @@ -212,7 +219,6 @@ def add_local_node(self, child_node, name=None): self._append_node(child_node) self.set_local(name or child_node.name, child_node) - def __getitem__(self, item): """method from the `dict` interface returning the first node associated with the given name in the locals dictionary @@ -221,7 +227,7 @@ def __getitem__(self, item): :param item: the name of the locally defined object :raises KeyError: if the name is not defined """ - return self.locals[item][0] + return self._locals[item][0] def __iter__(self): """method from the `dict` interface returning an iterator on @@ -233,27 +239,24 @@ def keys(self): """method from the `dict` interface returning a tuple containing locally defined names """ - return list(self.locals.keys()) + return list(self._locals.keys()) def values(self): """method from the `dict` interface returning a tuple containing - locally defined nodes which are instance of `Function` or `Class` + locally defined nodes which are instance of `FunctionDef` or `ClassDef` """ return [self[key] for key in self.keys()] def items(self): """method from the `dict` interface returning a list of tuple containing each locally defined name with its associated node, - which is an instance of `Function` or `Class` + which is an instance of `FunctionDef` or `ClassDef` """ return list(zip(self.keys(), self.values())) - def __contains__(self, name): - return name in self.locals - has_key = __contains__ + return name in self._locals -# Module ##################################################################### class Module(LocalsDictNodeNG): _astroid_fields = ('body',) @@ -265,9 +268,9 @@ class Module(LocalsDictNodeNG): # the file from which as been extracted the astroid representation. It may # be None if the representation has been built from a built-in module - file = None + source_file = None # Alternatively, if built from a string/bytes, this can be set - file_bytes = None + source_code = None # encoding of python source file, so we can get unicode out of it (python2 # only) file_encoding = None @@ -279,10 +282,10 @@ class Module(LocalsDictNodeNG): package = None # dictionary of globals with name as key and node defining the global # as value - globals = None + _globals = None # Future imports - future_imports = None + _future_imports = None # names of python special attributes (handled by getattr impl.) special_attributes = set(('__name__', '__doc__', '__file__', '__path__', @@ -294,15 +297,81 @@ def __init__(self, name, doc, pure_python=True): self.name = name self.doc = doc self.pure_python = pure_python - self.locals = self.globals = {} + self._locals = self._globals = {} self.body = [] - self.future_imports = set() + self._future_imports = set() + + # Future deprecation warnings + @property + def file(self): + util.rename_warning('file', 2.0, 'source_file') + return self.source_file + @file.setter + def file(self, source_file): + util.rename_warning('file', 2.0, 'source_file') + self.source_file = source_file + @file.deleter + def file(self): + util.rename_warning('file', 2.0, 'source_file') + del self.source_file + + @property + def path(self): + util.rename_warning('path', 2.0, 'source_file') + return self.source_file + @path.setter + def path(self, source_file): + util.rename_warning('path', 2.0, 'source_file') + self.source_file = source_file + @path.deleter + def path(self): + util.rename_warning('path', 2.0, 'source_file') + del self.source_file + + @property + def file_bytes(self): + util.rename_warning('file_bytes', 2.0, 'source_code') + return self.source_code + @file_bytes.setter + def file_bytes(self, source_code): + util.rename_warning('file_bytes', 2.0, 'source_code') + self.source_code = source_code + @file_bytes.deleter + def file_bytes(self): + util.rename_warning('file_bytes', 2.0, 'source_code') + del self.source_code + + @property + def globals(self): + util.attribute_to_function_warning('globals', 2.0, 'get_locals') + return self._locals + @globals.setter + def globals(self, _globals): + util.attribute_to_function_warning('globals', 2.0, 'get_locals') + self._locals = _globals + @globals.deleter + def globals(self): + util.attribute_to_function_warning('globals', 2.0, 'get_locals') + del self._locals + + @property + def future_imports(self): + util.attribute_to_function_warning('future_imports', 2.0, 'future_imports') + return self._future_imports + @future_imports.setter + def future_imports(self, _future_imports): + util.attribute_to_function_warning('future_imports', 2.0, 'future_imports') + self._future_imports = _future_imports + @future_imports.deleter + def future_imports(self): + util.attribute_to_function_warning('future_imports', 2.0, 'future_imports') + del self._future_imports def _get_stream(self): - if self.file_bytes is not None: - return BytesIO(self.file_bytes) - if self.file is not None: - stream = open(self.file, 'rb') + if self.source_code is not None: + return io.BytesIO(self.source_code) + if self.source_file is not None: + stream = open(self.source_file, 'rb') return stream return None @@ -337,10 +406,10 @@ def block_range(self, lineno): return self.fromlineno, self.tolineno def scope_lookup(self, node, name, offset=0): - if name in self.scope_attrs and not name in self.locals: + if name in self.scope_attrs and name not in self._locals: try: return self, self.getattr(name) - except NotFoundError: + except exceptions.NotFoundError: return self, () return self._scope_lookup(node, name, offset) @@ -350,44 +419,42 @@ def pytype(self): def display_type(self): return 'Module' + @remove_nodes(node_classes.DelName) def getattr(self, name, context=None, ignore_locals=False): if name in self.special_attributes: if name == '__file__': - return [cf(self.file)] + self.locals.get(name, []) + return [node_classes.const_factory(self.source_file)] + self._locals.get(name, []) if name == '__path__' and self.package: - return [List()] + self.locals.get(name, []) + return [node_classes.List()] + self._locals.get(name, []) return std_special_attributes(self, name) - if not ignore_locals and name in self.locals: - return self.locals[name] + if not ignore_locals and name in self._locals: + return self._locals[name] if self.package: try: return [self.import_module(name, relative_only=True)] - except AstroidBuildingException: - raise NotFoundError(name) + except exceptions.AstroidBuildingException: + raise exceptions.NotFoundError(name) except SyntaxError: - raise NotFoundError(name) - except Exception:# XXX pylint tests never pass here; do we need it? - import traceback - traceback.print_exc() - raise NotFoundError(name) - getattr = remove_nodes(getattr, DelName) + raise exceptions.NotFoundError(name) + raise exceptions.NotFoundError(name) def igetattr(self, name, context=None): """inferred getattr""" # set lookup name since this is necessary to infer on import nodes for # instance - context = copy_context(context) + context = contextmod.copy_context(context) context.lookupname = name try: - return _infer_stmts(self.getattr(name, context), context, frame=self) - except NotFoundError: - raise InferenceError(name) + return bases._infer_stmts(self.getattr(name, context), + context, frame=self) + except exceptions.NotFoundError: + raise exceptions.InferenceError(name) def fully_defined(self): """return True if this module has been built from a .py file and so contains a complete representation including the code """ - return self.file is not None and self.file.endswith('.py') + return self.source_file is not None and self.source_file.endswith('.py') def statement(self): """return the first parent node marked as statement node @@ -403,11 +470,11 @@ def next_sibling(self): """module has no sibling""" return - if sys.version_info < (2, 8): - @cachedproperty + if six.PY2: + @decorators_mod.cachedproperty def _absolute_import_activated(self): - for stmt in self.locals.get('absolute_import', ()): - if isinstance(stmt, From) and stmt.modname == '__future__': + for stmt in self._locals.get('absolute_import', ()): + if isinstance(stmt, node_classes.ImportFrom) and stmt.modname == '__future__': return True return False else: @@ -423,7 +490,7 @@ def import_module(self, modname, relative_only=False, level=None): absmodname = self.relative_to_absolute_name(modname, level) try: return MANAGER.ast_from_module_name(absmodname) - except AstroidBuildingException: + except exceptions.AstroidBuildingException: # we only want to import a sub module or package of this module, # skip here if relative_only: @@ -454,7 +521,6 @@ def relative_to_absolute_name(self, modname, level): return '%s.%s' % (package_name, modname) return modname - def wildcard_import_names(self): """return the list of imported names when this module is 'wildcard imported' @@ -462,19 +528,6 @@ def wildcard_import_names(self): It doesn't include the '__builtins__' name which is added by the current CPython implementation of wildcard imports. """ - # take advantage of a living module if it exists - try: - living = sys.modules[self.name] - except KeyError: - pass - else: - try: - return living.__all__ - except AttributeError: - return [name for name in living.__dict__.keys() - if not name.startswith('_')] - # else lookup the astroid - # # 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('_')] @@ -482,9 +535,10 @@ def wildcard_import_names(self): all = self['__all__'] except KeyError: return default + try: explicit = next(all.assigned_stmts()) - except InferenceError: + except exceptions.InferenceError: return default except AttributeError: # not an assignment node @@ -492,28 +546,34 @@ def wildcard_import_names(self): return default # Try our best to detect the exported name. - infered = [] + inferred = [] try: explicit = next(explicit.infer()) - except InferenceError: + except exceptions.InferenceError: return default - if not isinstance(explicit, (Tuple, List)): + if not isinstance(explicit, (node_classes.Tuple, node_classes.List)): return default - str_const = lambda node: (isinstance(node, Const) and + 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): - infered.append(node.value) + inferred.append(node.value) else: try: - infered_node = next(node.infer()) - except InferenceError: + inferred_node = next(node.infer()) + except exceptions.InferenceError: continue - if str_const(infered_node): - infered.append(infered_node.value) - return infered + if str_const(inferred_node): + inferred.append(inferred_node.value) + return inferred + def _public_names(self): + """Get the list of the names which are publicly available in this module.""" + return [name for name in self.keys() if not name.startswith('_')] + + def bool_value(self): + return True class ComprehensionScope(LocalsDictNodeNG): @@ -523,11 +583,11 @@ def frame(self): scope_lookup = LocalsDictNodeNG._scope_lookup -class GenExpr(ComprehensionScope): +class GeneratorExp(ComprehensionScope): _astroid_fields = ('elt', 'generators') def __init__(self): - self.locals = {} + self._locals = {} self.elt = None self.generators = [] @@ -536,7 +596,7 @@ class DictComp(ComprehensionScope): _astroid_fields = ('key', 'value', 'generators') def __init__(self): - self.locals = {} + self._locals = {} self.key = None self.value = None self.generators = [] @@ -546,97 +606,53 @@ class SetComp(ComprehensionScope): _astroid_fields = ('elt', 'generators') def __init__(self): - self.locals = {} + self._locals = {} self.elt = None self.generators = [] -class _ListComp(NodeNG): +class _ListComp(bases.NodeNG): """class representing a ListComp node""" _astroid_fields = ('elt', 'generators') elt = None generators = None -if sys.version_info >= (3, 0): + +if six.PY3: class ListComp(_ListComp, ComprehensionScope): """class representing a ListComp node""" def __init__(self): - self.locals = {} + self._locals = {} else: class ListComp(_ListComp): """class representing a ListComp node""" -# Function ################################################################### 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, Function): + if not isinstance(node, FunctionDef): return if not node.parent: return 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, InferenceError): - return - if isinstance(result, Instance): - result = result._proxied - if isinstance(result, Class): - if result.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - if result.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - - -def _function_type(self): - """ - Function type, possible values are: - method, function, staticmethod, classmethod. - """ - # Can't infer that this node is decorated - # with a subclass of `classmethod` where `type` is first set, - # so do it here. - if self.decorators: - for node in self.decorators.nodes: - if isinstance(node, CallFunc): - # Handle the following case: - # @some_decorator(arg1, arg2) - # def func(...) - # - try: - current = next(node.func.infer()) - except InferenceError: - continue - _type = _infer_decorator_callchain(current) - if _type is not None: - return _type - - try: - for infered in node.infer(): - # Check to see if this returns a static or a class method. - _type = _infer_decorator_callchain(infered) - if _type is not None: - return _type - - if not isinstance(infered, Class): - continue - for ancestor in infered.ancestors(): - if not isinstance(ancestor, Class): - continue - if ancestor.is_subtype_of('%s.classmethod' % BUILTINS): - return 'classmethod' - elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS): - return 'staticmethod' - except InferenceError: - pass - return self._type + # 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 + 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' -class Lambda(LocalsDictNodeNG, FilterStmtsMixin): +class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG): _astroid_fields = ('args', 'body',) name = '' @@ -644,7 +660,7 @@ class Lambda(LocalsDictNodeNG, FilterStmtsMixin): type = 'function' def __init__(self): - self.locals = {} + self._locals = {} self.args = [] self.body = [] @@ -689,9 +705,10 @@ def scope_lookup(self, node, name, offset=0): return frame._scope_lookup(node, name, offset) -class Function(Statement, Lambda): - if PY3K: - _astroid_fields = ('decorators', 'args', 'body', 'returns') + +class FunctionDef(bases.Statement, Lambda): + if six.PY3: + _astroid_fields = ('decorators', 'args', 'returns', 'body') returns = None else: _astroid_fields = ('decorators', 'args', 'body') @@ -699,32 +716,137 @@ class Function(Statement, Lambda): special_attributes = set(('__name__', '__doc__', '__dict__')) is_function = True # attributes below are set by the builder module or by raw factories - blockstart_tolineno = None decorators = None - _type = "function" - type = cachedproperty(_function_type) def __init__(self, name, doc): - self.locals = {} + self._locals = {} self.args = [] self.body = [] self.name = name self.doc = doc - self.extra_decorators = [] - self.instance_attrs = {} + self._instance_attrs = {} + + @property + def instance_attrs(self): + util.attribute_to_function_warning('instance_attrs', 2.0, 'get_attributes') + return self._instance_attrs + @instance_attrs.setter + def instance_attrs(self, _instance_attrs): + util.attribute_to_function_warning('instance_attrs', 2.0, 'get_attributes') + self._instance_attrs = _instance_attrs + @instance_attrs.deleter + def instance_attrs(self): + util.attribute_to_function_warning('instance_attrs', 2.0, 'get_attributes') + del self._instance_attrs + + @decorators_mod.cachedproperty + def extra_decorators(self): + """Get the extra decorators that this function can haves + 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. + """ + 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): + """Get the function type for this node. + + Possible values are: method, function, staticmethod, classmethod. + """ + builtin_descriptors = {'classmethod', 'staticmethod'} + + for decorator in self.extra_decorators: + if decorator.func.name in builtin_descriptors: + return decorator.func.name - @cachedproperty + frame = self.parent.frame() + type_name = 'function' + if isinstance(frame, ClassDef): + if self.name == '__new__': + return 'classmethod' + else: + type_name = 'method' + + if self.decorators: + 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): # 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) + for node in self.decorators.nodes) return lineno - @cachedproperty + @decorators_mod.cachedproperty def blockstart_tolineno(self): return self.args.tolineno @@ -740,29 +862,41 @@ def getattr(self, name, context=None): done by an Instance proxy at inference time. """ if name == '__module__': - return [cf(self.root().qname())] - if name in self.instance_attrs: - return self.instance_attrs[name] + return [node_classes.const_factory(self.root().qname())] + if name in self._instance_attrs: + return self._instance_attrs[name] return std_special_attributes(self, name, False) + 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.NotFoundError: + raise exceptions.InferenceError(name) + def is_method(self): """return true if the function node should be considered as a method""" - # check we are defined in a Class, because this is usually expected + # 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(), Class) + return self.type != 'function' and isinstance(self.parent.frame(), ClassDef) + @decorators_mod.cached def decoratornames(self): """return a list of decorator qualified names""" result = set() decoratornodes = [] if self.decorators is not None: + # pylint: disable=unsupported-binary-operation; damn flow control. decoratornodes += self.decorators.nodes decoratornodes += self.extra_decorators for decnode in decoratornodes: - for infnode in decnode.infer(): - result.add(infnode.qname()) + try: + for infnode in decnode.infer(): + result.add(infnode.qname()) + except exceptions.InferenceError: + continue return result - decoratornames = cached(decoratornames) def is_bound(self): """return true if the function is bound to an Instance or a class""" @@ -779,34 +913,34 @@ def is_abstract(self, pass_is_abstract=True): if self.decorators: for node in self.decorators.nodes: try: - infered = next(node.infer()) - except InferenceError: + inferred = next(node.infer()) + except exceptions.InferenceError: continue - if infered and infered.qname() in ('abc.abstractproperty', - 'abc.abstractmethod'): + if inferred and inferred.qname() in ('abc.abstractproperty', + 'abc.abstractmethod'): return True for child_node in self.body: - if isinstance(child_node, Raise): + if isinstance(child_node, node_classes.Raise): if child_node.raises_not_implemented(): return True - if pass_is_abstract and isinstance(child_node, Pass): - return True - return False + 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): """return true if this is a generator function""" - # XXX should be flagged, not computed - return next(self.nodes_of_class((Yield, YieldFrom), - skip_klass=(Function, Lambda)), False) + 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 a function is returning when called""" if self.is_generator(): - yield Generator() + result = bases.Generator() + result.parent = 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 @@ -818,25 +952,29 @@ def infer_call_result(self, caller, context=None): len(self.args.args) == 1 and self.args.vararg is not None): metaclass = next(caller.args[0].infer(context)) - if isinstance(metaclass, Class): - c = Class('temporary_class', None) + if isinstance(metaclass, ClassDef): + c = ClassDef('temporary_class', None) c.hide = True c.parent = self - bases = [next(b.infer(context)) for b in caller.args[1:]] - c.bases = [base for base in bases if base != YES] + class_bases = [next(b.infer(context)) for b in caller.args[1:]] + c.bases = [base for base in class_bases if base != util.YES] c._metaclass = metaclass yield c return - returns = self.nodes_of_class(Return, skip_klass=Function) + returns = self.nodes_of_class(node_classes.Return, skip_klass=FunctionDef) for returnnode in returns: if returnnode.value is None: - yield Const(None) + yield node_classes.Const(None) else: try: - for infered in returnnode.value.infer(context): - yield infered - except InferenceError: - yield YES + for inferred in returnnode.value.infer(context): + yield inferred + except exceptions.InferenceError: + yield util.YES + + +class AsyncFunctionDef(FunctionDef): + """Asynchronous function created with the `async` keyword.""" def _rec_get_names(args, names=None): @@ -844,16 +982,13 @@ def _rec_get_names(args, names=None): if names is None: names = [] for arg in args: - if isinstance(arg, Tuple): + if isinstance(arg, node_classes.Tuple): _rec_get_names(arg.elts, names) else: names.append(arg.name) return names -# Class ###################################################################### - - def _is_metaclass(klass, seen=None): """ Return if the given class can be used as a metaclass. @@ -865,30 +1000,31 @@ def _is_metaclass(klass, seen=None): for base in klass.bases: try: for baseobj in base.infer(): - if baseobj in seen: + baseobj_name = baseobj.qname() + if baseobj_name in seen: continue else: - seen.add(baseobj) - if isinstance(baseobj, Instance): + seen.add(baseobj_name) + if isinstance(baseobj, bases.Instance): # not abstract return False - if baseobj is YES: + if baseobj is util.YES: continue if baseobj is klass: continue - if not isinstance(baseobj, Class): + if not isinstance(baseobj, ClassDef): continue if baseobj._type == 'metaclass': return True if _is_metaclass(baseobj, seen): return True - except InferenceError: + except exceptions.InferenceError: continue return False def _class_type(klass, ancestors=None): - """return a Class node type to differ metaclass, interface and exception + """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 @@ -896,18 +1032,17 @@ def _class_type(klass, ancestors=None): return klass._type if _is_metaclass(klass): klass._type = 'metaclass' - elif klass.name.endswith('Interface'): - klass._type = 'interface' elif klass.name.endswith('Exception'): klass._type = 'exception' else: if ancestors is None: ancestors = set() - if klass in ancestors: + 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) + ancestors.add(klass_name) for base in klass.ancestors(recurs=False): name = _class_type(base, ancestors) if name != 'class': @@ -921,14 +1056,8 @@ def _class_type(klass, ancestors=None): klass._type = 'class' return klass._type -def _iface_hdlr(iface_node): - """a handler function used by interfaces to handle suspicious - interface nodes - """ - return True - -class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): +class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, bases.Statement): # some of the attributes below are set by the builder module or # by a raw factories @@ -939,26 +1068,38 @@ class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): decorators = None special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', '__bases__', '__mro__', '__subclasses__')) - blockstart_tolineno = None _type = None _metaclass_hack = False hide = False type = property(_class_type, doc="class'type, possible values are 'class' | " - "'metaclass' | 'interface' | 'exception'") + "'metaclass' | 'exception'") def __init__(self, name, doc): - self.instance_attrs = {} - self.locals = {} + self._instance_attrs = {} + self._locals = {} self.bases = [] self.body = [] self.name = name self.doc = doc + @property + def instance_attrs(self): + util.attribute_to_function_warning('instance_attrs', 2.0, 'get_attributes') + return self._instance_attrs + @instance_attrs.setter + def instance_attrs(self, _instance_attrs): + util.attribute_to_function_warning('instance_attrs', 2.0, 'get_attributes') + self._instance_attrs = _instance_attrs + @instance_attrs.deleter + def instance_attrs(self): + util.attribute_to_function_warning('instance_attrs', 2.0, 'get_attributes') + del self._instance_attrs + def _newstyle_impl(self, context=None): if context is None: - context = InferenceContext() + context = contextmod.InferenceContext() if self._newstyle is not None: return self._newstyle for base in self.ancestors(recurs=False, context=context): @@ -968,7 +1109,7 @@ def _newstyle_impl(self, context=None): klass = self._explicit_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, Class): + if klass is not None and isinstance(klass, ClassDef): self._newstyle = klass._newstyle_impl(context) if self._newstyle is None: self._newstyle = False @@ -979,7 +1120,7 @@ def _newstyle_impl(self, context=None): doc="boolean indicating if it's a new style class" "or not") - @cachedproperty + @decorators_mod.cachedproperty def blockstart_tolineno(self): if self.bases: return self.bases[-1].tolineno @@ -1011,32 +1152,52 @@ def is_subtype_of(self, type_name, context=None): if anc.qname() == type_name: return True + 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.YES + + 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 (YES is not an AST node), therefore we simply return YES here + # although we know at least the name of the class. + return util.YES + + # 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: - name_node = next(caller.args[0].infer(context)) - if (isinstance(name_node, Const) and - isinstance(name_node.value, six.string_types)): - name = name_node.value - else: - yield YES - return - result = Class(name, None) - bases = next(caller.args[1].infer(context)) - if isinstance(bases, (Tuple, List)): - result.bases = bases.itered() - else: - # There is currently no AST node that can represent an 'unknown' - # node (YES is not an AST node), therefore we simply return YES here - # although we know at least the name of the class. - yield YES - return - result.parent = caller.parent + 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 Instance(self) + yield bases.Instance(self) def scope_lookup(self, node, name, offset=0): + # pylint: disable=redefined-variable-type if any(node == base or base.parent_of(node) for base in self.bases): # Handle the case where we have either a name @@ -1060,11 +1221,10 @@ def scope_lookup(self, node, name, offset=0): frame = self return frame._scope_lookup(node, name, offset) - # list of parent class as a list of string (i.e. names as they appear - # in the class definition) XXX bw compat + @property def basenames(self): + """Get the list of parent class names, as they appear in the class definition.""" return [bnode.as_string() for bnode in self.bases] - basenames = property(basenames) def ancestors(self, recurs=True, context=None): """return an iterator on the node base classes in a prefixed @@ -1078,8 +1238,8 @@ def ancestors(self, recurs=True, context=None): # FIXME: inference make infinite loops possible here yielded = set([self]) if context is None: - context = InferenceContext() - if sys.version_info[0] >= 3: + context = contextmod.InferenceContext() + if six.PY3: if not self.bases and self.qname() != 'builtins.object': yield builtin_lookup("object")[1][0] return @@ -1088,15 +1248,14 @@ def ancestors(self, recurs=True, context=None): with context.restore_path(): try: for baseobj in stmt.infer(context): - if not isinstance(baseobj, Class): - if isinstance(baseobj, Instance): + if not isinstance(baseobj, ClassDef): + if isinstance(baseobj, bases.Instance): baseobj = baseobj._proxied else: - # duh ? continue if not baseobj.hide: if baseobj in yielded: - continue # cf xxx above + continue yielded.add(baseobj) yield baseobj if recurs: @@ -1106,18 +1265,28 @@ def ancestors(self, recurs=True, context=None): # This class is the ancestor of itself. break if grandpa in yielded: - continue # cf xxx above + continue yielded.add(grandpa) yield grandpa - except InferenceError: - # XXX log error ? + except exceptions.InferenceError: continue def local_attr_ancestors(self, name, context=None): """return an iterator on astroid representation of parent classes which have defined in their locals """ - for astroid in self.ancestors(context=context): + 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 @@ -1126,12 +1295,13 @@ def instance_attr_ancestors(self, name, context=None): which have defined in their instance attribute dictionary """ for astroid in self.ancestors(context=context): - if name in astroid.instance_attrs: + if name in astroid._instance_attrs: yield astroid def has_base(self, node): return node in self.bases + @remove_nodes(node_classes.DelAttr) def local_attr(self, name, context=None): """return the list of assign node associated to name in this class locals or in its parents @@ -1141,14 +1311,13 @@ def local_attr(self, name, context=None): its parent classes """ try: - return self.locals[name] + return self._locals[name] except KeyError: - # get if from the first parent implementing it if any for class_node in self.local_attr_ancestors(name, context): - return class_node.locals[name] - raise NotFoundError(name) - local_attr = remove_nodes(local_attr, DelAttr) + return class_node._locals[name] + raise exceptions.NotFoundError(name) + @remove_nodes(node_classes.DelAttr) def instance_attr(self, name, context=None): """return the astroid nodes associated to name in this class instance attributes dictionary and in its parents @@ -1157,20 +1326,24 @@ def instance_attr(self, name, context=None): if no attribute with this name has been find in this class or its parent classes """ - # Return a copy, so we don't modify self.instance_attrs, + # Return a copy, so we don't modify self._instance_attrs, # which could lead to infinite loop. - values = list(self.instance_attrs.get(name, [])) + 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 += class_node._instance_attrs[name] if not values: - raise NotFoundError(name) + raise exceptions.NotFoundError(name) return values - instance_attr = remove_nodes(instance_attr, DelAttr) + + def instantiate_class(self): + """return Instance of ClassDef node, else return self""" + return bases.Instance(self) def instanciate_class(self): - """return Instance of Class node, else return self""" - return Instance(self) + """return Instance of ClassDef node, else return self""" + util.rename_warning('instanciate_class()', 2.0, 'instantiate_class()') + return self.instantiate_class() def getattr(self, name, context=None): """this method doesn't look in the instance_attrs dictionary since it's @@ -1179,25 +1352,27 @@ def getattr(self, name, context=None): It may return a YES object if the attribute has not been actually found but a __getattr__ or __getattribute__ method is defined """ - values = self.locals.get(name, []) + values = self._locals.get(name, []) if name in self.special_attributes: if name == '__module__': - return [cf(self.root().qname())] + values - # FIXME: do we really need the actual list of ancestors? - # returning [Tuple()] + values don't break any test - # this is ticket http://www.logilab.org/ticket/52785 - # XXX need proper meta class handling + MRO implementation - if name == '__bases__' or (name == '__mro__' and self.newstyle): - node = Tuple() - node.items = self.ancestors(recurs=True, context=context) + return [node_classes.const_factory(self.root().qname())] + values + if name == '__bases__': + node = node_classes.Tuple() + elts = list(self._inferred_bases(context)) + node.elts = elts return [node] + values + if name == '__mro__' and self.newstyle: + mro = self.mro() + node = node_classes.Tuple() + node.elts = mro + return [node] return std_special_attributes(self, name) - # don't modify the list in self.locals! + # 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, []) + values += classnode._locals.get(name, []) if not values: - raise NotFoundError(name) + raise exceptions.NotFoundError(name) return values def igetattr(self, name, context=None): @@ -1206,46 +1381,50 @@ def igetattr(self, name, context=None): """ # set lookup name since this is necessary to infer on import nodes for # instance - context = copy_context(context) + context = contextmod.copy_context(context) context.lookupname = name try: - for infered in _infer_stmts(self.getattr(name, context), context, - frame=self): + for inferred in bases._infer_stmts(self.getattr(name, context), + context, frame=self): # yield YES object instead of descriptors when necessary - if not isinstance(infered, Const) and isinstance(infered, Instance): + if (not isinstance(inferred, node_classes.Const) + and isinstance(inferred, bases.Instance)): try: - infered._proxied.getattr('__get__', context) - except NotFoundError: - yield infered + inferred._proxied.getattr('__get__', context) + except exceptions.NotFoundError: + yield inferred else: - yield YES + yield util.YES else: - yield function_to_method(infered, self) - except NotFoundError: + yield function_to_method(inferred, self) + except exceptions.NotFoundError: if not name.startswith('__') and self.has_dynamic_getattr(context): # class handle some dynamic attributes, return a YES object - yield YES + yield util.YES else: - raise InferenceError(name) + raise exceptions.InferenceError(name) def has_dynamic_getattr(self, context=None): - """return True if the class has a custom __getattr__ or - __getattribute__ method """ - # need to explicitly handle optparse.Values (setattr is not detected) - if self.name == 'Values' and self.root().name == 'optparse': - return True + Check if the current instance has a custom __getattr__ + or a custom __getattribute__. + + If any such method is found and it is not from + builtins, nor from an extension module, then the function + will return True. + """ + def _valid_getattr(node): + root = node.root() + return root.name != BUILTINS and getattr(root, 'pure_python', None) + try: - self.getattr('__getattr__', context) - return True - except NotFoundError: + return _valid_getattr(self.getattr('__getattr__', context)[0]) + except exceptions.NotFoundError: #if self.newstyle: XXX cause an infinite recursion error try: getattribute = self.getattr('__getattribute__', context)[0] - if getattribute.root().name != BUILTINS: - # class has a custom __getattribute__ defined - return True - except NotFoundError: + return _valid_getattr(getattribute) + except exceptions.NotFoundError: pass return False @@ -1254,7 +1433,7 @@ def methods(self): its ancestors """ done = {} - for astroid in chain(iter((self,)), self.ancestors()): + for astroid in itertools.chain(iter((self,)), self.ancestors()): for meth in astroid.mymethods(): if meth.name in done: continue @@ -1264,31 +1443,19 @@ def methods(self): def mymethods(self): """return an iterator on all methods defined in the class""" for member in self.values(): - if isinstance(member, Function): + if isinstance(member, FunctionDef): yield member - def interfaces(self, herited=True, handler_func=_iface_hdlr): - """return an iterator on interfaces implemented by the given - class node + 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. """ - # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... - try: - implements = Instance(self).getattr('__implements__')[0] - except NotFoundError: - return - if not herited and not implements.frame() is self: - return - found = set() - missing = False - for iface in unpack_infer(implements): - if iface is YES: - missing = True - continue - if not iface in found and handler_func(iface): - found.add(iface) - yield iface - if missing: - raise InferenceError() + + if self.newstyle: + return builtin_lookup('type')[1][0] _metaclass = None def _explicit_metaclass(self): @@ -1304,29 +1471,29 @@ def _explicit_metaclass(self): for base in self.bases: try: for baseobj in base.infer(): - if isinstance(baseobj, Class) and baseobj.hide: + if isinstance(baseobj, ClassDef) and baseobj.hide: self._metaclass = baseobj._metaclass self._metaclass_hack = True break - except InferenceError: + 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 YES) - except (InferenceError, StopIteration): + if node is not util.YES) + except (exceptions.InferenceError, StopIteration): return None - if sys.version_info >= (3, ): + if six.PY3: return None - if '__metaclass__' in self.locals: - assignment = self.locals['__metaclass__'][-1] + 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__'] + 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 @@ -1335,34 +1502,42 @@ def _explicit_metaclass(self): return None try: - infered = next(assignment.infer()) - except InferenceError: + inferred = next(assignment.infer()) + except exceptions.InferenceError: return - if infered is YES: # don't expose this + if inferred is util.YES: # don't expose this return None - return infered + return inferred + + def _find_metaclass(self, seen=None): + if seen is None: + seen = set() + seen.add(self) + + klass = self._explicit_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): - """ Return the metaclass of this class. + """Return 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. """ - klass = self._explicit_metaclass() - if klass is None: - for parent in self.ancestors(): - klass = parent.metaclass() - if klass is not None: - break - return klass + 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: + if '__slots__' not in self._locals: return for slots in self.igetattr('__slots__'): # check if __slots__ is a valid type @@ -1370,12 +1545,12 @@ def _islots(self): try: slots.getattr(meth) break - except NotFoundError: + except exceptions.NotFoundError: continue else: continue - if isinstance(slots, Const): + 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: @@ -1385,30 +1560,50 @@ def _islots(self): # we can't obtain the values, maybe a .deque? continue - if isinstance(slots, Dict): + if isinstance(slots, node_classes.Dict): values = [item[0] for item in slots.items] else: values = slots.itered() - if values is YES: + if values is util.YES: 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 infered in elt.infer(): - if infered is YES: + for inferred in elt.infer(): + if inferred is util.YES: continue - if (not isinstance(infered, Const) or - not isinstance(infered.value, + if (not isinstance(inferred, node_classes.Const) or + not isinstance(inferred.value, six.string_types)): continue - if not infered.value: + if not inferred.value: continue - yield infered - except InferenceError: + 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 + # pylint: disable=unsupported-binary-operation; false positive + return [first] + list(slots) + # Cached, because inferring them all the time is expensive - @cached + @decorators_mod.cached def slots(self): """Get all the slots for this node. @@ -1417,19 +1612,30 @@ def slots(self): Also, it will return None in the case the slots weren't inferred. Otherwise, it will return a list of slot names. """ + 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 = self._islots() - try: - first = next(slots) - except StopIteration: - # The class doesn't have a __slots__ definition. + slots = list(grouped_slots()) + if not all(slot is not None for slot in slots): return None - return [first] + list(slots) - def _inferred_bases(self, recurs=True, context=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 @@ -1445,8 +1651,8 @@ def _inferred_bases(self, recurs=True, context=None): # only in SomeClass. if context is None: - context = InferenceContext() - if sys.version_info[0] >= 3: + context = contextmod.InferenceContext() + if six.PY3: if not self.bases and self.qname() != 'builtins.object': yield builtin_lookup("object")[1][0] return @@ -1454,15 +1660,17 @@ def _inferred_bases(self, recurs=True, context=None): for stmt in self.bases: try: baseobj = next(stmt.infer(context=context)) - except InferenceError: - # XXX log error ? + except exceptions.InferenceError: continue - if isinstance(baseobj, Instance): + if isinstance(baseobj, bases.Instance): baseobj = baseobj._proxied - if not isinstance(baseobj, Class): + if not isinstance(baseobj, ClassDef): continue if not baseobj.hide: yield baseobj + else: + for base in baseobj.bases: + yield base def mro(self, context=None): """Get the method resolution order, using C3 linearization. @@ -1476,9 +1684,33 @@ def mro(self, context=None): "Could not obtain mro for old-style classes.") bases = list(self._inferred_bases(context=context)) - unmerged_mro = ([[self]] + - [base.mro() for base in bases if base is not self] + - [bases]) - + bases_mro = [] + for base in bases: + try: + mro = base.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 + [bases]) _verify_duplicates_mro(unmerged_mro) return _c3_merge(unmerged_mro) + +def get_locals(node): + '''Stub function for forwards compatibility.''' + return node._locals + +def get_attributes(node): + '''Stub function for forwards compatibility.''' + return node._instance_attrs + +# Backwards-compatibility aliases +Class = node_classes.proxy_alias('Class', ClassDef) +Function = node_classes.proxy_alias('Function', FunctionDef) +GenExpr = node_classes.proxy_alias('GenExpr', GeneratorExp) diff --git a/pymode/libs/astroid/test_utils.py b/pymode/libs/astroid/test_utils.py index 19bd7b96..9e45abcf 100644 --- a/pymode/libs/astroid/test_utils.py +++ b/pymode/libs/astroid/test_utils.py @@ -1,7 +1,6 @@ """Utility functions for test code that uses astroid ASTs as input.""" import functools import sys -import textwrap from astroid import nodes from astroid import builder @@ -14,7 +13,6 @@ # when calling extract_node. _STATEMENT_SELECTOR = '#@' - def _extract_expressions(node): """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. @@ -28,7 +26,7 @@ def _extract_expressions(node): :yields: The sequence of wrapped expressions on the modified tree expression can be found. """ - if (isinstance(node, nodes.CallFunc) + if (isinstance(node, nodes.Call) and isinstance(node.func, nodes.Name) and node.func.name == _TRANSIENT_FUNCTION): real_expr = node.args[0] @@ -68,7 +66,7 @@ def _find_statement_by_line(node, line): can be found. :rtype: astroid.bases.NodeNG or None """ - if isinstance(node, (nodes.Class, nodes.Function)): + if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)): # This is an inaccuracy in the AST: the nodes that can be # decorated do not carry explicit information on which line # the actual definition (class/def), but .fromline seems to @@ -142,7 +140,7 @@ def extract_node(code, module_name=''): :rtype: astroid.bases.NodeNG, or a list of nodes. """ def _extract(node): - if isinstance(node, nodes.Discard): + if isinstance(node, nodes.Expr): return node.value else: return node @@ -152,7 +150,7 @@ def _extract(node): if line.strip().endswith(_STATEMENT_SELECTOR): requested_lines.append(idx + 1) - tree = build_module(code, module_name=module_name) + tree = builder.parse(code, module_name=module_name) extracted = [] if requested_lines: for line in requested_lines: @@ -171,21 +169,6 @@ def _extract(node): return extracted -def build_module(code, module_name='', path=None): - """Parses a string module with a builder. - :param code: The code for the module. - :type code: str - :param module_name: The name for the module - :type module_name: str - :param path: The path for the module - :type module_name: str - :returns: The module AST. - :rtype: astroid.bases.NodeNG - """ - code = textwrap.dedent(code) - return builder.AstroidBuilder(None).string_build(code, modname=module_name, path=path) - - def require_version(minver=None, maxver=None): """ Compare version of python interpreter to the given one. Skip the test if older. diff --git a/pymode/libs/pkg_resources/_vendor/__init__.py b/pymode/libs/astroid/tests/__init__.py similarity index 100% rename from pymode/libs/pkg_resources/_vendor/__init__.py rename to pymode/libs/astroid/tests/__init__.py diff --git a/pymode/libs/astroid/tests/resources.py b/pymode/libs/astroid/tests/resources.py new file mode 100644 index 00000000..7988d053 --- /dev/null +++ b/pymode/libs/astroid/tests/resources.py @@ -0,0 +1,72 @@ +# Copyright 2014 Google, Inc. All rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +import os +import sys + +import pkg_resources + +from astroid import builder +from astroid import MANAGER +from astroid.bases import BUILTINS + + +DATA_DIR = 'testdata/python{}/'.format(sys.version_info[0]) + +def find(name): + return pkg_resources.resource_filename( + 'astroid.tests', os.path.normpath(os.path.join(DATA_DIR, name))) + + +def build_file(path, modname=None): + return builder.AstroidBuilder().file_build(find(path), modname) + + +class SysPathSetup(object): + def setUp(self): + sys.path.insert(0, find('')) + + def tearDown(self): + del sys.path[0] + datadir = find('') + for key in list(sys.path_importer_cache): + if key.startswith(datadir): + del sys.path_importer_cache[key] + + +class AstroidCacheSetupMixin(object): + """Mixin for handling the astroid cache problems. + + When clearing the astroid cache, some tests fails due to + cache inconsistencies, where some objects had a different + builtins object referenced. + This saves the builtins module and makes sure to add it + back to the astroid_cache after the tests finishes. + The builtins module is special, since some of the + transforms for a couple of its objects (str, bytes etc) + are executed only once, so astroid_bootstrapping will be + useless for retrieving the original builtins module. + """ + + @classmethod + def setUpClass(cls): + cls._builtins = MANAGER.astroid_cache.get(BUILTINS) + + @classmethod + def tearDownClass(cls): + if cls._builtins: + MANAGER.astroid_cache[BUILTINS] = cls._builtins diff --git a/pymode/libs/astroid/tests/testdata/python2/data/MyPyPa-0.1.0-py2.5.egg b/pymode/libs/astroid/tests/testdata/python2/data/MyPyPa-0.1.0-py2.5.egg new file mode 100644 index 0000000000000000000000000000000000000000..f62599c7b10469b9eadabb31e9a42128a769c7cd GIT binary patch literal 1222 zcmWIWW@Zs#U|`^2V0WyvGL3P1+yvw;0%Bnx&aEt{EJ)OkkI&4@EQycTE2vDq{ik!f z_N!x_=Tq1;5?GSh74)7l32E~11UgKbs2I`Asd9`*h35;;UlWH_UOO6HYHA|00t?gX z;%fG=d001TTw@jTTsqTI^OvXQ%%iGRmNP4titd`3CYJVV<;$19c1~rT%G&wsg1;w&+sQ$d~(!s_JietmCU zt#fD2clU>H2n{g5U>x$C3CR?Y$GxZZOpXTX?t_}->h7-V>F4IJAM76*_t<%}h{;@`zS&LcdYtZG(rN*BxefrA0=WeO(-#dQ{Rhs@f zH_wS}{_3UWW$+{@h&$+WP|)W|+K-EkK5y#YsHJsMzvH~8uJ>8Sljx37u41p@1UiHr zh(TV1JEkPRAU-FxEHww@oYQM{R_J&&P!l5%%OYz|Ni9gtOG(X3u8hyg z%*!qYneiB(Zb4+-Rhb34#ffRD7&;CGJDSu1Rc;4j6deKHkRbf*tLy3GspENt7ZMAb zgAA@1KltQ*#&>JbhqXK_cs!mootAjf_@v3ZxLCMbYpqDoC(%!zyp4=LU)pK&sW`Zl zTj+A*ET_MF{{A`qcZZC(x6!BW3qNg1;w&+sQ$d~(!s_JietmCU zt#fD2clU>H2n{g5U>x$C3CR?Y$GxZZOpXTX?t_}->h7-V>F4IJAM76*_t<%}h{;@`zS&LcdYtZG(rN*BxefrA0=WeO(-#dQ{Rhs@f zH_wS}{_3UWW$+{@h&$+WP|)W|+K-EkK5y#YsHJsMzvH~8uJ>8Sljx37u41p@1UiHr zh(TV1JEkPRAU-FxEHww@oYQM{R_J&&P!l5%%OYz|Ni9gtOG(X3u8hyg z%*!qYneiB(Zb4+-Rhb34#ffRD7&;CGJDSu1Rc;4j6deKHkRbf*tLy3GspENt7ZMAb zgAA@1KltQ*#&>JbhqXK_cs!mootAjf_@v3ZxLCMbYpqDoC(%!zyp4=LU)pK&sW`Zl zTj+A*ET_MF{{A`qcZZC(x6!BW3qN> (2) +c = ~b +c = not b +d = [c] +e = d[:] +e = d[a:b:c] +raise_string(*args, **kwargs) +print >> stream, 'bonjour' +print >> stream, 'salut', + +def make_class(any, base=data.module.YO, *args, **kwargs): + """check base is correctly resolved to Concrete0""" + + + class Aaaa(base): + """dynamic class""" + + + return Aaaa +from os.path import abspath +import os as myos + + +class A: + pass + + + +class A(A): + pass + + +def generator(): + """A generator.""" + yield + +def not_a_generator(): + """A function that contains generator, but is not one.""" + + def generator(): + yield + genl = lambda : (yield) + +def with_metaclass(meta, *bases): + return meta('NewBase', bases, {}) + + +class NotMetaclass(with_metaclass(Metaclass)): + pass + + diff --git a/pymode/libs/astroid/tests/testdata/python2/data/noendingnewline.py b/pymode/libs/astroid/tests/testdata/python2/data/noendingnewline.py new file mode 100644 index 00000000..e1d6e4a1 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/noendingnewline.py @@ -0,0 +1,36 @@ +import unittest + + +class TestCase(unittest.TestCase): + + def setUp(self): + unittest.TestCase.setUp(self) + + + def tearDown(self): + unittest.TestCase.tearDown(self) + + def testIt(self): + self.a = 10 + self.xxx() + + + def xxx(self): + if False: + pass + print 'a' + + if False: + pass + pass + + if False: + pass + print 'rara' + + +if __name__ == '__main__': + print 'test2' + unittest.main() + + diff --git a/pymode/libs/astroid/tests/testdata/python2/data/nonregr.py b/pymode/libs/astroid/tests/testdata/python2/data/nonregr.py new file mode 100644 index 00000000..813469fe --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/nonregr.py @@ -0,0 +1,57 @@ +from __future__ import generators, print_function + +try: + enumerate = enumerate +except NameError: + + def enumerate(iterable): + """emulates the python2.3 enumerate() function""" + i = 0 + for val in iterable: + yield i, val + i += 1 + +def toto(value): + for k, v in value: + print(v.get('yo')) + + +import imp +fp, mpath, desc = imp.find_module('optparse',a) +s_opt = imp.load_module('std_optparse', fp, mpath, desc) + +class OptionParser(s_opt.OptionParser): + + def parse_args(self, args=None, values=None, real_optparse=False): + if real_optparse: + pass +## return super(OptionParser, self).parse_args() + else: + import optcomp + optcomp.completion(self) + + +class Aaa(object): + """docstring""" + def __init__(self): + self.__setattr__('a','b') + pass + + def one_public(self): + """docstring""" + pass + + def another_public(self): + """docstring""" + pass + +class Ccc(Aaa): + """docstring""" + + class Ddd(Aaa): + """docstring""" + pass + + class Eee(Ddd): + """docstring""" + pass diff --git a/pymode/libs/astroid/tests/testdata/python2/data/notall.py b/pymode/libs/astroid/tests/testdata/python2/data/notall.py new file mode 100644 index 00000000..7be27b18 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/notall.py @@ -0,0 +1,7 @@ +name = 'a' +_bla = 2 +other = 'o' +class Aaa: pass + +def func(): print('yo') + diff --git a/pymode/libs/astroid/tests/testdata/python2/data/package/__init__.py b/pymode/libs/astroid/tests/testdata/python2/data/package/__init__.py new file mode 100644 index 00000000..575d18b1 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/package/__init__.py @@ -0,0 +1,4 @@ +"""package's __init__ file""" + + +from . import subpackage diff --git a/pymode/libs/astroid/tests/testdata/python2/data/package/absimport.py b/pymode/libs/astroid/tests/testdata/python2/data/package/absimport.py new file mode 100644 index 00000000..33ed117c --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/package/absimport.py @@ -0,0 +1,6 @@ +from __future__ import absolute_import, print_function +import import_package_subpackage_module # fail +print(import_package_subpackage_module) + +from . import hello as hola + diff --git a/pymode/libs/astroid/tests/testdata/python2/data/package/hello.py b/pymode/libs/astroid/tests/testdata/python2/data/package/hello.py new file mode 100644 index 00000000..b154c844 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/package/hello.py @@ -0,0 +1,2 @@ +"""hello module""" + diff --git a/pymode/libs/astroid/tests/testdata/python2/data/package/import_package_subpackage_module.py b/pymode/libs/astroid/tests/testdata/python2/data/package/import_package_subpackage_module.py new file mode 100644 index 00000000..ad442c16 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/package/import_package_subpackage_module.py @@ -0,0 +1,49 @@ +# pylint: disable-msg=I0011,C0301,W0611 +"""I found some of my scripts trigger off an AttributeError in pylint +0.8.1 (with common 0.12.0 and astroid 0.13.1). + +Traceback (most recent call last): + File "/usr/bin/pylint", line 4, in ? + lint.Run(sys.argv[1:]) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 729, in __init__ + linter.check(args) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 412, in check + self.check_file(filepath, modname, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 426, in check_file + astroid = self._check_file(filepath, modname, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 450, in _check_file + self.check_astroid_module(astroid, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 494, in check_astroid_module + self.astroid_events(astroid, [checker for checker in checkers + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events + self.astroid_events(child, checkers, _reversed_checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events + self.astroid_events(child, checkers, _reversed_checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 508, in astroid_events + checker.visit(astroid) + File "/usr/lib/python2.4/site-packages/logilab/astroid/utils.py", line 84, in visit + method(node) + File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 295, in visit_import + self._check_module_attrs(node, module, name_parts[1:]) + File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 357, in _check_module_attrs + self.add_message('E0611', args=(name, module.name), +AttributeError: Import instance has no attribute 'name' + + +You can reproduce it by: +(1) create package structure like the following: + +package/ + __init__.py + subpackage/ + __init__.py + module.py + +(2) in package/__init__.py write: + +import subpackage + +(3) run pylint with a script importing package.subpackage.module. +""" +__revision__ = '$Id: import_package_subpackage_module.py,v 1.1 2005-11-10 15:59:32 syt Exp $' +import package.subpackage.module diff --git a/pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/__init__.py b/pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/__init__.py new file mode 100644 index 00000000..dc4782e6 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/__init__.py @@ -0,0 +1 @@ +"""package.subpackage""" diff --git a/pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/module.py b/pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/module.py new file mode 100644 index 00000000..4b7244ba --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/package/subpackage/module.py @@ -0,0 +1 @@ +"""package.subpackage.module""" diff --git a/pymode/libs/astroid/tests/testdata/python2/data/recursion.py b/pymode/libs/astroid/tests/testdata/python2/data/recursion.py new file mode 100644 index 00000000..85f65134 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/recursion.py @@ -0,0 +1,3 @@ +""" For issue #25 """ +class Base(object): + pass \ No newline at end of file diff --git a/pymode/libs/astroid/tests/testdata/python2/data/suppliermodule_test.py b/pymode/libs/astroid/tests/testdata/python2/data/suppliermodule_test.py new file mode 100644 index 00000000..ddacb477 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/suppliermodule_test.py @@ -0,0 +1,13 @@ +""" file suppliermodule.py """ + +class NotImplemented(Exception): + pass + +class Interface: + def get_value(self): + raise NotImplemented() + + def set_value(self, value): + raise NotImplemented() + +class DoNothing : pass diff --git a/pymode/libs/astroid/tests/testdata/python2/data/unicode_package/__init__.py b/pymode/libs/astroid/tests/testdata/python2/data/unicode_package/__init__.py new file mode 100644 index 00000000..713e5591 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python2/data/unicode_package/__init__.py @@ -0,0 +1 @@ +x = "șțîâ" \ No newline at end of file diff --git a/pymode/libs/astroid/tests/testdata/python2/data/unicode_package/core/__init__.py b/pymode/libs/astroid/tests/testdata/python2/data/unicode_package/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/astroid/tests/testdata/python3/data/MyPyPa-0.1.0-py2.5.egg b/pymode/libs/astroid/tests/testdata/python3/data/MyPyPa-0.1.0-py2.5.egg new file mode 100644 index 0000000000000000000000000000000000000000..f62599c7b10469b9eadabb31e9a42128a769c7cd GIT binary patch literal 1222 zcmWIWW@Zs#U|`^2V0WyvGL3P1+yvw;0%Bnx&aEt{EJ)OkkI&4@EQycTE2vDq{ik!f z_N!x_=Tq1;5?GSh74)7l32E~11UgKbs2I`Asd9`*h35;;UlWH_UOO6HYHA|00t?gX z;%fG=d001TTw@jTTsqTI^OvXQ%%iGRmNP4titd`3CYJVV<;$19c1~rT%G&wsg1;w&+sQ$d~(!s_JietmCU zt#fD2clU>H2n{g5U>x$C3CR?Y$GxZZOpXTX?t_}->h7-V>F4IJAM76*_t<%}h{;@`zS&LcdYtZG(rN*BxefrA0=WeO(-#dQ{Rhs@f zH_wS}{_3UWW$+{@h&$+WP|)W|+K-EkK5y#YsHJsMzvH~8uJ>8Sljx37u41p@1UiHr zh(TV1JEkPRAU-FxEHww@oYQM{R_J&&P!l5%%OYz|Ni9gtOG(X3u8hyg z%*!qYneiB(Zb4+-Rhb34#ffRD7&;CGJDSu1Rc;4j6deKHkRbf*tLy3GspENt7ZMAb zgAA@1KltQ*#&>JbhqXK_cs!mootAjf_@v3ZxLCMbYpqDoC(%!zyp4=LU)pK&sW`Zl zTj+A*ET_MF{{A`qcZZC(x6!BW3qNg1;w&+sQ$d~(!s_JietmCU zt#fD2clU>H2n{g5U>x$C3CR?Y$GxZZOpXTX?t_}->h7-V>F4IJAM76*_t<%}h{;@`zS&LcdYtZG(rN*BxefrA0=WeO(-#dQ{Rhs@f zH_wS}{_3UWW$+{@h&$+WP|)W|+K-EkK5y#YsHJsMzvH~8uJ>8Sljx37u41p@1UiHr zh(TV1JEkPRAU-FxEHww@oYQM{R_J&&P!l5%%OYz|Ni9gtOG(X3u8hyg z%*!qYneiB(Zb4+-Rhb34#ffRD7&;CGJDSu1Rc;4j6deKHkRbf*tLy3GspENt7ZMAb zgAA@1KltQ*#&>JbhqXK_cs!mootAjf_@v3ZxLCMbYpqDoC(%!zyp4=LU)pK&sW`Zl zTj+A*ET_MF{{A`qcZZC(x6!BW3qN> (2) +c = ~b +c = not b +d = [c] +e = d[:] +e = d[a:b:c] +raise_string(*args, **kwargs) +print('bonjour', file=stream) +print('salut', end=' ', file=stream) + +def make_class(any, base=data.module.YO, *args, **kwargs): + """check base is correctly resolved to Concrete0""" + + + class Aaaa(base): + """dynamic class""" + + + return Aaaa +from os.path import abspath +import os as myos + + +class A: + pass + + + +class A(A): + pass + + +def generator(): + """A generator.""" + yield + +def not_a_generator(): + """A function that contains generator, but is not one.""" + + def generator(): + yield + genl = lambda : (yield) + +def with_metaclass(meta, *bases): + return meta('NewBase', bases, {}) + + +class NotMetaclass(with_metaclass(Metaclass)): + pass + + diff --git a/pymode/libs/astroid/tests/testdata/python3/data/noendingnewline.py b/pymode/libs/astroid/tests/testdata/python3/data/noendingnewline.py new file mode 100644 index 00000000..e17b92cc --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/noendingnewline.py @@ -0,0 +1,36 @@ +import unittest + + +class TestCase(unittest.TestCase): + + def setUp(self): + unittest.TestCase.setUp(self) + + + def tearDown(self): + unittest.TestCase.tearDown(self) + + def testIt(self): + self.a = 10 + self.xxx() + + + def xxx(self): + if False: + pass + print('a') + + if False: + pass + pass + + if False: + pass + print('rara') + + +if __name__ == '__main__': + print('test2') + unittest.main() + + diff --git a/pymode/libs/astroid/tests/testdata/python3/data/nonregr.py b/pymode/libs/astroid/tests/testdata/python3/data/nonregr.py new file mode 100644 index 00000000..78765c85 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/nonregr.py @@ -0,0 +1,57 @@ + + +try: + enumerate = enumerate +except NameError: + + def enumerate(iterable): + """emulates the python2.3 enumerate() function""" + i = 0 + for val in iterable: + yield i, val + i += 1 + +def toto(value): + for k, v in value: + print(v.get('yo')) + + +import imp +fp, mpath, desc = imp.find_module('optparse',a) +s_opt = imp.load_module('std_optparse', fp, mpath, desc) + +class OptionParser(s_opt.OptionParser): + + def parse_args(self, args=None, values=None, real_optparse=False): + if real_optparse: + pass +## return super(OptionParser, self).parse_args() + else: + import optcomp + optcomp.completion(self) + + +class Aaa(object): + """docstring""" + def __init__(self): + self.__setattr__('a','b') + pass + + def one_public(self): + """docstring""" + pass + + def another_public(self): + """docstring""" + pass + +class Ccc(Aaa): + """docstring""" + + class Ddd(Aaa): + """docstring""" + pass + + class Eee(Ddd): + """docstring""" + pass diff --git a/pymode/libs/astroid/tests/testdata/python3/data/notall.py b/pymode/libs/astroid/tests/testdata/python3/data/notall.py new file mode 100644 index 00000000..9d35aa3a --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/notall.py @@ -0,0 +1,8 @@ + +name = 'a' +_bla = 2 +other = 'o' +class Aaa: pass + +def func(): print('yo') + diff --git a/pymode/libs/astroid/tests/testdata/python3/data/package/__init__.py b/pymode/libs/astroid/tests/testdata/python3/data/package/__init__.py new file mode 100644 index 00000000..575d18b1 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/package/__init__.py @@ -0,0 +1,4 @@ +"""package's __init__ file""" + + +from . import subpackage diff --git a/pymode/libs/astroid/tests/testdata/python3/data/package/absimport.py b/pymode/libs/astroid/tests/testdata/python3/data/package/absimport.py new file mode 100644 index 00000000..33ed117c --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/package/absimport.py @@ -0,0 +1,6 @@ +from __future__ import absolute_import, print_function +import import_package_subpackage_module # fail +print(import_package_subpackage_module) + +from . import hello as hola + diff --git a/pymode/libs/astroid/tests/testdata/python3/data/package/hello.py b/pymode/libs/astroid/tests/testdata/python3/data/package/hello.py new file mode 100644 index 00000000..b154c844 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/package/hello.py @@ -0,0 +1,2 @@ +"""hello module""" + diff --git a/pymode/libs/astroid/tests/testdata/python3/data/package/import_package_subpackage_module.py b/pymode/libs/astroid/tests/testdata/python3/data/package/import_package_subpackage_module.py new file mode 100644 index 00000000..ad442c16 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/package/import_package_subpackage_module.py @@ -0,0 +1,49 @@ +# pylint: disable-msg=I0011,C0301,W0611 +"""I found some of my scripts trigger off an AttributeError in pylint +0.8.1 (with common 0.12.0 and astroid 0.13.1). + +Traceback (most recent call last): + File "/usr/bin/pylint", line 4, in ? + lint.Run(sys.argv[1:]) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 729, in __init__ + linter.check(args) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 412, in check + self.check_file(filepath, modname, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 426, in check_file + astroid = self._check_file(filepath, modname, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 450, in _check_file + self.check_astroid_module(astroid, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 494, in check_astroid_module + self.astroid_events(astroid, [checker for checker in checkers + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events + self.astroid_events(child, checkers, _reversed_checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events + self.astroid_events(child, checkers, _reversed_checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 508, in astroid_events + checker.visit(astroid) + File "/usr/lib/python2.4/site-packages/logilab/astroid/utils.py", line 84, in visit + method(node) + File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 295, in visit_import + self._check_module_attrs(node, module, name_parts[1:]) + File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 357, in _check_module_attrs + self.add_message('E0611', args=(name, module.name), +AttributeError: Import instance has no attribute 'name' + + +You can reproduce it by: +(1) create package structure like the following: + +package/ + __init__.py + subpackage/ + __init__.py + module.py + +(2) in package/__init__.py write: + +import subpackage + +(3) run pylint with a script importing package.subpackage.module. +""" +__revision__ = '$Id: import_package_subpackage_module.py,v 1.1 2005-11-10 15:59:32 syt Exp $' +import package.subpackage.module diff --git a/pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/__init__.py b/pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/__init__.py new file mode 100644 index 00000000..dc4782e6 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/__init__.py @@ -0,0 +1 @@ +"""package.subpackage""" diff --git a/pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/module.py b/pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/module.py new file mode 100644 index 00000000..4b7244ba --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/package/subpackage/module.py @@ -0,0 +1 @@ +"""package.subpackage.module""" diff --git a/pymode/libs/astroid/tests/testdata/python3/data/recursion.py b/pymode/libs/astroid/tests/testdata/python3/data/recursion.py new file mode 100644 index 00000000..85f65134 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/recursion.py @@ -0,0 +1,3 @@ +""" For issue #25 """ +class Base(object): + pass \ No newline at end of file diff --git a/pymode/libs/astroid/tests/testdata/python3/data/suppliermodule_test.py b/pymode/libs/astroid/tests/testdata/python3/data/suppliermodule_test.py new file mode 100644 index 00000000..ddacb477 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/suppliermodule_test.py @@ -0,0 +1,13 @@ +""" file suppliermodule.py """ + +class NotImplemented(Exception): + pass + +class Interface: + def get_value(self): + raise NotImplemented() + + def set_value(self, value): + raise NotImplemented() + +class DoNothing : pass diff --git a/pymode/libs/astroid/tests/testdata/python3/data/unicode_package/__init__.py b/pymode/libs/astroid/tests/testdata/python3/data/unicode_package/__init__.py new file mode 100644 index 00000000..713e5591 --- /dev/null +++ b/pymode/libs/astroid/tests/testdata/python3/data/unicode_package/__init__.py @@ -0,0 +1 @@ +x = "șțîâ" \ No newline at end of file diff --git a/pymode/libs/astroid/tests/testdata/python3/data/unicode_package/core/__init__.py b/pymode/libs/astroid/tests/testdata/python3/data/unicode_package/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pymode/libs/astroid/tests/unittest_brain.py b/pymode/libs/astroid/tests/unittest_brain.py new file mode 100644 index 00000000..9dbbe1d0 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_brain.py @@ -0,0 +1,506 @@ +# Copyright 2013 Google Inc. All Rights Reserved. +# +# This file is part of astroid. +# +# logilab-astng is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# logilab-astng is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with logilab-astng. If not, see . +"""Tests for basic functionality in astroid.brain.""" +import sys +import unittest + +import six + +from astroid import MANAGER +from astroid import bases +from astroid import builder +from astroid import nodes +from astroid import test_utils +from astroid import util +import astroid + + +try: + import nose # pylint: disable=unused-import + HAS_NOSE = True +except ImportError: + HAS_NOSE = False + +try: + import multiprocessing # pylint: disable=unused-import + HAS_MULTIPROCESSING = True +except ImportError: + HAS_MULTIPROCESSING = False + +try: + import enum # pylint: disable=unused-import + HAS_ENUM = True +except ImportError: + HAS_ENUM = False + +try: + import dateutil # pylint: disable=unused-import + HAS_DATEUTIL = True +except ImportError: + HAS_DATEUTIL = False + +try: + import numpy # pylint: disable=unused-import + HAS_NUMPY = True +except ImportError: + HAS_NUMPY = False + +try: + import pytest # pylint: disable=unused-import + HAS_PYTEST = True +except ImportError: + HAS_PYTEST = False + + +class HashlibTest(unittest.TestCase): + def test_hashlib(self): + """Tests that brain extensions for hashlib work.""" + hashlib_module = MANAGER.ast_from_module_name('hashlib') + for class_name in ['md5', 'sha1']: + class_obj = hashlib_module[class_name] + self.assertIn('update', class_obj) + self.assertIn('digest', class_obj) + self.assertIn('hexdigest', class_obj) + self.assertIn('block_size', class_obj) + self.assertIn('digest_size', class_obj) + self.assertEqual(len(class_obj['__init__'].args.args), 2) + self.assertEqual(len(class_obj['__init__'].args.defaults), 1) + self.assertEqual(len(class_obj['update'].args.args), 2) + self.assertEqual(len(class_obj['digest'].args.args), 1) + self.assertEqual(len(class_obj['hexdigest'].args.args), 1) + + +class NamedTupleTest(unittest.TestCase): + + def test_namedtuple_base(self): + klass = test_utils.extract_node(""" + from collections import namedtuple + + class X(namedtuple("X", ["a", "b", "c"])): + pass + """) + self.assertEqual( + [anc.name for anc in klass.ancestors()], + ['X', 'tuple', 'object']) + for anc in klass.ancestors(): + self.assertFalse(anc.parent is None) + + def test_namedtuple_inference(self): + klass = test_utils.extract_node(""" + from collections import namedtuple + + name = "X" + fields = ["a", "b", "c"] + class X(namedtuple(name, fields)): + pass + """) + for base in klass.ancestors(): + if base.name == 'X': + break + self.assertSetEqual({"a", "b", "c"}, set(base._instance_attrs)) + + def test_namedtuple_inference_failure(self): + klass = test_utils.extract_node(""" + from collections import namedtuple + + def foo(fields): + return __(namedtuple("foo", fields)) + """) + self.assertIs(util.YES, next(klass.infer())) + + @unittest.skipIf(sys.version_info[0] > 2, + 'namedtuple inference is broken on Python 3') + def test_namedtuple_advanced_inference(self): + # urlparse return an object of class ParseResult, which has a + # namedtuple call and a mixin as base classes + result = test_utils.extract_node(""" + import urlparse + + result = __(urlparse.urlparse('gopher://')) + """) + instance = next(result.infer()) + self.assertEqual(len(instance.getattr('scheme')), 1) + self.assertEqual(len(instance.getattr('port')), 1) + with self.assertRaises(astroid.NotFoundError): + instance.getattr('foo') + self.assertEqual(len(instance.getattr('geturl')), 1) + self.assertEqual(instance.name, 'ParseResult') + + def test_namedtuple_instance_attrs(self): + result = test_utils.extract_node(''' + from collections import namedtuple + namedtuple('a', 'a b c')(1, 2, 3) #@ + ''') + inferred = next(result.infer()) + for name, attr in inferred._instance_attrs.items(): + self.assertEqual(attr[0].attrname, name) + + def test_namedtuple_uninferable_fields(self): + node = test_utils.extract_node(''' + x = [A] * 2 + from collections import namedtuple + l = namedtuple('a', x) + l(1) + ''') + inferred = next(node.infer()) + self.assertIs(util.YES, inferred) + + +class ModuleExtenderTest(unittest.TestCase): + def testExtensionModules(self): + transformer = MANAGER._transform + for extender, _ in transformer.transforms[nodes.Module]: + n = nodes.Module('__main__', None) + extender(n) + + +@unittest.skipUnless(HAS_NOSE, "This test requires nose library.") +class NoseBrainTest(unittest.TestCase): + + def test_nose_tools(self): + methods = test_utils.extract_node(""" + from nose.tools import assert_equal + from nose.tools import assert_equals + from nose.tools import assert_true + assert_equal = assert_equal #@ + assert_true = assert_true #@ + assert_equals = assert_equals #@ + """) + assert_equal = next(methods[0].value.infer()) + assert_true = next(methods[1].value.infer()) + assert_equals = next(methods[2].value.infer()) + + self.assertIsInstance(assert_equal, astroid.BoundMethod) + self.assertIsInstance(assert_true, astroid.BoundMethod) + self.assertIsInstance(assert_equals, astroid.BoundMethod) + self.assertEqual(assert_equal.qname(), + 'unittest.case.TestCase.assertEqual') + self.assertEqual(assert_true.qname(), + 'unittest.case.TestCase.assertTrue') + self.assertEqual(assert_equals.qname(), + 'unittest.case.TestCase.assertEqual') + + +class SixBrainTest(unittest.TestCase): + + def test_attribute_access(self): + ast_nodes = test_utils.extract_node(''' + import six + six.moves.http_client #@ + six.moves.urllib_parse #@ + six.moves.urllib_error #@ + six.moves.urllib.request #@ + ''') + http_client = next(ast_nodes[0].infer()) + self.assertIsInstance(http_client, nodes.Module) + self.assertEqual(http_client.name, + 'http.client' if six.PY3 else 'httplib') + + urllib_parse = next(ast_nodes[1].infer()) + if six.PY3: + self.assertIsInstance(urllib_parse, nodes.Module) + self.assertEqual(urllib_parse.name, 'urllib.parse') + else: + # On Python 2, this is a fake module, the same behaviour + # being mimicked in brain's tip for six.moves. + self.assertIsInstance(urllib_parse, astroid.Instance) + urljoin = next(urllib_parse.igetattr('urljoin')) + urlencode = next(urllib_parse.igetattr('urlencode')) + if six.PY2: + # In reality it's a function, but our implementations + # transforms it into a method. + self.assertIsInstance(urljoin, astroid.BoundMethod) + self.assertEqual(urljoin.qname(), 'urlparse.urljoin') + self.assertIsInstance(urlencode, astroid.BoundMethod) + self.assertEqual(urlencode.qname(), 'urllib.urlencode') + else: + self.assertIsInstance(urljoin, nodes.FunctionDef) + self.assertEqual(urljoin.qname(), 'urllib.parse.urljoin') + self.assertIsInstance(urlencode, nodes.FunctionDef) + self.assertEqual(urlencode.qname(), 'urllib.parse.urlencode') + + urllib_error = next(ast_nodes[2].infer()) + if six.PY3: + self.assertIsInstance(urllib_error, nodes.Module) + self.assertEqual(urllib_error.name, 'urllib.error') + else: + # On Python 2, this is a fake module, the same behaviour + # being mimicked in brain's tip for six.moves. + self.assertIsInstance(urllib_error, astroid.Instance) + urlerror = next(urllib_error.igetattr('URLError')) + self.assertIsInstance(urlerror, nodes.ClassDef) + content_too_short = next(urllib_error.igetattr('ContentTooShortError')) + self.assertIsInstance(content_too_short, nodes.ClassDef) + + urllib_request = next(ast_nodes[3].infer()) + if six.PY3: + self.assertIsInstance(urllib_request, nodes.Module) + self.assertEqual(urllib_request.name, 'urllib.request') + else: + self.assertIsInstance(urllib_request, astroid.Instance) + urlopen = next(urllib_request.igetattr('urlopen')) + urlretrieve = next(urllib_request.igetattr('urlretrieve')) + if six.PY2: + # In reality it's a function, but our implementations + # transforms it into a method. + self.assertIsInstance(urlopen, astroid.BoundMethod) + self.assertEqual(urlopen.qname(), 'urllib2.urlopen') + self.assertIsInstance(urlretrieve, astroid.BoundMethod) + self.assertEqual(urlretrieve.qname(), 'urllib.urlretrieve') + else: + self.assertIsInstance(urlopen, nodes.FunctionDef) + self.assertEqual(urlopen.qname(), 'urllib.request.urlopen') + self.assertIsInstance(urlretrieve, nodes.FunctionDef) + self.assertEqual(urlretrieve.qname(), 'urllib.request.urlretrieve') + + def test_from_imports(self): + ast_node = test_utils.extract_node(''' + from six.moves import http_client + http_client.HTTPSConnection #@ + ''') + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, nodes.ClassDef) + if six.PY3: + qname = 'http.client.HTTPSConnection' + else: + qname = 'httplib.HTTPSConnection' + self.assertEqual(inferred.qname(), qname) + + +@unittest.skipUnless(HAS_MULTIPROCESSING, + 'multiprocesing is required for this test, but ' + 'on some platforms it is missing ' + '(Jython for instance)') +class MultiprocessingBrainTest(unittest.TestCase): + + def test_multiprocessing_module_attributes(self): + # Test that module attributes are working, + # especially on Python 3.4+, where they are obtained + # from a context. + module = test_utils.extract_node(""" + import multiprocessing + """) + module = module.do_import_module('multiprocessing') + cpu_count = next(module.igetattr('cpu_count')) + if sys.version_info < (3, 4): + self.assertIsInstance(cpu_count, nodes.FunctionDef) + else: + self.assertIsInstance(cpu_count, astroid.BoundMethod) + + def test_module_name(self): + module = test_utils.extract_node(""" + import multiprocessing + multiprocessing.SyncManager() + """) + inferred_sync_mgr = next(module.infer()) + module = inferred_sync_mgr.root() + self.assertEqual(module.name, 'multiprocessing.managers') + + def test_multiprocessing_manager(self): + # Test that we have the proper attributes + # for a multiprocessing.managers.SyncManager + module = builder.parse(""" + import multiprocessing + manager = multiprocessing.Manager() + queue = manager.Queue() + joinable_queue = manager.JoinableQueue() + event = manager.Event() + rlock = manager.RLock() + bounded_semaphore = manager.BoundedSemaphore() + condition = manager.Condition() + barrier = manager.Barrier() + pool = manager.Pool() + list = manager.list() + dict = manager.dict() + value = manager.Value() + array = manager.Array() + namespace = manager.Namespace() + """) + queue = next(module['queue'].infer()) + self.assertEqual(queue.qname(), + "{}.Queue".format(six.moves.queue.__name__)) + + joinable_queue = next(module['joinable_queue'].infer()) + self.assertEqual(joinable_queue.qname(), + "{}.Queue".format(six.moves.queue.__name__)) + + event = next(module['event'].infer()) + event_name = "threading.{}".format("Event" if six.PY3 else "_Event") + self.assertEqual(event.qname(), event_name) + + rlock = next(module['rlock'].infer()) + rlock_name = "threading._RLock" + self.assertEqual(rlock.qname(), rlock_name) + + bounded_semaphore = next(module['bounded_semaphore'].infer()) + semaphore_name = "threading.{}".format( + "BoundedSemaphore" if six.PY3 else "_BoundedSemaphore") + self.assertEqual(bounded_semaphore.qname(), semaphore_name) + + pool = next(module['pool'].infer()) + pool_name = "multiprocessing.pool.Pool" + self.assertEqual(pool.qname(), pool_name) + + for attr in ('list', 'dict'): + obj = next(module[attr].infer()) + self.assertEqual(obj.qname(), + "{}.{}".format(bases.BUILTINS, attr)) + + array = next(module['array'].infer()) + self.assertEqual(array.qname(), "array.array") + + manager = next(module['manager'].infer()) + # Verify that we have these attributes + self.assertTrue(manager.getattr('start')) + self.assertTrue(manager.getattr('shutdown')) + + +@unittest.skipUnless(HAS_ENUM, + 'The enum module was only added in Python 3.4. Support for ' + 'older Python versions may be available through the enum34 ' + 'compatibility module.') +class EnumBrainTest(unittest.TestCase): + + def test_simple_enum(self): + module = builder.parse(""" + import enum + + class MyEnum(enum.Enum): + one = "one" + two = "two" + + def mymethod(self, x): + return 5 + + """) + + enum = next(module['MyEnum'].infer()) + one = enum['one'] + self.assertEqual(one.pytype(), '.MyEnum.one') + + property_type = '{}.property'.format(bases.BUILTINS) + for propname in ('name', 'value'): + prop = next(iter(one.getattr(propname))) + self.assertIn(property_type, prop.decoratornames()) + + meth = one.getattr('mymethod')[0] + self.assertIsInstance(meth, astroid.FunctionDef) + + def test_looks_like_enum_false_positive(self): + # Test that a class named Enumeration is not considered a builtin enum. + module = builder.parse(''' + class Enumeration(object): + def __init__(self, name, enum_list): + pass + test = 42 + ''') + enum = module['Enumeration'] + test = next(enum.igetattr('test')) + self.assertEqual(test.value, 42) + + def test_enum_multiple_base_classes(self): + module = builder.parse(""" + import enum + + class Mixin: + pass + + class MyEnum(Mixin, enum.Enum): + one = 1 + """) + enum = next(module['MyEnum'].infer()) + one = enum['one'] + + clazz = one.getattr('__class__')[0] + self.assertTrue(clazz.is_subtype_of('.Mixin'), + 'Enum instance should share base classes with generating class') + + def test_int_enum(self): + module = builder.parse(""" + import enum + + class MyEnum(enum.IntEnum): + one = 1 + """) + + enum = next(module['MyEnum'].infer()) + one = enum['one'] + + clazz = one.getattr('__class__')[0] + int_type = '{}.{}'.format(bases.BUILTINS, 'int') + self.assertTrue(clazz.is_subtype_of(int_type), + 'IntEnum based enums should be a subtype of int') + + def test_enum_func_form_is_class_not_instance(self): + cls, instance = test_utils.extract_node(''' + from enum import Enum + f = Enum('Audience', ['a', 'b', 'c']) + f #@ + f(1) #@ + ''') + inferred_cls = next(cls.infer()) + self.assertIsInstance(inferred_cls, bases.Instance) + inferred_instance = next(instance.infer()) + self.assertIsInstance(inferred_instance, bases.Instance) + self.assertIsInstance(next(inferred_instance.igetattr('name')), nodes.Const) + self.assertIsInstance(next(inferred_instance.igetattr('value')), nodes.Const) + + +@unittest.skipUnless(HAS_DATEUTIL, "This test requires the dateutil library.") +class DateutilBrainTest(unittest.TestCase): + def test_parser(self): + module = builder.parse(""" + from dateutil.parser import parse + d = parse('2000-01-01') + """) + d_type = next(module['d'].infer()) + self.assertEqual(d_type.qname(), "datetime.datetime") + + +@unittest.skipUnless(HAS_NUMPY, "This test requires the numpy library.") +class NumpyBrainTest(unittest.TestCase): + + def test_numpy(self): + node = test_utils.extract_node(''' + import numpy + numpy.ones #@ + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.FunctionDef) + + +@unittest.skipUnless(HAS_PYTEST, "This test requires the pytest library.") +class PytestBrainTest(unittest.TestCase): + + def test_pytest(self): + ast_node = test_utils.extract_node(''' + import pytest + pytest #@ + ''') + module = next(ast_node.infer()) + attrs = ['deprecated_call', 'warns', 'exit', 'fail', 'skip', + 'importorskip', 'xfail', 'mark', 'raises', 'freeze_includes', + 'set_trace', 'fixture', 'yield_fixture'] + if pytest.__version__.split('.')[0] == '3': + attrs += ['approx', 'register_assert_rewrite'] + + for attr in attrs: + self.assertIn(attr, module) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_builder.py b/pymode/libs/astroid/tests/unittest_builder.py new file mode 100644 index 00000000..920f36e8 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_builder.py @@ -0,0 +1,774 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +"""tests for the astroid builder and rebuilder module""" + +import os +import sys +import unittest + +import six + +from astroid import builder +from astroid import exceptions +from astroid import manager +from astroid import nodes +from astroid import test_utils +from astroid import util +from astroid.tests import resources + +MANAGER = manager.AstroidManager() +BUILTINS = six.moves.builtins.__name__ + + +class FromToLineNoTest(unittest.TestCase): + + def setUp(self): + self.astroid = resources.build_file('data/format.py') + + def test_callfunc_lineno(self): + stmts = self.astroid.body + # on line 4: + # function('aeozrijz\ + # earzer', hop) + discard = stmts[0] + self.assertIsInstance(discard, nodes.Expr) + self.assertEqual(discard.fromlineno, 4) + self.assertEqual(discard.tolineno, 5) + callfunc = discard.value + self.assertIsInstance(callfunc, nodes.Call) + self.assertEqual(callfunc.fromlineno, 4) + self.assertEqual(callfunc.tolineno, 5) + name = callfunc.func + self.assertIsInstance(name, nodes.Name) + self.assertEqual(name.fromlineno, 4) + self.assertEqual(name.tolineno, 4) + strarg = callfunc.args[0] + self.assertIsInstance(strarg, nodes.Const) + if hasattr(sys, 'pypy_version_info'): + lineno = 4 + else: + lineno = 5 # no way for this one in CPython (is 4 actually) + self.assertEqual(strarg.fromlineno, lineno) + self.assertEqual(strarg.tolineno, lineno) + namearg = callfunc.args[1] + self.assertIsInstance(namearg, nodes.Name) + self.assertEqual(namearg.fromlineno, 5) + self.assertEqual(namearg.tolineno, 5) + # on line 10: + # fonction(1, + # 2, + # 3, + # 4) + discard = stmts[2] + self.assertIsInstance(discard, nodes.Expr) + self.assertEqual(discard.fromlineno, 10) + self.assertEqual(discard.tolineno, 13) + callfunc = discard.value + self.assertIsInstance(callfunc, nodes.Call) + self.assertEqual(callfunc.fromlineno, 10) + self.assertEqual(callfunc.tolineno, 13) + name = callfunc.func + self.assertIsInstance(name, nodes.Name) + self.assertEqual(name.fromlineno, 10) + self.assertEqual(name.tolineno, 10) + for i, arg in enumerate(callfunc.args): + self.assertIsInstance(arg, nodes.Const) + self.assertEqual(arg.fromlineno, 10+i) + self.assertEqual(arg.tolineno, 10+i) + + def test_function_lineno(self): + stmts = self.astroid.body + # on line 15: + # def definition(a, + # b, + # c): + # return a + b + c + function = stmts[3] + self.assertIsInstance(function, nodes.FunctionDef) + self.assertEqual(function.fromlineno, 15) + self.assertEqual(function.tolineno, 18) + return_ = function.body[0] + self.assertIsInstance(return_, nodes.Return) + self.assertEqual(return_.fromlineno, 18) + self.assertEqual(return_.tolineno, 18) + if sys.version_info < (3, 0): + self.assertEqual(function.blockstart_tolineno, 17) + else: + self.skipTest('FIXME http://bugs.python.org/issue10445 ' + '(no line number on function args)') + + def test_decorated_function_lineno(self): + astroid = builder.parse(''' + @decorator + def function( + arg): + print (arg) + ''', __name__) + function = astroid['function'] + self.assertEqual(function.fromlineno, 3) # XXX discussable, but that's what is expected by pylint right now + self.assertEqual(function.tolineno, 5) + self.assertEqual(function.decorators.fromlineno, 2) + self.assertEqual(function.decorators.tolineno, 2) + if sys.version_info < (3, 0): + self.assertEqual(function.blockstart_tolineno, 4) + else: + self.skipTest('FIXME http://bugs.python.org/issue10445 ' + '(no line number on function args)') + + + def test_class_lineno(self): + stmts = self.astroid.body + # on line 20: + # class debile(dict, + # object): + # pass + class_ = stmts[4] + self.assertIsInstance(class_, nodes.ClassDef) + self.assertEqual(class_.fromlineno, 20) + self.assertEqual(class_.tolineno, 22) + self.assertEqual(class_.blockstart_tolineno, 21) + pass_ = class_.body[0] + self.assertIsInstance(pass_, nodes.Pass) + self.assertEqual(pass_.fromlineno, 22) + self.assertEqual(pass_.tolineno, 22) + + def test_if_lineno(self): + stmts = self.astroid.body + # on line 20: + # if aaaa: pass + # else: + # aaaa,bbbb = 1,2 + # aaaa,bbbb = bbbb,aaaa + if_ = stmts[5] + self.assertIsInstance(if_, nodes.If) + self.assertEqual(if_.fromlineno, 24) + self.assertEqual(if_.tolineno, 27) + self.assertEqual(if_.blockstart_tolineno, 24) + self.assertEqual(if_.orelse[0].fromlineno, 26) + self.assertEqual(if_.orelse[1].tolineno, 27) + + def test_for_while_lineno(self): + for code in (''' + for a in range(4): + print (a) + break + else: + print ("bouh") + ''', ''' + while a: + print (a) + break + else: + print ("bouh") + '''): + astroid = builder.parse(code, __name__) + stmt = astroid.body[0] + self.assertEqual(stmt.fromlineno, 2) + self.assertEqual(stmt.tolineno, 6) + self.assertEqual(stmt.blockstart_tolineno, 2) + self.assertEqual(stmt.orelse[0].fromlineno, 6) # XXX + self.assertEqual(stmt.orelse[0].tolineno, 6) + + def test_try_except_lineno(self): + astroid = builder.parse(''' + try: + print (a) + except: + pass + else: + print ("bouh") + ''', __name__) + try_ = astroid.body[0] + self.assertEqual(try_.fromlineno, 2) + self.assertEqual(try_.tolineno, 7) + self.assertEqual(try_.blockstart_tolineno, 2) + self.assertEqual(try_.orelse[0].fromlineno, 7) # XXX + self.assertEqual(try_.orelse[0].tolineno, 7) + hdlr = try_.handlers[0] + self.assertEqual(hdlr.fromlineno, 4) + self.assertEqual(hdlr.tolineno, 5) + self.assertEqual(hdlr.blockstart_tolineno, 4) + + + def test_try_finally_lineno(self): + astroid = builder.parse(''' + try: + print (a) + finally: + print ("bouh") + ''', __name__) + try_ = astroid.body[0] + self.assertEqual(try_.fromlineno, 2) + self.assertEqual(try_.tolineno, 5) + self.assertEqual(try_.blockstart_tolineno, 2) + self.assertEqual(try_.finalbody[0].fromlineno, 5) # XXX + self.assertEqual(try_.finalbody[0].tolineno, 5) + + + def test_try_finally_25_lineno(self): + astroid = builder.parse(''' + try: + print (a) + except: + pass + finally: + print ("bouh") + ''', __name__) + try_ = astroid.body[0] + self.assertEqual(try_.fromlineno, 2) + self.assertEqual(try_.tolineno, 7) + self.assertEqual(try_.blockstart_tolineno, 2) + self.assertEqual(try_.finalbody[0].fromlineno, 7) # XXX + self.assertEqual(try_.finalbody[0].tolineno, 7) + + + def test_with_lineno(self): + astroid = builder.parse(''' + from __future__ import with_statement + with file("/tmp/pouet") as f: + print (f) + ''', __name__) + with_ = astroid.body[1] + self.assertEqual(with_.fromlineno, 3) + self.assertEqual(with_.tolineno, 4) + self.assertEqual(with_.blockstart_tolineno, 3) + + +class BuilderTest(unittest.TestCase): + + def setUp(self): + self.builder = builder.AstroidBuilder() + + def test_data_build_null_bytes(self): + with self.assertRaises(exceptions.AstroidBuildingException): + self.builder.string_build('\x00') + + def test_data_build_invalid_x_escape(self): + with self.assertRaises(exceptions.AstroidBuildingException): + self.builder.string_build('"\\x1"') + + def test_missing_newline(self): + """check that a file with no trailing new line is parseable""" + resources.build_file('data/noendingnewline.py') + + def test_missing_file(self): + with self.assertRaises(exceptions.AstroidBuildingException): + resources.build_file('data/inexistant.py') + + def test_inspect_build0(self): + """test astroid tree build from a living object""" + builtin_ast = MANAGER.ast_from_module_name(BUILTINS) + if six.PY2: + fclass = builtin_ast['file'] + self.assertIn('name', fclass) + self.assertIn('mode', fclass) + self.assertIn('read', fclass) + self.assertTrue(fclass.newstyle) + self.assertTrue(fclass.pytype(), '%s.type' % BUILTINS) + self.assertIsInstance(fclass['read'], nodes.FunctionDef) + # check builtin function has args.args == None + dclass = builtin_ast['dict'] + self.assertIsNone(dclass['has_key'].args.args) + # just check type and object are there + builtin_ast.getattr('type') + objectastroid = builtin_ast.getattr('object')[0] + self.assertIsInstance(objectastroid.getattr('__new__')[0], nodes.FunctionDef) + # check open file alias + builtin_ast.getattr('open') + # check 'help' is there (defined dynamically by site.py) + builtin_ast.getattr('help') + # check property has __init__ + pclass = builtin_ast['property'] + self.assertIn('__init__', pclass) + self.assertIsInstance(builtin_ast['None'], nodes.Const) + self.assertIsInstance(builtin_ast['True'], nodes.Const) + self.assertIsInstance(builtin_ast['False'], nodes.Const) + if six.PY3: + self.assertIsInstance(builtin_ast['Exception'], nodes.ClassDef) + self.assertIsInstance(builtin_ast['NotImplementedError'], nodes.ClassDef) + else: + self.assertIsInstance(builtin_ast['Exception'], nodes.ImportFrom) + self.assertIsInstance(builtin_ast['NotImplementedError'], nodes.ImportFrom) + + def test_inspect_build1(self): + time_ast = MANAGER.ast_from_module_name('time') + self.assertTrue(time_ast) + self.assertEqual(time_ast['time'].args.defaults, []) + + if os.name == 'java': + test_inspect_build1 = unittest.expectedFailure(test_inspect_build1) + + def test_inspect_build2(self): + """test astroid tree build from a living object""" + try: + from mx import DateTime + except ImportError: + self.skipTest('test skipped: mxDateTime is not available') + else: + dt_ast = self.builder.inspect_build(DateTime) + dt_ast.getattr('DateTime') + # this one is failing since DateTimeType.__module__ = 'builtins' ! + #dt_ast.getattr('DateTimeType') + + def test_inspect_build3(self): + self.builder.inspect_build(unittest) + + @test_utils.require_version(maxver='3.0') + def test_inspect_build_instance(self): + """test astroid tree build from a living object""" + import exceptions + builtin_ast = self.builder.inspect_build(exceptions) + fclass = builtin_ast['OSError'] + # things like OSError.strerror are now (2.5) data descriptors on the + # class instead of entries in the __dict__ of an instance + container = fclass + self.assertIn('errno', container) + self.assertIn('strerror', container) + self.assertIn('filename', container) + + def test_inspect_build_type_object(self): + builtin_ast = MANAGER.ast_from_module_name(BUILTINS) + + inferred = list(builtin_ast.igetattr('object')) + self.assertEqual(len(inferred), 1) + inferred = inferred[0] + self.assertEqual(inferred.name, 'object') + inferred.as_string() # no crash test + + inferred = list(builtin_ast.igetattr('type')) + self.assertEqual(len(inferred), 1) + inferred = inferred[0] + self.assertEqual(inferred.name, 'type') + inferred.as_string() # no crash test + + def test_inspect_transform_module(self): + # ensure no cached version of the time module + MANAGER._mod_file_cache.pop(('time', None), None) + MANAGER.astroid_cache.pop('time', None) + def transform_time(node): + if node.name == 'time': + node.transformed = True + MANAGER.register_transform(nodes.Module, transform_time) + try: + time_ast = MANAGER.ast_from_module_name('time') + self.assertTrue(getattr(time_ast, 'transformed', False)) + finally: + MANAGER.unregister_transform(nodes.Module, transform_time) + + def test_package_name(self): + """test base properties and method of a astroid module""" + datap = resources.build_file('data/__init__.py', 'data') + self.assertEqual(datap.name, 'data') + self.assertEqual(datap.package, 1) + datap = resources.build_file('data/__init__.py', 'data.__init__') + self.assertEqual(datap.name, 'data') + self.assertEqual(datap.package, 1) + + def test_yield_parent(self): + """check if we added discard nodes as yield parent (w/ compiler)""" + code = """ + def yiell(): #@ + yield 0 + if noe: + yield more + """ + func = test_utils.extract_node(code) + self.assertIsInstance(func, nodes.FunctionDef) + stmt = func.body[0] + self.assertIsInstance(stmt, nodes.Expr) + self.assertIsInstance(stmt.value, nodes.Yield) + self.assertIsInstance(func.body[1].body[0], nodes.Expr) + self.assertIsInstance(func.body[1].body[0].value, nodes.Yield) + + def test_object(self): + obj_ast = self.builder.inspect_build(object) + self.assertIn('__setattr__', obj_ast) + + def test_newstyle_detection(self): + data = ''' + class A: + "old style" + + class B(A): + "old style" + + class C(object): + "new style" + + class D(C): + "new style" + + __metaclass__ = type + + class E(A): + "old style" + + class F: + "new style" + ''' + mod_ast = builder.parse(data, __name__) + if six.PY3: + self.assertTrue(mod_ast['A'].newstyle) + self.assertTrue(mod_ast['B'].newstyle) + self.assertTrue(mod_ast['E'].newstyle) + else: + self.assertFalse(mod_ast['A'].newstyle) + self.assertFalse(mod_ast['B'].newstyle) + self.assertFalse(mod_ast['E'].newstyle) + self.assertTrue(mod_ast['C'].newstyle) + self.assertTrue(mod_ast['D'].newstyle) + self.assertTrue(mod_ast['F'].newstyle) + + def test_globals(self): + data = ''' + CSTE = 1 + + def update_global(): + global CSTE + CSTE += 1 + + def global_no_effect(): + global CSTE2 + print (CSTE) + ''' + astroid = builder.parse(data, __name__) + self.assertEqual(len(astroid.getattr('CSTE')), 2) + self.assertIsInstance(astroid.getattr('CSTE')[0], nodes.AssignName) + self.assertEqual(astroid.getattr('CSTE')[0].fromlineno, 2) + self.assertEqual(astroid.getattr('CSTE')[1].fromlineno, 6) + with self.assertRaises(exceptions.NotFoundError): + astroid.getattr('CSTE2') + with self.assertRaises(exceptions.InferenceError): + next(astroid['global_no_effect'].ilookup('CSTE2')) + + @unittest.skipIf(os.name == 'java', + 'This test is skipped on Jython, because the ' + 'socket object is patched later on with the ' + 'methods we are looking for. Since we do not ' + 'understand setattr in for loops yet, we skip this') + def test_socket_build(self): + import socket + astroid = self.builder.module_build(socket) + # XXX just check the first one. Actually 3 objects are inferred (look at + # the socket module) but the last one as those attributes dynamically + # set and astroid is missing this. + for fclass in astroid.igetattr('socket'): + self.assertIn('connect', fclass) + self.assertIn('send', fclass) + self.assertIn('close', fclass) + break + + def test_gen_expr_var_scope(self): + data = 'l = list(n for n in range(10))\n' + astroid = builder.parse(data, __name__) + # n unavailable outside gen expr scope + self.assertNotIn('n', astroid) + # test n is inferable anyway + n = test_utils.get_name_node(astroid, 'n') + self.assertIsNot(n.scope(), astroid) + self.assertEqual([i.__class__ for i in n.infer()], + [util.YES.__class__]) + + def test_no_future_imports(self): + mod = builder.parse("import sys") + self.assertEqual(set(), mod._future_imports) + + def test_future_imports(self): + mod = builder.parse("from __future__ import print_function") + self.assertEqual(set(['print_function']), mod._future_imports) + + def test_two_future_imports(self): + mod = builder.parse(""" + from __future__ import print_function + from __future__ import absolute_import + """) + self.assertEqual(set(['print_function', 'absolute_import']), mod._future_imports) + + def test_inferred_build(self): + code = ''' + class A: pass + A.type = "class" + + def A_assign_type(self): + print (self) + A.assign_type = A_assign_type + ''' + astroid = builder.parse(code) + lclass = list(astroid.igetattr('A')) + self.assertEqual(len(lclass), 1) + lclass = lclass[0] + self.assertIn('assign_type', lclass._locals) + self.assertIn('type', lclass._locals) + + def test_augassign_attr(self): + builder.parse(""" + class Counter: + v = 0 + def inc(self): + self.v += 1 + """, __name__) + # TODO: Check self.v += 1 generate AugAssign(AssAttr(...)), + # not AugAssign(GetAttr(AssName...)) + + def test_inferred_dont_pollute(self): + code = ''' + def func(a=None): + a.custom_attr = 0 + def func2(a={}): + a.custom_attr = 0 + ''' + builder.parse(code) + nonetype = nodes.const_factory(None) + self.assertNotIn('custom_attr', nonetype._locals) + self.assertNotIn('custom_attr', nonetype._instance_attrs) + nonetype = nodes.const_factory({}) + self.assertNotIn('custom_attr', nonetype._locals) + self.assertNotIn('custom_attr', nonetype._instance_attrs) + + def test_asstuple(self): + code = 'a, b = range(2)' + astroid = builder.parse(code) + self.assertIn('b', astroid._locals) + code = ''' + def visit_if(self, node): + node.test, body = node.tests[0] + ''' + astroid = builder.parse(code) + self.assertIn('body', astroid['visit_if']._locals) + + def test_build_constants(self): + '''test expected values of constants after rebuilding''' + code = ''' + def func(): + return None + return + return 'None' + ''' + astroid = builder.parse(code) + none, nothing, chain = [ret.value for ret in astroid.body[0].body] + self.assertIsInstance(none, nodes.Const) + self.assertIsNone(none.value) + self.assertIsNone(nothing) + self.assertIsInstance(chain, nodes.Const) + self.assertEqual(chain.value, 'None') + + def test_not_implemented(self): + node = test_utils.extract_node(''' + NotImplemented #@ + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, NotImplemented) + + +class FileBuildTest(unittest.TestCase): + def setUp(self): + self.module = resources.build_file('data/module.py', 'data.module') + + def test_module_base_props(self): + """test base properties and method of a astroid module""" + module = self.module + self.assertEqual(module.name, 'data.module') + self.assertEqual(module.doc, "test module for astroid\n") + self.assertEqual(module.fromlineno, 0) + self.assertIsNone(module.parent) + self.assertEqual(module.frame(), module) + self.assertEqual(module.root(), module) + self.assertEqual(module.source_file, os.path.abspath(resources.find('data/module.py'))) + self.assertEqual(module.pure_python, 1) + self.assertEqual(module.package, 0) + self.assertFalse(module.is_statement) + self.assertEqual(module.statement(), module) + self.assertEqual(module.statement(), module) + + def test_module_locals(self): + """test the 'locals' dictionary of a astroid module""" + module = self.module + _locals = module._locals + self.assertIs(_locals, module._globals) + keys = sorted(_locals.keys()) + should = ['MY_DICT', 'NameNode', 'YO', 'YOUPI', + '__revision__', 'global_access', 'modutils', 'four_args', + 'os', 'redirect'] + should.sort() + self.assertEqual(keys, sorted(should)) + + def test_function_base_props(self): + """test base properties and method of a astroid function""" + module = self.module + function = module['global_access'] + self.assertEqual(function.name, 'global_access') + self.assertEqual(function.doc, 'function test') + self.assertEqual(function.fromlineno, 11) + self.assertTrue(function.parent) + self.assertEqual(function.frame(), function) + self.assertEqual(function.parent.frame(), module) + self.assertEqual(function.root(), module) + self.assertEqual([n.name for n in function.args.args], ['key', 'val']) + self.assertEqual(function.type, 'function') + + def test_function_locals(self): + """test the 'locals' dictionary of a astroid function""" + _locals = self.module['global_access']._locals + self.assertEqual(len(_locals), 4) + keys = sorted(_locals.keys()) + self.assertEqual(keys, ['i', 'key', 'local', 'val']) + + def test_class_base_props(self): + """test base properties and method of a astroid class""" + module = self.module + klass = module['YO'] + self.assertEqual(klass.name, 'YO') + self.assertEqual(klass.doc, 'hehe') + self.assertEqual(klass.fromlineno, 25) + self.assertTrue(klass.parent) + self.assertEqual(klass.frame(), klass) + self.assertEqual(klass.parent.frame(), module) + self.assertEqual(klass.root(), module) + self.assertEqual(klass.basenames, []) + if six.PY3: + self.assertTrue(klass.newstyle) + else: + self.assertFalse(klass.newstyle) + + def test_class_locals(self): + """test the 'locals' dictionary of a astroid class""" + module = self.module + klass1 = module['YO'] + locals1 = klass1._locals + keys = sorted(locals1.keys()) + self.assertEqual(keys, ['__init__', 'a']) + klass2 = module['YOUPI'] + locals2 = klass2._locals + keys = locals2.keys() + self.assertEqual(sorted(keys), + ['__init__', 'class_attr', 'class_method', + 'method', 'static_method']) + + def test_class_instance_attrs(self): + module = self.module + klass1 = module['YO'] + klass2 = module['YOUPI'] + self.assertEqual(list(klass1._instance_attrs.keys()), ['yo']) + self.assertEqual(list(klass2._instance_attrs.keys()), ['member']) + + def test_class_basenames(self): + module = self.module + klass1 = module['YO'] + klass2 = module['YOUPI'] + self.assertEqual(klass1.basenames, []) + self.assertEqual(klass2.basenames, ['YO']) + + def test_method_base_props(self): + """test base properties and method of a astroid method""" + klass2 = self.module['YOUPI'] + # "normal" method + method = klass2['method'] + self.assertEqual(method.name, 'method') + self.assertEqual([n.name for n in method.args.args], ['self']) + self.assertEqual(method.doc, 'method test') + self.assertEqual(method.fromlineno, 47) + self.assertEqual(method.type, 'method') + # class method + method = klass2['class_method'] + self.assertEqual([n.name for n in method.args.args], ['cls']) + self.assertEqual(method.type, 'classmethod') + # static method + method = klass2['static_method'] + self.assertEqual(method.args.args, []) + self.assertEqual(method.type, 'staticmethod') + + def test_method_locals(self): + """test the 'locals' dictionary of a astroid method""" + method = self.module['YOUPI']['method'] + _locals = method._locals + keys = sorted(_locals) + if sys.version_info < (3, 0): + self.assertEqual(len(_locals), 5) + self.assertEqual(keys, ['a', 'autre', 'b', 'local', 'self']) + else:# ListComp variables are no more accessible outside + self.assertEqual(len(_locals), 3) + self.assertEqual(keys, ['autre', 'local', 'self']) + + +class ModuleBuildTest(resources.SysPathSetup, FileBuildTest): + + def setUp(self): + super(ModuleBuildTest, self).setUp() + abuilder = builder.AstroidBuilder() + try: + import data.module + except ImportError: + # Make pylint happy. + self.skipTest('Unable to load data.module') + else: + self.module = abuilder.module_build(data.module, 'data.module') + +@unittest.skipIf(six.PY3, "guess_encoding not used on Python 3") +class TestGuessEncoding(unittest.TestCase): + def setUp(self): + self.guess_encoding = builder._guess_encoding + + def testEmacs(self): + e = self.guess_encoding('# -*- coding: UTF-8 -*-') + self.assertEqual(e, 'UTF-8') + e = self.guess_encoding('# -*- coding:UTF-8 -*-') + self.assertEqual(e, 'UTF-8') + e = self.guess_encoding(''' + ### -*- coding: ISO-8859-1 -*- + ''') + self.assertEqual(e, 'ISO-8859-1') + e = self.guess_encoding(''' + + ### -*- coding: ISO-8859-1 -*- + ''') + self.assertIsNone(e) + + def testVim(self): + e = self.guess_encoding('# vim:fileencoding=UTF-8') + self.assertEqual(e, 'UTF-8') + e = self.guess_encoding(''' + ### vim:fileencoding=ISO-8859-1 + ''') + self.assertEqual(e, 'ISO-8859-1') + e = self.guess_encoding(''' + + ### vim:fileencoding= ISO-8859-1 + ''') + self.assertIsNone(e) + + def test_wrong_coding(self): + # setting "coding" varaible + e = self.guess_encoding("coding = UTF-8") + self.assertIsNone(e) + # setting a dictionnary entry + e = self.guess_encoding("coding:UTF-8") + self.assertIsNone(e) + # setting an arguement + e = self.guess_encoding("def do_something(a_word_with_coding=None):") + self.assertIsNone(e) + + def testUTF8(self): + e = self.guess_encoding('\xef\xbb\xbf any UTF-8 data') + self.assertEqual(e, 'UTF-8') + e = self.guess_encoding(' any UTF-8 data \xef\xbb\xbf') + self.assertIsNone(e) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_inference.py b/pymode/libs/astroid/tests/unittest_inference.py new file mode 100644 index 00000000..86497727 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_inference.py @@ -0,0 +1,2130 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +"""tests for the astroid inference capabilities +""" +import sys +from functools import partial +import unittest +import warnings + +import six + +from astroid import InferenceError, builder, nodes +from astroid.builder import parse +from astroid.inference import infer_end as inference_infer_end +from astroid.bases import Instance, BoundMethod, UnboundMethod,\ + path_wrapper, BUILTINS +from astroid import arguments +from astroid import objects +from astroid import test_utils +from astroid import util +from astroid.tests import resources + + +def get_node_of_class(start_from, klass): + return next(start_from.nodes_of_class(klass)) + +builder = builder.AstroidBuilder() + +if sys.version_info < (3, 0): + EXC_MODULE = 'exceptions' +else: + EXC_MODULE = BUILTINS + + +class InferenceUtilsTest(unittest.TestCase): + + def test_path_wrapper(self): + def infer_default(self, *args): + raise InferenceError + infer_default = path_wrapper(infer_default) + infer_end = path_wrapper(inference_infer_end) + with self.assertRaises(InferenceError): + next(infer_default(1)) + self.assertEqual(next(infer_end(1)), 1) + + +def _assertInferElts(node_type, self, node, elts): + inferred = next(node.infer()) + self.assertIsInstance(inferred, node_type) + self.assertEqual(sorted(elt.value for elt in inferred.elts), + elts) + +def partialmethod(func, arg): + """similar to functools.partial but return a lambda instead of a class so returned value may be + turned into a method. + """ + return lambda *args, **kwargs: func(arg, *args, **kwargs) + +class InferenceTest(resources.SysPathSetup, unittest.TestCase): + + # additional assertInfer* method for builtin types + + def assertInferConst(self, node, expected): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, expected) + + def assertInferDict(self, node, expected): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Dict) + + elts = set([(key.value, value.value) + for (key, value) in inferred.items]) + self.assertEqual(sorted(elts), sorted(expected.items())) + + assertInferTuple = partialmethod(_assertInferElts, nodes.Tuple) + assertInferList = partialmethod(_assertInferElts, nodes.List) + assertInferSet = partialmethod(_assertInferElts, nodes.Set) + assertInferFrozenSet = partialmethod(_assertInferElts, objects.FrozenSet) + + CODE = ''' + class C(object): + "new style" + attr = 4 + + def meth1(self, arg1, optarg=0): + var = object() + print ("yo", arg1, optarg) + self.iattr = "hop" + return var + + def meth2(self): + self.meth1(*self.meth3) + + def meth3(self, d=attr): + b = self.attr + c = self.iattr + return b, c + + ex = Exception("msg") + v = C().meth1(1) + m_unbound = C.meth1 + m_bound = C().meth1 + a, b, c = ex, 1, "bonjour" + [d, e, f] = [ex, 1.0, ("bonjour", v)] + g, h = f + i, (j, k) = "glup", f + + a, b= b, a # Gasp ! + ''' + + ast = parse(CODE, __name__) + + def test_infer_abstract_property_return_values(self): + module = parse(''' + import abc + + class A(object): + @abc.abstractproperty + def test(self): + return 42 + + a = A() + x = a.test + ''') + inferred = next(module['x'].infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, 42) + + def test_module_inference(self): + inferred = self.ast.infer() + obj = next(inferred) + self.assertEqual(obj.name, __name__) + self.assertEqual(obj.root().name, __name__) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_class_inference(self): + inferred = self.ast['C'].infer() + obj = next(inferred) + self.assertEqual(obj.name, 'C') + self.assertEqual(obj.root().name, __name__) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_function_inference(self): + inferred = self.ast['C']['meth1'].infer() + obj = next(inferred) + self.assertEqual(obj.name, 'meth1') + self.assertEqual(obj.root().name, __name__) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_builtin_name_inference(self): + inferred = self.ast['C']['meth1']['var'].infer() + var = next(inferred) + self.assertEqual(var.name, 'object') + self.assertEqual(var.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_tupleassign_name_inference(self): + inferred = self.ast['a'].infer() + exc = next(inferred) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast['b'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 1) + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast['c'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "bonjour") + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_listassign_name_inference(self): + inferred = self.ast['d'].infer() + exc = next(inferred) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast['e'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 1.0) + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast['f'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Tuple) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_advanced_tupleassign_name_inference1(self): + inferred = self.ast['g'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "bonjour") + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast['h'].infer() + var = next(inferred) + self.assertEqual(var.name, 'object') + self.assertEqual(var.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_advanced_tupleassign_name_inference2(self): + inferred = self.ast['i'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, u"glup") + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast['j'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "bonjour") + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast['k'].infer() + var = next(inferred) + self.assertEqual(var.name, 'object') + self.assertEqual(var.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_swap_assign_inference(self): + inferred = self.ast._locals['a'][1].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 1) + self.assertRaises(StopIteration, partial(next, inferred)) + inferred = self.ast._locals['b'][1].infer() + exc = next(inferred) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_getattr_inference1(self): + inferred = self.ast['ex'].infer() + exc = next(inferred) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_getattr_inference2(self): + inferred = get_node_of_class(self.ast['C']['meth2'], nodes.Attribute).infer() + meth1 = next(inferred) + self.assertEqual(meth1.name, 'meth1') + self.assertEqual(meth1.root().name, __name__) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_getattr_inference3(self): + inferred = self.ast['C']['meth3']['b'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 4) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_getattr_inference4(self): + inferred = self.ast['C']['meth3']['c'].infer() + const = next(inferred) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "hop") + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_callfunc_inference(self): + inferred = self.ast['v'].infer() + meth1 = next(inferred) + self.assertIsInstance(meth1, Instance) + self.assertEqual(meth1.name, 'object') + self.assertEqual(meth1.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_unbound_method_inference(self): + inferred = self.ast['m_unbound'].infer() + meth1 = next(inferred) + self.assertIsInstance(meth1, UnboundMethod) + self.assertEqual(meth1.name, 'meth1') + self.assertEqual(meth1.parent.frame().name, 'C') + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_bound_method_inference(self): + inferred = self.ast['m_bound'].infer() + meth1 = next(inferred) + self.assertIsInstance(meth1, BoundMethod) + self.assertEqual(meth1.name, 'meth1') + self.assertEqual(meth1.parent.frame().name, 'C') + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_args_default_inference1(self): + optarg = test_utils.get_name_node(self.ast['C']['meth1'], 'optarg') + inferred = optarg.infer() + obj1 = next(inferred) + self.assertIsInstance(obj1, nodes.Const) + self.assertEqual(obj1.value, 0) + obj1 = next(inferred) + self.assertIs(obj1, util.YES, obj1) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_args_default_inference2(self): + inferred = self.ast['C']['meth3'].ilookup('d') + obj1 = next(inferred) + self.assertIsInstance(obj1, nodes.Const) + self.assertEqual(obj1.value, 4) + obj1 = next(inferred) + self.assertIs(obj1, util.YES, obj1) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_inference_restrictions(self): + inferred = test_utils.get_name_node(self.ast['C']['meth1'], 'arg1').infer() + obj1 = next(inferred) + self.assertIs(obj1, util.YES, obj1) + self.assertRaises(StopIteration, partial(next, inferred)) + + def test_ancestors_inference(self): + code = ''' + class A(object): #@ + pass + + class A(A): #@ + pass + ''' + a1, a2 = test_utils.extract_node(code, __name__) + a2_ancestors = list(a2.ancestors()) + self.assertEqual(len(a2_ancestors), 2) + self.assertIs(a2_ancestors[0], a1) + + def test_ancestors_inference2(self): + code = ''' + class A(object): #@ + pass + + class B(A): #@ + pass + + class A(B): #@ + pass + ''' + a1, b, a2 = test_utils.extract_node(code, __name__) + a2_ancestors = list(a2.ancestors()) + self.assertEqual(len(a2_ancestors), 3) + self.assertIs(a2_ancestors[0], b) + self.assertIs(a2_ancestors[1], a1) + + def test_f_arg_f(self): + code = ''' + def f(f=1): + return f + + a = f() + ''' + ast = parse(code, __name__) + a = ast['a'] + a_inferred = a.inferred() + self.assertEqual(a_inferred[0].value, 1) + self.assertEqual(len(a_inferred), 1) + + def test_infered_warning(self): + code = ''' + def f(f=1): + return f + + a = f() + ''' + ast = parse(code, __name__) + a = ast['a'] + + warnings.simplefilter('always') + with warnings.catch_warnings(record=True) as w: + a.infered() + self.assertIsInstance(w[0].message, PendingDeprecationWarning) + + def test_exc_ancestors(self): + code = ''' + def f(): + raise __(NotImplementedError) + ''' + error = test_utils.extract_node(code, __name__) + nie = error.inferred()[0] + self.assertIsInstance(nie, nodes.ClassDef) + nie_ancestors = [c.name for c in nie.ancestors()] + if sys.version_info < (3, 0): + self.assertEqual(nie_ancestors, ['RuntimeError', 'StandardError', 'Exception', 'BaseException', 'object']) + else: + self.assertEqual(nie_ancestors, ['RuntimeError', 'Exception', 'BaseException', 'object']) + + def test_except_inference(self): + code = ''' + try: + print (hop) + except NameError as ex: + ex1 = ex + except Exception as ex: + ex2 = ex + raise + ''' + ast = parse(code, __name__) + ex1 = ast['ex1'] + ex1_infer = ex1.infer() + ex1 = next(ex1_infer) + self.assertIsInstance(ex1, Instance) + self.assertEqual(ex1.name, 'NameError') + self.assertRaises(StopIteration, partial(next, ex1_infer)) + ex2 = ast['ex2'] + ex2_infer = ex2.infer() + ex2 = next(ex2_infer) + self.assertIsInstance(ex2, Instance) + self.assertEqual(ex2.name, 'Exception') + self.assertRaises(StopIteration, partial(next, ex2_infer)) + + def test_del1(self): + code = ''' + del undefined_attr + ''' + delete = test_utils.extract_node(code, __name__) + self.assertRaises(InferenceError, delete.infer) + + def test_del2(self): + code = ''' + a = 1 + b = a + del a + c = a + a = 2 + d = a + ''' + ast = parse(code, __name__) + n = ast['b'] + n_infer = n.infer() + inferred = next(n_infer) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, 1) + self.assertRaises(StopIteration, partial(next, n_infer)) + n = ast['c'] + n_infer = n.infer() + self.assertRaises(InferenceError, partial(next, n_infer)) + n = ast['d'] + n_infer = n.infer() + inferred = next(n_infer) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, 2) + self.assertRaises(StopIteration, partial(next, n_infer)) + + def test_builtin_types(self): + code = ''' + l = [1] + t = (2,) + d = {} + s = '' + s2 = '_' + ''' + ast = parse(code, __name__) + n = ast['l'] + inferred = next(n.infer()) + self.assertIsInstance(inferred, nodes.List) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.getitem(0).value, 1) + self.assertIsInstance(inferred._proxied, nodes.ClassDef) + self.assertEqual(inferred._proxied.name, 'list') + self.assertIn('append', inferred._proxied._locals) + n = ast['t'] + inferred = next(n.infer()) + self.assertIsInstance(inferred, nodes.Tuple) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.getitem(0).value, 2) + self.assertIsInstance(inferred._proxied, nodes.ClassDef) + self.assertEqual(inferred._proxied.name, 'tuple') + n = ast['d'] + inferred = next(n.infer()) + self.assertIsInstance(inferred, nodes.Dict) + self.assertIsInstance(inferred, Instance) + self.assertIsInstance(inferred._proxied, nodes.ClassDef) + self.assertEqual(inferred._proxied.name, 'dict') + self.assertIn('get', inferred._proxied._locals) + n = ast['s'] + inferred = next(n.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.name, 'str') + self.assertIn('lower', inferred._proxied._locals) + n = ast['s2'] + inferred = next(n.infer()) + self.assertEqual(inferred.getitem(0).value, '_') + + code = 's = {1}' + ast = parse(code, __name__) + n = ast['s'] + inferred = next(n.infer()) + self.assertIsInstance(inferred, nodes.Set) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.name, 'set') + self.assertIn('remove', inferred._proxied._locals) + + @test_utils.require_version(maxver='3.0') + def test_unicode_type(self): + code = '''u = u""''' + ast = parse(code, __name__) + n = ast['u'] + inferred = next(n.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.name, 'unicode') + self.assertIn('lower', inferred._proxied._locals) + + @unittest.expectedFailure + def test_descriptor_are_callable(self): + code = ''' + class A: + statm = staticmethod(open) + clsm = classmethod('whatever') + ''' + ast = parse(code, __name__) + statm = next(ast['A'].igetattr('statm')) + self.assertTrue(statm.callable()) + clsm = next(ast['A'].igetattr('clsm')) + self.assertFalse(clsm.callable()) + + def test_bt_ancestor_crash(self): + code = ''' + class Warning(Warning): + pass + ''' + ast = parse(code, __name__) + w = ast['Warning'] + ancestors = w.ancestors() + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'Warning') + self.assertEqual(ancestor.root().name, EXC_MODULE) + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'Exception') + self.assertEqual(ancestor.root().name, EXC_MODULE) + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'BaseException') + self.assertEqual(ancestor.root().name, EXC_MODULE) + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'object') + self.assertEqual(ancestor.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, ancestors)) + + def test_qqch(self): + code = ''' + from astroid.modutils import load_module_from_name + xxx = load_module_from_name('__pkginfo__') + ''' + ast = parse(code, __name__) + xxx = ast['xxx'] + self.assertSetEqual({n.__class__ for n in xxx.inferred()}, + {nodes.Const, util.YES.__class__}) + + def test_method_argument(self): + code = ''' + class ErudiEntitySchema: + """a entity has a type, a set of subject and or object relations""" + def __init__(self, e_type, **kwargs): + kwargs['e_type'] = e_type.capitalize().encode() + + def meth(self, e_type, *args, **kwargs): + kwargs['e_type'] = e_type.capitalize().encode() + print(args) + ''' + ast = parse(code, __name__) + arg = test_utils.get_name_node(ast['ErudiEntitySchema']['__init__'], 'e_type') + self.assertEqual([n.__class__ for n in arg.infer()], + [util.YES.__class__]) + arg = test_utils.get_name_node(ast['ErudiEntitySchema']['__init__'], 'kwargs') + self.assertEqual([n.__class__ for n in arg.infer()], + [nodes.Dict]) + arg = test_utils.get_name_node(ast['ErudiEntitySchema']['meth'], 'e_type') + self.assertEqual([n.__class__ for n in arg.infer()], + [util.YES.__class__]) + arg = test_utils.get_name_node(ast['ErudiEntitySchema']['meth'], 'args') + self.assertEqual([n.__class__ for n in arg.infer()], + [nodes.Tuple]) + arg = test_utils.get_name_node(ast['ErudiEntitySchema']['meth'], 'kwargs') + self.assertEqual([n.__class__ for n in arg.infer()], + [nodes.Dict]) + + def test_tuple_then_list(self): + code = ''' + def test_view(rql, vid, tags=()): + tags = list(tags) + __(tags).append(vid) + ''' + name = test_utils.extract_node(code, __name__) + it = name.infer() + tags = next(it) + self.assertIsInstance(tags, nodes.List) + self.assertEqual(tags.elts, []) + with self.assertRaises(StopIteration): + next(it) + + def test_mulassign_inference(self): + code = ''' + def first_word(line): + """Return the first word of a line""" + + return line.split()[0] + + def last_word(line): + """Return last word of a line""" + + return line.split()[-1] + + def process_line(word_pos): + """Silly function: returns (ok, callable) based on argument. + + For test purpose only. + """ + + if word_pos > 0: + return (True, first_word) + elif word_pos < 0: + return (True, last_word) + else: + return (False, None) + + if __name__ == '__main__': + + line_number = 0 + for a_line in file('test_callable.py'): + tupletest = process_line(line_number) + (ok, fct) = process_line(line_number) + if ok: + fct(a_line) + ''' + ast = parse(code, __name__) + self.assertEqual(len(list(ast['process_line'].infer_call_result(None))), 3) + self.assertEqual(len(list(ast['tupletest'].infer())), 3) + values = ['FunctionDef(first_word)', 'FunctionDef(last_word)', 'Const(NoneType)'] + self.assertEqual([str(inferred) + for inferred in ast['fct'].infer()], values) + + def test_float_complex_ambiguity(self): + code = ''' + def no_conjugate_member(magic_flag): #@ + """should not raise E1101 on something.conjugate""" + if magic_flag: + something = 1.0 + else: + something = 1.0j + if isinstance(something, float): + return something + return __(something).conjugate() + ''' + func, retval = test_utils.extract_node(code, __name__) + self.assertEqual( + [i.value for i in func.ilookup('something')], + [1.0, 1.0j]) + self.assertEqual( + [i.value for i in retval.infer()], + [1.0, 1.0j]) + + def test_lookup_cond_branches(self): + code = ''' + def no_conjugate_member(magic_flag): + """should not raise E1101 on something.conjugate""" + something = 1.0 + if magic_flag: + something = 1.0j + return something.conjugate() + ''' + ast = parse(code, __name__) + values = [i.value for i in test_utils.get_name_node(ast, 'something', -1).infer()] + self.assertEqual(values, [1.0, 1.0j]) + + + def test_simple_subscript(self): + code = ''' + class A(object): + def __getitem__(self, index): + return index + 42 + [1, 2, 3][0] #@ + (1, 2, 3)[1] #@ + (1, 2, 3)[-1] #@ + [1, 2, 3][0] + (2, )[0] + (3, )[-1] #@ + e = {'key': 'value'} + e['key'] #@ + "first"[0] #@ + list([1, 2, 3])[-1] #@ + tuple((4, 5, 6))[2] #@ + A()[0] #@ + A()[-1] #@ + ''' + ast_nodes = test_utils.extract_node(code, __name__) + expected = [1, 2, 3, 6, 'value', 'f', 3, 6, 42, 41] + for node, expected_value in zip(ast_nodes, expected): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, expected_value) + + def test_invalid_subscripts(self): + ast_nodes = test_utils.extract_node(''' + class NoGetitem(object): + pass + class InvalidGetitem(object): + def __getitem__(self): pass + class InvalidGetitem2(object): + __getitem__ = 42 + NoGetitem()[4] #@ + InvalidGetitem()[5] #@ + InvalidGetitem2()[10] #@ + [1, 2, 3][None] #@ + 'lala'['bala'] #@ + ''') + for node in ast_nodes[:3]: + self.assertRaises(InferenceError, next, node.infer()) + for node in ast_nodes[3:]: + self.assertEqual(next(node.infer()), util.YES) + + def test_bytes_subscript(self): + node = test_utils.extract_node('''b'a'[0]''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + if six.PY2: + self.assertEqual(inferred.value, 'a') + else: + self.assertEqual(inferred.value, 97) + + #def test_simple_tuple(self): + #"""test case for a simple tuple value""" + ## XXX tuple inference is not implemented ... + #code = """ +#a = (1,) +#b = (22,) +#some = a + b +#""" + #ast = builder.string_build(code, __name__, __file__) + #self.assertEqual(ast['some'].infer.next().as_string(), "(1, 22)") + + def test_simple_for(self): + code = ''' + for a in [1, 2, 3]: + print (a) + for b,c in [(1,2), (3,4)]: + print (b) + print (c) + + print ([(d,e) for e,d in ([1,2], [3,4])]) + ''' + ast = parse(code, __name__) + self.assertEqual([i.value for i in + test_utils.get_name_node(ast, 'a', -1).infer()], [1, 2, 3]) + self.assertEqual([i.value for i in + test_utils.get_name_node(ast, 'b', -1).infer()], [1, 3]) + self.assertEqual([i.value for i in + test_utils.get_name_node(ast, 'c', -1).infer()], [2, 4]) + self.assertEqual([i.value for i in + test_utils.get_name_node(ast, 'd', -1).infer()], [2, 4]) + self.assertEqual([i.value for i in + test_utils.get_name_node(ast, 'e', -1).infer()], [1, 3]) + + def test_simple_for_genexpr(self): + code = ''' + print ((d,e) for e,d in ([1,2], [3,4])) + ''' + ast = parse(code, __name__) + self.assertEqual([i.value for i in + test_utils.get_name_node(ast, 'd', -1).infer()], [2, 4]) + self.assertEqual([i.value for i in + test_utils.get_name_node(ast, 'e', -1).infer()], [1, 3]) + + + def test_builtin_help(self): + code = ''' + help() + ''' + # XXX failing since __builtin__.help assignment has + # been moved into a function... + node = test_utils.extract_node(code, __name__) + inferred = list(node.func.infer()) + self.assertEqual(len(inferred), 1, inferred) + self.assertIsInstance(inferred[0], Instance) + self.assertEqual(inferred[0].name, "_Helper") + + def test_builtin_open(self): + code = ''' + open("toto.txt") + ''' + node = test_utils.extract_node(code, __name__).func + inferred = list(node.infer()) + self.assertEqual(len(inferred), 1) + if hasattr(sys, 'pypy_version_info'): + self.assertIsInstance(inferred[0], nodes.ClassDef) + self.assertEqual(inferred[0].name, 'file') + else: + self.assertIsInstance(inferred[0], nodes.FunctionDef) + self.assertEqual(inferred[0].name, 'open') + + def test_callfunc_context_func(self): + code = ''' + def mirror(arg=None): + return arg + + un = mirror(1) + ''' + ast = parse(code, __name__) + inferred = list(ast.igetattr('un')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Const) + self.assertEqual(inferred[0].value, 1) + + def test_callfunc_context_lambda(self): + code = ''' + mirror = lambda x=None: x + + un = mirror(1) + ''' + ast = parse(code, __name__) + inferred = list(ast.igetattr('mirror')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Lambda) + inferred = list(ast.igetattr('un')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Const) + self.assertEqual(inferred[0].value, 1) + + def test_factory_method(self): + code = ''' + class Super(object): + @classmethod + def instance(cls): + return cls() + + class Sub(Super): + def method(self): + print ('method called') + + sub = Sub.instance() + ''' + ast = parse(code, __name__) + inferred = list(ast.igetattr('sub')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], Instance) + self.assertEqual(inferred[0]._proxied.name, 'Sub') + + + def test_import_as(self): + code = ''' + import os.path as osp + print (osp.dirname(__file__)) + + from os.path import exists as e + assert e(__file__) + + from new import code as make_code + print (make_code) + ''' + ast = parse(code, __name__) + inferred = list(ast.igetattr('osp')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Module) + self.assertEqual(inferred[0].name, 'os.path') + inferred = list(ast.igetattr('e')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.FunctionDef) + self.assertEqual(inferred[0].name, 'exists') + if sys.version_info >= (3, 0): + self.skipTest(' module has been removed') + inferred = list(ast.igetattr('make_code')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], Instance) + self.assertEqual(str(inferred[0]), + 'Instance of %s.type' % BUILTINS) + + def _test_const_inferred(self, node, value): + inferred = list(node.infer()) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Const) + self.assertEqual(inferred[0].value, value) + + def test_unary_not(self): + for code in ('a = not (1,); b = not ()', + 'a = not {1:2}; b = not {}'): + ast = builder.string_build(code, __name__, __file__) + self._test_const_inferred(ast['a'], False) + self._test_const_inferred(ast['b'], True) + + @test_utils.require_version(minver='3.5') + def test_matmul(self): + node = test_utils.extract_node(''' + class Array: + def __matmul__(self, other): + return 42 + Array() @ Array() #@ + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, 42) + + def test_binary_op_int_add(self): + ast = builder.string_build('a = 1 + 2', __name__, __file__) + self._test_const_inferred(ast['a'], 3) + + def test_binary_op_int_sub(self): + ast = builder.string_build('a = 1 - 2', __name__, __file__) + self._test_const_inferred(ast['a'], -1) + + def test_binary_op_float_div(self): + ast = builder.string_build('a = 1 / 2.', __name__, __file__) + self._test_const_inferred(ast['a'], 1 / 2.) + + def test_binary_op_str_mul(self): + ast = builder.string_build('a = "*" * 40', __name__, __file__) + self._test_const_inferred(ast['a'], "*" * 40) + + def test_binary_op_bitand(self): + ast = builder.string_build('a = 23&20', __name__, __file__) + self._test_const_inferred(ast['a'], 23&20) + + def test_binary_op_bitor(self): + ast = builder.string_build('a = 23|8', __name__, __file__) + self._test_const_inferred(ast['a'], 23|8) + + def test_binary_op_bitxor(self): + ast = builder.string_build('a = 23^9', __name__, __file__) + self._test_const_inferred(ast['a'], 23^9) + + def test_binary_op_shiftright(self): + ast = builder.string_build('a = 23 >>1', __name__, __file__) + self._test_const_inferred(ast['a'], 23>>1) + + def test_binary_op_shiftleft(self): + ast = builder.string_build('a = 23 <<1', __name__, __file__) + self._test_const_inferred(ast['a'], 23<<1) + + + def test_binary_op_list_mul(self): + for code in ('a = [[]] * 2', 'a = 2 * [[]]'): + ast = builder.string_build(code, __name__, __file__) + inferred = list(ast['a'].infer()) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.List) + self.assertEqual(len(inferred[0].elts), 2) + self.assertIsInstance(inferred[0].elts[0], nodes.List) + self.assertIsInstance(inferred[0].elts[1], nodes.List) + + def test_binary_op_list_mul_none(self): + 'test correct handling on list multiplied by None' + ast = builder.string_build('a = [1] * None\nb = [1] * "r"') + inferred = ast['a'].inferred() + self.assertEqual(len(inferred), 1) + self.assertEqual(inferred[0], util.YES) + inferred = ast['b'].inferred() + self.assertEqual(len(inferred), 1) + self.assertEqual(inferred[0], util.YES) + + def test_binary_op_list_mul_int(self): + 'test correct handling on list multiplied by int when there are more than one' + code = ''' + from ctypes import c_int + seq = [c_int()] * 4 + ''' + ast = parse(code, __name__) + inferred = ast['seq'].inferred() + self.assertEqual(len(inferred), 1) + listval = inferred[0] + self.assertIsInstance(listval, nodes.List) + self.assertEqual(len(listval.itered()), 4) + + def test_binary_op_tuple_add(self): + ast = builder.string_build('a = (1,) + (2,)', __name__, __file__) + inferred = list(ast['a'].infer()) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Tuple) + self.assertEqual(len(inferred[0].elts), 2) + self.assertEqual(inferred[0].elts[0].value, 1) + self.assertEqual(inferred[0].elts[1].value, 2) + + def test_binary_op_custom_class(self): + code = ''' + class myarray: + def __init__(self, array): + self.array = array + def __mul__(self, x): + return myarray([2,4,6]) + def astype(self): + return "ASTYPE" + + def randint(maximum): + if maximum is not None: + return myarray([1,2,3]) * 2 + else: + return int(5) + + x = randint(1) + ''' + ast = parse(code, __name__) + inferred = list(ast.igetattr('x')) + self.assertEqual(len(inferred), 2) + value = [str(v) for v in inferred] + # The __name__ trick here makes it work when invoked directly + # (__name__ == '__main__') and through pytest (__name__ == + # 'unittest_inference') + self.assertEqual(value, ['Instance of %s.myarray' % __name__, + 'Instance of %s.int' % BUILTINS]) + + def test_nonregr_lambda_arg(self): + code = ''' + def f(g = lambda: None): + __(g()).x +''' + callfuncnode = test_utils.extract_node(code) + inferred = list(callfuncnode.infer()) + self.assertEqual(len(inferred), 2, inferred) + inferred.remove(util.YES) + self.assertIsInstance(inferred[0], nodes.Const) + self.assertIsNone(inferred[0].value) + + def test_nonregr_getitem_empty_tuple(self): + code = ''' + def f(x): + a = ()[x] + ''' + ast = parse(code, __name__) + inferred = list(ast['f'].ilookup('a')) + self.assertEqual(len(inferred), 1) + self.assertEqual(inferred[0], util.YES) + + def test_nonregr_instance_attrs(self): + """non regression for instance_attrs infinite loop : pylint / #4""" + + code = """ + class Foo(object): + + def set_42(self): + self.attr = 42 + + class Bar(Foo): + + def __init__(self): + self.attr = 41 + """ + ast = parse(code, __name__) + foo_class = ast['Foo'] + bar_class = ast['Bar'] + bar_self = ast['Bar']['__init__']['self'] + assattr = bar_class._instance_attrs['attr'][0] + self.assertEqual(len(foo_class._instance_attrs['attr']), 1) + self.assertEqual(len(bar_class._instance_attrs['attr']), 1) + self.assertEqual(bar_class._instance_attrs, {'attr': [assattr]}) + # call 'instance_attr' via 'Instance.getattr' to trigger the bug: + instance = bar_self.inferred()[0] + instance.getattr('attr') + self.assertEqual(len(bar_class._instance_attrs['attr']), 1) + self.assertEqual(len(foo_class._instance_attrs['attr']), 1) + self.assertEqual(bar_class._instance_attrs, {'attr': [assattr]}) + + def test_python25_generator_exit(self): + # pylint: disable=redefined-variable-type + buffer = six.StringIO() + sys.stderr = buffer + try: + data = "b = {}[str(0)+''].a" + ast = builder.string_build(data, __name__, __file__) + list(ast['b'].infer()) + output = buffer.getvalue() + finally: + sys.stderr = sys.__stderr__ + # I have no idea how to test for this in another way... + msg = ("Exception exceptions.RuntimeError: " + "'generator ignored GeneratorExit' in " + "ignored") + self.assertNotIn("RuntimeError", output, msg) + + def test_python25_no_relative_import(self): + ast = resources.build_file('data/package/absimport.py') + self.assertTrue(ast.absolute_import_activated(), True) + inferred = next(test_utils.get_name_node(ast, 'import_package_subpackage_module').infer()) + # failed to import since absolute_import is activated + self.assertIs(inferred, util.YES) + + def test_nonregr_absolute_import(self): + ast = resources.build_file('data/absimp/string.py', 'data.absimp.string') + self.assertTrue(ast.absolute_import_activated(), True) + inferred = next(test_utils.get_name_node(ast, 'string').infer()) + self.assertIsInstance(inferred, nodes.Module) + self.assertEqual(inferred.name, 'string') + self.assertIn('ascii_letters', inferred._locals) + + def test_mechanize_open(self): + try: + import mechanize # pylint: disable=unused-variable + except ImportError: + self.skipTest('require mechanize installed') + data = ''' + from mechanize import Browser + print(Browser) + b = Browser() + ''' + ast = parse(data, __name__) + browser = next(test_utils.get_name_node(ast, 'Browser').infer()) + self.assertIsInstance(browser, nodes.ClassDef) + bopen = list(browser.igetattr('open')) + self.skipTest('the commit said: "huum, see that later"') + self.assertEqual(len(bopen), 1) + self.assertIsInstance(bopen[0], nodes.FunctionDef) + self.assertTrue(bopen[0].callable()) + b = next(test_utils.get_name_node(ast, 'b').infer()) + self.assertIsInstance(b, Instance) + bopen = list(b.igetattr('open')) + self.assertEqual(len(bopen), 1) + self.assertIsInstance(bopen[0], BoundMethod) + self.assertTrue(bopen[0].callable()) + + def test_property(self): + code = ''' + from smtplib import SMTP + class SendMailController(object): + + @property + def smtp(self): + return SMTP(mailhost, port) + + @property + def me(self): + return self + + my_smtp = SendMailController().smtp + my_me = SendMailController().me + ''' + decorators = set(['%s.property' % BUILTINS]) + ast = parse(code, __name__) + self.assertEqual(ast['SendMailController']['smtp'].decoratornames(), + decorators) + propinferred = list(ast.body[2].value.infer()) + self.assertEqual(len(propinferred), 1) + propinferred = propinferred[0] + self.assertIsInstance(propinferred, Instance) + self.assertEqual(propinferred.name, 'SMTP') + self.assertEqual(propinferred.root().name, 'smtplib') + self.assertEqual(ast['SendMailController']['me'].decoratornames(), + decorators) + propinferred = list(ast.body[3].value.infer()) + self.assertEqual(len(propinferred), 1) + propinferred = propinferred[0] + self.assertIsInstance(propinferred, Instance) + self.assertEqual(propinferred.name, 'SendMailController') + self.assertEqual(propinferred.root().name, __name__) + + def test_im_func_unwrap(self): + code = ''' + class EnvBasedTC: + def pactions(self): + pass + pactions = EnvBasedTC.pactions.im_func + print (pactions) + + class EnvBasedTC2: + pactions = EnvBasedTC.pactions.im_func + print (pactions) + ''' + ast = parse(code, __name__) + pactions = test_utils.get_name_node(ast, 'pactions') + inferred = list(pactions.infer()) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.FunctionDef) + pactions = test_utils.get_name_node(ast['EnvBasedTC2'], 'pactions') + inferred = list(pactions.infer()) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.FunctionDef) + + def test_augassign(self): + code = ''' + a = 1 + a += 2 + print (a) + ''' + ast = parse(code, __name__) + inferred = list(test_utils.get_name_node(ast, 'a').infer()) + + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Const) + self.assertEqual(inferred[0].value, 3) + + def test_nonregr_func_arg(self): + code = ''' + def foo(self, bar): + def baz(): + pass + def qux(): + return baz + spam = bar(None, qux) + print (spam) + ''' + ast = parse(code, __name__) + inferred = list(test_utils.get_name_node(ast['foo'], 'spam').infer()) + self.assertEqual(len(inferred), 1) + self.assertIs(inferred[0], util.YES) + + def test_nonregr_func_global(self): + code = ''' + active_application = None + + def get_active_application(): + global active_application + return active_application + + class Application(object): + def __init__(self): + global active_application + active_application = self + + class DataManager(object): + def __init__(self, app=None): + self.app = get_active_application() + def test(self): + p = self.app + print (p) + ''' + ast = parse(code, __name__) + inferred = list(Instance(ast['DataManager']).igetattr('app')) + self.assertEqual(len(inferred), 2, inferred) # None / Instance(Application) + inferred = list(test_utils.get_name_node(ast['DataManager']['test'], 'p').infer()) + self.assertEqual(len(inferred), 2, inferred) + for node in inferred: + if isinstance(node, Instance) and node.name == 'Application': + break + else: + self.fail('expected to find an instance of Application in %s' % inferred) + + def test_list_inference(self): + """#20464""" + code = ''' + from unknown import Unknown + A = [] + B = [] + + def test(): + xyz = [ + Unknown + ] + A + B + return xyz + + Z = test() + ''' + ast = parse(code, __name__) + inferred = next(ast['Z'].infer()) + self.assertIsInstance(inferred, nodes.List) + self.assertEqual(len(inferred.elts), 1) + self.assertIs(inferred.elts[0], util.YES) + + def test__new__(self): + code = ''' + class NewTest(object): + "doc" + def __new__(cls, arg): + self = object.__new__(cls) + self.arg = arg + return self + + n = NewTest() + ''' + ast = parse(code, __name__) + self.assertRaises(InferenceError, list, ast['NewTest'].igetattr('arg')) + n = next(ast['n'].infer()) + inferred = list(n.igetattr('arg')) + self.assertEqual(len(inferred), 1, inferred) + + def test__new__bound_methods(self): + node = test_utils.extract_node(''' + class cls(object): pass + cls().__new__(cls) #@ + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred._proxied, node.root()['cls']) + + def test_two_parents_from_same_module(self): + code = ''' + from data import nonregr + class Xxx(nonregr.Aaa, nonregr.Ccc): + "doc" + ''' + ast = parse(code, __name__) + parents = list(ast['Xxx'].ancestors()) + self.assertEqual(len(parents), 3, parents) # Aaa, Ccc, object + + def test_pluggable_inference(self): + code = ''' + from collections import namedtuple + A = namedtuple('A', ['a', 'b']) + B = namedtuple('B', 'a b') + ''' + ast = parse(code, __name__) + aclass = ast['A'].inferred()[0] + self.assertIsInstance(aclass, nodes.ClassDef) + self.assertIn('a', aclass._instance_attrs) + self.assertIn('b', aclass._instance_attrs) + bclass = ast['B'].inferred()[0] + self.assertIsInstance(bclass, nodes.ClassDef) + self.assertIn('a', bclass._instance_attrs) + self.assertIn('b', bclass._instance_attrs) + + def test_infer_arguments(self): + code = ''' + class A(object): + def first(self, arg1, arg2): + return arg1 + @classmethod + def method(cls, arg1, arg2): + return arg2 + @classmethod + def empty(cls): + return 2 + @staticmethod + def static(arg1, arg2): + return arg1 + def empty_method(self): + return [] + x = A().first(1, []) + y = A.method(1, []) + z = A.static(1, []) + empty = A.empty() + empty_list = A().empty_method() + ''' + ast = parse(code, __name__) + int_node = ast['x'].inferred()[0] + self.assertIsInstance(int_node, nodes.Const) + self.assertEqual(int_node.value, 1) + list_node = ast['y'].inferred()[0] + self.assertIsInstance(list_node, nodes.List) + int_node = ast['z'].inferred()[0] + self.assertIsInstance(int_node, nodes.Const) + self.assertEqual(int_node.value, 1) + empty = ast['empty'].inferred()[0] + self.assertIsInstance(empty, nodes.Const) + self.assertEqual(empty.value, 2) + empty_list = ast['empty_list'].inferred()[0] + self.assertIsInstance(empty_list, nodes.List) + + def test_infer_variable_arguments(self): + code = ''' + def test(*args, **kwargs): + vararg = args + kwarg = kwargs + ''' + ast = parse(code, __name__) + func = ast['test'] + vararg = func.body[0].value + kwarg = func.body[1].value + + kwarg_inferred = kwarg.inferred()[0] + self.assertIsInstance(kwarg_inferred, nodes.Dict) + self.assertIs(kwarg_inferred.parent, func.args) + + vararg_inferred = vararg.inferred()[0] + self.assertIsInstance(vararg_inferred, nodes.Tuple) + self.assertIs(vararg_inferred.parent, func.args) + + def test_infer_nested(self): + code = """ + def nested(): + from threading import Thread + + class NestedThread(Thread): + def __init__(self): + Thread.__init__(self) + """ + # Test that inferring Thread.__init__ looks up in + # the nested scope. + ast = parse(code, __name__) + callfunc = next(ast.nodes_of_class(nodes.Call)) + func = callfunc.func + inferred = func.inferred()[0] + self.assertIsInstance(inferred, UnboundMethod) + + def test_instance_binary_operations(self): + code = """ + class A(object): + def __mul__(self, other): + return 42 + a = A() + b = A() + sub = a - b + mul = a * b + """ + ast = parse(code, __name__) + sub = ast['sub'].inferred()[0] + mul = ast['mul'].inferred()[0] + self.assertIs(sub, util.YES) + self.assertIsInstance(mul, nodes.Const) + self.assertEqual(mul.value, 42) + + def test_instance_binary_operations_parent(self): + code = """ + class A(object): + def __mul__(self, other): + return 42 + class B(A): + pass + a = B() + b = B() + sub = a - b + mul = a * b + """ + ast = parse(code, __name__) + sub = ast['sub'].inferred()[0] + mul = ast['mul'].inferred()[0] + self.assertIs(sub, util. YES) + self.assertIsInstance(mul, nodes.Const) + self.assertEqual(mul.value, 42) + + def test_instance_binary_operations_multiple_methods(self): + code = """ + class A(object): + def __mul__(self, other): + return 42 + class B(A): + def __mul__(self, other): + return [42] + a = B() + b = B() + sub = a - b + mul = a * b + """ + ast = parse(code, __name__) + sub = ast['sub'].inferred()[0] + mul = ast['mul'].inferred()[0] + self.assertIs(sub, util.YES) + self.assertIsInstance(mul, nodes.List) + self.assertIsInstance(mul.elts[0], nodes.Const) + self.assertEqual(mul.elts[0].value, 42) + + def test_infer_call_result_crash(self): + code = """ + class A(object): + def __mul__(self, other): + return type.__new__() + + a = A() + b = A() + c = a * b + """ + ast = parse(code, __name__) + node = ast['c'] + self.assertEqual(node.inferred(), [util.YES]) + + def test_infer_empty_nodes(self): + # Should not crash when trying to infer EmptyNodes. + node = nodes.EmptyNode() + self.assertEqual(node.inferred(), [util.YES]) + + def test_infinite_loop_for_decorators(self): + # Issue https://bitbucket.org/logilab/astroid/issue/50 + # A decorator that returns itself leads to an infinite loop. + code = """ + def decorator(): + def wrapper(): + return decorator() + return wrapper + + @decorator() + def do_a_thing(): + pass + """ + ast = parse(code, __name__) + node = ast['do_a_thing'] + self.assertEqual(node.type, 'function') + + def test_no_infinite_ancestor_loop(self): + klass = test_utils.extract_node(""" + import datetime + + def method(self): + datetime.datetime = something() + + class something(datetime.datetime): #@ + pass + """) + self.assertIn( + 'object', + [base.name for base in klass.ancestors()]) + + def test_stop_iteration_leak(self): + code = """ + class Test: + def __init__(self): + self.config = {0: self.config[0]} + self.config[0].test() #@ + """ + ast = test_utils.extract_node(code, __name__) + expr = ast.func.expr + self.assertRaises(InferenceError, next, expr.infer()) + + def test_tuple_builtin_inference(self): + code = """ + var = (1, 2) + tuple() #@ + tuple([1]) #@ + tuple({2}) #@ + tuple("abc") #@ + tuple({1: 2}) #@ + tuple(var) #@ + tuple(tuple([1])) #@ + + tuple(None) #@ + tuple(1) #@ + tuple(1, 2) #@ + """ + ast = test_utils.extract_node(code, __name__) + + self.assertInferTuple(ast[0], []) + self.assertInferTuple(ast[1], [1]) + self.assertInferTuple(ast[2], [2]) + self.assertInferTuple(ast[3], ["a", "b", "c"]) + self.assertInferTuple(ast[4], [1]) + self.assertInferTuple(ast[5], [1, 2]) + self.assertInferTuple(ast[6], [1]) + + for node in ast[7:]: + inferred = next(node.infer()) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.qname(), "{}.tuple".format(BUILTINS)) + + def test_frozenset_builtin_inference(self): + code = """ + var = (1, 2) + frozenset() #@ + frozenset([1, 2, 1]) #@ + frozenset({2, 3, 1}) #@ + frozenset("abcab") #@ + frozenset({1: 2}) #@ + frozenset(var) #@ + frozenset(tuple([1])) #@ + + frozenset(set(tuple([4, 5, set([2])]))) #@ + frozenset(None) #@ + frozenset(1) #@ + frozenset(1, 2) #@ + """ + ast = test_utils.extract_node(code, __name__) + + self.assertInferFrozenSet(ast[0], []) + self.assertInferFrozenSet(ast[1], [1, 2]) + self.assertInferFrozenSet(ast[2], [1, 2, 3]) + self.assertInferFrozenSet(ast[3], ["a", "b", "c"]) + self.assertInferFrozenSet(ast[4], [1]) + self.assertInferFrozenSet(ast[5], [1, 2]) + self.assertInferFrozenSet(ast[6], [1]) + + for node in ast[7:]: + infered = next(node.infer()) + self.assertIsInstance(infered, Instance) + self.assertEqual(infered.qname(), "{}.frozenset".format(BUILTINS)) + + def test_set_builtin_inference(self): + code = """ + var = (1, 2) + set() #@ + set([1, 2, 1]) #@ + set({2, 3, 1}) #@ + set("abcab") #@ + set({1: 2}) #@ + set(var) #@ + set(tuple([1])) #@ + + set(set(tuple([4, 5, set([2])]))) #@ + set(None) #@ + set(1) #@ + set(1, 2) #@ + """ + ast = test_utils.extract_node(code, __name__) + + self.assertInferSet(ast[0], []) + self.assertInferSet(ast[1], [1, 2]) + self.assertInferSet(ast[2], [1, 2, 3]) + self.assertInferSet(ast[3], ["a", "b", "c"]) + self.assertInferSet(ast[4], [1]) + self.assertInferSet(ast[5], [1, 2]) + self.assertInferSet(ast[6], [1]) + + for node in ast[7:]: + inferred = next(node.infer()) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.qname(), "{}.set".format(BUILTINS)) + + def test_list_builtin_inference(self): + code = """ + var = (1, 2) + list() #@ + list([1, 2, 1]) #@ + list({2, 3, 1}) #@ + list("abcab") #@ + list({1: 2}) #@ + list(var) #@ + list(tuple([1])) #@ + + list(list(tuple([4, 5, list([2])]))) #@ + list(None) #@ + list(1) #@ + list(1, 2) #@ + """ + ast = test_utils.extract_node(code, __name__) + self.assertInferList(ast[0], []) + self.assertInferList(ast[1], [1, 1, 2]) + self.assertInferList(ast[2], [1, 2, 3]) + self.assertInferList(ast[3], ["a", "a", "b", "b", "c"]) + self.assertInferList(ast[4], [1]) + self.assertInferList(ast[5], [1, 2]) + self.assertInferList(ast[6], [1]) + + for node in ast[7:]: + inferred = next(node.infer()) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.qname(), "{}.list".format(BUILTINS)) + + @test_utils.require_version('3.0') + def test_builtin_inference_py3k(self): + code = """ + list(b"abc") #@ + tuple(b"abc") #@ + set(b"abc") #@ + """ + ast = test_utils.extract_node(code, __name__) + self.assertInferList(ast[0], [97, 98, 99]) + self.assertInferTuple(ast[1], [97, 98, 99]) + self.assertInferSet(ast[2], [97, 98, 99]) + + def test_dict_inference(self): + code = """ + dict() #@ + dict(a=1, b=2, c=3) #@ + dict([(1, 2), (2, 3)]) #@ + dict([[1, 2], [2, 3]]) #@ + dict([(1, 2), [2, 3]]) #@ + dict([('a', 2)], b=2, c=3) #@ + dict({1: 2}) #@ + dict({'c': 2}, a=4, b=5) #@ + def func(): + return dict(a=1, b=2) + func() #@ + var = {'x': 2, 'y': 3} + dict(var, a=1, b=2) #@ + + dict([1, 2, 3]) #@ + dict([(1, 2), (1, 2, 3)]) #@ + dict({1: 2}, {1: 2}) #@ + dict({1: 2}, (1, 2)) #@ + dict({1: 2}, (1, 2), a=4) #@ + dict([(1, 2), ([4, 5], 2)]) #@ + dict([None, None]) #@ + + def using_unknown_kwargs(**kwargs): + return dict(**kwargs) + using_unknown_kwargs(a=1, b=2) #@ + """ + ast = test_utils.extract_node(code, __name__) + self.assertInferDict(ast[0], {}) + self.assertInferDict(ast[1], {'a': 1, 'b': 2, 'c': 3}) + for i in range(2, 5): + self.assertInferDict(ast[i], {1: 2, 2: 3}) + self.assertInferDict(ast[5], {'a': 2, 'b': 2, 'c': 3}) + self.assertInferDict(ast[6], {1: 2}) + self.assertInferDict(ast[7], {'c': 2, 'a': 4, 'b': 5}) + self.assertInferDict(ast[8], {'a': 1, 'b': 2}) + self.assertInferDict(ast[9], {'x': 2, 'y': 3, 'a': 1, 'b': 2}) + + for node in ast[10:]: + inferred = next(node.infer()) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.qname(), "{}.dict".format(BUILTINS)) + + def test_dict_inference_kwargs(self): + ast_node = test_utils.extract_node('''dict(a=1, b=2, **{'c': 3})''') + self.assertInferDict(ast_node, {'a': 1, 'b': 2, 'c': 3}) + + @test_utils.require_version('3.5') + def test_dict_inference_for_multiple_starred(self): + pairs = [ + ('dict(a=1, **{"b": 2}, **{"c":3})', {'a':1, 'b':2, 'c':3}), + ('dict(a=1, **{"b": 2}, d=4, **{"c":3})', {'a':1, 'b':2, 'c':3, 'd':4}), + ('dict({"a":1}, b=2, **{"c":3})', {'a':1, 'b':2, 'c':3}), + ] + for code, expected_value in pairs: + node = test_utils.extract_node(code) + self.assertInferDict(node, expected_value) + + def test_dict_invalid_args(self): + invalid_values = [ + 'dict(*1)', + 'dict(**lala)', + 'dict(**[])', + ] + for invalid in invalid_values: + ast_node = test_utils.extract_node(invalid) + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.qname(), "{}.dict".format(BUILTINS)) + + def test_str_methods(self): + code = """ + ' '.decode() #@ + + ' '.encode() #@ + ' '.join('abcd') #@ + ' '.replace('a', 'b') #@ + ' '.format('a') #@ + ' '.capitalize() #@ + ' '.title() #@ + ' '.lower() #@ + ' '.upper() #@ + ' '.swapcase() #@ + ' '.strip() #@ + ' '.rstrip() #@ + ' '.lstrip() #@ + ' '.rjust() #@ + ' '.ljust() #@ + ' '.center() #@ + + ' '.index() #@ + ' '.find() #@ + ' '.count() #@ + """ + ast = test_utils.extract_node(code, __name__) + self.assertInferConst(ast[0], u'') + for i in range(1, 16): + self.assertInferConst(ast[i], '') + for i in range(16, 19): + self.assertInferConst(ast[i], 0) + + def test_unicode_methods(self): + code = """ + u' '.encode() #@ + + u' '.decode() #@ + u' '.join('abcd') #@ + u' '.replace('a', 'b') #@ + u' '.format('a') #@ + u' '.capitalize() #@ + u' '.title() #@ + u' '.lower() #@ + u' '.upper() #@ + u' '.swapcase() #@ + u' '.strip() #@ + u' '.rstrip() #@ + u' '.lstrip() #@ + u' '.rjust() #@ + u' '.ljust() #@ + u' '.center() #@ + + u' '.index() #@ + u' '.find() #@ + u' '.count() #@ + """ + ast = test_utils.extract_node(code, __name__) + self.assertInferConst(ast[0], '') + for i in range(1, 16): + self.assertInferConst(ast[i], u'') + for i in range(16, 19): + self.assertInferConst(ast[i], 0) + + def test_scope_lookup_same_attributes(self): + code = ''' + import collections + class Second(collections.Counter): + def collections(self): + return "second" + + ''' + ast = parse(code, __name__) + bases = ast['Second'].bases[0] + inferred = next(bases.infer()) + self.assertTrue(inferred) + self.assertIsInstance(inferred, nodes.ClassDef) + self.assertEqual(inferred.qname(), 'collections.Counter') + + +class ArgumentsTest(unittest.TestCase): + + @staticmethod + def _get_dict_value(inferred): + items = inferred.items + return sorted((key.value, value.value) for key, value in items) + + @staticmethod + def _get_tuple_value(inferred): + elts = inferred.elts + return tuple(elt.value for elt in elts) + + def test_args(self): + expected_values = [(), (1, ), (2, 3), (4, 5), + (3, ), (), (3, 4, 5), + (), (), (4, ), (4, 5), + (), (3, ), (), (), (3, ), (42, )] + ast_nodes = test_utils.extract_node(''' + def func(*args): + return args + func() #@ + func(1) #@ + func(2, 3) #@ + func(*(4, 5)) #@ + def func(a, b, *args): + return args + func(1, 2, 3) #@ + func(1, 2) #@ + func(1, 2, 3, 4, 5) #@ + def func(a, b, c=42, *args): + return args + func(1, 2) #@ + func(1, 2, 3) #@ + func(1, 2, 3, 4) #@ + func(1, 2, 3, 4, 5) #@ + func = lambda a, b, *args: args + func(1, 2) #@ + func(1, 2, 3) #@ + func = lambda a, b=42, *args: args + func(1) #@ + func(1, 2) #@ + func(1, 2, 3) #@ + func(1, 2, *(42, )) #@ + ''') + for node, expected_value in zip(ast_nodes, expected_values): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Tuple) + self.assertEqual(self._get_tuple_value(inferred), expected_value) + + @test_utils.require_version('3.5') + def test_multiple_starred_args(self): + expected_values = [ + (1, 2, 3), + (1, 4, 2, 3, 5, 6, 7), + ] + ast_nodes = test_utils.extract_node(''' + def func(a, b, *args): + return args + func(1, 2, *(1, ), *(2, 3)) #@ + func(1, 2, *(1, ), 4, *(2, 3), 5, *(6, 7)) #@ + ''') + for node, expected_value in zip(ast_nodes, expected_values): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Tuple) + self.assertEqual(self._get_tuple_value(inferred), expected_value) + + def test_defaults(self): + expected_values = [42, 3, 41, 42] + ast_nodes = test_utils.extract_node(''' + def func(a, b, c=42, *args): + return c + func(1, 2) #@ + func(1, 2, 3) #@ + func(1, 2, c=41) #@ + func(1, 2, 42, 41) #@ + ''') + for node, expected_value in zip(ast_nodes, expected_values): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, expected_value) + + @test_utils.require_version('3.0') + def test_kwonly_args(self): + expected_values = [24, 24, 42, 23, 24, 24, 54] + ast_nodes = test_utils.extract_node(''' + def test(*, f, b): return f + test(f=24, b=33) #@ + def test(a, *, f): return f + test(1, f=24) #@ + def test(a, *, f=42): return f + test(1) #@ + test(1, f=23) #@ + def test(a, b, c=42, *args, f=24): + return f + test(1, 2, 3) #@ + test(1, 2, 3, 4) #@ + test(1, 2, 3, 4, 5, f=54) #@ + ''') + for node, expected_value in zip(ast_nodes, expected_values): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, expected_value) + + def test_kwargs(self): + expected = [ + [('a', 1), ('b', 2), ('c', 3)], + [('a', 1)], + [('a', 'b')], + ] + ast_nodes = test_utils.extract_node(''' + def test(**kwargs): + return kwargs + test(a=1, b=2, c=3) #@ + test(a=1) #@ + test(**{'a': 'b'}) #@ + ''') + for node, expected_value in zip(ast_nodes, expected): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Dict) + value = self._get_dict_value(inferred) + self.assertEqual(value, expected_value) + + def test_kwargs_and_other_named_parameters(self): + ast_nodes = test_utils.extract_node(''' + def test(a=42, b=24, **kwargs): + return kwargs + test(42, 24, c=3, d=4) #@ + test(49, b=24, d=4) #@ + test(a=42, b=33, c=3, d=42) #@ + test(a=42, **{'c':42}) #@ + ''') + expected_values = [ + [('c', 3), ('d', 4)], + [('d', 4)], + [('c', 3), ('d', 42)], + [('c', 42)], + ] + for node, expected_value in zip(ast_nodes, expected_values): + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Dict) + value = self._get_dict_value(inferred) + self.assertEqual(value, expected_value) + + def test_kwargs_access_by_name(self): + expected_values = [42, 42, 42, 24] + ast_nodes = test_utils.extract_node(''' + def test(**kwargs): + return kwargs['f'] + test(f=42) #@ + test(**{'f': 42}) #@ + test(**dict(f=42)) #@ + def test(f=42, **kwargs): + return kwargs['l'] + test(l=24) #@ + ''') + for ast_node, value in zip(ast_nodes, expected_values): + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, value) + + def test_infer_call_result_invalid_dunder_call_on_instance(self): + ast_nodes = test_utils.extract_node(''' + class A: + __call__ = 42 + class B: + __call__ = A() + class C: + __call = None + A() #@ + B() #@ + C() #@ + ''') + for node in ast_nodes: + inferred = next(node.infer()) + self.assertRaises(InferenceError, next, inferred.infer_call_result(node)) + + + def test_subscript_inference_error(self): + # Used to raise StopIteration + ast_node = test_utils.extract_node(''' + class AttributeDict(dict): + def __getitem__(self, name): + return self + flow = AttributeDict() + flow['app'] = AttributeDict() + flow['app']['config'] = AttributeDict() + flow['app']['config']['doffing'] = AttributeDict() #@ + ''') + self.assertIsNone(util.safe_infer(ast_node.targets[0])) + + def test_classmethod_inferred_by_context(self): + ast_node = test_utils.extract_node(''' + class Super(object): + def instance(cls): + return cls() + instance = classmethod(instance) + + class Sub(Super): + def method(self): + return self + + # should see the Sub.instance() is returning a Sub + # instance, not a Super instance + Sub.instance().method() #@ + ''') + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, Instance) + self.assertEqual(inferred.name, 'Sub') + + @test_utils.require_version('3.5') + def test_multiple_kwargs(self): + expected_value = [ + ('a', 1), + ('b', 2), + ('c', 3), + ('d', 4), + ('f', 42), + ] + ast_node = test_utils.extract_node(''' + def test(**kwargs): + return kwargs + test(a=1, b=2, **{'c': 3}, **{'d': 4}, f=42) #@ + ''') + inferred = next(ast_node.infer()) + self.assertIsInstance(inferred, nodes.Dict) + value = self._get_dict_value(inferred) + self.assertEqual(value, expected_value) + + def test_kwargs_are_overriden(self): + ast_nodes = test_utils.extract_node(''' + def test(f): + return f + test(f=23, **{'f': 34}) #@ + def test(f=None): + return f + test(f=23, **{'f':23}) #@ + ''') + for ast_node in ast_nodes: + inferred = next(ast_node.infer()) + self.assertEqual(inferred, util.YES) + + def test_fail_to_infer_args(self): + ast_nodes = test_utils.extract_node(''' + def test(a, **kwargs): return a + test(*missing) #@ + test(*object) #@ + test(*1) #@ + + + def test(**kwargs): return kwargs + test(**miss) #@ + test(**(1, 2)) #@ + test(**1) #@ + test(**{misss:1}) #@ + test(**{object:1}) #@ + test(**{1:1}) #@ + test(**{'a':1, 'a':1}) #@ + + def test(a): return a + test() #@ + test(1, 2, 3) #@ + + from unknown import unknown + test(*unknown) #@ + def test(*args): return args + test(*unknown) #@ + ''') + for node in ast_nodes: + inferred = next(node.infer()) + self.assertEqual(inferred, util.YES) + +class CallSiteTest(unittest.TestCase): + + @staticmethod + def _call_site_from_call(call): + return arguments.CallSite.from_call(call) + + def _test_call_site_pair(self, code, expected_args, expected_keywords): + ast_node = test_utils.extract_node(code) + call_site = self._call_site_from_call(ast_node) + self.assertEqual(len(call_site.positional_arguments), len(expected_args)) + self.assertEqual([arg.value for arg in call_site.positional_arguments], + expected_args) + self.assertEqual(len(call_site.keyword_arguments), len(expected_keywords)) + for keyword, value in expected_keywords.items(): + self.assertIn(keyword, call_site.keyword_arguments) + self.assertEqual(call_site.keyword_arguments[keyword].value, value) + + def _test_call_site(self, pairs): + for pair in pairs: + self._test_call_site_pair(*pair) + + @test_utils.require_version('3.5') + def test_call_site_starred_args(self): + pairs = [ + ( + "f(*(1, 2), *(2, 3), *(3, 4), **{'a':1}, **{'b': 2})", + [1, 2, 2, 3, 3, 4], + {'a': 1, 'b': 2} + ), + ( + "f(1, 2, *(3, 4), 5, *(6, 7), f=24, **{'c':3})", + [1, 2, 3, 4, 5, 6, 7], + {'f':24, 'c': 3}, + ), + # Too many fs passed into. + ( + "f(f=24, **{'f':24})", [], {}, + ), + ] + self._test_call_site(pairs) + + def test_call_site(self): + pairs = [ + ( + "f(1, 2)", [1, 2], {} + ), + ( + "f(1, 2, *(1, 2))", [1, 2, 1, 2], {} + ), + ( + "f(a=1, b=2, c=3)", [], {'a':1, 'b':2, 'c':3} + ) + ] + self._test_call_site(pairs) + + def _test_call_site_valid_arguments(self, values, invalid): + for value in values: + ast_node = test_utils.extract_node(value) + call_site = self._call_site_from_call(ast_node) + self.assertEqual(call_site.has_invalid_arguments(), invalid) + + def test_call_site_valid_arguments(self): + values = [ + "f(*lala)", "f(*1)", "f(*object)", + ] + self._test_call_site_valid_arguments(values, invalid=True) + values = [ + "f()", "f(*(1, ))", "f(1, 2, *(2, 3))", + ] + self._test_call_site_valid_arguments(values, invalid=False) + + def test_duplicated_keyword_arguments(self): + ast_node = test_utils.extract_node('f(f=24, **{"f": 25})') + site = self._call_site_from_call(ast_node) + self.assertIn('f', site.duplicated_keywords) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_lookup.py b/pymode/libs/astroid/tests/unittest_lookup.py new file mode 100644 index 00000000..bd1786d5 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_lookup.py @@ -0,0 +1,352 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +"""tests for the astroid variable lookup capabilities +""" +import functools +import sys +import unittest + +from astroid import builder +from astroid import exceptions +from astroid import nodes +from astroid import scoped_nodes +from astroid import test_utils +from astroid import util +from astroid.tests import resources + + +class LookupTest(resources.SysPathSetup, unittest.TestCase): + + def setUp(self): + super(LookupTest, self).setUp() + self.module = resources.build_file('data/module.py', 'data.module') + self.module2 = resources.build_file('data/module2.py', 'data.module2') + self.nonregr = resources.build_file('data/nonregr.py', 'data.nonregr') + + def test_limit(self): + code = ''' + l = [a + for a,b in list] + + a = 1 + b = a + a = None + + def func(): + c = 1 + ''' + astroid = builder.parse(code, __name__) + # a & b + a = next(astroid.nodes_of_class(nodes.Name)) + self.assertEqual(a.lineno, 2) + if sys.version_info < (3, 0): + self.assertEqual(len(astroid.lookup('b')[1]), 1) + self.assertEqual(len(astroid.lookup('a')[1]), 1) + b = astroid._locals['b'][1] + else: + self.assertEqual(len(astroid.lookup('b')[1]), 1) + self.assertEqual(len(astroid.lookup('a')[1]), 1) + b = astroid._locals['b'][0] + + stmts = a.lookup('a')[1] + self.assertEqual(len(stmts), 1) + self.assertEqual(b.lineno, 6) + b_infer = b.infer() + b_value = next(b_infer) + self.assertEqual(b_value.value, 1) + # c + self.assertRaises(StopIteration, functools.partial(next, b_infer)) + func = astroid._locals['func'][0] + self.assertEqual(len(func.lookup('c')[1]), 1) + + def test_module(self): + astroid = builder.parse('pass', __name__) + # built-in objects + none = next(astroid.ilookup('None')) + self.assertIsNone(none.value) + obj = next(astroid.ilookup('object')) + self.assertIsInstance(obj, nodes.ClassDef) + self.assertEqual(obj.name, 'object') + self.assertRaises(exceptions.InferenceError, + functools.partial(next, astroid.ilookup('YOAA'))) + + # XXX + self.assertEqual(len(list(self.nonregr.ilookup('enumerate'))), 2) + + def test_class_ancestor_name(self): + code = ''' + class A: + pass + + class A(A): + pass + ''' + astroid = builder.parse(code, __name__) + cls1 = astroid._locals['A'][0] + cls2 = astroid._locals['A'][1] + name = next(cls2.nodes_of_class(nodes.Name)) + self.assertEqual(next(name.infer()), cls1) + + ### backport those test to inline code + def test_method(self): + method = self.module['YOUPI']['method'] + my_dict = next(method.ilookup('MY_DICT')) + self.assertTrue(isinstance(my_dict, nodes.Dict), my_dict) + none = next(method.ilookup('None')) + self.assertIsNone(none.value) + self.assertRaises(exceptions.InferenceError, + functools.partial(next, method.ilookup('YOAA'))) + + def test_function_argument_with_default(self): + make_class = self.module2['make_class'] + base = next(make_class.ilookup('base')) + self.assertTrue(isinstance(base, nodes.ClassDef), base.__class__) + self.assertEqual(base.name, 'YO') + self.assertEqual(base.root().name, 'data.module') + + def test_class(self): + klass = self.module['YOUPI'] + my_dict = next(klass.ilookup('MY_DICT')) + self.assertIsInstance(my_dict, nodes.Dict) + none = next(klass.ilookup('None')) + self.assertIsNone(none.value) + obj = next(klass.ilookup('object')) + self.assertIsInstance(obj, nodes.ClassDef) + self.assertEqual(obj.name, 'object') + self.assertRaises(exceptions.InferenceError, + functools.partial(next, klass.ilookup('YOAA'))) + + def test_inner_classes(self): + ddd = list(self.nonregr['Ccc'].ilookup('Ddd')) + self.assertEqual(ddd[0].name, 'Ddd') + + def test_loopvar_hiding(self): + astroid = builder.parse(""" + x = 10 + for x in range(5): + print (x) + + if x > 0: + print ('#' * x) + """, __name__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'x'] + # inside the loop, only one possible assignment + self.assertEqual(len(xnames[0].lookup('x')[1]), 1) + # outside the loop, two possible assignments + self.assertEqual(len(xnames[1].lookup('x')[1]), 2) + self.assertEqual(len(xnames[2].lookup('x')[1]), 2) + + def test_list_comps(self): + astroid = builder.parse(""" + print ([ i for i in range(10) ]) + print ([ i for i in range(10) ]) + print ( list( i for i in range(10) ) ) + """, __name__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + self.assertEqual(len(xnames[2].lookup('i')[1]), 1) + self.assertEqual(xnames[2].lookup('i')[1][0].lineno, 4) + + def test_list_comp_target(self): + """test the list comprehension target""" + astroid = builder.parse(""" + ten = [ var for var in range(10) ] + var + """) + var = astroid.body[1].value + if sys.version_info < (3, 0): + self.assertEqual(var.inferred(), [util.YES]) + else: + self.assertRaises(exceptions.UnresolvableName, var.inferred) + + def test_dict_comps(self): + astroid = builder.parse(""" + print ({ i: j for i in range(10) for j in range(10) }) + print ({ i: j for i in range(10) for j in range(10) }) + """, __name__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'j'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + + def test_set_comps(self): + astroid = builder.parse(""" + print ({ i for i in range(10) }) + print ({ i for i in range(10) }) + """, __name__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + + def test_set_comp_closure(self): + astroid = builder.parse(""" + ten = { var for var in range(10) } + var + """) + var = astroid.body[1].value + self.assertRaises(exceptions.UnresolvableName, var.inferred) + + def test_generator_attributes(self): + tree = builder.parse(""" + def count(): + "test" + yield 0 + + iterer = count() + num = iterer.next() + """) + next_node = tree.body[2].value.func + gener = next_node.expr.inferred()[0] + if sys.version_info < (3, 0): + self.assertIsInstance(gener.getattr('next')[0], nodes.FunctionDef) + else: + self.assertIsInstance(gener.getattr('__next__')[0], nodes.FunctionDef) + self.assertIsInstance(gener.getattr('send')[0], nodes.FunctionDef) + self.assertIsInstance(gener.getattr('throw')[0], nodes.FunctionDef) + self.assertIsInstance(gener.getattr('close')[0], nodes.FunctionDef) + + def test_explicit___name__(self): + code = ''' + class Pouet: + __name__ = "pouet" + p1 = Pouet() + + class PouetPouet(Pouet): pass + p2 = Pouet() + + class NoName: pass + p3 = NoName() + ''' + astroid = builder.parse(code, __name__) + p1 = next(astroid['p1'].infer()) + self.assertTrue(p1.getattr('__name__')) + p2 = next(astroid['p2'].infer()) + self.assertTrue(p2.getattr('__name__')) + self.assertTrue(astroid['NoName'].getattr('__name__')) + p3 = next(astroid['p3'].infer()) + self.assertRaises(exceptions.NotFoundError, p3.getattr, '__name__') + + def test_function_module_special(self): + astroid = builder.parse(''' + def initialize(linter): + """initialize linter with checkers in this package """ + package_load(linter, __path__[0]) + ''', 'data.__init__') + path = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == '__path__'][0] + self.assertEqual(len(path.lookup('__path__')[1]), 1) + + def test_builtin_lookup(self): + self.assertEqual(scoped_nodes.builtin_lookup('__dict__')[1], ()) + intstmts = scoped_nodes.builtin_lookup('int')[1] + self.assertEqual(len(intstmts), 1) + self.assertIsInstance(intstmts[0], nodes.ClassDef) + self.assertEqual(intstmts[0].name, 'int') + self.assertIs(intstmts[0], nodes.const_factory(1)._proxied) + + def test_decorator_arguments_lookup(self): + code = ''' + def decorator(value): + def wrapper(function): + return function + return wrapper + + class foo: + member = 10 #@ + + @decorator(member) #This will cause pylint to complain + def test(self): + pass + ''' + member = test_utils.extract_node(code, __name__).targets[0] + it = member.infer() + obj = next(it) + self.assertIsInstance(obj, nodes.Const) + self.assertEqual(obj.value, 10) + self.assertRaises(StopIteration, functools.partial(next, it)) + + def test_inner_decorator_member_lookup(self): + code = ''' + class FileA: + def decorator(bla): + return bla + + @__(decorator) + def funcA(): + return 4 + ''' + decname = test_utils.extract_node(code, __name__) + it = decname.infer() + obj = next(it) + self.assertIsInstance(obj, nodes.FunctionDef) + self.assertRaises(StopIteration, functools.partial(next, it)) + + def test_static_method_lookup(self): + code = ''' + class FileA: + @staticmethod + def funcA(): + return 4 + + + class Test: + FileA = [1,2,3] + + def __init__(self): + print (FileA.funcA()) + ''' + astroid = builder.parse(code, __name__) + it = astroid['Test']['__init__'].ilookup('FileA') + obj = next(it) + self.assertIsInstance(obj, nodes.ClassDef) + self.assertRaises(StopIteration, functools.partial(next, it)) + + def test_global_delete(self): + code = ''' + def run2(): + f = Frobble() + + class Frobble: + pass + Frobble.mumble = True + + del Frobble + + def run1(): + f = Frobble() + ''' + astroid = builder.parse(code, __name__) + stmts = astroid['run2'].lookup('Frobbel')[1] + self.assertEqual(len(stmts), 0) + stmts = astroid['run1'].lookup('Frobbel')[1] + self.assertEqual(len(stmts), 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_manager.py b/pymode/libs/astroid/tests/unittest_manager.py new file mode 100644 index 00000000..452b759e --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_manager.py @@ -0,0 +1,216 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +import os +import platform +import sys +import unittest + +import six + +from astroid import exceptions +from astroid import manager +from astroid.tests import resources + + +BUILTINS = six.moves.builtins.__name__ + + +def _get_file_from_object(obj): + if platform.python_implementation() == 'Jython': + return obj.__file__.split("$py.class")[0] + ".py" + if sys.version_info > (3, 0): + return obj.__file__ + if not obj.__file__.endswith(".py"): + return obj.__file__[:-1] + return obj.__file__ + + +class AstroidManagerTest(resources.SysPathSetup, + resources.AstroidCacheSetupMixin, + unittest.TestCase): + + def setUp(self): + super(AstroidManagerTest, self).setUp() + self.manager = manager.AstroidManager() + self.manager.clear_cache(self._builtins) # take care of borg + + def test_ast_from_file(self): + filepath = unittest.__file__ + astroid = self.manager.ast_from_file(filepath) + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_file_cache(self): + filepath = unittest.__file__ + self.manager.ast_from_file(filepath) + astroid = self.manager.ast_from_file('unhandledName', 'unittest') + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_file_astro_builder(self): + filepath = unittest.__file__ + astroid = self.manager.ast_from_file(filepath, None, True, True) + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_file_name_astro_builder_exception(self): + self.assertRaises(exceptions.AstroidBuildingException, + self.manager.ast_from_file, 'unhandledName') + + def test_do_not_expose_main(self): + obj = self.manager.ast_from_module_name('__main__') + self.assertEqual(obj.name, '__main__') + self.assertEqual(obj.items(), []) + + def test_ast_from_module_name(self): + astroid = self.manager.ast_from_module_name('unittest') + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_module_name_not_python_source(self): + astroid = self.manager.ast_from_module_name('time') + self.assertEqual(astroid.name, 'time') + self.assertIn('time', self.manager.astroid_cache) + self.assertEqual(astroid.pure_python, False) + + def test_ast_from_module_name_astro_builder_exception(self): + self.assertRaises(exceptions.AstroidBuildingException, + self.manager.ast_from_module_name, + 'unhandledModule') + + def _test_ast_from_zip(self, archive): + origpath = sys.path[:] + sys.modules.pop('mypypa', None) + archive_path = resources.find(archive) + sys.path.insert(0, archive_path) + try: + module = self.manager.ast_from_module_name('mypypa') + self.assertEqual(module.name, 'mypypa') + end = os.path.join(archive, 'mypypa') + self.assertTrue(module.source_file.endswith(end), + "%s doesn't endswith %s" % (module.source_file, end)) + finally: + # remove the module, else after importing egg, we don't get the zip + if 'mypypa' in self.manager.astroid_cache: + del self.manager.astroid_cache['mypypa'] + del self.manager._mod_file_cache[('mypypa', None)] + if archive_path in sys.path_importer_cache: + del sys.path_importer_cache[archive_path] + sys.path = origpath + + def test_ast_from_module_name_egg(self): + self._test_ast_from_zip( + os.path.sep.join(['data', os.path.normcase('MyPyPa-0.1.0-py2.5.egg')]) + ) + + def test_ast_from_module_name_zip(self): + self._test_ast_from_zip( + os.path.sep.join(['data', os.path.normcase('MyPyPa-0.1.0-py2.5.zip')]) + ) + + def test_zip_import_data(self): + """check if zip_import_data works""" + filepath = resources.find('data/MyPyPa-0.1.0-py2.5.zip/mypypa') + astroid = self.manager.zip_import_data(filepath) + self.assertEqual(astroid.name, 'mypypa') + + def test_zip_import_data_without_zipimport(self): + """check if zip_import_data return None without zipimport""" + self.assertEqual(self.manager.zip_import_data('path'), None) + + def test_file_from_module(self): + """check if the unittest filepath is equals to the result of the method""" + self.assertEqual( + _get_file_from_object(unittest), + self.manager.file_from_module_name('unittest', None)[0]) + + def test_file_from_module_name_astro_building_exception(self): + """check if the method launch a exception with a wrong module name""" + self.assertRaises(exceptions.AstroidBuildingException, + self.manager.file_from_module_name, 'unhandledModule', None) + + def test_ast_from_module(self): + astroid = self.manager.ast_from_module(unittest) + self.assertEqual(astroid.pure_python, True) + import time + astroid = self.manager.ast_from_module(time) + self.assertEqual(astroid.pure_python, False) + + def test_ast_from_module_cache(self): + """check if the module is in the cache manager""" + astroid = self.manager.ast_from_module(unittest) + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_class(self): + astroid = self.manager.ast_from_class(int) + self.assertEqual(astroid.name, 'int') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + + astroid = self.manager.ast_from_class(object) + self.assertEqual(astroid.name, 'object') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + self.assertIn('__setattr__', astroid) + + def test_ast_from_class_with_module(self): + """check if the method works with the module name""" + astroid = self.manager.ast_from_class(int, int.__module__) + self.assertEqual(astroid.name, 'int') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + + astroid = self.manager.ast_from_class(object, object.__module__) + self.assertEqual(astroid.name, 'object') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + self.assertIn('__setattr__', astroid) + + def test_ast_from_class_attr_error(self): + """give a wrong class at the ast_from_class method""" + self.assertRaises(exceptions.AstroidBuildingException, + self.manager.ast_from_class, None) + + def testFailedImportHooks(self): + def hook(modname): + if modname == 'foo.bar': + return unittest + else: + raise exceptions.AstroidBuildingException() + + with self.assertRaises(exceptions.AstroidBuildingException): + self.manager.ast_from_module_name('foo.bar') + self.manager.register_failed_import_hook(hook) + self.assertEqual(unittest, self.manager.ast_from_module_name('foo.bar')) + with self.assertRaises(exceptions.AstroidBuildingException): + self.manager.ast_from_module_name('foo.bar.baz') + del self.manager._failed_import_hooks[0] + + +class BorgAstroidManagerTC(unittest.TestCase): + + def test_borg(self): + """test that the AstroidManager is really a borg, i.e. that two different + instances has same cache""" + first_manager = manager.AstroidManager() + built = first_manager.ast_from_module_name(BUILTINS) + + second_manager = manager.AstroidManager() + second_built = second_manager.ast_from_module_name(BUILTINS) + self.assertIs(built, second_built) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_modutils.py b/pymode/libs/astroid/tests/unittest_modutils.py new file mode 100644 index 00000000..dffc3b8d --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_modutils.py @@ -0,0 +1,269 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# astroid is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +""" +unit tests for module modutils (module manipulation utilities) +""" +import os +import sys +import unittest + +from astroid import modutils +from astroid.tests import resources + + +def _get_file_from_object(obj): + return modutils._path_from_filename(obj.__file__) + + +class ModuleFileTest(unittest.TestCase): + package = "mypypa" + + def tearDown(self): + for k in list(sys.path_importer_cache.keys()): + if 'MyPyPa' in k: + del sys.path_importer_cache[k] + + def test_find_zipped_module(self): + mtype, mfile = modutils._module_file( + [self.package], [resources.find('data/MyPyPa-0.1.0-py2.5.zip')]) + self.assertEqual(mtype, modutils.PY_ZIPMODULE) + self.assertEqual(mfile.split(os.sep)[-3:], ["data", "MyPyPa-0.1.0-py2.5.zip", self.package]) + + def test_find_egg_module(self): + mtype, mfile = modutils._module_file( + [self.package], [resources.find('data/MyPyPa-0.1.0-py2.5.egg')]) + self.assertEqual(mtype, modutils.PY_ZIPMODULE) + self.assertEqual(mfile.split(os.sep)[-3:], ["data", "MyPyPa-0.1.0-py2.5.egg", self.package]) + + +class LoadModuleFromNameTest(unittest.TestCase): + """ load a python module from it's name """ + + def test_knownValues_load_module_from_name_1(self): + self.assertEqual(modutils.load_module_from_name('sys'), sys) + + def test_knownValues_load_module_from_name_2(self): + self.assertEqual(modutils.load_module_from_name('os.path'), os.path) + + def test_raise_load_module_from_name_1(self): + self.assertRaises(ImportError, + modutils.load_module_from_name, 'os.path', use_sys=0) + + +class GetModulePartTest(unittest.TestCase): + """given a dotted name return the module part of the name""" + + def test_knownValues_get_module_part_1(self): + self.assertEqual(modutils.get_module_part('astroid.modutils'), + 'astroid.modutils') + + def test_knownValues_get_module_part_2(self): + self.assertEqual(modutils.get_module_part('astroid.modutils.get_module_part'), + 'astroid.modutils') + + def test_knownValues_get_module_part_3(self): + """relative import from given file""" + self.assertEqual(modutils.get_module_part('node_classes.AssName', + modutils.__file__), 'node_classes') + + def test_knownValues_get_compiled_module_part(self): + self.assertEqual(modutils.get_module_part('math.log10'), 'math') + self.assertEqual(modutils.get_module_part('math.log10', __file__), 'math') + + def test_knownValues_get_builtin_module_part(self): + self.assertEqual(modutils.get_module_part('sys.path'), 'sys') + self.assertEqual(modutils.get_module_part('sys.path', '__file__'), 'sys') + + def test_get_module_part_exception(self): + self.assertRaises(ImportError, modutils.get_module_part, 'unknown.module', + modutils.__file__) + + +class ModPathFromFileTest(unittest.TestCase): + """ given an absolute file path return the python module's path as a list """ + + def test_knownValues_modpath_from_file_1(self): + from xml.etree import ElementTree + self.assertEqual(modutils.modpath_from_file(ElementTree.__file__), + ['xml', 'etree', 'ElementTree']) + + def test_knownValues_modpath_from_file_2(self): + self.assertEqual(modutils.modpath_from_file('unittest_modutils.py', + {os.getcwd(): 'arbitrary.pkg'}), + ['arbitrary', 'pkg', 'unittest_modutils']) + + def test_raise_modpath_from_file_Exception(self): + self.assertRaises(Exception, modutils.modpath_from_file, '/turlututu') + + +class LoadModuleFromPathTest(resources.SysPathSetup, unittest.TestCase): + + def test_do_not_load_twice(self): + modutils.load_module_from_modpath(['data', 'lmfp', 'foo']) + modutils.load_module_from_modpath(['data', 'lmfp']) + self.assertEqual(len(sys.just_once), 1) + del sys.just_once + + +class FileFromModPathTest(resources.SysPathSetup, unittest.TestCase): + """given a mod path (i.e. splited module / package name), return the + corresponding file, giving priority to source file over precompiled file + if it exists""" + + def test_site_packages(self): + filename = _get_file_from_object(modutils) + result = modutils.file_from_modpath(['astroid', 'modutils']) + self.assertEqual(os.path.realpath(result), os.path.realpath(filename)) + + def test_std_lib(self): + from os import path + self.assertEqual(os.path.realpath(modutils.file_from_modpath(['os', 'path']).replace('.pyc', '.py')), + os.path.realpath(path.__file__.replace('.pyc', '.py'))) + + def test_xmlplus(self): + try: + # don't fail if pyxml isn't installed + from xml.dom import ext + except ImportError: + pass + else: + self.assertEqual(os.path.realpath(modutils.file_from_modpath(['xml', 'dom', 'ext']).replace('.pyc', '.py')), + os.path.realpath(ext.__file__.replace('.pyc', '.py'))) + + def test_builtin(self): + self.assertEqual(modutils.file_from_modpath(['sys']), + None) + + + def test_unexisting(self): + self.assertRaises(ImportError, modutils.file_from_modpath, ['turlututu']) + + def test_unicode_in_package_init(self): + # file_from_modpath should not crash when reading an __init__ + # file with unicode characters. + modutils.file_from_modpath(["data", "unicode_package", "core"]) + + +class GetSourceFileTest(unittest.TestCase): + + def test(self): + filename = _get_file_from_object(os.path) + self.assertEqual(modutils.get_source_file(os.path.__file__), + os.path.normpath(filename)) + + def test_raise(self): + self.assertRaises(modutils.NoSourceFile, modutils.get_source_file, 'whatever') + + +class StandardLibModuleTest(resources.SysPathSetup, unittest.TestCase): + """ + return true if the module may be considered as a module from the standard + library + """ + + def test_datetime(self): + # This is an interesting example, since datetime, on pypy, + # is under lib_pypy, rather than the usual Lib directory. + self.assertTrue(modutils.is_standard_module('datetime')) + + def test_builtins(self): + if sys.version_info < (3, 0): + self.assertEqual(modutils.is_standard_module('__builtin__'), True) + self.assertEqual(modutils.is_standard_module('builtins'), False) + else: + self.assertEqual(modutils.is_standard_module('__builtin__'), False) + self.assertEqual(modutils.is_standard_module('builtins'), True) + + def test_builtin(self): + self.assertEqual(modutils.is_standard_module('sys'), True) + self.assertEqual(modutils.is_standard_module('marshal'), True) + + def test_nonstandard(self): + self.assertEqual(modutils.is_standard_module('astroid'), False) + + def test_unknown(self): + self.assertEqual(modutils.is_standard_module('unknown'), False) + + def test_4(self): + self.assertEqual(modutils.is_standard_module('hashlib'), True) + self.assertEqual(modutils.is_standard_module('pickle'), True) + self.assertEqual(modutils.is_standard_module('email'), True) + self.assertEqual(modutils.is_standard_module('io'), sys.version_info >= (2, 6)) + self.assertEqual(modutils.is_standard_module('StringIO'), sys.version_info < (3, 0)) + self.assertEqual(modutils.is_standard_module('unicodedata'), True) + + def test_custom_path(self): + datadir = resources.find('') + if datadir.startswith(modutils.EXT_LIB_DIR): + self.skipTest('known breakage of is_standard_module on installed package') + self.assertEqual(modutils.is_standard_module('data.module', (datadir,)), True) + self.assertEqual(modutils.is_standard_module('data.module', (os.path.abspath(datadir),)), True) + + def test_failing_edge_cases(self): + from xml import etree + # using a subpackage/submodule path as std_path argument + self.assertEqual(modutils.is_standard_module('xml.etree', etree.__path__), False) + # using a module + object name as modname argument + self.assertEqual(modutils.is_standard_module('sys.path'), True) + # this is because only the first package/module is considered + self.assertEqual(modutils.is_standard_module('sys.whatever'), True) + self.assertEqual(modutils.is_standard_module('xml.whatever', etree.__path__), False) + + +class IsRelativeTest(unittest.TestCase): + + + def test_knownValues_is_relative_1(self): + import email + self.assertEqual(modutils.is_relative('utils', email.__path__[0]), + True) + + def test_knownValues_is_relative_2(self): + from xml.etree import ElementTree + self.assertEqual(modutils.is_relative('ElementPath', ElementTree.__file__), + True) + + def test_knownValues_is_relative_3(self): + import astroid + self.assertEqual(modutils.is_relative('astroid', astroid.__path__[0]), + False) + + +class GetModuleFilesTest(unittest.TestCase): + + def test_get_module_files_1(self): + package = resources.find('data/find_test') + modules = set(modutils.get_module_files(package, [])) + expected = ['__init__.py', 'module.py', 'module2.py', + 'noendingnewline.py', 'nonregr.py'] + self.assertEqual(modules, + {os.path.join(package, x) for x in expected}) + + def test_load_module_set_attribute(self): + import xml.etree.ElementTree + import xml + del xml.etree.ElementTree + del sys.modules['xml.etree.ElementTree'] + m = modutils.load_module_from_modpath(['xml', 'etree', 'ElementTree']) + self.assertTrue(hasattr(xml, 'etree')) + self.assertTrue(hasattr(xml.etree, 'ElementTree')) + self.assertTrue(m is xml.etree.ElementTree) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_nodes.py b/pymode/libs/astroid/tests/unittest_nodes.py new file mode 100644 index 00000000..6fa4b6f3 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_nodes.py @@ -0,0 +1,764 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +"""tests for specific behaviour of astroid nodes +""" +import os +import sys +import textwrap +import unittest +import warnings + +import six + +from astroid import bases +from astroid import builder +from astroid import context as contextmod +from astroid import exceptions +from astroid import node_classes +from astroid import nodes +from astroid import parse +from astroid import util +from astroid import test_utils +from astroid import transforms +from astroid.tests import resources + + +abuilder = builder.AstroidBuilder() +BUILTINS = six.moves.builtins.__name__ + + +class AsStringTest(resources.SysPathSetup, unittest.TestCase): + + def test_tuple_as_string(self): + def build(string): + return abuilder.string_build(string).body[0].value + + self.assertEqual(build('1,').as_string(), '(1, )') + self.assertEqual(build('1, 2, 3').as_string(), '(1, 2, 3)') + self.assertEqual(build('(1, )').as_string(), '(1, )') + self.assertEqual(build('1, 2, 3').as_string(), '(1, 2, 3)') + + def test_as_string_for_list_containing_uninferable(self): + node = test_utils.extract_node(''' + def foo(arg): + bar = [arg] * 1 + ''') + binop = node.body[0].value + inferred = next(binop.infer()) + self.assertEqual(inferred.as_string(), '[Uninferable]') + self.assertEqual(binop.as_string(), '([arg]) * (1)') + + def test_frozenset_as_string(self): + nodes = test_utils.extract_node(''' + frozenset((1, 2, 3)) #@ + frozenset({1, 2, 3}) #@ + frozenset([1, 2, 3,]) #@ + + frozenset(None) #@ + frozenset(1) #@ + ''') + nodes = [next(node.infer()) for node in nodes] + + self.assertEqual(nodes[0].as_string(), 'frozenset((1, 2, 3))') + self.assertEqual(nodes[1].as_string(), 'frozenset({1, 2, 3})') + self.assertEqual(nodes[2].as_string(), 'frozenset([1, 2, 3])') + + self.assertNotEqual(nodes[3].as_string(), 'frozenset(None)') + self.assertNotEqual(nodes[4].as_string(), 'frozenset(1)') + + @test_utils.require_version(minver='3.0') + def test_func_signature_issue_185(self): + code = textwrap.dedent(''' + def test(a, b, c=42, *, x=42, **kwargs): + print(a, b, c, args) + ''') + node = parse(code) + self.assertEqual(node.as_string().strip(), code.strip()) + def test_varargs_kwargs_as_string(self): + ast = abuilder.string_build('raise_string(*args, **kwargs)').body[0] + self.assertEqual(ast.as_string(), 'raise_string(*args, **kwargs)') + + def test_module_as_string(self): + """check as_string on a whole module prepared to be returned identically + """ + module = resources.build_file('data/module.py', 'data.module') + with open(resources.find('data/module.py'), 'r') as fobj: + self.assertMultiLineEqual(module.as_string(), fobj.read()) + + def test_module2_as_string(self): + """check as_string on a whole module prepared to be returned identically + """ + module2 = resources.build_file('data/module2.py', 'data.module2') + with open(resources.find('data/module2.py'), 'r') as fobj: + self.assertMultiLineEqual(module2.as_string(), fobj.read()) + + def test_as_string(self): + """check as_string for python syntax >= 2.7""" + code = '''one_two = {1, 2} +b = {v: k for (k, v) in enumerate('string')} +cdd = {k for k in b}\n\n''' + ast = abuilder.string_build(code) + self.assertMultiLineEqual(ast.as_string(), code) + + @test_utils.require_version('3.0') + def test_3k_as_string(self): + """check as_string for python 3k syntax""" + code = '''print() + +def function(var): + nonlocal counter + try: + hello + except NameError as nexc: + (*hell, o) = b'hello' + raise AttributeError from nexc +\n''' + ast = abuilder.string_build(code) + self.assertEqual(ast.as_string(), code) + + @test_utils.require_version('3.0') + @unittest.expectedFailure + def test_3k_annotations_and_metaclass(self): + code_annotations = textwrap.dedent(''' + def function(var:int): + nonlocal counter + + class Language(metaclass=Natural): + """natural language""" + ''') + + ast = abuilder.string_build(code_annotations) + self.assertEqual(ast.as_string(), code_annotations) + + def test_ellipsis(self): + ast = abuilder.string_build('a[...]').body[0] + self.assertEqual(ast.as_string(), 'a[...]') + + def test_slices(self): + for code in ('a[0]', 'a[1:3]', 'a[:-1:step]', 'a[:,newaxis]', + 'a[newaxis,:]', 'del L[::2]', 'del A[1]', 'del Br[:]'): + ast = abuilder.string_build(code).body[0] + self.assertEqual(ast.as_string(), code) + + def test_slice_and_subscripts(self): + code = """a[:1] = bord[2:] +a[:1] = bord[2:] +del bree[3:d] +bord[2:] +del av[d::f], a[df:] +a[:1] = bord[2:] +del SRC[::1,newaxis,1:] +tous[vals] = 1010 +del thousand[key] +del a[::2], a[:-1:step] +del Fee.form[left:] +aout.vals = miles.of_stuff +del (ccok, (name.thing, foo.attrib.value)), Fee.form[left:] +if all[1] == bord[0:]: + pass\n\n""" + ast = abuilder.string_build(code) + self.assertEqual(ast.as_string(), code) + + +class _NodeTest(unittest.TestCase): + """test transformation of If Node""" + CODE = None + + @property + def astroid(self): + try: + return self.__class__.__dict__['CODE_Astroid'] + except KeyError: + astroid = builder.parse(self.CODE) + self.__class__.CODE_Astroid = astroid + return astroid + + +class IfNodeTest(_NodeTest): + """test transformation of If Node""" + CODE = """ + if 0: + print() + + if True: + print() + else: + pass + + if "": + print() + elif []: + raise + + if 1: + print() + elif True: + print() + elif func(): + pass + else: + raise + """ + + def test_if_elif_else_node(self): + """test transformation for If node""" + self.assertEqual(len(self.astroid.body), 4) + for stmt in self.astroid.body: + self.assertIsInstance(stmt, nodes.If) + self.assertFalse(self.astroid.body[0].orelse) # simple If + self.assertIsInstance(self.astroid.body[1].orelse[0], nodes.Pass) # If / else + self.assertIsInstance(self.astroid.body[2].orelse[0], nodes.If) # If / elif + self.assertIsInstance(self.astroid.body[3].orelse[0].orelse[0], nodes.If) + + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.block_range(1), (0, 22)) + self.assertEqual(self.astroid.block_range(10), (0, 22)) # XXX (10, 22) ? + self.assertEqual(self.astroid.body[1].block_range(5), (5, 6)) + self.assertEqual(self.astroid.body[1].block_range(6), (6, 6)) + self.assertEqual(self.astroid.body[1].orelse[0].block_range(7), (7, 8)) + self.assertEqual(self.astroid.body[1].orelse[0].block_range(8), (8, 8)) + + +class TryExceptNodeTest(_NodeTest): + CODE = """ + try: + print ('pouet') + except IOError: + pass + except UnicodeError: + print() + else: + print() + """ + + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.body[0].block_range(1), (1, 8)) + self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) + self.assertEqual(self.astroid.body[0].block_range(3), (3, 8)) + self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) + self.assertEqual(self.astroid.body[0].block_range(5), (5, 5)) + self.assertEqual(self.astroid.body[0].block_range(6), (6, 6)) + self.assertEqual(self.astroid.body[0].block_range(7), (7, 7)) + self.assertEqual(self.astroid.body[0].block_range(8), (8, 8)) + + +class TryFinallyNodeTest(_NodeTest): + CODE = """ + try: + print ('pouet') + finally: + print ('pouet') + """ + + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.body[0].block_range(1), (1, 4)) + self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) + self.assertEqual(self.astroid.body[0].block_range(3), (3, 4)) + self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) + + +class TryExceptFinallyNodeTest(_NodeTest): + CODE = """ + try: + print('pouet') + except Exception: + print ('oops') + finally: + print ('pouet') + """ + + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.body[0].block_range(1), (1, 6)) + self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) + self.assertEqual(self.astroid.body[0].block_range(3), (3, 4)) + self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) + self.assertEqual(self.astroid.body[0].block_range(5), (5, 5)) + self.assertEqual(self.astroid.body[0].block_range(6), (6, 6)) + + +@unittest.skipIf(six.PY3, "Python 2 specific test.") +class TryExcept2xNodeTest(_NodeTest): + CODE = """ + try: + hello + except AttributeError, (retval, desc): + pass + """ + + + def test_tuple_attribute(self): + handler = self.astroid.body[0].handlers[0] + self.assertIsInstance(handler.name, nodes.Tuple) + + +class ImportNodeTest(resources.SysPathSetup, unittest.TestCase): + def setUp(self): + super(ImportNodeTest, self).setUp() + self.module = resources.build_file('data/module.py', 'data.module') + self.module2 = resources.build_file('data/module2.py', 'data.module2') + + def test_import_self_resolve(self): + myos = next(self.module2.igetattr('myos')) + self.assertTrue(isinstance(myos, nodes.Module), myos) + self.assertEqual(myos.name, 'os') + self.assertEqual(myos.qname(), 'os') + self.assertEqual(myos.pytype(), '%s.module' % BUILTINS) + + def test_from_self_resolve(self): + namenode = next(self.module.igetattr('NameNode')) + self.assertTrue(isinstance(namenode, nodes.ClassDef), namenode) + self.assertEqual(namenode.root().name, 'astroid.node_classes') + self.assertEqual(namenode.qname(), 'astroid.node_classes.Name') + self.assertEqual(namenode.pytype(), '%s.type' % BUILTINS) + abspath = next(self.module2.igetattr('abspath')) + self.assertTrue(isinstance(abspath, nodes.FunctionDef), abspath) + self.assertEqual(abspath.root().name, 'os.path') + self.assertEqual(abspath.qname(), 'os.path.abspath') + self.assertEqual(abspath.pytype(), '%s.function' % BUILTINS) + + def test_real_name(self): + from_ = self.module['NameNode'] + self.assertEqual(from_.real_name('NameNode'), 'Name') + imp_ = self.module['os'] + self.assertEqual(imp_.real_name('os'), 'os') + self.assertRaises(exceptions.NotFoundError, imp_.real_name, 'os.path') + imp_ = self.module['NameNode'] + self.assertEqual(imp_.real_name('NameNode'), 'Name') + self.assertRaises(exceptions.NotFoundError, imp_.real_name, 'Name') + imp_ = self.module2['YO'] + self.assertEqual(imp_.real_name('YO'), 'YO') + self.assertRaises(exceptions.NotFoundError, imp_.real_name, 'data') + + def test_as_string(self): + ast = self.module['modutils'] + self.assertEqual(ast.as_string(), "from astroid import modutils") + ast = self.module['NameNode'] + self.assertEqual(ast.as_string(), "from astroid.node_classes import Name as NameNode") + ast = self.module['os'] + self.assertEqual(ast.as_string(), "import os.path") + code = """from . import here +from .. import door +from .store import bread +from ..cave import wine\n\n""" + ast = abuilder.string_build(code) + self.assertMultiLineEqual(ast.as_string(), code) + + def test_bad_import_inference(self): + # Explication of bug + '''When we import PickleError from nonexistent, a call to the infer + method of this From node will be made by unpack_infer. + inference.infer_from will try to import this module, which will fail and + raise a InferenceException (by mixins.do_import_module). The infer_name + will catch this exception and yield and YES instead. + ''' + + code = ''' + try: + from pickle import PickleError + except ImportError: + from nonexistent import PickleError + + try: + pass + except PickleError: + pass + ''' + astroid = builder.parse(code) + handler_type = astroid.body[1].handlers[0].type + + excs = list(node_classes.unpack_infer(handler_type)) + # The number of returned object can differ on Python 2 + # and Python 3. In one version, an additional item will + # be returned, from the _pickle module, which is not + # present in the other version. + self.assertIsInstance(excs[0], nodes.ClassDef) + self.assertEqual(excs[0].name, 'PickleError') + self.assertIs(excs[-1], util.YES) + + def test_absolute_import(self): + astroid = resources.build_file('data/absimport.py') + ctx = contextmod.InferenceContext() + # will fail if absolute import failed + ctx.lookupname = 'message' + next(astroid['message'].infer(ctx)) + ctx.lookupname = 'email' + m = next(astroid['email'].infer(ctx)) + self.assertFalse(m.source_file.startswith(os.path.join('data', 'email.py'))) + + def test_more_absolute_import(self): + astroid = resources.build_file('data/module1abs/__init__.py', 'data.module1abs') + self.assertIn('sys', astroid._locals) + + +class CmpNodeTest(unittest.TestCase): + def test_as_string(self): + ast = abuilder.string_build("a == 2").body[0] + self.assertEqual(ast.as_string(), "a == 2") + + +class ConstNodeTest(unittest.TestCase): + + def _test(self, value): + node = nodes.const_factory(value) + self.assertIsInstance(node._proxied, nodes.ClassDef) + self.assertEqual(node._proxied.name, value.__class__.__name__) + self.assertIs(node.value, value) + self.assertTrue(node._proxied.parent) + self.assertEqual(node._proxied.root().name, value.__class__.__module__) + + def test_none(self): + self._test(None) + + def test_bool(self): + self._test(True) + + def test_int(self): + self._test(1) + + def test_float(self): + self._test(1.0) + + def test_complex(self): + self._test(1.0j) + + def test_str(self): + self._test('a') + + def test_unicode(self): + self._test(u'a') + + +class NameNodeTest(unittest.TestCase): + def test_assign_to_True(self): + """test that True and False assignements don't crash""" + code = """ + True = False + def hello(False): + pass + del True + """ + if sys.version_info >= (3, 0): + with self.assertRaises(exceptions.AstroidBuildingException): + builder.parse(code) + else: + ast = builder.parse(code) + assign_true = ast['True'] + self.assertIsInstance(assign_true, nodes.AssignName) + self.assertEqual(assign_true.name, "True") + del_true = ast.body[2].targets[0] + self.assertIsInstance(del_true, nodes.DelName) + self.assertEqual(del_true.name, "True") + + +class ArgumentsNodeTC(unittest.TestCase): + def test_linenumbering(self): + ast = builder.parse(''' + def func(a, + b): pass + x = lambda x: None + ''') + self.assertEqual(ast['func'].args.fromlineno, 2) + self.assertFalse(ast['func'].args.is_statement) + xlambda = next(ast['x'].infer()) + self.assertEqual(xlambda.args.fromlineno, 4) + self.assertEqual(xlambda.args.tolineno, 4) + self.assertFalse(xlambda.args.is_statement) + if sys.version_info < (3, 0): + self.assertEqual(ast['func'].args.tolineno, 3) + else: + self.skipTest('FIXME http://bugs.python.org/issue10445 ' + '(no line number on function args)') + + def test_builtin_fromlineno_missing(self): + cls = test_utils.extract_node(''' + class Foo(Exception): #@ + pass + ''') + new = cls.getattr('__new__')[-1] + self.assertEqual(new.args.fromlineno, 0) + + +class UnboundMethodNodeTest(unittest.TestCase): + + def test_no_super_getattr(self): + # This is a test for issue + # https://bitbucket.org/logilab/astroid/issue/91, which tests + # that UnboundMethod doesn't call super when doing .getattr. + + ast = builder.parse(''' + class A(object): + def test(self): + pass + meth = A.test + ''') + node = next(ast['meth'].infer()) + with self.assertRaises(exceptions.NotFoundError): + node.getattr('__missssing__') + name = node.getattr('__name__')[0] + self.assertIsInstance(name, nodes.Const) + self.assertEqual(name.value, 'test') + + +class BoundMethodNodeTest(unittest.TestCase): + + def test_is_property(self): + ast = builder.parse(''' + import abc + + def cached_property(): + # Not a real decorator, but we don't care + pass + def reify(): + # Same as cached_property + pass + def lazy_property(): + pass + def lazyproperty(): + pass + def lazy(): pass + class A(object): + @property + def builtin_property(self): + return 42 + @abc.abstractproperty + def abc_property(self): + return 42 + @cached_property + def cached_property(self): return 42 + @reify + def reified(self): return 42 + @lazy_property + def lazy_prop(self): return 42 + @lazyproperty + def lazyprop(self): return 42 + def not_prop(self): pass + @lazy + def decorated_with_lazy(self): return 42 + + cls = A() + builtin_property = cls.builtin_property + abc_property = cls.abc_property + cached_p = cls.cached_property + reified = cls.reified + not_prop = cls.not_prop + lazy_prop = cls.lazy_prop + lazyprop = cls.lazyprop + decorated_with_lazy = cls.decorated_with_lazy + ''') + for prop in ('builtin_property', 'abc_property', 'cached_p', 'reified', + 'lazy_prop', 'lazyprop', 'decorated_with_lazy'): + inferred = next(ast[prop].infer()) + self.assertIsInstance(inferred, nodes.Const, prop) + self.assertEqual(inferred.value, 42, prop) + + inferred = next(ast['not_prop'].infer()) + self.assertIsInstance(inferred, bases.BoundMethod) + + +class AliasesTest(unittest.TestCase): + + def setUp(self): + self.transformer = transforms.TransformVisitor() + + def parse_transform(self, code): + module = parse(code, apply_transforms=False) + return self.transformer.visit(module) + + def test_aliases(self): + def test_from(node): + node.names = node.names + [('absolute_import', None)] + return node + + def test_class(node): + node.name = 'Bar' + return node + + def test_function(node): + node.name = 'another_test' + return node + + def test_callfunc(node): + if node.func.name == 'Foo': + node.func.name = 'Bar' + return node + + def test_assname(node): + if node.name == 'foo': + n = nodes.AssignName() + n.name = 'bar' + return n + def test_assattr(node): + if node.attrname == 'a': + node.attrname = 'b' + return node + + def test_getattr(node): + if node.attrname == 'a': + node.attrname = 'b' + return node + + def test_genexpr(node): + if node.elt.value == 1: + node.elt = nodes.Const(2) + return node + + self.transformer.register_transform(nodes.From, test_from) + self.transformer.register_transform(nodes.Class, test_class) + self.transformer.register_transform(nodes.Function, test_function) + self.transformer.register_transform(nodes.CallFunc, test_callfunc) + self.transformer.register_transform(nodes.AssName, test_assname) + self.transformer.register_transform(nodes.AssAttr, test_assattr) + self.transformer.register_transform(nodes.Getattr, test_getattr) + self.transformer.register_transform(nodes.GenExpr, test_genexpr) + + string = ''' + from __future__ import print_function + + class Foo: pass + + def test(a): return a + + foo = Foo() + foo.a = test(42) + foo.a + (1 for _ in range(0, 42)) + ''' + + module = self.parse_transform(string) + + self.assertEqual(len(module.body[0].names), 2) + self.assertIsInstance(module.body[0], nodes.ImportFrom) + self.assertEqual(module.body[1].name, 'Bar') + self.assertIsInstance(module.body[1], nodes.ClassDef) + self.assertEqual(module.body[2].name, 'another_test') + self.assertIsInstance(module.body[2], nodes.FunctionDef) + self.assertEqual(module.body[3].targets[0].name, 'bar') + self.assertIsInstance(module.body[3].targets[0], nodes.AssignName) + self.assertEqual(module.body[3].value.func.name, 'Bar') + self.assertIsInstance(module.body[3].value, nodes.Call) + self.assertEqual(module.body[4].targets[0].attrname, 'b') + self.assertIsInstance(module.body[4].targets[0], nodes.AssignAttr) + self.assertIsInstance(module.body[5], nodes.Expr) + self.assertEqual(module.body[5].value.attrname, 'b') + self.assertIsInstance(module.body[5].value, nodes.Attribute) + self.assertEqual(module.body[6].value.elt.value, 2) + self.assertIsInstance(module.body[6].value, nodes.GeneratorExp) + + @unittest.skipIf(six.PY3, "Python 3 doesn't have Repr nodes.") + def test_repr(self): + def test_backquote(node): + node.value.name = 'bar' + return node + + self.transformer.register_transform(nodes.Backquote, test_backquote) + + module = self.parse_transform('`foo`') + + self.assertEqual(module.body[0].value.value.name, 'bar') + self.assertIsInstance(module.body[0].value, nodes.Repr) + + +class DeprecationWarningsTest(unittest.TestCase): + def test_asstype_warnings(self): + string = ''' + class C: pass + c = C() + with warnings.catch_warnings(record=True) as w: + pass + ''' + module = parse(string) + filter_stmts_mixin = module.body[0] + assign_type_mixin = module.body[1].targets[0] + parent_assign_type_mixin = module.body[2] + + warnings.simplefilter('always') + + with warnings.catch_warnings(record=True) as w: + filter_stmts_mixin.ass_type() + self.assertIsInstance(w[0].message, PendingDeprecationWarning) + with warnings.catch_warnings(record=True) as w: + assign_type_mixin.ass_type() + self.assertIsInstance(w[0].message, PendingDeprecationWarning) + with warnings.catch_warnings(record=True) as w: + parent_assign_type_mixin.ass_type() + self.assertIsInstance(w[0].message, PendingDeprecationWarning) + + def test_isinstance_warnings(self): + msg_format = ("%r is deprecated and slated for removal in astroid " + "2.0, use %r instead") + for cls in (nodes.Discard, nodes.Backquote, nodes.AssName, + nodes.AssAttr, nodes.Getattr, nodes.CallFunc, nodes.From): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + isinstance(42, cls) + self.assertIsInstance(w[0].message, PendingDeprecationWarning) + actual_msg = msg_format % (cls.__class__.__name__, cls.__wrapped__.__name__) + self.assertEqual(str(w[0].message), actual_msg) + + +@test_utils.require_version('3.5') +class Python35AsyncTest(unittest.TestCase): + + def test_async_await_keywords(self): + async_def, async_for, async_with, await_node = test_utils.extract_node(''' + async def func(): #@ + async for i in range(10): #@ + f = __(await i) + async with test(): #@ + pass + ''') + self.assertIsInstance(async_def, nodes.AsyncFunctionDef) + self.assertIsInstance(async_for, nodes.AsyncFor) + self.assertIsInstance(async_with, nodes.AsyncWith) + self.assertIsInstance(await_node, nodes.Await) + self.assertIsInstance(await_node.value, nodes.Name) + + def _test_await_async_as_string(self, code): + ast_node = parse(code) + self.assertEqual(ast_node.as_string().strip(), code.strip()) + + def test_await_as_string(self): + code = textwrap.dedent(''' + async def function(): + await 42 + ''') + self._test_await_async_as_string(code) + + def test_asyncwith_as_string(self): + code = textwrap.dedent(''' + async def function(): + async with (42): + pass + ''') + self._test_await_async_as_string(code) + + def test_asyncfor_as_string(self): + code = textwrap.dedent(''' + async def function(): + async for i in range(10): + await 42 + ''') + self._test_await_async_as_string(code) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_objects.py b/pymode/libs/astroid/tests/unittest_objects.py new file mode 100644 index 00000000..62d3f4ff --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_objects.py @@ -0,0 +1,530 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +import unittest + +from astroid import bases +from astroid import exceptions +from astroid import nodes +from astroid import objects +from astroid import test_utils + + +class ObjectsTest(unittest.TestCase): + + def test_frozenset(self): + node = test_utils.extract_node(""" + frozenset({1: 2, 2: 3}) #@ + """) + infered = next(node.infer()) + self.assertIsInstance(infered, objects.FrozenSet) + + self.assertEqual(infered.pytype(), "%s.frozenset" % bases.BUILTINS) + + itered = infered.itered() + self.assertEqual(len(itered), 2) + self.assertIsInstance(itered[0], nodes.Const) + self.assertEqual([const.value for const in itered], [1, 2]) + + proxied = infered._proxied + self.assertEqual(infered.qname(), "%s.frozenset" % bases.BUILTINS) + self.assertIsInstance(proxied, nodes.ClassDef) + + +class SuperTests(unittest.TestCase): + + def test_inferring_super_outside_methods(self): + ast_nodes = test_utils.extract_node(''' + class Module(object): + pass + class StaticMethod(object): + @staticmethod + def static(): + # valid, but we don't bother with it. + return super(StaticMethod, StaticMethod) #@ + # super outside methods aren't inferred + super(Module, Module) #@ + # no argument super is not recognised outside methods as well. + super() #@ + ''') + in_static = next(ast_nodes[0].value.infer()) + self.assertIsInstance(in_static, bases.Instance) + self.assertEqual(in_static.qname(), "%s.super" % bases.BUILTINS) + + module_level = next(ast_nodes[1].infer()) + self.assertIsInstance(module_level, bases.Instance) + self.assertEqual(in_static.qname(), "%s.super" % bases.BUILTINS) + + no_arguments = next(ast_nodes[2].infer()) + self.assertIsInstance(no_arguments, bases.Instance) + self.assertEqual(no_arguments.qname(), "%s.super" % bases.BUILTINS) + + def test_inferring_unbound_super_doesnt_work(self): + node = test_utils.extract_node(''' + class Test(object): + def __init__(self): + super(Test) #@ + ''') + unbounded = next(node.infer()) + self.assertIsInstance(unbounded, bases.Instance) + self.assertEqual(unbounded.qname(), "%s.super" % bases.BUILTINS) + + def test_use_default_inference_on_not_inferring_args(self): + ast_nodes = test_utils.extract_node(''' + class Test(object): + def __init__(self): + super(Lala, self) #@ + super(Test, lala) #@ + ''') + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, bases.Instance) + self.assertEqual(first.qname(), "%s.super" % bases.BUILTINS) + + second = next(ast_nodes[1].infer()) + self.assertIsInstance(second, bases.Instance) + self.assertEqual(second.qname(), "%s.super" % bases.BUILTINS) + + @test_utils.require_version(maxver='3.0') + def test_super_on_old_style_class(self): + # super doesn't work on old style class, but leave + # that as an error for pylint. We'll infer Super objects, + # but every call will result in a failure at some point. + node = test_utils.extract_node(''' + class OldStyle: + def __init__(self): + super(OldStyle, self) #@ + ''') + old = next(node.infer()) + self.assertIsInstance(old, objects.Super) + self.assertIsInstance(old.mro_pointer, nodes.ClassDef) + self.assertEqual(old.mro_pointer.name, 'OldStyle') + with self.assertRaises(exceptions.SuperError) as cm: + old.super_mro() + self.assertEqual(str(cm.exception), + "Unable to call super on old-style classes.") + + @test_utils.require_version(minver='3.0') + def test_no_arguments_super(self): + ast_nodes = test_utils.extract_node(''' + class First(object): pass + class Second(First): + def test(self): + super() #@ + @classmethod + def test_classmethod(cls): + super() #@ + ''') + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, objects.Super) + self.assertIsInstance(first.type, bases.Instance) + self.assertEqual(first.type.name, 'Second') + self.assertIsInstance(first.mro_pointer, nodes.ClassDef) + self.assertEqual(first.mro_pointer.name, 'Second') + + second = next(ast_nodes[1].infer()) + self.assertIsInstance(second, objects.Super) + self.assertIsInstance(second.type, nodes.ClassDef) + self.assertEqual(second.type.name, 'Second') + self.assertIsInstance(second.mro_pointer, nodes.ClassDef) + self.assertEqual(second.mro_pointer.name, 'Second') + + def test_super_simple_cases(self): + ast_nodes = test_utils.extract_node(''' + class First(object): pass + class Second(First): pass + class Third(First): + def test(self): + super(Third, self) #@ + super(Second, self) #@ + + # mro position and the type + super(Third, Third) #@ + super(Third, Second) #@ + super(Fourth, Fourth) #@ + + class Fourth(Third): + pass + ''') + + # .type is the object which provides the mro. + # .mro_pointer is the position in the mro from where + # the lookup should be done. + + # super(Third, self) + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, objects.Super) + self.assertIsInstance(first.type, bases.Instance) + self.assertEqual(first.type.name, 'Third') + self.assertIsInstance(first.mro_pointer, nodes.ClassDef) + self.assertEqual(first.mro_pointer.name, 'Third') + + # super(Second, self) + second = next(ast_nodes[1].infer()) + self.assertIsInstance(second, objects.Super) + self.assertIsInstance(second.type, bases.Instance) + self.assertEqual(second.type.name, 'Third') + self.assertIsInstance(first.mro_pointer, nodes.ClassDef) + self.assertEqual(second.mro_pointer.name, 'Second') + + # super(Third, Third) + third = next(ast_nodes[2].infer()) + self.assertIsInstance(third, objects.Super) + self.assertIsInstance(third.type, nodes.ClassDef) + self.assertEqual(third.type.name, 'Third') + self.assertIsInstance(third.mro_pointer, nodes.ClassDef) + self.assertEqual(third.mro_pointer.name, 'Third') + + # super(Third, second) + fourth = next(ast_nodes[3].infer()) + self.assertIsInstance(fourth, objects.Super) + self.assertIsInstance(fourth.type, nodes.ClassDef) + self.assertEqual(fourth.type.name, 'Second') + self.assertIsInstance(fourth.mro_pointer, nodes.ClassDef) + self.assertEqual(fourth.mro_pointer.name, 'Third') + + # Super(Fourth, Fourth) + fifth = next(ast_nodes[4].infer()) + self.assertIsInstance(fifth, objects.Super) + self.assertIsInstance(fifth.type, nodes.ClassDef) + self.assertEqual(fifth.type.name, 'Fourth') + self.assertIsInstance(fifth.mro_pointer, nodes.ClassDef) + self.assertEqual(fifth.mro_pointer.name, 'Fourth') + + def test_super_infer(self): + node = test_utils.extract_node(''' + class Super(object): + def __init__(self): + super(Super, self) #@ + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, objects.Super) + reinferred = next(inferred.infer()) + self.assertIsInstance(reinferred, objects.Super) + self.assertIs(inferred, reinferred) + + def test_inferring_invalid_supers(self): + ast_nodes = test_utils.extract_node(''' + class Super(object): + def __init__(self): + # MRO pointer is not a type + super(1, self) #@ + # MRO type is not a subtype + super(Super, 1) #@ + # self is not a subtype of Bupper + super(Bupper, self) #@ + class Bupper(Super): + pass + ''') + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, objects.Super) + with self.assertRaises(exceptions.SuperError) as cm: + first.super_mro() + self.assertEqual(str(cm.exception), "The first super argument must be type.") + + for node in ast_nodes[1:]: + inferred = next(node.infer()) + self.assertIsInstance(inferred, objects.Super, node) + with self.assertRaises(exceptions.SuperArgumentTypeError) as cm: + inferred.super_mro() + self.assertEqual(str(cm.exception), + "super(type, obj): obj must be an instance " + "or subtype of type", node) + + def test_proxied(self): + node = test_utils.extract_node(''' + class Super(object): + def __init__(self): + super(Super, self) #@ + ''') + infered = next(node.infer()) + proxied = infered._proxied + self.assertEqual(proxied.qname(), "%s.super" % bases.BUILTINS) + self.assertIsInstance(proxied, nodes.ClassDef) + + def test_super_bound_model(self): + ast_nodes = test_utils.extract_node(''' + class First(object): + def method(self): + pass + @classmethod + def class_method(cls): + pass + class Super_Type_Type(First): + def method(self): + super(Super_Type_Type, Super_Type_Type).method #@ + super(Super_Type_Type, Super_Type_Type).class_method #@ + @classmethod + def class_method(cls): + super(Super_Type_Type, Super_Type_Type).method #@ + super(Super_Type_Type, Super_Type_Type).class_method #@ + + class Super_Type_Object(First): + def method(self): + super(Super_Type_Object, self).method #@ + super(Super_Type_Object, self).class_method #@ + ''') + # Super(type, type) is the same for both functions and classmethods. + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, nodes.FunctionDef) + self.assertEqual(first.name, 'method') + + second = next(ast_nodes[1].infer()) + self.assertIsInstance(second, bases.BoundMethod) + self.assertEqual(second.bound.name, 'First') + self.assertEqual(second.type, 'classmethod') + + third = next(ast_nodes[2].infer()) + self.assertIsInstance(third, nodes.FunctionDef) + self.assertEqual(third.name, 'method') + + fourth = next(ast_nodes[3].infer()) + self.assertIsInstance(fourth, bases.BoundMethod) + self.assertEqual(fourth.bound.name, 'First') + self.assertEqual(fourth.type, 'classmethod') + + # Super(type, obj) can lead to different attribute bindings + # depending on the type of the place where super was called. + fifth = next(ast_nodes[4].infer()) + self.assertIsInstance(fifth, bases.BoundMethod) + self.assertEqual(fifth.bound.name, 'First') + self.assertEqual(fifth.type, 'method') + + sixth = next(ast_nodes[5].infer()) + self.assertIsInstance(sixth, bases.BoundMethod) + self.assertEqual(sixth.bound.name, 'First') + self.assertEqual(sixth.type, 'classmethod') + + def test_super_getattr_single_inheritance(self): + ast_nodes = test_utils.extract_node(''' + class First(object): + def test(self): pass + class Second(First): + def test2(self): pass + class Third(Second): + test3 = 42 + def __init__(self): + super(Third, self).test2 #@ + super(Third, self).test #@ + # test3 is local, no MRO lookup is done. + super(Third, self).test3 #@ + super(Third, self) #@ + + # Unbounds. + super(Third, Third).test2 #@ + super(Third, Third).test #@ + + ''') + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, bases.BoundMethod) + self.assertEqual(first.bound.name, 'Second') + + second = next(ast_nodes[1].infer()) + self.assertIsInstance(second, bases.BoundMethod) + self.assertEqual(second.bound.name, 'First') + + with self.assertRaises(exceptions.InferenceError): + next(ast_nodes[2].infer()) + fourth = next(ast_nodes[3].infer()) + with self.assertRaises(exceptions.NotFoundError): + fourth.getattr('test3') + with self.assertRaises(exceptions.NotFoundError): + next(fourth.igetattr('test3')) + + first_unbound = next(ast_nodes[4].infer()) + self.assertIsInstance(first_unbound, nodes.FunctionDef) + self.assertEqual(first_unbound.name, 'test2') + self.assertEqual(first_unbound.parent.name, 'Second') + + second_unbound = next(ast_nodes[5].infer()) + self.assertIsInstance(second_unbound, nodes.FunctionDef) + self.assertEqual(second_unbound.name, 'test') + self.assertEqual(second_unbound.parent.name, 'First') + + def test_super_invalid_mro(self): + node = test_utils.extract_node(''' + class A(object): + test = 42 + class Super(A, A): + def __init__(self): + super(Super, self) #@ + ''') + inferred = next(node.infer()) + with self.assertRaises(exceptions.NotFoundError): + next(inferred.getattr('test')) + + def test_super_complex_mro(self): + ast_nodes = test_utils.extract_node(''' + class A(object): + def spam(self): return "A" + def foo(self): return "A" + @staticmethod + def static(self): pass + class B(A): + def boo(self): return "B" + def spam(self): return "B" + class C(A): + def boo(self): return "C" + class E(C, B): + def __init__(self): + super(E, self).boo #@ + super(C, self).boo #@ + super(E, self).spam #@ + super(E, self).foo #@ + super(E, self).static #@ + ''') + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, bases.BoundMethod) + self.assertEqual(first.bound.name, 'C') + second = next(ast_nodes[1].infer()) + self.assertIsInstance(second, bases.BoundMethod) + self.assertEqual(second.bound.name, 'B') + third = next(ast_nodes[2].infer()) + self.assertIsInstance(third, bases.BoundMethod) + self.assertEqual(third.bound.name, 'B') + fourth = next(ast_nodes[3].infer()) + self.assertEqual(fourth.bound.name, 'A') + static = next(ast_nodes[4].infer()) + self.assertIsInstance(static, nodes.FunctionDef) + self.assertEqual(static.parent.scope().name, 'A') + + def test_super_data_model(self): + ast_nodes = test_utils.extract_node(''' + class X(object): pass + class A(X): + def __init__(self): + super(A, self) #@ + super(A, A) #@ + super(X, A) #@ + ''') + first = next(ast_nodes[0].infer()) + thisclass = first.getattr('__thisclass__')[0] + self.assertIsInstance(thisclass, nodes.ClassDef) + self.assertEqual(thisclass.name, 'A') + selfclass = first.getattr('__self_class__')[0] + self.assertIsInstance(selfclass, nodes.ClassDef) + self.assertEqual(selfclass.name, 'A') + self_ = first.getattr('__self__')[0] + self.assertIsInstance(self_, bases.Instance) + self.assertEqual(self_.name, 'A') + cls = first.getattr('__class__')[0] + self.assertEqual(cls, first._proxied) + + second = next(ast_nodes[1].infer()) + thisclass = second.getattr('__thisclass__')[0] + self.assertEqual(thisclass.name, 'A') + self_ = second.getattr('__self__')[0] + self.assertIsInstance(self_, nodes.ClassDef) + self.assertEqual(self_.name, 'A') + + third = next(ast_nodes[2].infer()) + thisclass = third.getattr('__thisclass__')[0] + self.assertEqual(thisclass.name, 'X') + selfclass = third.getattr('__self_class__')[0] + self.assertEqual(selfclass.name, 'A') + + def assertEqualMro(self, klass, expected_mro): + self.assertEqual( + [member.name for member in klass.super_mro()], + expected_mro) + + def test_super_mro(self): + ast_nodes = test_utils.extract_node(''' + class A(object): pass + class B(A): pass + class C(A): pass + class E(C, B): + def __init__(self): + super(E, self) #@ + super(C, self) #@ + super(B, self) #@ + + super(B, 1) #@ + super(1, B) #@ + ''') + first = next(ast_nodes[0].infer()) + self.assertEqualMro(first, ['C', 'B', 'A', 'object']) + second = next(ast_nodes[1].infer()) + self.assertEqualMro(second, ['B', 'A', 'object']) + third = next(ast_nodes[2].infer()) + self.assertEqualMro(third, ['A', 'object']) + + fourth = next(ast_nodes[3].infer()) + with self.assertRaises(exceptions.SuperError): + fourth.super_mro() + fifth = next(ast_nodes[4].infer()) + with self.assertRaises(exceptions.SuperError): + fifth.super_mro() + + def test_super_yes_objects(self): + ast_nodes = test_utils.extract_node(''' + from collections import Missing + class A(object): + def __init__(self): + super(Missing, self) #@ + super(A, Missing) #@ + ''') + first = next(ast_nodes[0].infer()) + self.assertIsInstance(first, bases.Instance) + second = next(ast_nodes[1].infer()) + self.assertIsInstance(second, bases.Instance) + + def test_super_invalid_types(self): + node = test_utils.extract_node(''' + import collections + class A(object): + def __init__(self): + super(A, collections) #@ + ''') + inferred = next(node.infer()) + with self.assertRaises(exceptions.SuperError): + inferred.super_mro() + with self.assertRaises(exceptions.SuperArgumentTypeError): + inferred.super_mro() + + def test_super_pytype_display_type_name(self): + node = test_utils.extract_node(''' + class A(object): + def __init__(self): + super(A, self) #@ + ''') + inferred = next(node.infer()) + self.assertEqual(inferred.pytype(), "%s.super" % bases.BUILTINS) + self.assertEqual(inferred.display_type(), 'Super of') + self.assertEqual(inferred.name, 'A') + + def test_super_properties(self): + node = test_utils.extract_node(''' + class Foo(object): + @property + def dict(self): + return 42 + + class Bar(Foo): + @property + def dict(self): + return super(Bar, self).dict + + Bar().dict + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, 42) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_peephole.py b/pymode/libs/astroid/tests/unittest_peephole.py new file mode 100644 index 00000000..78349898 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_peephole.py @@ -0,0 +1,121 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +"""Tests for the astroid AST peephole optimizer.""" + +import ast +import textwrap +import unittest + +import astroid +from astroid import astpeephole +from astroid import builder +from astroid import manager +from astroid import test_utils +from astroid.tests import resources + + +MANAGER = manager.AstroidManager() + + +class PeepholeOptimizer(unittest.TestCase): + @classmethod + def setUpClass(cls): + MANAGER.optimize_ast = True + + @classmethod + def tearDownClass(cls): + MANAGER.optimize_ast = False + + def setUp(self): + self._optimizer = astpeephole.ASTPeepholeOptimizer() + + @staticmethod + def _get_binops(code): + module = ast.parse(textwrap.dedent(code)) + return [node.value for node in module.body + if isinstance(node, ast.Expr)] + + @test_utils.require_version(maxver='3.0') + def test_optimize_binop_unicode(self): + nodes = self._get_binops(""" + u"a" + u"b" + u"c" + + u"a" + "c" + "b" + u"a" + b"c" + """) + + result = self._optimizer.optimize_binop(nodes[0]) + self.assertIsInstance(result, astroid.Const) + self.assertEqual(result.value, u"abc") + + self.assertIsNone(self._optimizer.optimize_binop(nodes[1])) + self.assertIsNone(self._optimizer.optimize_binop(nodes[2])) + + def test_optimize_binop(self): + nodes = self._get_binops(""" + "a" + "b" + "c" + "d" + b"a" + b"b" + b"c" + b"d" + "a" + "b" + + "a" + "b" + 1 + object + var = 4 + "a" + "b" + var + "c" + "a" + "b" + "c" - "4" + "a" + "b" + "c" + "d".format() + "a" - "b" + "a" + 1 + 4 + 5 + 6 + """) + + result = self._optimizer.optimize_binop(nodes[0]) + self.assertIsInstance(result, astroid.Const) + self.assertEqual(result.value, "abcd") + + result = self._optimizer.optimize_binop(nodes[1]) + self.assertIsInstance(result, astroid.Const) + self.assertEqual(result.value, b"abcd") + + for node in nodes[2:]: + self.assertIsNone(self._optimizer.optimize_binop(node)) + + def test_big_binop_crash(self): + # Test that we don't fail on a lot of joined strings + # through the addition operator. + module = resources.build_file('data/joined_strings.py') + element = next(module['x'].infer()) + self.assertIsInstance(element, astroid.Const) + self.assertEqual(len(element.value), 61660) + + def test_optimisation_disabled(self): + try: + MANAGER.optimize_ast = False + module = builder.parse(""" + '1' + '2' + '3' + """) + self.assertIsInstance(module.body[0], astroid.Expr) + self.assertIsInstance(module.body[0].value, astroid.BinOp) + self.assertIsInstance(module.body[0].value.left, astroid.BinOp) + self.assertIsInstance(module.body[0].value.left.left, + astroid.Const) + finally: + MANAGER.optimize_ast = True + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_protocols.py b/pymode/libs/astroid/tests/unittest_protocols.py new file mode 100644 index 00000000..16745129 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_protocols.py @@ -0,0 +1,176 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +import contextlib +import unittest + +import astroid +from astroid.test_utils import extract_node, require_version +from astroid import InferenceError +from astroid import nodes +from astroid import util +from astroid.node_classes import AssignName, Const, Name, Starred + + +@contextlib.contextmanager +def _add_transform(manager, node, transform, predicate=None): + manager.register_transform(node, transform, predicate) + try: + yield + finally: + manager.unregister_transform(node, transform, predicate) + + +class ProtocolTests(unittest.TestCase): + + def assertConstNodesEqual(self, nodes_list_expected, nodes_list_got): + self.assertEqual(len(nodes_list_expected), len(nodes_list_got)) + for node in nodes_list_got: + self.assertIsInstance(node, Const) + for node, expected_value in zip(nodes_list_got, nodes_list_expected): + self.assertEqual(expected_value, node.value) + + def assertNameNodesEqual(self, nodes_list_expected, nodes_list_got): + self.assertEqual(len(nodes_list_expected), len(nodes_list_got)) + for node in nodes_list_got: + self.assertIsInstance(node, Name) + for node, expected_name in zip(nodes_list_got, nodes_list_expected): + self.assertEqual(expected_name, node.name) + + def test_assigned_stmts_simple_for(self): + assign_stmts = extract_node(""" + for a in (1, 2, 3): #@ + pass + + for b in range(3): #@ + pass + """) + + for1_assnode = next(assign_stmts[0].nodes_of_class(AssignName)) + assigned = list(for1_assnode.assigned_stmts()) + self.assertConstNodesEqual([1, 2, 3], assigned) + + for2_assnode = next(assign_stmts[1].nodes_of_class(AssignName)) + self.assertRaises(InferenceError, + list, for2_assnode.assigned_stmts()) + + @require_version(minver='3.0') + def test_assigned_stmts_starred_for(self): + assign_stmts = extract_node(""" + for *a, b in ((1, 2, 3), (4, 5, 6, 7)): #@ + pass + """) + + for1_starred = next(assign_stmts.nodes_of_class(Starred)) + assigned = next(for1_starred.assigned_stmts()) + self.assertEqual(assigned, util.YES) + + def _get_starred_stmts(self, code): + assign_stmt = extract_node("{} #@".format(code)) + starred = next(assign_stmt.nodes_of_class(Starred)) + return next(starred.assigned_stmts()) + + def _helper_starred_expected_const(self, code, expected): + stmts = self._get_starred_stmts(code) + self.assertIsInstance(stmts, nodes.List) + stmts = stmts.elts + self.assertConstNodesEqual(expected, stmts) + + def _helper_starred_expected(self, code, expected): + stmts = self._get_starred_stmts(code) + self.assertEqual(expected, stmts) + + def _helper_starred_inference_error(self, code): + assign_stmt = extract_node("{} #@".format(code)) + starred = next(assign_stmt.nodes_of_class(Starred)) + self.assertRaises(InferenceError, list, starred.assigned_stmts()) + + @require_version(minver='3.0') + def test_assigned_stmts_starred_assnames(self): + self._helper_starred_expected_const( + "a, *b = (1, 2, 3, 4) #@", [2, 3, 4]) + self._helper_starred_expected_const( + "*a, b = (1, 2, 3) #@", [1, 2]) + self._helper_starred_expected_const( + "a, *b, c = (1, 2, 3, 4, 5) #@", + [2, 3, 4]) + self._helper_starred_expected_const( + "a, *b = (1, 2) #@", [2]) + self._helper_starred_expected_const( + "*b, a = (1, 2) #@", [1]) + self._helper_starred_expected_const( + "[*b] = (1, 2) #@", [1, 2]) + + @require_version(minver='3.0') + def test_assigned_stmts_starred_yes(self): + # Not something iterable and known + self._helper_starred_expected("a, *b = range(3) #@", util.YES) + # Not something inferrable + self._helper_starred_expected("a, *b = balou() #@", util.YES) + # In function, unknown. + self._helper_starred_expected(""" + def test(arg): + head, *tail = arg #@""", util.YES) + # These cases aren't worth supporting. + self._helper_starred_expected( + "a, (*b, c), d = (1, (2, 3, 4), 5) #@", util.YES) + + @require_version(minver='3.0') + def test_assign_stmts_starred_fails(self): + # Too many starred + self._helper_starred_inference_error("a, *b, *c = (1, 2, 3) #@") + # Too many lhs values + self._helper_starred_inference_error("a, *b, c = (1, 2) #@") + # This could be solved properly, but it complicates needlessly the + # code for assigned_stmts, without oferring real benefit. + self._helper_starred_inference_error( + "(*a, b), (c, *d) = (1, 2, 3), (4, 5, 6) #@") + + def test_assigned_stmts_assignments(self): + assign_stmts = extract_node(""" + c = a #@ + + d, e = b, c #@ + """) + + simple_assnode = next(assign_stmts[0].nodes_of_class(AssignName)) + assigned = list(simple_assnode.assigned_stmts()) + self.assertNameNodesEqual(['a'], assigned) + + assnames = assign_stmts[1].nodes_of_class(AssignName) + simple_mul_assnode_1 = next(assnames) + assigned = list(simple_mul_assnode_1.assigned_stmts()) + self.assertNameNodesEqual(['b'], assigned) + simple_mul_assnode_2 = next(assnames) + assigned = list(simple_mul_assnode_2.assigned_stmts()) + self.assertNameNodesEqual(['c'], assigned) + + def test_sequence_assigned_stmts_not_accepting_empty_node(self): + def transform(node): + node.root().locals['__all__'] = [node.value] + + manager = astroid.MANAGER + with _add_transform(manager, astroid.Assign, transform): + module = astroid.parse(''' + __all__ = ['a'] + ''') + module.wildcard_import_names() + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_python3.py b/pymode/libs/astroid/tests/unittest_python3.py new file mode 100644 index 00000000..87010571 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_python3.py @@ -0,0 +1,254 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +from textwrap import dedent +import unittest + +from astroid import nodes +from astroid.node_classes import Assign, Expr, YieldFrom, Name, Const +from astroid.builder import AstroidBuilder +from astroid.scoped_nodes import ClassDef, FunctionDef +from astroid.test_utils import require_version, extract_node + + +class Python3TC(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.builder = AstroidBuilder() + + @require_version('3.0') + def test_starred_notation(self): + astroid = self.builder.string_build("*a, b = [1, 2, 3]", 'test', 'test') + + # Get the star node + node = next(next(next(astroid.get_children()).get_children()).get_children()) + + self.assertTrue(isinstance(node.assign_type(), Assign)) + + @require_version('3.3') + def test_yield_from(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertIsInstance(func, FunctionDef) + yieldfrom_stmt = func.body[0] + + self.assertIsInstance(yieldfrom_stmt, Expr) + self.assertIsInstance(yieldfrom_stmt.value, YieldFrom) + self.assertEqual(yieldfrom_stmt.as_string(), + 'yield from iter([1, 2])') + + @require_version('3.3') + def test_yield_from_is_generator(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertIsInstance(func, FunctionDef) + self.assertTrue(func.is_generator()) + + @require_version('3.3') + def test_yield_from_as_string(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + value = yield from other() + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertEqual(func.as_string().strip(), body.strip()) + + # metaclass tests + + @require_version('3.0') + def test_simple_metaclass(self): + astroid = self.builder.string_build("class Test(metaclass=type): pass") + klass = astroid.body[0] + + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, ClassDef) + self.assertEqual(metaclass.name, 'type') + + @require_version('3.0') + def test_metaclass_error(self): + astroid = self.builder.string_build("class Test(metaclass=typ): pass") + klass = astroid.body[0] + self.assertFalse(klass.metaclass()) + + @require_version('3.0') + def test_metaclass_imported(self): + astroid = self.builder.string_build(dedent(""" + from abc import ABCMeta + class Test(metaclass=ABCMeta): pass""")) + klass = astroid.body[1] + + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, ClassDef) + self.assertEqual(metaclass.name, 'ABCMeta') + + @require_version('3.0') + def test_as_string(self): + body = dedent(""" + from abc import ABCMeta + class Test(metaclass=ABCMeta): pass""") + astroid = self.builder.string_build(body) + klass = astroid.body[1] + + self.assertEqual(klass.as_string(), + '\n\nclass Test(metaclass=ABCMeta):\n pass\n') + + @require_version('3.0') + def test_old_syntax_works(self): + astroid = self.builder.string_build(dedent(""" + class Test: + __metaclass__ = type + class SubTest(Test): pass + """)) + klass = astroid['SubTest'] + metaclass = klass.metaclass() + self.assertIsNone(metaclass) + + @require_version('3.0') + def test_metaclass_yes_leak(self): + astroid = self.builder.string_build(dedent(""" + # notice `ab` instead of `abc` + from ab import ABCMeta + + class Meta(metaclass=ABCMeta): pass + """)) + klass = astroid['Meta'] + self.assertIsNone(klass.metaclass()) + + @require_version('3.0') + def test_parent_metaclass(self): + astroid = self.builder.string_build(dedent(""" + from abc import ABCMeta + class Test(metaclass=ABCMeta): pass + class SubTest(Test): pass + """)) + klass = astroid['SubTest'] + self.assertTrue(klass.newstyle) + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, ClassDef) + self.assertEqual(metaclass.name, 'ABCMeta') + + @require_version('3.0') + def test_metaclass_ancestors(self): + astroid = self.builder.string_build(dedent(""" + from abc import ABCMeta + + class FirstMeta(metaclass=ABCMeta): pass + class SecondMeta(metaclass=type): + pass + + class Simple: + pass + + class FirstImpl(FirstMeta): pass + class SecondImpl(FirstImpl): pass + class ThirdImpl(Simple, SecondMeta): + pass + """)) + classes = { + 'ABCMeta': ('FirstImpl', 'SecondImpl'), + 'type': ('ThirdImpl', ) + } + for metaclass, names in classes.items(): + for name in names: + impl = astroid[name] + meta = impl.metaclass() + self.assertIsInstance(meta, ClassDef) + self.assertEqual(meta.name, metaclass) + + @require_version('3.0') + def test_annotation_support(self): + astroid = self.builder.string_build(dedent(""" + def test(a: int, b: str, c: None, d, e, + *args: float, **kwargs: int)->int: + pass + """)) + func = astroid['test'] + self.assertIsInstance(func.args.varargannotation, Name) + self.assertEqual(func.args.varargannotation.name, 'float') + self.assertIsInstance(func.args.kwargannotation, Name) + self.assertEqual(func.args.kwargannotation.name, 'int') + self.assertIsInstance(func.returns, Name) + self.assertEqual(func.returns.name, 'int') + arguments = func.args + self.assertIsInstance(arguments.annotations[0], Name) + self.assertEqual(arguments.annotations[0].name, 'int') + self.assertIsInstance(arguments.annotations[1], Name) + self.assertEqual(arguments.annotations[1].name, 'str') + self.assertIsInstance(arguments.annotations[2], Const) + self.assertIsNone(arguments.annotations[2].value) + self.assertIsNone(arguments.annotations[3]) + self.assertIsNone(arguments.annotations[4]) + + astroid = self.builder.string_build(dedent(""" + def test(a: int=1, b: str=2): + pass + """)) + func = astroid['test'] + self.assertIsInstance(func.args.annotations[0], Name) + self.assertEqual(func.args.annotations[0].name, 'int') + self.assertIsInstance(func.args.annotations[1], Name) + self.assertEqual(func.args.annotations[1].name, 'str') + self.assertIsNone(func.returns) + + @require_version('3.0') + def test_annotation_as_string(self): + code1 = dedent(''' + def test(a, b:int=4, c=2, f:'lala'=4)->2: + pass''') + code2 = dedent(''' + def test(a:typing.Generic[T], c:typing.Any=24)->typing.Iterable: + pass''') + for code in (code1, code2): + func = extract_node(code) + self.assertEqual(func.as_string(), code) + + @require_version('3.5') + def test_unpacking_in_dicts(self): + code = "{'x': 1, **{'y': 2}}" + node = extract_node(code) + self.assertEqual(node.as_string(), code) + keys = [key for (key, _) in node.items] + self.assertIsInstance(keys[0], nodes.Const) + self.assertIsInstance(keys[1], nodes.DictUnpack) + + @require_version('3.5') + def test_nested_unpacking_in_dicts(self): + code = "{'x': 1, **{'y': 2, **{'z': 3}}}" + node = extract_node(code) + self.assertEqual(node.as_string(), code) + + @require_version('3.5') + def test_unpacking_in_dict_getitem(self): + node = extract_node('{1:2, **{2:3, 3:4}, **{5: 6}}') + for key, expected in ((1, 2), (2, 3), (3, 4), (5, 6)): + value = node.getitem(key) + self.assertIsInstance(value, nodes.Const) + self.assertEqual(value.value, expected) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_raw_building.py b/pymode/libs/astroid/tests/unittest_raw_building.py new file mode 100644 index 00000000..2bdaac17 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_raw_building.py @@ -0,0 +1,85 @@ +import inspect +import os +import unittest + +from six.moves import builtins # pylint: disable=import-error + +from astroid.builder import AstroidBuilder +from astroid.raw_building import ( + attach_dummy_node, build_module, + build_class, build_function, build_from_import +) +from astroid import test_utils +from astroid import nodes +from astroid.bases import BUILTINS + + +class RawBuildingTC(unittest.TestCase): + + def test_attach_dummy_node(self): + node = build_module('MyModule') + attach_dummy_node(node, 'DummyNode') + self.assertEqual(1, len(list(node.get_children()))) + + def test_build_module(self): + node = build_module('MyModule') + self.assertEqual(node.name, 'MyModule') + self.assertEqual(node.pure_python, False) + self.assertEqual(node.package, False) + self.assertEqual(node.parent, None) + + def test_build_class(self): + node = build_class('MyClass') + self.assertEqual(node.name, 'MyClass') + self.assertEqual(node.doc, None) + + def test_build_function(self): + node = build_function('MyFunction') + self.assertEqual(node.name, 'MyFunction') + self.assertEqual(node.doc, None) + + def test_build_function_args(self): + args = ['myArgs1', 'myArgs2'] + node = build_function('MyFunction', args) + self.assertEqual('myArgs1', node.args.args[0].name) + self.assertEqual('myArgs2', node.args.args[1].name) + self.assertEqual(2, len(node.args.args)) + + def test_build_function_defaults(self): + defaults = ['defaults1', 'defaults2'] + node = build_function('MyFunction', None, defaults) + self.assertEqual(2, len(node.args.defaults)) + + def test_build_from_import(self): + names = ['exceptions, inference, inspector'] + node = build_from_import('astroid', names) + self.assertEqual(len(names), len(node.names)) + + @test_utils.require_version(minver='3.0') + def test_io_is__io(self): + # _io module calls itself io. This leads + # to cyclic dependencies when astroid tries to resolve + # what io.BufferedReader is. The code that handles this + # is in astroid.raw_building.imported_member, which verifies + # the true name of the module. + import _io + + builder = AstroidBuilder() + module = builder.inspect_build(_io) + buffered_reader = module.getattr('BufferedReader')[0] + self.assertEqual(buffered_reader.root().name, 'io') + + @unittest.skipUnless(os.name == 'java', 'Requires Jython') + def test_open_is_inferred_correctly(self): + # Lot of Jython builtins don't have a __module__ attribute. + for name, _ in inspect.getmembers(builtins, predicate=inspect.isbuiltin): + if name == 'print': + continue + node = test_utils.extract_node('{0} #@'.format(name)) + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.FunctionDef, name) + self.assertEqual(inferred.root().name, BUILTINS, name) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_regrtest.py b/pymode/libs/astroid/tests/unittest_regrtest.py new file mode 100644 index 00000000..158c7119 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_regrtest.py @@ -0,0 +1,364 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +import sys +import unittest +import textwrap + +import six + +from astroid import MANAGER, Instance, nodes +from astroid.bases import BUILTINS +from astroid.builder import AstroidBuilder +from astroid import exceptions +from astroid.raw_building import build_module +from astroid.manager import AstroidManager +from astroid.test_utils import require_version, extract_node +from astroid.tests import resources +from astroid import transforms + + +class NonRegressionTests(resources.AstroidCacheSetupMixin, + unittest.TestCase): + + def setUp(self): + sys.path.insert(0, resources.find('data')) + MANAGER.always_load_extensions = True + MANAGER.astroid_cache[BUILTINS] = self._builtins + + def tearDown(self): + # Since we may have created a brainless manager, leading + # to a new cache builtin module and proxy classes in the constants, + # clear out the global manager cache. + MANAGER.clear_cache(self._builtins) + MANAGER.always_load_extensions = False + sys.path.pop(0) + sys.path_importer_cache.pop(resources.find('data'), None) + + def brainless_manager(self): + manager = AstroidManager() + # avoid caching into the AstroidManager borg since we get problems + # with other tests : + manager.__dict__ = {} + manager._failed_import_hooks = [] + manager.astroid_cache = {} + manager._mod_file_cache = {} + manager._transform = transforms.TransformVisitor() + manager.clear_cache() # trigger proper bootstraping + return manager + + def test_module_path(self): + man = self.brainless_manager() + mod = man.ast_from_module_name('package.import_package_subpackage_module') + package = next(mod.igetattr('package')) + self.assertEqual(package.name, 'package') + subpackage = next(package.igetattr('subpackage')) + self.assertIsInstance(subpackage, nodes.Module) + self.assertTrue(subpackage.package) + self.assertEqual(subpackage.name, 'package.subpackage') + module = next(subpackage.igetattr('module')) + self.assertEqual(module.name, 'package.subpackage.module') + + + def test_package_sidepackage(self): + manager = self.brainless_manager() + assert 'package.sidepackage' not in MANAGER.astroid_cache + package = manager.ast_from_module_name('absimp') + self.assertIsInstance(package, nodes.Module) + self.assertTrue(package.package) + subpackage = next(package.getattr('sidepackage')[0].infer()) + self.assertIsInstance(subpackage, nodes.Module) + self.assertTrue(subpackage.package) + self.assertEqual(subpackage.name, 'absimp.sidepackage') + + + def test_living_property(self): + builder = AstroidBuilder() + builder._done = {} + builder._module = sys.modules[__name__] + builder.object_build(build_module('module_name', ''), Whatever) + + + def test_new_style_class_detection(self): + try: + import pygtk # pylint: disable=unused-variable + except ImportError: + self.skipTest('test skipped: pygtk is not available') + # XXX may fail on some pygtk version, because objects in + # gobject._gobject have __module__ set to gobject :( + builder = AstroidBuilder() + data = """ +import pygtk +pygtk.require("2.6") +import gobject + +class A(gobject.GObject): + pass +""" + astroid = builder.string_build(data, __name__, __file__) + a = astroid['A'] + self.assertTrue(a.newstyle) + + + def test_pylint_config_attr(self): + try: + from pylint import lint # pylint: disable=unused-variable + except ImportError: + self.skipTest('pylint not available') + mod = MANAGER.ast_from_module_name('pylint.lint') + pylinter = mod['PyLinter'] + expect = ['OptionsManagerMixIn', 'object', 'MessagesHandlerMixIn', + 'ReportsHandlerMixIn', 'BaseTokenChecker', 'BaseChecker', + 'OptionsProviderMixIn'] + self.assertListEqual([c.name for c in pylinter.ancestors()], + expect) + self.assertTrue(list(Instance(pylinter).getattr('config'))) + inferred = list(Instance(pylinter).igetattr('config')) + self.assertEqual(len(inferred), 1) + self.assertEqual(inferred[0].root().name, 'optparse') + self.assertEqual(inferred[0].name, 'Values') + + def test_numpy_crash(self): + """test don't crash on numpy""" + #a crash occured somewhere in the past, and an + # InferenceError instead of a crash was better, but now we even infer! + try: + import numpy # pylint: disable=unused-variable + except ImportError: + self.skipTest('test skipped: numpy is not available') + builder = AstroidBuilder() + data = """ +from numpy import multiply + +multiply(1, 2, 3) +""" + astroid = builder.string_build(data, __name__, __file__) + callfunc = astroid.body[1].value.func + inferred = callfunc.inferred() + self.assertEqual(len(inferred), 2) + + @require_version('3.0') + def test_nameconstant(self): + # used to fail for Python 3.4 + builder = AstroidBuilder() + astroid = builder.string_build("def test(x=True): pass") + default = astroid.body[0].args.args[0] + self.assertEqual(default.name, 'x') + self.assertEqual(next(default.infer()).value, True) + + @require_version('2.7') + def test_with_infer_assignnames(self): + builder = AstroidBuilder() + data = """ +with open('a.txt') as stream, open('b.txt'): + stream.read() +""" + astroid = builder.string_build(data, __name__, __file__) + # Used to crash due to the fact that the second + # context manager didn't use an assignment name. + list(astroid.nodes_of_class(nodes.Call))[-1].inferred() + + def test_recursion_regression_issue25(self): + builder = AstroidBuilder() + data = """ +import recursion as base + +_real_Base = base.Base + +class Derived(_real_Base): + pass + +def run(): + base.Base = Derived +""" + astroid = builder.string_build(data, __name__, __file__) + # Used to crash in _is_metaclass, due to wrong + # ancestors chain + classes = astroid.nodes_of_class(nodes.ClassDef) + for klass in classes: + # triggers the _is_metaclass call + klass.type # pylint: disable=pointless-statement + + def test_decorator_callchain_issue42(self): + builder = AstroidBuilder() + data = """ + +def test(): + def factory(func): + def newfunc(): + func() + return newfunc + return factory + +@test() +def crash(): + pass +""" + astroid = builder.string_build(data, __name__, __file__) + self.assertEqual(astroid['crash'].type, 'function') + + def test_filter_stmts_scoping(self): + builder = AstroidBuilder() + data = """ +def test(): + compiler = int() + class B(compiler.__class__): + pass + compiler = B() + return compiler +""" + astroid = builder.string_build(data, __name__, __file__) + test = astroid['test'] + result = next(test.infer_call_result(astroid)) + self.assertIsInstance(result, Instance) + base = next(result._proxied.bases[0].infer()) + self.assertEqual(base.name, 'int') + + def test_ancestors_patching_class_recursion(self): + node = AstroidBuilder().string_build(textwrap.dedent(""" + import string + Template = string.Template + + class A(Template): + pass + + class B(A): + pass + + def test(x=False): + if x: + string.Template = A + else: + string.Template = B + """)) + klass = node['A'] + ancestors = list(klass.ancestors()) + self.assertEqual(ancestors[0].qname(), 'string.Template') + + def test_ancestors_yes_in_bases(self): + # Test for issue https://bitbucket.org/logilab/astroid/issue/84 + # This used to crash astroid with a TypeError, because an YES + # node was present in the bases + node = extract_node(""" + def with_metaclass(meta, *bases): + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + import lala + + class A(with_metaclass(object, lala.lala)): #@ + pass + """) + ancestors = list(node.ancestors()) + if six.PY3: + self.assertEqual(len(ancestors), 1) + self.assertEqual(ancestors[0].qname(), + "{}.object".format(BUILTINS)) + else: + self.assertEqual(len(ancestors), 0) + + def test_ancestors_missing_from_function(self): + # Test for https://www.logilab.org/ticket/122793 + node = extract_node(''' + def gen(): yield + GEN = gen() + next(GEN) + ''') + self.assertRaises(exceptions.InferenceError, next, node.infer()) + + def test_unicode_in_docstring(self): + # Crashed for astroid==1.4.1 + # Test for https://bitbucket.org/logilab/astroid/issues/273/ + + # In a regular file, "coding: utf-8" would have been used. + node = extract_node(u''' + from __future__ import unicode_literals + + class MyClass(object): + def method(self): + "With unicode : %s " + + instance = MyClass() + ''' % u"\u2019") + + next(node.value.infer()).as_string() + + def test_binop_generates_nodes_with_parents(self): + node = extract_node(''' + def no_op(*args): + pass + def foo(*args): + def inner(*more_args): + args + more_args #@ + return inner + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Tuple) + self.assertIsNotNone(inferred.parent) + self.assertIsInstance(inferred.parent, nodes.BinOp) + + def test_decorator_names_inference_error_leaking(self): + node = extract_node(''' + class Parent(object): + @property + def foo(self): + pass + + class Child(Parent): + @Parent.foo.getter + def foo(self): #@ + return super(Child, self).foo + ['oink'] + ''') + inferred = next(node.infer()) + self.assertEqual(inferred.decoratornames(), set()) + + def test_ssl_protocol(self): + node = extract_node(''' + import ssl + ssl.PROTOCOL_TLSv1 + ''') + inferred = next(node.infer()) + self.assertIsInstance(inferred, nodes.Const) + + def test_uninferable_string_argument_of_namedtuple(self): + node = extract_node(''' + import collections + collections.namedtuple('{}'.format("a"), '')() + ''') + next(node.infer()) + + @require_version(maxver='3.0') + def test_reassignment_in_except_handler(self): + node = extract_node(''' + import exceptions + try: + {}["a"] + except KeyError, exceptions.IndexError: + pass + + IndexError #@ + ''') + self.assertEqual(len(node.inferred()), 1) + + +class Whatever(object): + a = property(lambda x: x, lambda x: x) + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_scoped_nodes.py b/pymode/libs/astroid/tests/unittest_scoped_nodes.py new file mode 100644 index 00000000..a15c923a --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_scoped_nodes.py @@ -0,0 +1,1583 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +"""tests for specific behaviour of astroid scoped nodes (i.e. module, class and +function) +""" +import os +import sys +from functools import partial +import unittest +import warnings + +from astroid import builder +from astroid import nodes +from astroid import scoped_nodes +from astroid import util +from astroid.exceptions import ( + InferenceError, NotFoundError, + NoDefault, ResolveError, MroError, + InconsistentMroError, DuplicateBasesError, +) +from astroid.bases import ( + BUILTINS, Instance, + BoundMethod, UnboundMethod, Generator +) +from astroid import __pkginfo__ +from astroid import test_utils +from astroid.tests import resources + + +def _test_dict_interface(self, node, test_attr): + self.assertIs(node[test_attr], node[test_attr]) + self.assertIn(test_attr, node) + node.keys() + node.values() + node.items() + iter(node) + + +class ModuleLoader(resources.SysPathSetup): + def setUp(self): + super(ModuleLoader, self).setUp() + self.module = resources.build_file('data/module.py', 'data.module') + self.module2 = resources.build_file('data/module2.py', 'data.module2') + self.nonregr = resources.build_file('data/nonregr.py', 'data.nonregr') + self.pack = resources.build_file('data/__init__.py', 'data') + + +class ModuleNodeTest(ModuleLoader, unittest.TestCase): + + def test_special_attributes(self): + self.assertEqual(len(self.module.getattr('__name__')), 1) + self.assertIsInstance(self.module.getattr('__name__')[0], nodes.Const) + self.assertEqual(self.module.getattr('__name__')[0].value, 'data.module') + self.assertEqual(len(self.module.getattr('__doc__')), 1) + self.assertIsInstance(self.module.getattr('__doc__')[0], nodes.Const) + self.assertEqual(self.module.getattr('__doc__')[0].value, 'test module for astroid\n') + self.assertEqual(len(self.module.getattr('__file__')), 1) + self.assertIsInstance(self.module.getattr('__file__')[0], nodes.Const) + self.assertEqual(self.module.getattr('__file__')[0].value, + os.path.abspath(resources.find('data/module.py'))) + self.assertEqual(len(self.module.getattr('__dict__')), 1) + self.assertIsInstance(self.module.getattr('__dict__')[0], nodes.Dict) + self.assertRaises(NotFoundError, self.module.getattr, '__path__') + self.assertEqual(len(self.pack.getattr('__path__')), 1) + self.assertIsInstance(self.pack.getattr('__path__')[0], nodes.List) + + def test_dict_interface(self): + _test_dict_interface(self, self.module, 'YO') + + def test_getattr(self): + yo = self.module.getattr('YO')[0] + self.assertIsInstance(yo, nodes.ClassDef) + self.assertEqual(yo.name, 'YO') + red = next(self.module.igetattr('redirect')) + self.assertIsInstance(red, nodes.FunctionDef) + self.assertEqual(red.name, 'four_args') + namenode = next(self.module.igetattr('NameNode')) + self.assertIsInstance(namenode, nodes.ClassDef) + self.assertEqual(namenode.name, 'Name') + # resolve packageredirection + mod = resources.build_file('data/appl/myConnection.py', + 'data.appl.myConnection') + ssl = next(mod.igetattr('SSL1')) + cnx = next(ssl.igetattr('Connection')) + self.assertEqual(cnx.__class__, nodes.ClassDef) + self.assertEqual(cnx.name, 'Connection') + self.assertEqual(cnx.root().name, 'data.SSL1.Connection1') + self.assertEqual(len(self.nonregr.getattr('enumerate')), 2) + # raise ResolveError + self.assertRaises(InferenceError, self.nonregr.igetattr, 'YOAA') + + def test_wildcard_import_names(self): + m = resources.build_file('data/all.py', 'all') + self.assertEqual(m.wildcard_import_names(), ['Aaa', '_bla', 'name']) + m = resources.build_file('data/notall.py', 'notall') + res = sorted(m.wildcard_import_names()) + self.assertEqual(res, ['Aaa', 'func', 'name', 'other']) + + def test_public_names(self): + m = builder.parse(''' + name = 'a' + _bla = 2 + other = 'o' + class Aaa: pass + def func(): print('yo') + __all__ = 'Aaa', '_bla', 'name' + ''') + values = sorted(['Aaa', 'name', 'other', 'func']) + self.assertEqual(sorted(m._public_names()), values) + m = builder.parse(''' + name = 'a' + _bla = 2 + other = 'o' + class Aaa: pass + + def func(): return 'yo' + ''') + res = sorted(m._public_names()) + self.assertEqual(res, values) + + m = builder.parse(''' + from missing import tzop + trop = "test" + __all__ = (trop, "test1", tzop, 42) + ''') + res = sorted(m._public_names()) + self.assertEqual(res, ["trop", "tzop"]) + + m = builder.parse(''' + test = tzop = 42 + __all__ = ('test', ) + ('tzop', ) + ''') + res = sorted(m._public_names()) + self.assertEqual(res, ['test', 'tzop']) + + def test_module_getattr(self): + data = ''' + appli = application + appli += 2 + del appli + ''' + astroid = builder.parse(data, __name__) + # test del statement not returned by getattr + self.assertEqual(len(astroid.getattr('appli')), 2, + astroid.getattr('appli')) + + def test_relative_to_absolute_name(self): + # package + mod = nodes.Module('very.multi.package', 'doc') + mod.package = True + modname = mod.relative_to_absolute_name('utils', 1) + self.assertEqual(modname, 'very.multi.package.utils') + modname = mod.relative_to_absolute_name('utils', 2) + self.assertEqual(modname, 'very.multi.utils') + modname = mod.relative_to_absolute_name('utils', 0) + self.assertEqual(modname, 'very.multi.package.utils') + modname = mod.relative_to_absolute_name('', 1) + self.assertEqual(modname, 'very.multi.package') + # non package + mod = nodes.Module('very.multi.module', 'doc') + mod.package = False + modname = mod.relative_to_absolute_name('utils', 0) + self.assertEqual(modname, 'very.multi.utils') + modname = mod.relative_to_absolute_name('utils', 1) + self.assertEqual(modname, 'very.multi.utils') + modname = mod.relative_to_absolute_name('utils', 2) + self.assertEqual(modname, 'very.utils') + modname = mod.relative_to_absolute_name('', 1) + self.assertEqual(modname, 'very.multi') + + def test_import_1(self): + data = '''from . import subpackage''' + sys.path.insert(0, resources.find('data')) + astroid = builder.parse(data, 'package', 'data/package/__init__.py') + try: + m = astroid.import_module('', level=1) + self.assertEqual(m.name, 'package') + inferred = list(astroid.igetattr('subpackage')) + self.assertEqual(len(inferred), 1) + self.assertEqual(inferred[0].name, 'package.subpackage') + finally: + del sys.path[0] + + + def test_import_2(self): + data = '''from . import subpackage as pouet''' + astroid = builder.parse(data, 'package', 'data/package/__init__.py') + sys.path.insert(0, resources.find('data')) + try: + m = astroid.import_module('', level=1) + self.assertEqual(m.name, 'package') + inferred = list(astroid.igetattr('pouet')) + self.assertEqual(len(inferred), 1) + self.assertEqual(inferred[0].name, 'package.subpackage') + finally: + del sys.path[0] + + + def test_file_stream_in_memory(self): + data = '''irrelevant_variable is irrelevant''' + astroid = builder.parse(data, 'in_memory') + with warnings.catch_warnings(record=True): + self.assertEqual(astroid.file_stream.read().decode(), data) + + def test_file_stream_physical(self): + path = resources.find('data/all.py') + astroid = builder.AstroidBuilder().file_build(path, 'all') + with open(path, 'rb') as file_io: + with warnings.catch_warnings(record=True): + self.assertEqual(astroid.file_stream.read(), file_io.read()) + + def test_file_stream_api(self): + path = resources.find('data/all.py') + astroid = builder.AstroidBuilder().file_build(path, 'all') + if __pkginfo__.numversion >= (1, 6): + # file_stream is slated for removal in astroid 1.6. + with self.assertRaises(AttributeError): + # pylint: disable=pointless-statement + astroid.file_stream + else: + # Until astroid 1.6, Module.file_stream will emit + # PendingDeprecationWarning in 1.4, DeprecationWarning + # in 1.5 and finally it will be removed in 1.6, leaving + # only Module.stream as the recommended way to retrieve + # its file stream. + with warnings.catch_warnings(record=True) as cm: + warnings.simplefilter("always") + self.assertIsNot(astroid.file_stream, astroid.file_stream) + self.assertGreater(len(cm), 1) + self.assertEqual(cm[0].category, PendingDeprecationWarning) + + def test_stream_api(self): + path = resources.find('data/all.py') + astroid = builder.AstroidBuilder().file_build(path, 'all') + stream = astroid.stream() + self.assertTrue(hasattr(stream, 'close')) + with stream: + with open(path, 'rb') as file_io: + self.assertEqual(stream.read(), file_io.read()) + + +class FunctionNodeTest(ModuleLoader, unittest.TestCase): + + def test_special_attributes(self): + func = self.module2['make_class'] + self.assertEqual(len(func.getattr('__name__')), 1) + self.assertIsInstance(func.getattr('__name__')[0], nodes.Const) + self.assertEqual(func.getattr('__name__')[0].value, 'make_class') + self.assertEqual(len(func.getattr('__doc__')), 1) + self.assertIsInstance(func.getattr('__doc__')[0], nodes.Const) + self.assertEqual(func.getattr('__doc__')[0].value, 'check base is correctly resolved to Concrete0') + self.assertEqual(len(self.module.getattr('__dict__')), 1) + self.assertIsInstance(self.module.getattr('__dict__')[0], nodes.Dict) + + def test_dict_interface(self): + _test_dict_interface(self, self.module['global_access'], 'local') + + def test_default_value(self): + func = self.module2['make_class'] + self.assertIsInstance(func.args.default_value('base'), nodes.Attribute) + self.assertRaises(NoDefault, func.args.default_value, 'args') + self.assertRaises(NoDefault, func.args.default_value, 'kwargs') + self.assertRaises(NoDefault, func.args.default_value, 'any') + #self.assertIsInstance(func.mularg_class('args'), nodes.Tuple) + #self.assertIsInstance(func.mularg_class('kwargs'), nodes.Dict) + #self.assertIsNone(func.mularg_class('base')) + + def test_navigation(self): + function = self.module['global_access'] + self.assertEqual(function.statement(), function) + l_sibling = function.previous_sibling() + # check taking parent if child is not a stmt + self.assertIsInstance(l_sibling, nodes.Assign) + child = function.args.args[0] + self.assertIs(l_sibling, child.previous_sibling()) + r_sibling = function.next_sibling() + self.assertIsInstance(r_sibling, nodes.ClassDef) + self.assertEqual(r_sibling.name, 'YO') + self.assertIs(r_sibling, child.next_sibling()) + last = r_sibling.next_sibling().next_sibling().next_sibling() + self.assertIsInstance(last, nodes.Assign) + self.assertIsNone(last.next_sibling()) + first = l_sibling.root().body[0] + self.assertIsNone(first.previous_sibling()) + + def test_nested_args(self): + if sys.version_info >= (3, 0): + self.skipTest("nested args has been removed in py3.x") + code = ''' + def nested_args(a, (b, c, d)): + "nested arguments test" + ''' + tree = builder.parse(code) + func = tree['nested_args'] + self.assertEqual(sorted(func._locals), ['a', 'b', 'c', 'd']) + self.assertEqual(func.args.format_args(), 'a, (b, c, d)') + + def test_four_args(self): + func = self.module['four_args'] + #self.assertEqual(func.args.args, ['a', ('b', 'c', 'd')]) + local = sorted(func.keys()) + self.assertEqual(local, ['a', 'b', 'c', 'd']) + self.assertEqual(func.type, 'function') + + def test_format_args(self): + func = self.module2['make_class'] + self.assertEqual(func.args.format_args(), + 'any, base=data.module.YO, *args, **kwargs') + func = self.module['four_args'] + self.assertEqual(func.args.format_args(), 'a, b, c, d') + + def test_is_generator(self): + self.assertTrue(self.module2['generator'].is_generator()) + self.assertFalse(self.module2['not_a_generator'].is_generator()) + self.assertFalse(self.module2['make_class'].is_generator()) + + def test_is_abstract(self): + method = self.module2['AbstractClass']['to_override'] + self.assertTrue(method.is_abstract(pass_is_abstract=False)) + self.assertEqual(method.qname(), 'data.module2.AbstractClass.to_override') + self.assertEqual(method.pytype(), '%s.instancemethod' % BUILTINS) + method = self.module2['AbstractClass']['return_something'] + self.assertFalse(method.is_abstract(pass_is_abstract=False)) + # non regression : test raise "string" doesn't cause an exception in is_abstract + func = self.module2['raise_string'] + self.assertFalse(func.is_abstract(pass_is_abstract=False)) + + def test_is_abstract_decorated(self): + methods = test_utils.extract_node(""" + import abc + + class Klass(object): + @abc.abstractproperty + def prop(self): #@ + pass + + @abc.abstractmethod + def method1(self): #@ + pass + + some_other_decorator = lambda x: x + @some_other_decorator + def method2(self): #@ + pass + """) + self.assertTrue(methods[0].is_abstract(pass_is_abstract=False)) + self.assertTrue(methods[1].is_abstract(pass_is_abstract=False)) + self.assertFalse(methods[2].is_abstract(pass_is_abstract=False)) + +## def test_raises(self): +## method = self.module2['AbstractClass']['to_override'] +## self.assertEqual([str(term) for term in method.raises()], +## ["Call(Name('NotImplementedError'), [], None, None)"] ) + +## def test_returns(self): +## method = self.module2['AbstractClass']['return_something'] +## # use string comp since Node doesn't handle __cmp__ +## self.assertEqual([str(term) for term in method.returns()], +## ["Const('toto')", "Const(None)"]) + + def test_lambda_pytype(self): + data = ''' + def f(): + g = lambda: None + ''' + astroid = builder.parse(data) + g = list(astroid['f'].ilookup('g'))[0] + self.assertEqual(g.pytype(), '%s.function' % BUILTINS) + + def test_lambda_qname(self): + astroid = builder.parse('lmbd = lambda: None', __name__) + self.assertEqual('%s.' % __name__, astroid['lmbd'].parent.value.qname()) + + def test_is_method(self): + data = ''' + class A: + def meth1(self): + return 1 + @classmethod + def meth2(cls): + return 2 + @staticmethod + def meth3(): + return 3 + + def function(): + return 0 + + @staticmethod + def sfunction(): + return -1 + ''' + astroid = builder.parse(data) + self.assertTrue(astroid['A']['meth1'].is_method()) + self.assertTrue(astroid['A']['meth2'].is_method()) + self.assertTrue(astroid['A']['meth3'].is_method()) + self.assertFalse(astroid['function'].is_method()) + self.assertFalse(astroid['sfunction'].is_method()) + + def test_argnames(self): + if sys.version_info < (3, 0): + code = 'def f(a, (b, c), *args, **kwargs): pass' + else: + code = 'def f(a, b, c, *args, **kwargs): pass' + astroid = builder.parse(code, __name__) + self.assertEqual(astroid['f'].argnames(), ['a', 'b', 'c', 'args', 'kwargs']) + + def test_return_nothing(self): + """test inferred value on a function with empty return""" + data = ''' + def func(): + return + + a = func() + ''' + astroid = builder.parse(data) + call = astroid.body[1].value + func_vals = call.inferred() + self.assertEqual(len(func_vals), 1) + self.assertIsInstance(func_vals[0], nodes.Const) + self.assertIsNone(func_vals[0].value) + + def test_func_instance_attr(self): + """test instance attributes for functions""" + data = """ + def test(): + print(test.bar) + + test.bar = 1 + test() + """ + astroid = builder.parse(data, 'mod') + func = astroid.body[2].value.func.inferred()[0] + self.assertIsInstance(func, nodes.FunctionDef) + self.assertEqual(func.name, 'test') + one = func.getattr('bar')[0].inferred()[0] + self.assertIsInstance(one, nodes.Const) + self.assertEqual(one.value, 1) + + def test_type_builtin_descriptor_subclasses(self): + astroid = builder.parse(""" + class classonlymethod(classmethod): + pass + class staticonlymethod(staticmethod): + pass + + class Node: + @classonlymethod + def clsmethod_subclass(cls): + pass + @classmethod + def clsmethod(cls): + pass + @staticonlymethod + def staticmethod_subclass(cls): + pass + @staticmethod + def stcmethod(cls): + pass + """) + node = astroid._locals['Node'][0] + self.assertEqual(node._locals['clsmethod_subclass'][0].type, + 'classmethod') + self.assertEqual(node._locals['clsmethod'][0].type, + 'classmethod') + self.assertEqual(node._locals['staticmethod_subclass'][0].type, + 'staticmethod') + self.assertEqual(node._locals['stcmethod'][0].type, + 'staticmethod') + + def test_decorator_builtin_descriptors(self): + astroid = builder.parse(""" + def static_decorator(platform=None, order=50): + def wrapper(f): + f.cgm_module = True + f.cgm_module_order = order + f.cgm_module_platform = platform + return staticmethod(f) + return wrapper + + def long_classmethod_decorator(platform=None, order=50): + def wrapper(f): + def wrapper2(f): + def wrapper3(f): + f.cgm_module = True + f.cgm_module_order = order + f.cgm_module_platform = platform + return classmethod(f) + return wrapper3(f) + return wrapper2(f) + return wrapper + + def classmethod_decorator(platform=None): + def wrapper(f): + f.platform = platform + return classmethod(f) + return wrapper + + def classmethod_wrapper(fn): + def wrapper(cls, *args, **kwargs): + result = fn(cls, *args, **kwargs) + return result + + return classmethod(wrapper) + + def staticmethod_wrapper(fn): + def wrapper(*args, **kwargs): + return fn(*args, **kwargs) + return staticmethod(wrapper) + + class SomeClass(object): + @static_decorator() + def static(node, cfg): + pass + @classmethod_decorator() + def classmethod(cls): + pass + @static_decorator + def not_so_static(node): + pass + @classmethod_decorator + def not_so_classmethod(node): + pass + @classmethod_wrapper + def classmethod_wrapped(cls): + pass + @staticmethod_wrapper + def staticmethod_wrapped(): + pass + @long_classmethod_decorator() + def long_classmethod(cls): + pass + """) + node = astroid._locals['SomeClass'][0] + self.assertEqual(node._locals['static'][0].type, + 'staticmethod') + self.assertEqual(node._locals['classmethod'][0].type, + 'classmethod') + self.assertEqual(node._locals['not_so_static'][0].type, + 'method') + self.assertEqual(node._locals['not_so_classmethod'][0].type, + 'method') + self.assertEqual(node._locals['classmethod_wrapped'][0].type, + 'classmethod') + self.assertEqual(node._locals['staticmethod_wrapped'][0].type, + 'staticmethod') + self.assertEqual(node._locals['long_classmethod'][0].type, + 'classmethod') + + def test_igetattr(self): + func = test_utils.extract_node(''' + def test(): + pass + ''') + func._instance_attrs['value'] = [nodes.Const(42)] + value = func.getattr('value') + self.assertEqual(len(value), 1) + self.assertIsInstance(value[0], nodes.Const) + self.assertEqual(value[0].value, 42) + inferred = next(func.igetattr('value')) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, 42) + + @test_utils.require_version(minver='3.0') + def test_return_annotation_is_not_the_last(self): + func = builder.parse(''' + def test() -> bytes: + pass + pass + return + ''').body[0] + last_child = func.last_child() + self.assertIsInstance(last_child, nodes.Return) + self.assertEqual(func.tolineno, 5) + + +class ClassNodeTest(ModuleLoader, unittest.TestCase): + + def test_dict_interface(self): + _test_dict_interface(self, self.module['YOUPI'], 'method') + + def test_cls_special_attributes_1(self): + cls = self.module['YO'] + self.assertEqual(len(cls.getattr('__bases__')), 1) + self.assertEqual(len(cls.getattr('__name__')), 1) + self.assertIsInstance(cls.getattr('__name__')[0], nodes.Const) + self.assertEqual(cls.getattr('__name__')[0].value, 'YO') + self.assertEqual(len(cls.getattr('__doc__')), 1) + self.assertIsInstance(cls.getattr('__doc__')[0], nodes.Const) + self.assertEqual(cls.getattr('__doc__')[0].value, 'hehe') + self.assertEqual(len(cls.getattr('__module__')), 1) + self.assertIsInstance(cls.getattr('__module__')[0], nodes.Const) + self.assertEqual(cls.getattr('__module__')[0].value, 'data.module') + self.assertEqual(len(cls.getattr('__dict__')), 1) + if not cls.newstyle: + self.assertRaises(NotFoundError, cls.getattr, '__mro__') + for cls in (nodes.List._proxied, nodes.Const(1)._proxied): + self.assertEqual(len(cls.getattr('__bases__')), 1) + self.assertEqual(len(cls.getattr('__name__')), 1) + self.assertEqual(len(cls.getattr('__doc__')), 1, (cls, cls.getattr('__doc__'))) + self.assertEqual(cls.getattr('__doc__')[0].value, cls.doc) + self.assertEqual(len(cls.getattr('__module__')), 1) + self.assertEqual(len(cls.getattr('__dict__')), 1) + self.assertEqual(len(cls.getattr('__mro__')), 1) + + def test__mro__attribute(self): + node = test_utils.extract_node(''' + class A(object): pass + class B(object): pass + class C(A, B): pass + ''') + mro = node.getattr('__mro__')[0] + self.assertIsInstance(mro, nodes.Tuple) + self.assertEqual(mro.elts, node.mro()) + + def test__bases__attribute(self): + node = test_utils.extract_node(''' + class A(object): pass + class B(object): pass + class C(A, B): pass + class D(C): pass + ''') + bases = node.getattr('__bases__')[0] + self.assertIsInstance(bases, nodes.Tuple) + self.assertEqual(len(bases.elts), 1) + self.assertIsInstance(bases.elts[0], nodes.ClassDef) + self.assertEqual(bases.elts[0].name, 'C') + + def test_cls_special_attributes_2(self): + astroid = builder.parse(''' + class A: pass + class B: pass + + A.__bases__ += (B,) + ''', __name__) + self.assertEqual(len(astroid['A'].getattr('__bases__')), 2) + self.assertIsInstance(astroid['A'].getattr('__bases__')[0], nodes.Tuple) + self.assertIsInstance(astroid['A'].getattr('__bases__')[1], nodes.AssignAttr) + + def test_instance_special_attributes(self): + for inst in (Instance(self.module['YO']), nodes.List(), nodes.Const(1)): + self.assertRaises(NotFoundError, inst.getattr, '__mro__') + self.assertRaises(NotFoundError, inst.getattr, '__bases__') + self.assertRaises(NotFoundError, inst.getattr, '__name__') + self.assertEqual(len(inst.getattr('__dict__')), 1) + self.assertEqual(len(inst.getattr('__doc__')), 1) + + def test_navigation(self): + klass = self.module['YO'] + self.assertEqual(klass.statement(), klass) + l_sibling = klass.previous_sibling() + self.assertTrue(isinstance(l_sibling, nodes.FunctionDef), l_sibling) + self.assertEqual(l_sibling.name, 'global_access') + r_sibling = klass.next_sibling() + self.assertIsInstance(r_sibling, nodes.ClassDef) + self.assertEqual(r_sibling.name, 'YOUPI') + + def test_local_attr_ancestors(self): + module = builder.parse(''' + class A(): + def __init__(self): pass + class B(A): pass + class C(B): pass + class D(object): pass + class F(): pass + class E(F, D): pass + ''') + # Test old-style (Python 2) / new-style (Python 3+) ancestors lookups + klass2 = module['C'] + it = klass2.local_attr_ancestors('__init__') + anc_klass = next(it) + self.assertIsInstance(anc_klass, nodes.ClassDef) + self.assertEqual(anc_klass.name, 'A') + if sys.version_info[0] == 2: + self.assertRaises(StopIteration, partial(next, it)) + else: + anc_klass = next(it) + self.assertIsInstance(anc_klass, nodes.ClassDef) + self.assertEqual(anc_klass.name, 'object') + self.assertRaises(StopIteration, partial(next, it)) + + it = klass2.local_attr_ancestors('method') + self.assertRaises(StopIteration, partial(next, it)) + + # Test mixed-style ancestor lookups + klass2 = module['E'] + it = klass2.local_attr_ancestors('__init__') + anc_klass = next(it) + self.assertIsInstance(anc_klass, nodes.ClassDef) + self.assertEqual(anc_klass.name, 'object') + self.assertRaises(StopIteration, partial(next, it)) + + def test_local_attr_mro(self): + module = builder.parse(''' + class A(object): + def __init__(self): pass + class B(A): + def __init__(self, arg, arg2): pass + class C(A): pass + class D(C, B): pass + ''') + dclass = module['D'] + init = dclass.local_attr('__init__')[0] + self.assertIsInstance(init, nodes.FunctionDef) + self.assertEqual(init.parent.name, 'B') + + cclass = module['C'] + init = cclass.local_attr('__init__')[0] + self.assertIsInstance(init, nodes.FunctionDef) + self.assertEqual(init.parent.name, 'A') + + ancestors = list(dclass.local_attr_ancestors('__init__')) + self.assertEqual([node.name for node in ancestors], ['B', 'A', 'object']) + + def test_instance_attr_ancestors(self): + klass2 = self.module['YOUPI'] + it = klass2.instance_attr_ancestors('yo') + anc_klass = next(it) + self.assertIsInstance(anc_klass, nodes.ClassDef) + self.assertEqual(anc_klass.name, 'YO') + self.assertRaises(StopIteration, partial(next, it)) + klass2 = self.module['YOUPI'] + it = klass2.instance_attr_ancestors('member') + self.assertRaises(StopIteration, partial(next, it)) + + def test_methods(self): + expected_methods = {'__init__', 'class_method', 'method', 'static_method'} + klass2 = self.module['YOUPI'] + methods = {m.name for m in klass2.methods()} + self.assertTrue( + methods.issuperset(expected_methods)) + methods = {m.name for m in klass2.mymethods()} + self.assertSetEqual(expected_methods, methods) + klass2 = self.module2['Specialization'] + methods = {m.name for m in klass2.mymethods()} + self.assertSetEqual(set([]), methods) + method_locals = klass2.local_attr('method') + self.assertEqual(len(method_locals), 1) + self.assertEqual(method_locals[0].name, 'method') + self.assertRaises(NotFoundError, klass2.local_attr, 'nonexistant') + methods = {m.name for m in klass2.methods()} + self.assertTrue(methods.issuperset(expected_methods)) + + #def test_rhs(self): + # my_dict = self.module['MY_DICT'] + # self.assertIsInstance(my_dict.rhs(), nodes.Dict) + # a = self.module['YO']['a'] + # value = a.rhs() + # self.assertIsInstance(value, nodes.Const) + # self.assertEqual(value.value, 1) + + @unittest.skipIf(sys.version_info[0] >= 3, "Python 2 class semantics required.") + def test_ancestors(self): + klass = self.module['YOUPI'] + self.assertEqual(['YO'], [a.name for a in klass.ancestors()]) + klass = self.module2['Specialization'] + self.assertEqual(['YOUPI', 'YO'], [a.name for a in klass.ancestors()]) + + @unittest.skipIf(sys.version_info[0] < 3, "Python 3 class semantics required.") + def test_ancestors_py3(self): + klass = self.module['YOUPI'] + self.assertEqual(['YO', 'object'], [a.name for a in klass.ancestors()]) + klass = self.module2['Specialization'] + self.assertEqual(['YOUPI', 'YO', 'object'], [a.name for a in klass.ancestors()]) + + def test_type(self): + klass = self.module['YOUPI'] + self.assertEqual(klass.type, 'class') + klass = self.module2['Metaclass'] + self.assertEqual(klass.type, 'metaclass') + klass = self.module2['MyException'] + self.assertEqual(klass.type, 'exception') + klass = self.module2['MyError'] + self.assertEqual(klass.type, 'exception') + # the following class used to be detected as a metaclass + # after the fix which used instance._proxied in .ancestors(), + # when in fact it is a normal class + klass = self.module2['NotMetaclass'] + self.assertEqual(klass.type, 'class') + + def test_inner_classes(self): + eee = self.nonregr['Ccc']['Eee'] + self.assertEqual([n.name for n in eee.ancestors()], ['Ddd', 'Aaa', 'object']) + + + def test_classmethod_attributes(self): + data = ''' + class WebAppObject(object): + def registered(cls, application): + cls.appli = application + cls.schema = application.schema + cls.config = application.config + return cls + registered = classmethod(registered) + ''' + astroid = builder.parse(data, __name__) + cls = astroid['WebAppObject'] + self.assertEqual(sorted(cls._locals.keys()), + ['appli', 'config', 'registered', 'schema']) + + def test_class_getattr(self): + data = ''' + class WebAppObject(object): + appli = application + appli += 2 + del self.appli + ''' + astroid = builder.parse(data, __name__) + cls = astroid['WebAppObject'] + # test del statement not returned by getattr + self.assertEqual(len(cls.getattr('appli')), 2) + + + def test_instance_getattr(self): + data = ''' + class WebAppObject(object): + def __init__(self, application): + self.appli = application + self.appli += 2 + del self.appli + ''' + astroid = builder.parse(data) + inst = Instance(astroid['WebAppObject']) + # test del statement not returned by getattr + self.assertEqual(len(inst.getattr('appli')), 2) + + + def test_instance_getattr_with_class_attr(self): + data = ''' + class Parent: + aa = 1 + cc = 1 + + class Klass(Parent): + aa = 0 + bb = 0 + + def incr(self, val): + self.cc = self.aa + if val > self.aa: + val = self.aa + if val < self.bb: + val = self.bb + self.aa += val + ''' + astroid = builder.parse(data) + inst = Instance(astroid['Klass']) + self.assertEqual(len(inst.getattr('aa')), 3, inst.getattr('aa')) + self.assertEqual(len(inst.getattr('bb')), 1, inst.getattr('bb')) + self.assertEqual(len(inst.getattr('cc')), 2, inst.getattr('cc')) + + + def test_getattr_method_transform(self): + data = ''' + class Clazz(object): + + def m1(self, value): + self.value = value + m2 = m1 + + def func(arg1, arg2): + "function that will be used as a method" + return arg1.value + arg2 + + Clazz.m3 = func + inst = Clazz() + inst.m4 = func + ''' + astroid = builder.parse(data) + cls = astroid['Clazz'] + # test del statement not returned by getattr + for method in ('m1', 'm2', 'm3'): + inferred = list(cls.igetattr(method)) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], UnboundMethod) + inferred = list(Instance(cls).igetattr(method)) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], BoundMethod) + inferred = list(Instance(cls).igetattr('m4')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.FunctionDef) + + def test_getattr_from_grandpa(self): + data = ''' + class Future: + attr = 1 + + class Present(Future): + pass + + class Past(Present): + pass + ''' + astroid = builder.parse(data) + past = astroid['Past'] + attr = past.getattr('attr') + self.assertEqual(len(attr), 1) + attr1 = attr[0] + self.assertIsInstance(attr1, nodes.AssignName) + self.assertEqual(attr1.name, 'attr') + + def test_function_with_decorator_lineno(self): + data = ''' + @f(a=2, + b=3) + def g1(x): + print(x) + + @f(a=2, + b=3) + def g2(): + pass + ''' + astroid = builder.parse(data) + self.assertEqual(astroid['g1'].fromlineno, 4) + self.assertEqual(astroid['g1'].tolineno, 5) + self.assertEqual(astroid['g2'].fromlineno, 9) + self.assertEqual(astroid['g2'].tolineno, 10) + + @test_utils.require_version(maxver='3.0') + def test_simple_metaclass(self): + astroid = builder.parse(""" + class Test(object): + __metaclass__ = type + """) + klass = astroid['Test'] + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, scoped_nodes.ClassDef) + self.assertEqual(metaclass.name, 'type') + + def test_metaclass_error(self): + astroid = builder.parse(""" + class Test(object): + __metaclass__ = typ + """) + klass = astroid['Test'] + self.assertFalse(klass.metaclass()) + + @test_utils.require_version(maxver='3.0') + def test_metaclass_imported(self): + astroid = builder.parse(""" + from abc import ABCMeta + class Test(object): + __metaclass__ = ABCMeta + """) + klass = astroid['Test'] + + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, scoped_nodes.ClassDef) + self.assertEqual(metaclass.name, 'ABCMeta') + + def test_metaclass_yes_leak(self): + astroid = builder.parse(""" + # notice `ab` instead of `abc` + from ab import ABCMeta + + class Meta(object): + __metaclass__ = ABCMeta + """) + klass = astroid['Meta'] + self.assertIsNone(klass.metaclass()) + + @test_utils.require_version(maxver='3.0') + def test_newstyle_and_metaclass_good(self): + astroid = builder.parse(""" + from abc import ABCMeta + class Test: + __metaclass__ = ABCMeta + """) + klass = astroid['Test'] + self.assertTrue(klass.newstyle) + self.assertEqual(klass.metaclass().name, 'ABCMeta') + astroid = builder.parse(""" + from abc import ABCMeta + __metaclass__ = ABCMeta + class Test: + pass + """) + klass = astroid['Test'] + self.assertTrue(klass.newstyle) + self.assertEqual(klass.metaclass().name, 'ABCMeta') + + @test_utils.require_version(maxver='3.0') + def test_nested_metaclass(self): + astroid = builder.parse(""" + from abc import ABCMeta + class A(object): + __metaclass__ = ABCMeta + class B: pass + + __metaclass__ = ABCMeta + class C: + __metaclass__ = type + class D: pass + """) + a = astroid['A'] + b = a._locals['B'][0] + c = astroid['C'] + d = c._locals['D'][0] + self.assertEqual(a.metaclass().name, 'ABCMeta') + self.assertFalse(b.newstyle) + self.assertIsNone(b.metaclass()) + self.assertEqual(c.metaclass().name, 'type') + self.assertEqual(d.metaclass().name, 'ABCMeta') + + @test_utils.require_version(maxver='3.0') + def test_parent_metaclass(self): + astroid = builder.parse(""" + from abc import ABCMeta + class Test: + __metaclass__ = ABCMeta + class SubTest(Test): pass + """) + klass = astroid['SubTest'] + self.assertTrue(klass.newstyle) + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, scoped_nodes.ClassDef) + self.assertEqual(metaclass.name, 'ABCMeta') + + @test_utils.require_version(maxver='3.0') + def test_metaclass_ancestors(self): + astroid = builder.parse(""" + from abc import ABCMeta + + class FirstMeta(object): + __metaclass__ = ABCMeta + + class SecondMeta(object): + __metaclass__ = type + + class Simple(object): + pass + + class FirstImpl(FirstMeta): pass + class SecondImpl(FirstImpl): pass + class ThirdImpl(Simple, SecondMeta): + pass + """) + classes = { + 'ABCMeta': ('FirstImpl', 'SecondImpl'), + 'type': ('ThirdImpl', ) + } + for metaclass, names in classes.items(): + for name in names: + impl = astroid[name] + meta = impl.metaclass() + self.assertIsInstance(meta, nodes.ClassDef) + self.assertEqual(meta.name, metaclass) + + def test_metaclass_type(self): + klass = test_utils.extract_node(""" + def with_metaclass(meta, base=object): + return meta("NewBase", (base, ), {}) + + class ClassWithMeta(with_metaclass(type)): #@ + pass + """) + self.assertEqual( + ['NewBase', 'object'], + [base.name for base in klass.ancestors()]) + + def test_no_infinite_metaclass_loop(self): + klass = test_utils.extract_node(""" + class SSS(object): + + class JJJ(object): + pass + + @classmethod + def Init(cls): + cls.JJJ = type('JJJ', (cls.JJJ,), {}) + + class AAA(SSS): + pass + + class BBB(AAA.JJJ): + pass + """) + self.assertFalse(scoped_nodes._is_metaclass(klass)) + ancestors = [base.name for base in klass.ancestors()] + self.assertIn('object', ancestors) + self.assertIn('JJJ', ancestors) + + def test_no_infinite_metaclass_loop_with_redefine(self): + nodes = test_utils.extract_node(""" + import datetime + + class A(datetime.date): #@ + @classmethod + def now(cls): + return cls() + + class B(datetime.date): #@ + pass + + datetime.date = A + datetime.date = B + """) + for klass in nodes: + self.assertEqual(None, klass.metaclass()) + + def test_metaclass_generator_hack(self): + klass = test_utils.extract_node(""" + import six + + class WithMeta(six.with_metaclass(type, object)): #@ + pass + """) + self.assertEqual( + ['object'], + [base.name for base in klass.ancestors()]) + self.assertEqual( + 'type', klass.metaclass().name) + + def test_using_six_add_metaclass(self): + klass = test_utils.extract_node(''' + import six + import abc + + @six.add_metaclass(abc.ABCMeta) + class WithMeta(object): + pass + ''') + inferred = next(klass.infer()) + metaclass = inferred.metaclass() + self.assertIsInstance(metaclass, scoped_nodes.ClassDef) + self.assertEqual(metaclass.qname(), 'abc.ABCMeta') + + def test_using_invalid_six_add_metaclass_call(self): + klass = test_utils.extract_node(''' + import six + @six.add_metaclass() + class Invalid(object): + pass + ''') + inferred = next(klass.infer()) + self.assertIsNone(inferred.metaclass()) + + def test_nonregr_infer_callresult(self): + astroid = builder.parse(""" + class Delegate(object): + def __get__(self, obj, cls): + return getattr(obj._subject, self.attribute) + + class CompositeBuilder(object): + __call__ = Delegate() + + builder = CompositeBuilder(result, composite) + tgts = builder() + """) + instance = astroid['tgts'] + # used to raise "'_Yes' object is not iterable", see + # https://bitbucket.org/logilab/astroid/issue/17 + self.assertEqual(list(instance.infer()), [util.YES]) + + def test_slots(self): + astroid = builder.parse(""" + from collections import deque + from textwrap import dedent + + class First(object): #@ + __slots__ = ("a", "b", 1) + class Second(object): #@ + __slots__ = "a" + class Third(object): #@ + __slots__ = deque(["a", "b", "c"]) + class Fourth(object): #@ + __slots__ = {"a": "a", "b": "b"} + class Fifth(object): #@ + __slots__ = list + class Sixth(object): #@ + __slots__ = "" + class Seventh(object): #@ + __slots__ = dedent.__name__ + class Eight(object): #@ + __slots__ = ("parens") + class Ninth(object): #@ + pass + class Ten(object): #@ + __slots__ = dict({"a": "b", "c": "d"}) + """) + expected = [ + ('First', ('a', 'b')), + ('Second', ('a', )), + ('Third', None), + ('Fourth', ('a', 'b')), + ('Fifth', None), + ('Sixth', None), + ('Seventh', ('dedent', )), + ('Eight', ('parens', )), + ('Ninth', None), + ('Ten', ('a', 'c')), + ] + for cls, expected_value in expected: + slots = astroid[cls].slots() + if expected_value is None: + self.assertIsNone(slots) + else: + self.assertEqual(list(expected_value), + [node.value for node in slots]) + + @test_utils.require_version(maxver='3.0') + def test_slots_py2(self): + module = builder.parse(""" + class UnicodeSlots(object): + __slots__ = (u"a", u"b", "c") + """) + slots = module['UnicodeSlots'].slots() + self.assertEqual(len(slots), 3) + self.assertEqual(slots[0].value, "a") + self.assertEqual(slots[1].value, "b") + self.assertEqual(slots[2].value, "c") + + @test_utils.require_version(maxver='3.0') + def test_slots_py2_not_implemented(self): + module = builder.parse(""" + class OldStyle: + __slots__ = ("a", "b") + """) + msg = "The concept of slots is undefined for old-style classes." + with self.assertRaises(NotImplementedError) as cm: + module['OldStyle'].slots() + self.assertEqual(str(cm.exception), msg) + + def test_slots_empty_list_of_slots(self): + module = builder.parse(""" + class Klass(object): + __slots__ = () + """) + cls = module['Klass'] + self.assertEqual(cls.slots(), []) + + def test_slots_taken_from_parents(self): + module = builder.parse(''' + class FirstParent(object): + __slots__ = ('a', 'b', 'c') + class SecondParent(FirstParent): + __slots__ = ('d', 'e') + class Third(SecondParent): + __slots__ = ('d', ) + ''') + cls = module['Third'] + slots = cls.slots() + self.assertEqual(sorted(set(slot.value for slot in slots)), + ['a', 'b', 'c', 'd', 'e']) + + def test_all_ancestors_need_slots(self): + module = builder.parse(''' + class A(object): + __slots__ = ('a', ) + class B(A): pass + class C(B): + __slots__ = ('a', ) + ''') + cls = module['C'] + self.assertIsNone(cls.slots()) + cls = module['B'] + self.assertIsNone(cls.slots()) + + def assertEqualMro(self, klass, expected_mro): + self.assertEqual( + [member.name for member in klass.mro()], + expected_mro) + + @test_utils.require_version(maxver='3.0') + def test_no_mro_for_old_style(self): + node = test_utils.extract_node(""" + class Old: pass""") + with self.assertRaises(NotImplementedError) as cm: + node.mro() + self.assertEqual(str(cm.exception), "Could not obtain mro for " + "old-style classes.") + + @test_utils.require_version(maxver='3.0') + def test_combined_newstyle_oldstyle_in_mro(self): + node = test_utils.extract_node(''' + class Old: + pass + class New(object): + pass + class New1(object): + pass + class New2(New, New1): + pass + class NewOld(New2, Old): #@ + pass + ''') + self.assertEqualMro(node, ['NewOld', 'New2', 'New', 'New1', 'object', 'Old']) + self.assertTrue(node.newstyle) + + def test_with_metaclass_mro(self): + astroid = builder.parse(""" + import six + + class C(object): + pass + class B(C): + pass + class A(six.with_metaclass(type, B)): + pass + """) + self.assertEqualMro(astroid['A'], ['A', 'B', 'C', 'object']) + + def test_mro(self): + astroid = builder.parse(""" + class C(object): pass + class D(dict, C): pass + + class A1(object): pass + class B1(A1): pass + class C1(A1): pass + class D1(B1, C1): pass + class E1(C1, B1): pass + class F1(D1, E1): pass + class G1(E1, D1): pass + + class Boat(object): pass + class DayBoat(Boat): pass + class WheelBoat(Boat): pass + class EngineLess(DayBoat): pass + class SmallMultihull(DayBoat): pass + class PedalWheelBoat(EngineLess, WheelBoat): pass + class SmallCatamaran(SmallMultihull): pass + class Pedalo(PedalWheelBoat, SmallCatamaran): pass + + class OuterA(object): + class Inner(object): + pass + class OuterB(OuterA): + class Inner(OuterA.Inner): + pass + class OuterC(OuterA): + class Inner(OuterA.Inner): + pass + class OuterD(OuterC): + class Inner(OuterC.Inner, OuterB.Inner): + pass + class Duplicates(str, str): pass + + """) + self.assertEqualMro(astroid['D'], ['D', 'dict', 'C', 'object']) + self.assertEqualMro(astroid['D1'], ['D1', 'B1', 'C1', 'A1', 'object']) + self.assertEqualMro(astroid['E1'], ['E1', 'C1', 'B1', 'A1', 'object']) + with self.assertRaises(InconsistentMroError) as cm: + astroid['F1'].mro() + self.assertEqual(str(cm.exception), + "Cannot create a consistent method resolution order " + "for bases (B1, C1, A1, object), " + "(C1, B1, A1, object)") + + with self.assertRaises(InconsistentMroError) as cm: + astroid['G1'].mro() + self.assertEqual(str(cm.exception), + "Cannot create a consistent method resolution order " + "for bases (C1, B1, A1, object), " + "(B1, C1, A1, object)") + + self.assertEqualMro( + astroid['PedalWheelBoat'], + ["PedalWheelBoat", "EngineLess", + "DayBoat", "WheelBoat", "Boat", "object"]) + + self.assertEqualMro( + astroid["SmallCatamaran"], + ["SmallCatamaran", "SmallMultihull", "DayBoat", "Boat", "object"]) + + self.assertEqualMro( + astroid["Pedalo"], + ["Pedalo", "PedalWheelBoat", "EngineLess", "SmallCatamaran", + "SmallMultihull", "DayBoat", "WheelBoat", "Boat", "object"]) + + self.assertEqualMro( + astroid['OuterD']['Inner'], + ['Inner', 'Inner', 'Inner', 'Inner', 'object']) + + with self.assertRaises(DuplicateBasesError) as cm: + astroid['Duplicates'].mro() + self.assertEqual(str(cm.exception), "Duplicates found in the mro.") + self.assertTrue(issubclass(cm.exception.__class__, MroError)) + self.assertTrue(issubclass(cm.exception.__class__, ResolveError)) + + def test_generator_from_infer_call_result_parent(self): + func = test_utils.extract_node(""" + import contextlib + + @contextlib.contextmanager + def test(): #@ + yield + """) + result = next(func.infer_call_result(func)) + self.assertIsInstance(result, Generator) + self.assertEqual(result.parent, func) + + def test_type_three_arguments(self): + classes = test_utils.extract_node(""" + type('A', (object, ), {"a": 1, "b": 2, missing: 3}) #@ + """) + first = next(classes.infer()) + self.assertIsInstance(first, nodes.ClassDef) + self.assertEqual(first.name, "A") + self.assertEqual(first.basenames, ["object"]) + self.assertIsInstance(first["a"], nodes.Const) + self.assertEqual(first["a"].value, 1) + self.assertIsInstance(first["b"], nodes.Const) + self.assertEqual(first["b"].value, 2) + with self.assertRaises(NotFoundError): + first.getattr("missing") + + def test_implicit_metaclass(self): + cls = test_utils.extract_node(""" + class A(object): + pass + """) + type_cls = scoped_nodes.builtin_lookup("type")[1][0] + self.assertEqual(cls.implicit_metaclass(), type_cls) + + @test_utils.require_version(maxver='3.0') + def test_implicit_metaclass_is_none(self): + cls = test_utils.extract_node(""" + class A: pass + """) + self.assertIsNone(cls.implicit_metaclass()) + + def test_local_attr_invalid_mro(self): + cls = test_utils.extract_node(""" + # A has an invalid MRO, local_attr should fallback + # to using .ancestors. + class A(object, object): + test = 42 + class B(A): #@ + pass + """) + local = cls.local_attr('test')[0] + inferred = next(local.infer()) + self.assertIsInstance(inferred, nodes.Const) + self.assertEqual(inferred.value, 42) + + def test_has_dynamic_getattr(self): + module = builder.parse(""" + class Getattr(object): + def __getattr__(self, attrname): + pass + + class Getattribute(object): + def __getattribute__(self, attrname): + pass + + class ParentGetattr(Getattr): + pass + """) + self.assertTrue(module['Getattr'].has_dynamic_getattr()) + self.assertTrue(module['Getattribute'].has_dynamic_getattr()) + self.assertTrue(module['ParentGetattr'].has_dynamic_getattr()) + + # Test that objects analyzed through the live introspection + # aren't considered to have dynamic getattr implemented. + import datetime + astroid_builder = builder.AstroidBuilder() + module = astroid_builder.module_build(datetime) + self.assertFalse(module['timedelta'].has_dynamic_getattr()) + + def test_duplicate_bases_namedtuple(self): + module = builder.parse(""" + import collections + _A = collections.namedtuple('A', 'a') + + class A(_A): pass + + class B(A): pass + """) + self.assertRaises(DuplicateBasesError, module['B'].mro) + + def test_instance_bound_method_lambdas(self): + ast_nodes = test_utils.extract_node(''' + class Test(object): #@ + lam = lambda self: self + not_method = lambda xargs: xargs + Test() #@ + ''') + cls = next(ast_nodes[0].infer()) + self.assertIsInstance(next(cls.igetattr('lam')), scoped_nodes.Lambda) + self.assertIsInstance(next(cls.igetattr('not_method')), scoped_nodes.Lambda) + + instance = next(ast_nodes[1].infer()) + lam = next(instance.igetattr('lam')) + self.assertIsInstance(lam, BoundMethod) + not_method = next(instance.igetattr('not_method')) + self.assertIsInstance(not_method, scoped_nodes.Lambda) + + def test_class_extra_decorators_frame_is_not_class(self): + ast_node = test_utils.extract_node(''' + def ala(): + def bala(): #@ + func = 42 + ''') + self.assertEqual(ast_node.extra_decorators, []) + + def test_class_extra_decorators_only_callfunc_are_considered(self): + ast_node = test_utils.extract_node(''' + class Ala(object): + def func(self): #@ + pass + func = 42 + ''') + self.assertEqual(ast_node.extra_decorators, []) + + def test_class_extra_decorators_only_assignment_names_are_considered(self): + ast_node = test_utils.extract_node(''' + class Ala(object): + def func(self): #@ + pass + def __init__(self): + self.func = staticmethod(func) + + ''') + self.assertEqual(ast_node.extra_decorators, []) + + def test_class_extra_decorators_only_same_name_considered(self): + ast_node = test_utils.extract_node(''' + class Ala(object): + def func(self): #@ + pass + bala = staticmethod(func) + ''') + self.assertEqual(ast_node.extra_decorators, []) + self.assertEqual(ast_node.type, 'method') + + def test_class_extra_decorators(self): + static_method, clsmethod = test_utils.extract_node(''' + class Ala(object): + def static(self): #@ + pass + def class_method(self): #@ + pass + class_method = classmethod(class_method) + static = staticmethod(static) + ''') + self.assertEqual(len(clsmethod.extra_decorators), 1) + self.assertEqual(clsmethod.type, 'classmethod') + self.assertEqual(len(static_method.extra_decorators), 1) + self.assertEqual(static_method.type, 'staticmethod') + + def test_extra_decorators_only_class_level_assignments(self): + node = test_utils.extract_node(''' + def _bind(arg): + return arg.bind + + class A(object): + @property + def bind(self): + return 42 + def irelevant(self): + # This is important, because it used to trigger + # a maximum recursion error. + bind = _bind(self) + return bind + A() #@ + ''') + inferred = next(node.infer()) + bind = next(inferred.igetattr('bind')) + self.assertIsInstance(bind, nodes.Const) + self.assertEqual(bind.value, 42) + parent = bind.scope() + self.assertEqual(len(parent.extra_decorators), 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_transforms.py b/pymode/libs/astroid/tests/unittest_transforms.py new file mode 100644 index 00000000..1553bfc4 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_transforms.py @@ -0,0 +1,245 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +from __future__ import print_function + +import contextlib +import time +import unittest + +from astroid import builder +from astroid import nodes +from astroid import parse +from astroid import transforms + + +@contextlib.contextmanager +def add_transform(manager, node, transform, predicate=None): + manager.register_transform(node, transform, predicate) + try: + yield + finally: + manager.unregister_transform(node, transform, predicate) + + +class TestTransforms(unittest.TestCase): + + def setUp(self): + self.transformer = transforms.TransformVisitor() + + def parse_transform(self, code): + module = parse(code, apply_transforms=False) + return self.transformer.visit(module) + + def test_function_inlining_transform(self): + def transform_call(node): + # Let's do some function inlining + inferred = next(node.infer()) + return inferred + + self.transformer.register_transform(nodes.Call, + transform_call) + + module = self.parse_transform(''' + def test(): return 42 + test() #@ + ''') + + self.assertIsInstance(module.body[1], nodes.Expr) + self.assertIsInstance(module.body[1].value, nodes.Const) + self.assertEqual(module.body[1].value.value, 42) + + def test_recursive_transforms_into_astroid_fields(self): + # Test that the transformer walks properly the tree + # by going recursively into the _astroid_fields per each node. + def transform_compare(node): + # Let's check the values of the ops + _, right = node.ops[0] + # Assume they are Consts and they were transformed before + # us. + return nodes.const_factory(node.left.value < right.value) + + def transform_name(node): + # Should be Consts + return next(node.infer()) + + self.transformer.register_transform(nodes.Compare, transform_compare) + self.transformer.register_transform(nodes.Name, transform_name) + + module = self.parse_transform(''' + a = 42 + b = 24 + a < b + ''') + + self.assertIsInstance(module.body[2], nodes.Expr) + self.assertIsInstance(module.body[2].value, nodes.Const) + self.assertFalse(module.body[2].value.value) + + def test_transform_patches_locals(self): + def transform_function(node): + assign = nodes.Assign() + name = nodes.AssignName() + name.name = 'value' + assign.targets = [name] + assign.value = nodes.const_factory(42) + node.body.append(assign) + + self.transformer.register_transform(nodes.FunctionDef, + transform_function) + + module = self.parse_transform(''' + def test(): + pass + ''') + + func = module.body[0] + self.assertEqual(len(func.body), 2) + self.assertIsInstance(func.body[1], nodes.Assign) + self.assertEqual(func.body[1].as_string(), 'value = 42') + + def test_predicates(self): + def transform_call(node): + inferred = next(node.infer()) + return inferred + + def should_inline(node): + return node.func.name.startswith('inlineme') + + self.transformer.register_transform(nodes.Call, + transform_call, + should_inline) + + module = self.parse_transform(''' + def inlineme_1(): + return 24 + def dont_inline_me(): + return 42 + def inlineme_2(): + return 2 + inlineme_1() + dont_inline_me() + inlineme_2() + ''') + values = module.body[-3:] + self.assertIsInstance(values[0], nodes.Expr) + self.assertIsInstance(values[0].value, nodes.Const) + self.assertEqual(values[0].value.value, 24) + self.assertIsInstance(values[1], nodes.Expr) + self.assertIsInstance(values[1].value, nodes.Call) + self.assertIsInstance(values[2], nodes.Expr) + self.assertIsInstance(values[2].value, nodes.Const) + self.assertEqual(values[2].value.value, 2) + + def test_transforms_are_separated(self): + # Test that the transforming is done at a separate + # step, which means that we are not doing inference + # on a partially constructred tree anymore, which was the + # source of crashes in the past when certain inference rules + # were used in a transform. + def transform_function(node): + if node.decorators: + for decorator in node.decorators.nodes: + inferred = next(decorator.infer()) + if inferred.qname() == 'abc.abstractmethod': + return next(node.infer_call_result(node)) + + manager = builder.MANAGER + with add_transform(manager, nodes.FunctionDef, transform_function): + module = builder.parse(''' + import abc + from abc import abstractmethod + + class A(object): + @abc.abstractmethod + def ala(self): + return 24 + + @abstractmethod + def bala(self): + return 42 + ''') + + cls = module['A'] + ala = cls.body[0] + bala = cls.body[1] + self.assertIsInstance(ala, nodes.Const) + self.assertEqual(ala.value, 24) + self.assertIsInstance(bala, nodes.Const) + self.assertEqual(bala.value, 42) + + def test_transforms_are_called_for_builtin_modules(self): + # Test that transforms are called for builtin modules. + def transform_function(node): + name = nodes.AssignName() + name.name = 'value' + node.args.args = [name] + return node + + manager = builder.MANAGER + predicate = lambda node: node.root().name == 'time' + with add_transform(manager, nodes.FunctionDef, + transform_function, predicate): + builder_instance = builder.AstroidBuilder() + module = builder_instance.module_build(time) + + asctime = module['asctime'] + self.assertEqual(len(asctime.args.args), 1) + self.assertIsInstance(asctime.args.args[0], nodes.AssignName) + self.assertEqual(asctime.args.args[0].name, 'value') + + def test_builder_apply_transforms(self): + def transform_function(node): + return nodes.const_factory(42) + + manager = builder.MANAGER + with add_transform(manager, nodes.FunctionDef, transform_function): + astroid_builder = builder.AstroidBuilder(apply_transforms=False) + module = astroid_builder.string_build('''def test(): pass''') + + # The transform wasn't applied. + self.assertIsInstance(module.body[0], nodes.FunctionDef) + + def test_transform_crashes_on_is_subtype_of(self): + # Test that we don't crash when having is_subtype_of + # in a transform, as per issue #188. This happened + # before, when the transforms weren't in their own step. + def transform_class(cls): + if cls.is_subtype_of('django.db.models.base.Model'): + return cls + return cls + + self.transformer.register_transform(nodes.ClassDef, + transform_class) + + self.parse_transform(''' + # Change environ to automatically call putenv() if it exists + import os + putenv = os.putenv + try: + # This will fail if there's no putenv + putenv + except NameError: + pass + else: + import UserDict + ''') + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/tests/unittest_utils.py b/pymode/libs/astroid/tests/unittest_utils.py new file mode 100644 index 00000000..ef832252 --- /dev/null +++ b/pymode/libs/astroid/tests/unittest_utils.py @@ -0,0 +1,124 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +import unittest + +from astroid import builder +from astroid import InferenceError +from astroid import nodes +from astroid import node_classes +from astroid import test_utils +from astroid import util as astroid_util + + +class InferenceUtil(unittest.TestCase): + + def test_not_exclusive(self): + module = builder.parse(""" + x = 10 + for x in range(5): + print (x) + + if x > 0: + print ('#' * x) + """, __name__, __file__) + xass1 = module.locals['x'][0] + assert xass1.lineno == 2 + xnames = [n for n in module.nodes_of_class(nodes.Name) if n.name == 'x'] + assert len(xnames) == 3 + assert xnames[1].lineno == 6 + self.assertEqual(node_classes.are_exclusive(xass1, xnames[1]), False) + self.assertEqual(node_classes.are_exclusive(xass1, xnames[2]), False) + + def test_if(self): + module = builder.parse(''' + if 1: + a = 1 + a = 2 + elif 2: + a = 12 + a = 13 + else: + a = 3 + a = 4 + ''') + a1 = module.locals['a'][0] + a2 = module.locals['a'][1] + a3 = module.locals['a'][2] + a4 = module.locals['a'][3] + a5 = module.locals['a'][4] + a6 = module.locals['a'][5] + self.assertEqual(node_classes.are_exclusive(a1, a2), False) + self.assertEqual(node_classes.are_exclusive(a1, a3), True) + self.assertEqual(node_classes.are_exclusive(a1, a5), True) + self.assertEqual(node_classes.are_exclusive(a3, a5), True) + self.assertEqual(node_classes.are_exclusive(a3, a4), False) + self.assertEqual(node_classes.are_exclusive(a5, a6), False) + + def test_try_except(self): + module = builder.parse(''' + try: + def exclusive_func2(): + "docstring" + except TypeError: + def exclusive_func2(): + "docstring" + except: + def exclusive_func2(): + "docstring" + else: + def exclusive_func2(): + "this one redefine the one defined line 42" + ''') + f1 = module.locals['exclusive_func2'][0] + f2 = module.locals['exclusive_func2'][1] + f3 = module.locals['exclusive_func2'][2] + f4 = module.locals['exclusive_func2'][3] + self.assertEqual(node_classes.are_exclusive(f1, f2), True) + self.assertEqual(node_classes.are_exclusive(f1, f3), True) + self.assertEqual(node_classes.are_exclusive(f1, f4), False) + self.assertEqual(node_classes.are_exclusive(f2, f4), True) + self.assertEqual(node_classes.are_exclusive(f3, f4), True) + self.assertEqual(node_classes.are_exclusive(f3, f2), True) + + self.assertEqual(node_classes.are_exclusive(f2, f1), True) + self.assertEqual(node_classes.are_exclusive(f4, f1), False) + self.assertEqual(node_classes.are_exclusive(f4, f2), True) + + def test_unpack_infer_uninferable_nodes(self): + node = test_utils.extract_node(''' + x = [A] * 1 + f = [x, [A] * 2] + f + ''') + inferred = next(node.infer()) + unpacked = list(node_classes.unpack_infer(inferred)) + self.assertEqual(len(unpacked), 3) + self.assertTrue(all(elt is astroid_util.YES + for elt in unpacked)) + + def test_unpack_infer_empty_tuple(self): + node = test_utils.extract_node(''' + () + ''') + inferred = next(node.infer()) + with self.assertRaises(InferenceError): + list(node_classes.unpack_infer(inferred)) + + +if __name__ == '__main__': + unittest.main() diff --git a/pymode/libs/astroid/transforms.py b/pymode/libs/astroid/transforms.py new file mode 100644 index 00000000..5d8fc91b --- /dev/null +++ b/pymode/libs/astroid/transforms.py @@ -0,0 +1,96 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . + +import collections +import warnings + + +class TransformVisitor(object): + """A visitor for handling transforms. + + The standard approach of using it is to call + :meth:`~visit` with an *astroid* module and the class + will take care of the rest, walking the tree and running the + transforms for each encountered node. + """ + + def __init__(self): + self.transforms = collections.defaultdict(list) + + def _transform(self, node): + """Call matching transforms for the given node if any and return the + transformed node. + """ + cls = node.__class__ + if cls not in self.transforms: + # no transform registered for this class of node + return node + + transforms = self.transforms[cls] + orig_node = node # copy the reference + for transform_func, predicate in transforms: + if predicate is None or predicate(node): + ret = transform_func(node) + # if the transformation function returns something, it's + # expected to be a replacement for the node + if ret is not None: + if node is not orig_node: + # node has already be modified by some previous + # transformation, warn about it + warnings.warn('node %s substituted multiple times' % node) + node = ret + return node + + def _visit(self, node): + if hasattr(node, '_astroid_fields'): + for field in node._astroid_fields: + value = getattr(node, field) + visited = self._visit_generic(value) + setattr(node, field, visited) + return self._transform(node) + + def _visit_generic(self, node): + if isinstance(node, list): + return [self._visit_generic(child) for child in node] + elif isinstance(node, tuple): + return tuple(self._visit_generic(child) for child in node) + else: + return self._visit(node) + + def register_transform(self, node_class, transform, predicate=None): + """Register `transform(node)` function to be applied on the given + astroid's `node_class` if `predicate` is None or returns true + when called with the node as argument. + + The transform function may return a value which is then used to + substitute the original node in the tree. + """ + self.transforms[node_class].append((transform, predicate)) + + def unregister_transform(self, node_class, transform, predicate=None): + """Unregister the given transform.""" + self.transforms[node_class].remove((transform, predicate)) + + def visit(self, module): + """Walk the given astroid *tree* and transform each encountered node + + Only the nodes which have transforms registered will actually + be replaced or changed. + """ + module.body = [self._visit(child) for child in module.body] + return self._transform(module) diff --git a/pymode/libs/astroid/util.py b/pymode/libs/astroid/util.py new file mode 100644 index 00000000..44e2039d --- /dev/null +++ b/pymode/libs/astroid/util.py @@ -0,0 +1,89 @@ +# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 2.1 of the License, or (at your +# option) any later version. +# +# astroid is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with astroid. If not, see . +# +# The code in this file was originally part of logilab-common, licensed under +# the same license. +import warnings + +from astroid import exceptions + + +def generate_warning(message, warning): + return lambda *args: warnings.warn(message % args, warning, stacklevel=3) + +rename_warning = generate_warning( + "%r is deprecated and will be removed in astroid %.1f, use %r instead", + PendingDeprecationWarning) + +attribute_to_method_warning = generate_warning( + "%s is deprecated and will be removed in astroid %.1f, use the " + "method '%s()' instead.", PendingDeprecationWarning) + +attribute_to_function_warning = generate_warning( + "%s is deprecated and will be removed in astroid %.1f, use the " + "function '%s()' instead.", PendingDeprecationWarning) + +method_to_function_warning = generate_warning( + "%s() is deprecated and will be removed in astroid %.1f, use the " + "function '%s()' instead.", PendingDeprecationWarning) + + +class _Yes(object): + """Special inference object, which is returned when inference fails.""" + def __repr__(self): + return 'YES' + + __str__ = __repr__ + + def __getattribute__(self, name): + if name == 'next': + raise AttributeError('next method should not be called') + if name.startswith('__') and name.endswith('__'): + return super(_Yes, self).__getattribute__(name) + if name == 'accept': + return super(_Yes, self).__getattribute__(name) + return self + + def __call__(self, *args, **kwargs): + return self + + def accept(self, visitor): + func = getattr(visitor, "visit_yes") + return func(self) + + +YES = _Yes() + +def safe_infer(node, context=None): + """Return the inferred value for the given node. + + Return None if inference failed or if there is some ambiguity (more than + one node has been inferred). + """ + try: + inferit = node.infer(context=context) + value = next(inferit) + except exceptions.InferenceError: + return + try: + next(inferit) + return # None if there is ambiguity on the inferred node + except exceptions.InferenceError: + return # there is some kind of ambiguity + except StopIteration: + return value diff --git a/pymode/libs/astroid/utils.py b/pymode/libs/astroid/utils.py deleted file mode 100644 index ae72a92c..00000000 --- a/pymode/libs/astroid/utils.py +++ /dev/null @@ -1,239 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid is free software: you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by the -# Free Software Foundation, either version 2.1 of the License, or (at your -# option) any later version. -# -# astroid is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -# for more details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with astroid. If not, see . -"""this module contains some utilities to navigate in the tree or to -extract information from it -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from astroid.exceptions import AstroidBuildingException -from astroid.builder import parse - - -class ASTWalker(object): - """a walker visiting a tree in preorder, calling on the handler: - - * visit_ on entering a node, where class name is the class of - the node in lower case - - * leave_ on leaving a node, where class name is the class of - the node in lower case - """ - - def __init__(self, handler): - self.handler = handler - self._cache = {} - - def walk(self, node, _done=None): - """walk on the tree from , getting callbacks from handler""" - if _done is None: - _done = set() - if node in _done: - raise AssertionError((id(node), node, node.parent)) - _done.add(node) - self.visit(node) - for child_node in node.get_children(): - self.handler.set_context(node, child_node) - assert child_node is not node - self.walk(child_node, _done) - self.leave(node) - assert node.parent is not node - - def get_callbacks(self, node): - """get callbacks from handler for the visited node""" - klass = node.__class__ - methods = self._cache.get(klass) - if methods is None: - handler = self.handler - kid = klass.__name__.lower() - e_method = getattr(handler, 'visit_%s' % kid, - getattr(handler, 'visit_default', None)) - l_method = getattr(handler, 'leave_%s' % kid, - getattr(handler, 'leave_default', None)) - self._cache[klass] = (e_method, l_method) - else: - e_method, l_method = methods - return e_method, l_method - - def visit(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[0] - if method is not None: - method(node) - - def leave(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[1] - if method is not None: - method(node) - - -class LocalsVisitor(ASTWalker): - """visit a project by traversing the locals dictionary""" - def __init__(self): - ASTWalker.__init__(self, self) - self._visited = {} - - def visit(self, node): - """launch the visit starting from the given node""" - if node in self._visited: - return - self._visited[node] = 1 # FIXME: use set ? - methods = self.get_callbacks(node) - if methods[0] is not None: - methods[0](node) - if 'locals' in node.__dict__: # skip Instance and other proxy - for local_node in node.values(): - self.visit(local_node) - if methods[1] is not None: - return methods[1](node) - - -def _check_children(node): - """a helper function to check children - parent relations""" - for child in node.get_children(): - ok = False - if child is None: - print("Hm, child of %s is None" % node) - continue - if not hasattr(child, 'parent'): - print(" ERROR: %s has child %s %x with no parent" % ( - node, child, id(child))) - elif not child.parent: - print(" ERROR: %s has child %s %x with parent %r" % ( - node, child, id(child), child.parent)) - elif child.parent is not node: - print(" ERROR: %s %x has child %s %x with wrong parent %s" % ( - node, id(node), child, id(child), child.parent)) - else: - ok = True - if not ok: - print("lines;", node.lineno, child.lineno) - print("of module", node.root(), node.root().name) - raise AstroidBuildingException - _check_children(child) - - -class TreeTester(object): - '''A helper class to see _ast tree and compare with astroid tree - - indent: string for tree indent representation - lineno: bool to tell if we should print the line numbers - - >>> tester = TreeTester('print') - >>> print tester.native_tree_repr() - - - . body = [ - . - . . nl = True - . ] - >>> print tester.astroid_tree_repr() - Module() - body = [ - Print() - dest = - values = [ - ] - ] - ''' - - indent = '. ' - lineno = False - - def __init__(self, sourcecode): - self._string = '' - self.sourcecode = sourcecode - self._ast_node = None - self.build_ast() - - def build_ast(self): - """build the _ast tree from the source code""" - self._ast_node = parse(self.sourcecode) - - def native_tree_repr(self, node=None, indent=''): - """get a nice representation of the _ast tree""" - self._string = '' - if node is None: - node = self._ast_node - self._native_repr_tree(node, indent) - return self._string - - - def _native_repr_tree(self, node, indent, _done=None): - """recursive method for the native tree representation""" - from _ast import Load as _Load, Store as _Store, Del as _Del - from _ast import AST as Node - if _done is None: - _done = set() - if node in _done: - self._string += '\nloop in tree: %r (%s)' % ( - node, getattr(node, 'lineno', None)) - return - _done.add(node) - self._string += '\n' + indent + '<%s>' % node.__class__.__name__ - indent += self.indent - if not hasattr(node, '__dict__'): - self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node) - return - node_dict = node.__dict__ - if hasattr(node, '_attributes'): - for a in node._attributes: - attr = node_dict[a] - if attr is None: - continue - if a in ("lineno", "col_offset") and not self.lineno: - continue - self._string += '\n' + indent + a + " = " + repr(attr) - for field in node._fields or (): - attr = node_dict[field] - if attr is None: - continue - if isinstance(attr, list): - if not attr: - continue - self._string += '\n' + indent + field + ' = [' - for elt in attr: - self._native_repr_tree(elt, indent, _done) - self._string += '\n' + indent + ']' - continue - if isinstance(attr, (_Load, _Store, _Del)): - continue - if isinstance(attr, Node): - self._string += '\n' + indent + field + " = " - self._native_repr_tree(attr, indent, _done) - else: - self._string += '\n' + indent + field + " = " + repr(attr) - - - def build_astroid_tree(self): - """build astroid tree from the _ast tree - """ - from astroid.builder import AstroidBuilder - tree = AstroidBuilder().string_build(self.sourcecode) - return tree - - def astroid_tree_repr(self, ids=False): - """build the astroid tree and return a nice tree representation""" - mod = self.build_astroid_tree() - return mod.repr_tree(ids) - - -__all__ = ('LocalsVisitor', 'ASTWalker',) - diff --git a/pymode/libs/backports.functools_lru_cache-1.3-py3.5-nspkg.pth b/pymode/libs/backports.functools_lru_cache-1.3-py3.5-nspkg.pth new file mode 100644 index 00000000..0b1f79dd --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3-py3.5-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('backports',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and sys.modules.setdefault('backports', types.ModuleType('backports'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/DESCRIPTION.rst b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/DESCRIPTION.rst new file mode 100644 index 00000000..ab650b3c --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/DESCRIPTION.rst @@ -0,0 +1,28 @@ +.. image:: https://img.shields.io/pypi/v/backports.functools_lru_cache.svg + :target: https://pypi.org/project/backports.functools_lru_cache + +.. image:: https://img.shields.io/pypi/pyversions/backports.functools_lru_cache.svg + +.. image:: https://img.shields.io/pypi/dm/backports.functools_lru_cache.svg + +.. image:: https://img.shields.io/travis/jaraco/backports.functools_lru_cache/master.svg + :target: http://travis-ci.org/jaraco/backports.functools_lru_cache + +License is indicated in the project metadata (typically one or more +of the Trove classifiers). For more details, see `this explanation +`_. + +Backport of functools.lru_cache from Python 3.3 as published at `ActiveState +`_. + +Usage +----- + +Consider using this technique for importing the 'lru_cache' function:: + + try: + from functools import lru_cache + except ImportError: + from backports.functools_lru_cache import lru_cache + + diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/INSTALLER b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/METADATA b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/METADATA new file mode 100644 index 00000000..758d5304 --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/METADATA @@ -0,0 +1,44 @@ +Metadata-Version: 2.0 +Name: backports.functools-lru-cache +Version: 1.3 +Summary: backports.functools_lru_cache +Home-page: https://github.com/jaraco/backports.functools_lru_cache +Author: Jason R. Coombs +Author-email: jaraco@jaraco.com +License: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 + +.. image:: https://img.shields.io/pypi/v/backports.functools_lru_cache.svg + :target: https://pypi.org/project/backports.functools_lru_cache + +.. image:: https://img.shields.io/pypi/pyversions/backports.functools_lru_cache.svg + +.. image:: https://img.shields.io/pypi/dm/backports.functools_lru_cache.svg + +.. image:: https://img.shields.io/travis/jaraco/backports.functools_lru_cache/master.svg + :target: http://travis-ci.org/jaraco/backports.functools_lru_cache + +License is indicated in the project metadata (typically one or more +of the Trove classifiers). For more details, see `this explanation +`_. + +Backport of functools.lru_cache from Python 3.3 as published at `ActiveState +`_. + +Usage +----- + +Consider using this technique for importing the 'lru_cache' function:: + + try: + from functools import lru_cache + except ImportError: + from backports.functools_lru_cache import lru_cache + + diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/RECORD b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/RECORD new file mode 100644 index 00000000..124767c8 --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/RECORD @@ -0,0 +1,11 @@ +backports.functools_lru_cache-1.3-py3.5-nspkg.pth,sha256=JHydVi7598aklfRVCkCh9f5qqKRWgzZ9rAwKAApSu4w,314 +backports/functools_lru_cache.py,sha256=Rz9eeee4g7nSELRnqQCYwf7_i19lGXCZlLzfqa_Zjsk,7317 +backports.functools_lru_cache-1.3.dist-info/DESCRIPTION.rst,sha256=iYa72BvLZDki4qjxP2qLBxLIDop4i1c1SQS-mwkabsU,997 +backports.functools_lru_cache-1.3.dist-info/METADATA,sha256=M3kggZIt4_i_Q0rkatLxYI-9oQTI_vLqVJSGHXgv674,1565 +backports.functools_lru_cache-1.3.dist-info/RECORD,, +backports.functools_lru_cache-1.3.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +backports.functools_lru_cache-1.3.dist-info/metadata.json,sha256=kJgZvBIl8AQCESL4_9mDLCbaEBEW7-Xe0jHuHte_iVU,695 +backports.functools_lru_cache-1.3.dist-info/namespace_packages.txt,sha256=cGjaLMOoBR1FK0ApojtzWVmViTtJ7JGIK_HwXiEsvtU,10 +backports.functools_lru_cache-1.3.dist-info/top_level.txt,sha256=cGjaLMOoBR1FK0ApojtzWVmViTtJ7JGIK_HwXiEsvtU,10 +backports.functools_lru_cache-1.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +backports/functools_lru_cache.pyc,, diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/WHEEL b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/WHEEL new file mode 100644 index 00000000..8b6dd1b5 --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/metadata.json b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/metadata.json new file mode 100644 index 00000000..e7912716 --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "jaraco@jaraco.com", "name": "Jason R. Coombs", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/jaraco/backports.functools_lru_cache"}}}, "generator": "bdist_wheel (0.29.0)", "metadata_version": "2.0", "name": "backports.functools-lru-cache", "summary": "backports.functools_lru_cache", "version": "1.3"} \ No newline at end of file diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/namespace_packages.txt b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/namespace_packages.txt new file mode 100644 index 00000000..99d2be5b --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/namespace_packages.txt @@ -0,0 +1 @@ +backports diff --git a/pymode/libs/backports.functools_lru_cache-1.3.dist-info/top_level.txt b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/top_level.txt new file mode 100644 index 00000000..99d2be5b --- /dev/null +++ b/pymode/libs/backports.functools_lru_cache-1.3.dist-info/top_level.txt @@ -0,0 +1 @@ +backports diff --git a/pymode/libs/backports/configparser/__init__.py b/pymode/libs/backports/configparser/__init__.py new file mode 100644 index 00000000..06d7a085 --- /dev/null +++ b/pymode/libs/backports/configparser/__init__.py @@ -0,0 +1,1390 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Configuration file parser. + +A configuration file consists of sections, lead by a "[section]" header, +and followed by "name: value" entries, with continuations and such in +the style of RFC 822. + +Intrinsic defaults can be specified by passing them into the +ConfigParser constructor as a dictionary. + +class: + +ConfigParser -- responsible for parsing a list of + configuration files, and managing the parsed database. + + methods: + + __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, + delimiters=('=', ':'), comment_prefixes=('#', ';'), + inline_comment_prefixes=None, strict=True, + empty_lines_in_values=True, default_section='DEFAULT', + interpolation=, converters=): + Create the parser. When `defaults' is given, it is initialized into the + dictionary or intrinsic defaults. The keys must be strings, the values + must be appropriate for %()s string interpolation. + + When `dict_type' is given, it will be used to create the dictionary + objects for the list of sections, for the options within a section, and + for the default values. + + When `delimiters' is given, it will be used as the set of substrings + that divide keys from values. + + When `comment_prefixes' is given, it will be used as the set of + substrings that prefix comments in empty lines. Comments can be + indented. + + When `inline_comment_prefixes' is given, it will be used as the set of + substrings that prefix comments in non-empty lines. + + When `strict` is True, the parser won't allow for any section or option + duplicates while reading from a single source (file, string or + dictionary). Default is True. + + When `empty_lines_in_values' is False (default: True), each empty line + marks the end of an option. Otherwise, internal empty lines of + a multiline option are kept as part of the value. + + When `allow_no_value' is True (default: False), options without + values are accepted; the value presented for these is None. + + sections() + Return all the configuration section names, sans DEFAULT. + + has_section(section) + Return whether the given section exists. + + has_option(section, option) + Return whether the given option exists in the given section. + + options(section) + Return list of configuration options for the named section. + + read(filenames, encoding=None) + Read and parse the list of named configuration files, given by + name. A single filename is also allowed. Non-existing files + are ignored. Return list of successfully read files. + + read_file(f, filename=None) + Read and parse one configuration file, given as a file object. + The filename defaults to f.name; it is only used in error + messages (if f has no `name' attribute, the string `' is used). + + read_string(string) + Read configuration from a given string. + + read_dict(dictionary) + Read configuration from a dictionary. Keys are section names, + values are dictionaries with keys and values that should be present + in the section. If the used dictionary type preserves order, sections + and their keys will be added in order. Values are automatically + converted to strings. + + get(section, option, raw=False, vars=None, fallback=_UNSET) + Return a string value for the named option. All % interpolations are + expanded in the return values, based on the defaults passed into the + constructor and the DEFAULT section. Additional substitutions may be + provided using the `vars' argument, which must be a dictionary whose + contents override any pre-existing defaults. If `option' is a key in + `vars', the value from `vars' is used. + + getint(section, options, raw=False, vars=None, fallback=_UNSET) + Like get(), but convert value to an integer. + + getfloat(section, options, raw=False, vars=None, fallback=_UNSET) + Like get(), but convert value to a float. + + getboolean(section, options, raw=False, vars=None, fallback=_UNSET) + Like get(), but convert value to a boolean (currently case + insensitively defined as 0, false, no, off for False, and 1, true, + yes, on for True). Returns False or True. + + items(section=_UNSET, raw=False, vars=None) + If section is given, return a list of tuples with (name, value) for + each option in the section. Otherwise, return a list of tuples with + (section_name, section_proxy) for each section, including DEFAULTSECT. + + remove_section(section) + Remove the given file section and all its options. + + remove_option(section, option) + Remove the given option from the given section. + + set(section, option, value) + Set the given option. + + write(fp, space_around_delimiters=True) + Write the configuration state in .ini format. If + `space_around_delimiters' is True (the default), delimiters + between keys and values are surrounded by spaces. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from collections import MutableMapping +import functools +import io +import itertools +import re +import sys +import warnings + +from backports.configparser.helpers import OrderedDict as _default_dict +from backports.configparser.helpers import ChainMap as _ChainMap +from backports.configparser.helpers import from_none, open, str, PY2 + +__all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError", + "NoOptionError", "InterpolationError", "InterpolationDepthError", + "InterpolationMissingOptionError", "InterpolationSyntaxError", + "ParsingError", "MissingSectionHeaderError", + "ConfigParser", "SafeConfigParser", "RawConfigParser", + "Interpolation", "BasicInterpolation", "ExtendedInterpolation", + "LegacyInterpolation", "SectionProxy", "ConverterMapping", + "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] + +DEFAULTSECT = "DEFAULT" + +MAX_INTERPOLATION_DEPTH = 10 + + +# exception classes +class Error(Exception): + """Base class for ConfigParser exceptions.""" + + def __init__(self, msg=''): + self.message = msg + Exception.__init__(self, msg) + + def __repr__(self): + return self.message + + __str__ = __repr__ + + +class NoSectionError(Error): + """Raised when no section matches a requested option.""" + + def __init__(self, section): + Error.__init__(self, 'No section: %r' % (section,)) + self.section = section + self.args = (section, ) + + +class DuplicateSectionError(Error): + """Raised when a section is repeated in an input source. + + Possible repetitions that raise this exception are: multiple creation + using the API or in strict parsers when a section is found more than once + in a single input file, string or dictionary. + """ + + def __init__(self, section, source=None, lineno=None): + msg = [repr(section), " already exists"] + if source is not None: + message = ["While reading from ", repr(source)] + if lineno is not None: + message.append(" [line {0:2d}]".format(lineno)) + message.append(": section ") + message.extend(msg) + msg = message + else: + msg.insert(0, "Section ") + Error.__init__(self, "".join(msg)) + self.section = section + self.source = source + self.lineno = lineno + self.args = (section, source, lineno) + + +class DuplicateOptionError(Error): + """Raised by strict parsers when an option is repeated in an input source. + + Current implementation raises this exception only when an option is found + more than once in a single file, string or dictionary. + """ + + def __init__(self, section, option, source=None, lineno=None): + msg = [repr(option), " in section ", repr(section), + " already exists"] + if source is not None: + message = ["While reading from ", repr(source)] + if lineno is not None: + message.append(" [line {0:2d}]".format(lineno)) + message.append(": option ") + message.extend(msg) + msg = message + else: + msg.insert(0, "Option ") + Error.__init__(self, "".join(msg)) + self.section = section + self.option = option + self.source = source + self.lineno = lineno + self.args = (section, option, source, lineno) + + +class NoOptionError(Error): + """A requested option was not found.""" + + def __init__(self, option, section): + Error.__init__(self, "No option %r in section: %r" % + (option, section)) + self.option = option + self.section = section + self.args = (option, section) + + +class InterpolationError(Error): + """Base class for interpolation-related exceptions.""" + + def __init__(self, option, section, msg): + Error.__init__(self, msg) + self.option = option + self.section = section + self.args = (option, section, msg) + + +class InterpolationMissingOptionError(InterpolationError): + """A string substitution required a setting which was not available.""" + + def __init__(self, option, section, rawval, reference): + msg = ("Bad value substitution: option {0!r} in section {1!r} contains " + "an interpolation key {2!r} which is not a valid option name. " + "Raw value: {3!r}".format(option, section, reference, rawval)) + InterpolationError.__init__(self, option, section, msg) + self.reference = reference + self.args = (option, section, rawval, reference) + + +class InterpolationSyntaxError(InterpolationError): + """Raised when the source text contains invalid syntax. + + Current implementation raises this exception when the source text into + which substitutions are made does not conform to the required syntax. + """ + + +class InterpolationDepthError(InterpolationError): + """Raised when substitutions are nested too deeply.""" + + def __init__(self, option, section, rawval): + msg = ("Recursion limit exceeded in value substitution: option {0!r} " + "in section {1!r} contains an interpolation key which " + "cannot be substituted in {2} steps. Raw value: {3!r}" + "".format(option, section, MAX_INTERPOLATION_DEPTH, + rawval)) + InterpolationError.__init__(self, option, section, msg) + self.args = (option, section, rawval) + + +class ParsingError(Error): + """Raised when a configuration file does not follow legal syntax.""" + + def __init__(self, source=None, filename=None): + # Exactly one of `source'/`filename' arguments has to be given. + # `filename' kept for compatibility. + if filename and source: + raise ValueError("Cannot specify both `filename' and `source'. " + "Use `source'.") + elif not filename and not source: + raise ValueError("Required argument `source' not given.") + elif filename: + source = filename + Error.__init__(self, 'Source contains parsing errors: %r' % source) + self.source = source + self.errors = [] + self.args = (source, ) + + @property + def filename(self): + """Deprecated, use `source'.""" + warnings.warn( + "The 'filename' attribute will be removed in future versions. " + "Use 'source' instead.", + DeprecationWarning, stacklevel=2 + ) + return self.source + + @filename.setter + def filename(self, value): + """Deprecated, user `source'.""" + warnings.warn( + "The 'filename' attribute will be removed in future versions. " + "Use 'source' instead.", + DeprecationWarning, stacklevel=2 + ) + self.source = value + + def append(self, lineno, line): + self.errors.append((lineno, line)) + self.message += '\n\t[line %2d]: %s' % (lineno, line) + + +class MissingSectionHeaderError(ParsingError): + """Raised when a key-value pair is found before any section header.""" + + def __init__(self, filename, lineno, line): + Error.__init__( + self, + 'File contains no section headers.\nfile: %r, line: %d\n%r' % + (filename, lineno, line)) + self.source = filename + self.lineno = lineno + self.line = line + self.args = (filename, lineno, line) + + +# Used in parser getters to indicate the default behaviour when a specific +# option is not found it to raise an exception. Created to enable `None' as +# a valid fallback value. +_UNSET = object() + + +class Interpolation(object): + """Dummy interpolation that passes the value through with no changes.""" + + def before_get(self, parser, section, option, value, defaults): + return value + + def before_set(self, parser, section, option, value): + return value + + def before_read(self, parser, section, option, value): + return value + + def before_write(self, parser, section, option, value): + return value + + +class BasicInterpolation(Interpolation): + """Interpolation as implemented in the classic ConfigParser. + + The option values can contain format strings which refer to other values in + the same section, or values in the special default section. + + For example: + + something: %(dir)s/whatever + + would resolve the "%(dir)s" to the value of dir. All reference + expansions are done late, on demand. If a user needs to use a bare % in + a configuration file, she can escape it by writing %%. Other % usage + is considered a user error and raises `InterpolationSyntaxError'.""" + + _KEYCRE = re.compile(r"%\(([^)]+)\)s") + + def before_get(self, parser, section, option, value, defaults): + L = [] + self._interpolate_some(parser, option, L, value, section, defaults, 1) + return ''.join(L) + + def before_set(self, parser, section, option, value): + tmp_value = value.replace('%%', '') # escaped percent signs + tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax + if '%' in tmp_value: + raise ValueError("invalid interpolation syntax in %r at " + "position %d" % (value, tmp_value.find('%'))) + return value + + def _interpolate_some(self, parser, option, accum, rest, section, map, + depth): + rawval = parser.get(section, option, raw=True, fallback=rest) + if depth > MAX_INTERPOLATION_DEPTH: + raise InterpolationDepthError(option, section, rawval) + while rest: + p = rest.find("%") + if p < 0: + accum.append(rest) + return + if p > 0: + accum.append(rest[:p]) + rest = rest[p:] + # p is no longer used + c = rest[1:2] + if c == "%": + accum.append("%") + rest = rest[2:] + elif c == "(": + m = self._KEYCRE.match(rest) + if m is None: + raise InterpolationSyntaxError(option, section, + "bad interpolation variable reference %r" % rest) + var = parser.optionxform(m.group(1)) + rest = rest[m.end():] + try: + v = map[var] + except KeyError: + raise from_none(InterpolationMissingOptionError( + option, section, rawval, var)) + if "%" in v: + self._interpolate_some(parser, option, accum, v, + section, map, depth + 1) + else: + accum.append(v) + else: + raise InterpolationSyntaxError( + option, section, + "'%%' must be followed by '%%' or '(', " + "found: %r" % (rest,)) + + +class ExtendedInterpolation(Interpolation): + """Advanced variant of interpolation, supports the syntax used by + `zc.buildout'. Enables interpolation between sections.""" + + _KEYCRE = re.compile(r"\$\{([^}]+)\}") + + def before_get(self, parser, section, option, value, defaults): + L = [] + self._interpolate_some(parser, option, L, value, section, defaults, 1) + return ''.join(L) + + def before_set(self, parser, section, option, value): + tmp_value = value.replace('$$', '') # escaped dollar signs + tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax + if '$' in tmp_value: + raise ValueError("invalid interpolation syntax in %r at " + "position %d" % (value, tmp_value.find('$'))) + return value + + def _interpolate_some(self, parser, option, accum, rest, section, map, + depth): + rawval = parser.get(section, option, raw=True, fallback=rest) + if depth > MAX_INTERPOLATION_DEPTH: + raise InterpolationDepthError(option, section, rawval) + while rest: + p = rest.find("$") + if p < 0: + accum.append(rest) + return + if p > 0: + accum.append(rest[:p]) + rest = rest[p:] + # p is no longer used + c = rest[1:2] + if c == "$": + accum.append("$") + rest = rest[2:] + elif c == "{": + m = self._KEYCRE.match(rest) + if m is None: + raise InterpolationSyntaxError(option, section, + "bad interpolation variable reference %r" % rest) + path = m.group(1).split(':') + rest = rest[m.end():] + sect = section + opt = option + try: + if len(path) == 1: + opt = parser.optionxform(path[0]) + v = map[opt] + elif len(path) == 2: + sect = path[0] + opt = parser.optionxform(path[1]) + v = parser.get(sect, opt, raw=True) + else: + raise InterpolationSyntaxError( + option, section, + "More than one ':' found: %r" % (rest,)) + except (KeyError, NoSectionError, NoOptionError): + raise from_none(InterpolationMissingOptionError( + option, section, rawval, ":".join(path))) + if "$" in v: + self._interpolate_some(parser, opt, accum, v, sect, + dict(parser.items(sect, raw=True)), + depth + 1) + else: + accum.append(v) + else: + raise InterpolationSyntaxError( + option, section, + "'$' must be followed by '$' or '{', " + "found: %r" % (rest,)) + + +class LegacyInterpolation(Interpolation): + """Deprecated interpolation used in old versions of ConfigParser. + Use BasicInterpolation or ExtendedInterpolation instead.""" + + _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") + + def before_get(self, parser, section, option, value, vars): + rawval = value + depth = MAX_INTERPOLATION_DEPTH + while depth: # Loop through this until it's done + depth -= 1 + if value and "%(" in value: + replace = functools.partial(self._interpolation_replace, + parser=parser) + value = self._KEYCRE.sub(replace, value) + try: + value = value % vars + except KeyError as e: + raise from_none(InterpolationMissingOptionError( + option, section, rawval, e.args[0])) + else: + break + if value and "%(" in value: + raise InterpolationDepthError(option, section, rawval) + return value + + def before_set(self, parser, section, option, value): + return value + + @staticmethod + def _interpolation_replace(match, parser): + s = match.group(1) + if s is None: + return match.group() + else: + return "%%(%s)s" % parser.optionxform(s) + + +class RawConfigParser(MutableMapping): + """ConfigParser that does not do interpolation.""" + + # Regular expressions for parsing section headers and options + _SECT_TMPL = r""" + \[ # [ + (?P
[^]]+) # very permissive! + \] # ] + """ + _OPT_TMPL = r""" + (?P