File: shell_plus.py

package info (click to toggle)
python-django-extensions 1.7.4-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 2,016 kB
  • ctags: 1,342
  • sloc: python: 8,873; makefile: 117
file content (418 lines) | stat: -rw-r--r-- 17,531 bytes parent folder | download
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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# -*- coding: utf-8 -*-
import os
import six
import sys
import time
import traceback

from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from django.db.backends import utils
from django.utils.six import PY3

from django_extensions.management.shells import import_objects
from django_extensions.management.utils import signalcommand


def use_vi_mode():
    editor = os.environ.get('EDITOR')
    if not editor:
        return False
    editor = os.path.basename(editor)
    return editor.startswith('vi') or editor.endswith('vim')


class Command(BaseCommand):
    help = "Like the 'shell' command but autoloads the models of all installed Django apps."

    def add_arguments(self, parser):
        super(Command, self).add_arguments(parser)
        parser.add_argument(
            '--plain', action='store_true', dest='plain',
            help='Tells Django to use plain Python, not BPython nor IPython.')
        parser.add_argument(
            '--bpython', action='store_true', dest='bpython',
            help='Tells Django to use BPython, not IPython.')
        parser.add_argument(
            '--ptpython', action='store_true', dest='ptpython',
            help='Tells Django to use PTPython, not IPython.')
        parser.add_argument(
            '--ptipython', action='store_true', dest='ptipython',
            help='Tells Django to use PT-IPython, not IPython.')
        parser.add_argument(
            '--ipython', action='store_true', dest='ipython',
            help='Tells Django to use IPython, not BPython.')
        parser.add_argument(
            '--notebook', action='store_true', dest='notebook',
            help='Tells Django to use IPython Notebook.')
        parser.add_argument(
            '--kernel', action='store_true', dest='kernel',
            help='Tells Django to start an IPython Kernel.')
        parser.add_argument('--connection-file', action='store', dest='connection_file',
                    help='Specifies the connection file to use if using the --kernel option'),
        parser.add_argument(
            '--use-pythonrc', action='store_true', dest='use_pythonrc',
            help='Tells Django to execute PYTHONSTARTUP file '
            '(BE CAREFULL WITH THIS!)')
        parser.add_argument(
            '--print-sql', action='store_true', default=False,
            help="Print SQL queries as they're executed")
        parser.add_argument(
            '--dont-load', action='append', dest='dont_load', default=[],
            help='Ignore autoloading of some apps/models. Can be used '
            'several times.')
        parser.add_argument(
            '--quiet-load', action='store_true', default=False,
            dest='quiet_load', help='Do not display loaded models messages')
        parser.add_argument(
            '--vi', action='store_true', default=use_vi_mode(), dest='vi_mode',
            help='Load Vi key bindings (for --ptpython and --ptipython)')
        parser.add_argument(
            '--no-browser', action='store_true', default=False,
            dest='no_browser',
            help='Don\'t open the notebook in a browser after startup.')

    def get_ipython_arguments(self, options):
        return getattr(settings, 'IPYTHON_ARGUMENTS', [])

    def get_notebook_arguments(self, options):
        return getattr(settings, 'NOTEBOOK_ARGUMENTS', [])

    @signalcommand
    def handle(self, *args, **options):
        use_kernel = options.get('kernel', False)
        use_notebook = options.get('notebook', False)
        use_ipython = options.get('ipython', False)
        use_bpython = options.get('bpython', False)
        use_plain = options.get('plain', False)
        use_ptpython = options.get('ptpython', False)
        use_ptipython = options.get('ptipython', False)
        use_pythonrc = options.get('use_pythonrc', True)
        no_browser = options.get('no_browser', False)
        verbosity = int(options.get('verbosity', 1))
        print_sql = getattr(settings, 'SHELL_PLUS_PRINT_SQL', False)

        if options.get("print_sql", False) or print_sql:

            # Code from http://gist.github.com/118990
            sqlparse = None
            try:
                import sqlparse
            except ImportError:
                pass

            class PrintQueryWrapper(utils.CursorDebugWrapper):
                def execute(self, sql, params=()):
                    starttime = time.time()
                    try:
                        return self.cursor.execute(sql, params)
                    finally:
                        execution_time = time.time() - starttime
                        raw_sql = self.db.ops.last_executed_query(self.cursor, sql, params)
                        if sqlparse:
                            print(sqlparse.format(raw_sql, reindent=True))
                        else:
                            print(raw_sql)
                        print("")
                        print('Execution time: %.6fs [Database: %s]' % (execution_time, self.db.alias))
                        print("")

            utils.CursorDebugWrapper = PrintQueryWrapper

        def get_kernel():
            try:
                from IPython import release
                if release.version_info[0] < 2:
                    print(self.style.ERROR("--kernel requires at least IPython version 2.0"))
                    return
                from IPython import start_kernel
            except ImportError:
                return traceback.format_exc()

            def run_kernel():
                imported_objects = import_objects(options, self.style)
                kwargs = dict(
                    argv=[],
                    user_ns=imported_objects,
                )
                connection_file = options.get('connection_file')
                if connection_file:
                    kwargs['connection_file'] = connection_file
                start_kernel(**kwargs)
            return run_kernel

        def get_notebook():
            from IPython import release
            try:
                from notebook.notebookapp import NotebookApp
            except ImportError:
                try:
                    from IPython.html.notebookapp import NotebookApp
                except ImportError:
                    if release.version_info[0] >= 3:
                        raise
                    try:
                        from IPython.frontend.html.notebook import notebookapp
                        NotebookApp = notebookapp.NotebookApp
                    except ImportError:
                        return traceback.format_exc()

            def install_kernel_spec(app, display_name, ipython_arguments):
                """install an IPython >= 3.0 kernelspec that loads django extensions"""
                ksm = app.kernel_spec_manager
                try_spec_names = getattr(settings, 'NOTEBOOK_KERNEL_SPEC_NAMES', [
                    'python3' if PY3 else 'python2',
                    'python',
                ])
                if isinstance(try_spec_names, six.string_types):
                    try_spec_names = [try_spec_names]
                ks = None
                for spec_name in try_spec_names:
                    try:
                        ks = ksm.get_kernel_spec(spec_name)
                        break
                    except:
                        continue
                if not ks:
                    raise CommandError("No notebook (Python) kernel specs found")
                ks.argv.extend(ipython_arguments)
                ks.display_name = display_name

                manage_py_dir, manage_py = os.path.split(os.path.realpath(sys.argv[0]))

                if manage_py == 'manage.py' and os.path.isdir(manage_py_dir) and manage_py_dir != os.getcwd():
                    pythonpath = ks.env.get('PYTHONPATH', os.environ.get('PYTHONPATH', ''))
                    pythonpath = pythonpath.split(':')
                    if manage_py_dir not in pythonpath:
                        pythonpath.append(manage_py_dir)

                    ks.env['PYTHONPATH'] = ':'.join(filter(None, pythonpath))

                kernel_dir = os.path.join(ksm.user_kernel_dir, 'django_extensions')
                if not os.path.exists(kernel_dir):
                    os.makedirs(kernel_dir)
                with open(os.path.join(kernel_dir, 'kernel.json'), 'w') as f:
                    f.write(ks.to_json())

            def run_notebook():
                app = NotebookApp.instance()

                # Treat IPYTHON_ARGUMENTS from settings
                ipython_arguments = self.get_ipython_arguments(options)
                if 'django_extensions.management.notebook_extension' not in ipython_arguments:
                    ipython_arguments.extend(['--ext', 'django_extensions.management.notebook_extension'])

                # Treat NOTEBOOK_ARGUMENTS from settings
                notebook_arguments = self.get_notebook_arguments(options)
                if no_browser and '--no-browser' not in notebook_arguments:
                    notebook_arguments.append('--no-browser')
                if '--notebook-dir' not in notebook_arguments:
                    notebook_arguments.extend(['--notebook-dir', '.'])

                # IPython < 3 passes through kernel args from notebook CLI
                if release.version_info[0] < 3:
                    notebook_arguments.extend(ipython_arguments)

                app.initialize(notebook_arguments)

                # IPython >= 3 uses kernelspecs to specify kernel CLI args
                if release.version_info[0] >= 3:
                    display_name = getattr(settings, 'IPYTHON_KERNEL_DISPLAY_NAME', "Django Shell-Plus")
                    install_kernel_spec(app, display_name, ipython_arguments)

                app.start()
            return run_notebook

        def get_plain():
            # Using normal Python shell
            import code
            imported_objects = import_objects(options, self.style)
            try:
                # Try activating rlcompleter, because it's handy.
                import readline
            except ImportError:
                pass
            else:
                # We don't have to wrap the following import in a 'try', because
                # we already know 'readline' was imported successfully.
                import rlcompleter
                readline.set_completer(rlcompleter.Completer(imported_objects).complete)
                readline.parse_and_bind("tab:complete")

            # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
            # conventions and get $PYTHONSTARTUP first then import user.
            if use_pythonrc:
                pythonrc = os.environ.get("PYTHONSTARTUP")
                if pythonrc and os.path.isfile(pythonrc):
                    global_ns = {}
                    with open(pythonrc) as rcfile:
                        try:
                            six.exec_(compile(rcfile.read(), pythonrc, 'exec'), global_ns)
                            imported_objects.update(global_ns)
                        except NameError:
                            pass
                # This will import .pythonrc.py as a side-effect
                try:
                    import user  # NOQA
                except ImportError:
                    pass

            def run_plain():
                code.interact(local=imported_objects)
            return run_plain

        def get_bpython():
            try:
                from bpython import embed
            except ImportError:
                return traceback.format_exc()

            def run_bpython():
                imported_objects = import_objects(options, self.style)
                embed(imported_objects)
            return run_bpython

        def get_ipython():
            try:
                from IPython import start_ipython

                def run_ipython():
                    imported_objects = import_objects(options, self.style)
                    ipython_arguments = self.get_ipython_arguments(options)
                    start_ipython(argv=ipython_arguments, user_ns=imported_objects)
                return run_ipython
            except ImportError:
                str_exc = traceback.format_exc()
                # IPython < 0.11
                # Explicitly pass an empty list as arguments, because otherwise
                # IPython would use sys.argv from this script.
                # Notebook not supported for IPython < 0.11.
                try:
                    from IPython.Shell import IPShell
                except ImportError:
                    return str_exc + "\n" + traceback.format_exc()

                def run_ipython():
                    imported_objects = import_objects(options, self.style)
                    shell = IPShell(argv=[], user_ns=imported_objects)
                    shell.mainloop()
                return run_ipython

        def get_ptpython():
            try:
                from ptpython.repl import embed, run_config
            except ImportError:
                tb = traceback.format_exc()
                try:  # prompt_toolkit < v0.27
                    from prompt_toolkit.contrib.repl import embed, run_config
                except ImportError:
                    return tb

            def run_ptpython():
                imported_objects = import_objects(options, self.style)
                history_filename = os.path.expanduser('~/.ptpython_history')
                embed(globals=imported_objects, history_filename=history_filename,
                      vi_mode=options.get('vi_mode', False), configure=run_config)
            return run_ptpython

        def get_ptipython():
            try:
                from ptpython.repl import run_config
                from ptpython.ipython import embed
            except ImportError:
                tb = traceback.format_exc()
                try:  # prompt_toolkit < v0.27
                    from prompt_toolkit.contrib.repl import run_config
                    from prompt_toolkit.contrib.ipython import embed
                except ImportError:
                    return tb

            def run_ptipython():
                imported_objects = import_objects(options, self.style)
                history_filename = os.path.expanduser('~/.ptpython_history')
                embed(user_ns=imported_objects, history_filename=history_filename,
                      vi_mode=options.get('vi_mode', False), configure=run_config)
            return run_ptipython

        def set_application_name():
            """Set the application_name on PostgreSQL connection

            Use the fallback_application_name to let the user override
            it with PGAPPNAME env variable

            http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS  # noqa
            """
            supported_backends = ['django.db.backends.postgresql_psycopg2']
            opt_name = 'fallback_application_name'
            default_app_name = 'django_shell'
            app_name = default_app_name
            dbs = getattr(settings, 'DATABASES', [])

            # lookup over all the databases entry
            for db in dbs.keys():
                if dbs[db]['ENGINE'] in supported_backends:
                    try:
                        options = dbs[db]['OPTIONS']
                    except KeyError:
                        options = {}

                    # dot not override a defined value
                    if opt_name in options.keys():
                        app_name = dbs[db]['OPTIONS'][opt_name]
                    else:
                        dbs[db].setdefault('OPTIONS', {}).update({opt_name: default_app_name})
                        app_name = default_app_name

            return app_name

        shells = (
            ('ptipython', get_ptipython),
            ('ptpython', get_ptpython),
            ('bpython', get_bpython),
            ('ipython', get_ipython),
            ('plain', get_plain),
        )
        SETTINGS_SHELL_PLUS = getattr(settings, 'SHELL_PLUS', None)

        shell = None
        shell_name = "any"
        set_application_name()
        if use_kernel:
            shell = get_kernel()
            shell_name = "IPython Kernel"
        elif use_notebook:
            shell = get_notebook()
            shell_name = "IPython Notebook"
        elif use_plain:
            shell = get_plain()
            shell_name = "plain"
        elif use_ipython:
            shell = get_ipython()
            shell_name = "IPython"
        elif use_bpython:
            shell = get_bpython()
            shell_name = "BPython"
        elif use_ptpython:
            shell = get_ptpython()
            shell_name = "ptpython"
        elif use_ptipython:
            shell = get_ptipython()
            shell_name = "ptipython"
        elif SETTINGS_SHELL_PLUS:
            shell_name = SETTINGS_SHELL_PLUS
            shell = dict(shells)[shell_name]()
        else:
            for shell_name, func in shells:
                shell = func()
                if callable(shell):
                    if verbosity > 1:
                        print(self.style.NOTICE("Using shell %s." % shell_name))
                    break

        if not callable(shell):
            if shell:
                print(shell)
            print(self.style.ERROR("Could not load %s interactive Python environment." % shell_name))
            return

        shell()