summaryrefslogtreecommitdiff
path: root/astroid/brain/brain_six.py
blob: a1043ea5eea564757aff793430ad3025fb7208be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# 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 <http://www.gnu.org/licenses/>.

"""Astroid hooks for six module."""

import sys
from textwrap import dedent

from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
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'.

    If 'predicate' is provided, 'prefix' will only be added to the lines
    where 'predicate(line)' is True. If 'predicate' is not provided,
    it will default to adding 'prefix' to all non-empty lines that do not
    consist solely of whitespace characters.
    """
    if predicate is None:
        predicate = lambda line: line.strip()

    def prefixed_lines():
        for line in text.splitlines(True):
            yield prefix + line if predicate(line) else line
    return ''.join(prefixed_lines())


if sys.version_info[0] == 2:
    _IMPORTS_2 = """
    import BaseHTTPServer
    import CGIHTTPServer
    import SimpleHTTPServer

    from StringIO import StringIO
    from cStringIO import StringIO as cStringIO
    from UserDict import UserDict
    from UserList import UserList
    from UserString import UserString

    import __builtin__ as builtins
    import thread as _thread
    import dummy_thread as _dummy_thread
    import ConfigParser as configparser
    import copy_reg as copyreg
    from itertools import (imap as map,
                           ifilter as filter,
                           ifilterfalse as filterfalse,
                           izip_longest as zip_longest,
                           izip as zip)
    import htmlentitydefs as html_entities
    import HTMLParser as html_parser
    import httplib as http_client
    import cookielib as http_cookiejar
    import Cookie as http_cookies
    import Queue as queue
    import repr as reprlib
    from pipes import quote as shlex_quote
    import SocketServer as socketserver
    import SimpleXMLRPCServer as xmlrpc_server
    import xmlrpclib as xmlrpc_client
    import _winreg as winreg
    import robotparser as urllib_robotparser
    import Tkinter as tkinter
    import tkFileDialog as tkinter_tkfiledialog

    input = raw_input
    intern = intern
    range = xrange
    xrange = xrange
    reduce = reduce
    reload_module = reload

    class UrllibParse(object):
        import urlparse as _urlparse
        import urllib as _urllib
        ParseResult = _urlparse.ParseResult
        SplitResult = _urlparse.SplitResult
        parse_qs = _urlparse.parse_qs
        parse_qsl = _urlparse.parse_qsl
        urldefrag = _urlparse.urldefrag
        urljoin = _urlparse.urljoin
        urlparse = _urlparse.urlparse
        urlsplit = _urlparse.urlsplit
        urlunparse = _urlparse.urlunparse
        urlunsplit = _urlparse.urlunsplit
        quote = _urllib.quote
        quote_plus = _urllib.quote_plus
        unquote = _urllib.unquote
        unquote_plus = _urllib.unquote_plus
        urlencode = _urllib.urlencode
        splitquery = _urllib.splitquery
        splittag = _urllib.splittag
        splituser = _urllib.splituser
        uses_fragment = _urlparse.uses_fragment
        uses_netloc = _urlparse.uses_netloc
        uses_params = _urlparse.uses_params
        uses_query = _urlparse.uses_query
        uses_relative = _urlparse.uses_relative

    class UrllibError(object):
        import urllib2 as _urllib2
        import urllib as _urllib
        URLError = _urllib2.URLError
        HTTPError = _urllib2.HTTPError
        ContentTooShortError = _urllib.ContentTooShortError

    class DummyModule(object):
        pass

    class UrllibRequest(object):
        import urlparse as _urlparse
        import urllib2 as _urllib2
        import urllib as _urllib
        urlopen = _urllib2.urlopen
        install_opener = _urllib2.install_opener
        build_opener = _urllib2.build_opener
        pathname2url = _urllib.pathname2url
        url2pathname = _urllib.url2pathname
        getproxies = _urllib.getproxies
        Request = _urllib2.Request
        OpenerDirector = _urllib2.OpenerDirector
        HTTPDefaultErrorHandler = _urllib2.HTTPDefaultErrorHandler
        HTTPRedirectHandler = _urllib2.HTTPRedirectHandler
        HTTPCookieProcessor = _urllib2.HTTPCookieProcessor
        ProxyHandler = _urllib2.ProxyHandler
        BaseHandler = _urllib2.BaseHandler
        HTTPPasswordMgr = _urllib2.HTTPPasswordMgr
        HTTPPasswordMgrWithDefaultRealm = _urllib2.HTTPPasswordMgrWithDefaultRealm
        AbstractBasicAuthHandler = _urllib2.AbstractBasicAuthHandler
        HTTPBasicAuthHandler = _urllib2.HTTPBasicAuthHandler
        ProxyBasicAuthHandler = _urllib2.ProxyBasicAuthHandler
        AbstractDigestAuthHandler = _urllib2.AbstractDigestAuthHandler
        HTTPDigestAuthHandler = _urllib2.HTTPDigestAuthHandler
        ProxyDigestAuthHandler = _urllib2.ProxyDigestAuthHandler
        HTTPHandler = _urllib2.HTTPHandler
        HTTPSHandler = _urllib2.HTTPSHandler
        FileHandler = _urllib2.FileHandler
        FTPHandler = _urllib2.FTPHandler
        CacheFTPHandler = _urllib2.CacheFTPHandler
        UnknownHandler = _urllib2.UnknownHandler
        HTTPErrorProcessor = _urllib2.HTTPErrorProcessor
        urlretrieve = _urllib.urlretrieve
        urlcleanup = _urllib.urlcleanup
        proxy_bypass = _urllib.proxy_bypass

    urllib_parse = UrllibParse()
    urllib_error = UrllibError()
    urllib = DummyModule()
    urllib.request = UrllibRequest()
    urllib.parse = UrllibParse()
    urllib.error = UrllibError()
    """
else:
    _IMPORTS_3 = """
    import _io
    cStringIO = _io.StringIO
    filter = filter
    from itertools import filterfalse
    input = input
    from sys import intern
    map = map
    range = range
    from imp import reload as reload_module
    from functools import reduce
    from shlex import quote as shlex_quote
    from io import StringIO
    from collections import UserDict, UserList, UserString
    xrange = range
    zip = zip
    from itertools import zip_longest
    import builtins
    import configparser
    import copyreg
    import _dummy_thread
    import http.cookiejar as http_cookiejar
    import http.cookies as http_cookies
    import html.entities as html_entities
    import html.parser as html_parser
    import http.client as http_client
    import http.server
    BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server
    import pickle as cPickle
    import queue
    import reprlib
    import socketserver
    import _thread
    import winreg
    import xmlrpc.server as xmlrpc_server
    import xmlrpc.client as xmlrpc_client
    import urllib.robotparser as urllib_robotparser
    import email.mime.multipart as email_mime_multipart
    import email.mime.nonmultipart as email_mime_nonmultipart
    import email.mime.text as email_mime_text
    import email.mime.base as email_mime_base
    import urllib.parse as urllib_parse
    import urllib.error as urllib_error
    import tkinter
    import tkinter.dialog as tkinter_dialog
    import tkinter.filedialog as tkinter_filedialog
    import tkinter.scrolledtext as tkinter_scrolledtext
    import tkinter.simpledialog as tkinder_simpledialog
    import tkinter.tix as tkinter_tix
    import tkinter.ttk as tkinter_ttk
    import tkinter.constants as tkinter_constants
    import tkinter.dnd as tkinter_dnd
    import tkinter.colorchooser as tkinter_colorchooser
    import tkinter.commondialog as tkinter_commondialog
    import tkinter.filedialog as tkinter_tkfiledialog
    import tkinter.font as tkinter_font
    import tkinter.messagebox as tkinter_messagebox
    import urllib.request
    import urllib.robotparser as urllib_robotparser
    import urllib.parse as urllib_parse
    import urllib.error as urllib_error
    """
if sys.version_info[0] == 2:
    _IMPORTS = dedent(_IMPORTS_2)
else:
    _IMPORTS = dedent(_IMPORTS_3)


def six_moves_transform():
    code = dedent('''
    class Moves(object):
    {}
    moves = Moves()
    ''').format(_indent(_IMPORTS, "    "))
    module = AstroidBuilder(MANAGER).string_build(code)
    module.name = 'six.moves'
    return module


def _six_fail_hook(modname):
    if modname != 'six.moves':
        raise AstroidBuildingException
    module = AstroidBuilder(MANAGER).string_build(_IMPORTS)
    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)