1# coding=utf-8 2""" 3Exports data from optparse or argparse based manage.py commands and reports it to _xml.XmlDumper. 4This module encapsulates Django semi-public API knowledge, and not very stable because of it. 5Optional env var ``_PYCHARM_DJANGO_DEFAULT_TIMEOUT`` sets timeout (in seconds) to wait for each command to be 6fetched. 7""" 8import sys 9import threading 10from _jb_utils import VersionAgnosticUtils 11from django.core.management import ManagementUtility, get_commands 12from django_manage_commands_provider._parser import _optparse, _argparse 13import os 14 15__author__ = 'Ilya.Kazakevich' 16 17 18class _Fetcher(threading.Thread): 19 def __init__(self, utility, command_name): 20 super(_Fetcher, self).__init__() 21 self.result = None 22 self.__utility = utility 23 self.__command_name = command_name 24 self.command_lead_to_exception = False 25 26 def run(self): 27 try: 28 self.result = self.__utility.fetch_command(self.__command_name) 29 except Exception as e: 30 sys.stderr.write("Error fetching command '{0}': {1}\n".format(self.__command_name, e)) 31 self.command_lead_to_exception = True 32 33 34def report_data(dumper, commands_to_skip): 35 """ 36 Fetches data from management commands and reports it to dumper. 37 38 :type dumper _xml.XmlDumper 39 :type commands_to_skip list 40 :param commands_to_skip list of commands to skip 41 :param dumper: destination to report 42 """ 43 utility = ManagementUtility() 44 for command_name in get_commands().keys(): 45 46 if command_name in commands_to_skip: 47 sys.stderr.write("Skipping command '{0}' due to config\n".format(command_name)) 48 continue 49 50 fetcher = _Fetcher(utility, command_name) 51 fetcher.daemon = True 52 fetcher.start() 53 fetcher.join(int(os.getenv("_PYCHARM_DJANGO_DEFAULT_TIMEOUT", "2"))) 54 command = fetcher.result 55 if not command: 56 if fetcher.command_lead_to_exception: 57 sys.stderr.write("Command '{0}' skipped\n".format(command_name)) 58 continue 59 else: 60 sys.stderr.write( 61 "Command '{0}' took too long and may freeze everything. Consider adding it to 'skip commands' list\n".format( 62 command_name)) 63 sys.exit(1) 64 65 use_argparse = False 66 # There is no optparse in 1.10 67 if _is_django_10(): 68 use_argparse = True 69 else: 70 try: 71 use_argparse = command.use_argparse 72 except AttributeError: 73 pass 74 75 try: 76 parser = command.create_parser("", command_name) 77 except Exception as e: 78 sys.stderr.write("Error parsing command {0}: {1}\n".format(command_name, e)) 79 continue 80 81 try: # and there is no "usage()" since 1.10 82 usage = command.usage("") 83 except AttributeError: 84 usage = command.help 85 86 dumper.start_command(command_name=command_name, 87 command_help_text=VersionAgnosticUtils().to_unicode(usage).replace("%prog", 88 command_name)) 89 module_to_use = _argparse if use_argparse else _optparse # Choose appropriate module: argparse, optparse 90 module_to_use.process_command(dumper, command, parser) 91 dumper.close_command() 92 93 94def _is_django_10(): 95 """ 96 97 :return: is Django >= 1.10 98 """ 99 try: 100 from distutils.version import StrictVersion, LooseVersion 101 import django 102 try: 103 return StrictVersion(django.get_version()) >= StrictVersion("1.10") 104 except ValueError: 105 return LooseVersion(django.get_version()) >= LooseVersion("1.10") 106 except (ImportError, AttributeError): 107 return False 108