mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Initial support for argparse (django 1.8) for PY-11855 (manage.py).
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Exports data from optparse-based manage.py commands and reports it to _xml.XmlDumper.
|
||||
This module encapsulates Django semi-public API knowledge, and not very stable because of it.
|
||||
"""
|
||||
from optparse import Option
|
||||
import django
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.management import ManagementUtility, get_commands, BaseCommand
|
||||
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
|
||||
|
||||
def report_data(dumper):
|
||||
"""
|
||||
Fetches data from management commands and reports it to dumper.
|
||||
|
||||
:type dumper _xml.XmlDumper
|
||||
:param dumper: destination to report
|
||||
"""
|
||||
utility = ManagementUtility()
|
||||
for command_name in get_commands().keys():
|
||||
try:
|
||||
command = utility.fetch_command(command_name)
|
||||
except ImproperlyConfigured:
|
||||
continue # TODO: Log somehow
|
||||
|
||||
assert isinstance(command, BaseCommand)
|
||||
dumper.start_command(command_name=command_name,
|
||||
command_help_text=str(command.usage("").replace("%prog", command_name)),
|
||||
# TODO: support subcommands
|
||||
command_args_text=str(command.args))
|
||||
for opt in command.option_list:
|
||||
num_of_args = int(opt.nargs) if opt.nargs else 0
|
||||
opt_type = None
|
||||
if num_of_args > 0:
|
||||
# If option accepts arg, we need to determine its type. It could be int, choices, or something other
|
||||
# See https://docs.python.org/2/library/optparse.html#standard-option-types
|
||||
if opt.type in ["int", "long"]:
|
||||
opt_type = "int"
|
||||
elif opt.choices:
|
||||
assert isinstance(opt.choices, list), "Choices should be list"
|
||||
opt_type = opt.choices
|
||||
|
||||
# There is no official way to access this field, so I use protected one. At least it is public API.
|
||||
# noinspection PyProtectedMember
|
||||
dumper.add_command_option(
|
||||
long_opt_names=opt._long_opts,
|
||||
short_opt_names=opt._short_opts,
|
||||
help_text=opt.help,
|
||||
argument_info=(num_of_args, opt_type) if num_of_args else None)
|
||||
dumper.close_command()
|
||||
@@ -0,0 +1,6 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
This package hides differences between argparse and optparse.
|
||||
Use "parser" module as entry point
|
||||
"""
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
@@ -0,0 +1,50 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Fetches arguments from argparse-based Django (1.8+)
|
||||
"""
|
||||
from argparse import Action, _StoreTrueAction, _StoreFalseAction
|
||||
|
||||
from _parser import _utils
|
||||
|
||||
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
# Command here by contract
|
||||
def process_command(dumper, command, parser):
|
||||
"""
|
||||
Fetches arguments and options from command and parser and reports em to dumper.
|
||||
|
||||
:param dumper dumper to output data to
|
||||
:param parser arg parser to use
|
||||
:param command django command
|
||||
|
||||
:type dumper _xml.XmlDumper
|
||||
:type parser argparse.ArgumentParser
|
||||
:type command django.core.management.base.BaseCommand
|
||||
"""
|
||||
|
||||
argument_names = []
|
||||
# No public API to fetch actions from argparse.
|
||||
# noinspection PyProtectedMember
|
||||
for action in parser._actions:
|
||||
assert isinstance(action, Action)
|
||||
if action.option_strings:
|
||||
# Long opts start with --
|
||||
long_opt_names = set(filter(lambda opt_name: str(opt_name).startswith("--"), action.option_strings))
|
||||
# All other opts are short
|
||||
short_opt_names = set(action.option_strings) - long_opt_names
|
||||
argument_info = None
|
||||
# The only difference between bool option and argument-based option is the one has store=True
|
||||
bool_option = isinstance(action, _StoreTrueAction) or isinstance(action, _StoreFalseAction)
|
||||
if not bool_option:
|
||||
# TODO: Support nargs. It can be +, ?, * and number. Not only 1.
|
||||
argument_info = (1, _utils.get_opt_type(action))
|
||||
|
||||
dumper.add_command_option(long_opt_names, short_opt_names, str(action.help), argument_info)
|
||||
else:
|
||||
# TODO: Fetch optionality/mandatority from argument info because it has nargs field
|
||||
argument_names.append("[" + str(action.metavar if action.metavar else action.dest) + "]")
|
||||
|
||||
dumper.set_arguments(" ".join(argument_names))
|
||||
@@ -0,0 +1,37 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Fetches arguments from optparse-based Django (< 1.8)
|
||||
"""
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
from _parser import _utils
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
# Parser here by contract
|
||||
def process_command(dumper, command, parser):
|
||||
"""
|
||||
Fetches arguments and options from command and parser and reports em to dumper.
|
||||
|
||||
:param dumper dumper to output data to
|
||||
:param parser opt parser to use
|
||||
:param command django command
|
||||
|
||||
:type dumper _xml.XmlDumper
|
||||
:type parser optparse.OptionParser
|
||||
:type command django.core.management.base.BaseCommand
|
||||
"""
|
||||
dumper.set_arguments(command.args)
|
||||
# TODO: support subcommands
|
||||
for opt in command.option_list:
|
||||
num_of_args = int(opt.nargs) if opt.nargs else 0
|
||||
opt_type = None
|
||||
if num_of_args > 0:
|
||||
opt_type = _utils.get_opt_type(opt)
|
||||
|
||||
# There is no official way to access this field, so I use protected one. At least it is public API.
|
||||
# noinspection PyProtectedMember
|
||||
dumper.add_command_option(
|
||||
long_opt_names=opt._long_opts,
|
||||
short_opt_names=opt._short_opts,
|
||||
help_text=opt.help,
|
||||
argument_info=(num_of_args, opt_type) if num_of_args else None)
|
||||
@@ -0,0 +1,22 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Internal package tools shared between argparse and optparse
|
||||
"""
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
|
||||
|
||||
def get_opt_type(opt):
|
||||
"""
|
||||
If option accepts arg, we need to determine its type. It could be int, choices, or something other.
|
||||
Accepts option (from arg or opt) and returns its type (as scalar or list in case of choices).
|
||||
Arg should have "type" and "choices" field.
|
||||
|
||||
:param opt option or action from argparse or optparse that has type and choices field
|
||||
:return: type
|
||||
"""
|
||||
if opt.type in ["int", "long"]:
|
||||
return "int"
|
||||
elif opt.choices:
|
||||
assert isinstance(opt.choices, list), "Choices should be list"
|
||||
return opt.choices
|
||||
return "str"
|
||||
@@ -0,0 +1,41 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Exports data from optparse or argparse based manage.py commands and reports it to _xml.XmlDumper.
|
||||
This module encapsulates Django semi-public API knowledge, and not very stable because of it.
|
||||
"""
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.management import ManagementUtility, get_commands, BaseCommand
|
||||
|
||||
from _parser import _optparse, _argparse
|
||||
|
||||
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
|
||||
|
||||
def report_data(dumper):
|
||||
"""
|
||||
Fetches data from management commands and reports it to dumper.
|
||||
|
||||
:type dumper _xml.XmlDumper
|
||||
:param dumper: destination to report
|
||||
"""
|
||||
utility = ManagementUtility()
|
||||
for command_name in get_commands().keys():
|
||||
try:
|
||||
command = utility.fetch_command(command_name)
|
||||
except ImproperlyConfigured:
|
||||
continue # TODO: Log somehow
|
||||
|
||||
assert isinstance(command, BaseCommand)
|
||||
|
||||
use_argparse = False
|
||||
try:
|
||||
use_argparse = command.use_argparse
|
||||
except AttributeError:
|
||||
pass
|
||||
dumper.start_command(command_name=command_name,
|
||||
command_help_text=str(command.usage("").replace("%prog", command_name)))
|
||||
module_to_use = _argparse if use_argparse else _optparse # Choose appropriate module: argparse, optparse
|
||||
module_to_use.process_command(dumper, command, command.create_parser("", command_name))
|
||||
dumper.close_command()
|
||||
|
||||
@@ -19,6 +19,8 @@ It does not have schema (yet!) but here is XML format it uses.
|
||||
|
||||
Classes like DjangoCommandsInfo is used on Java side.
|
||||
|
||||
TODO: Since Django 1.8 we can fetch much more info from argparse like positional argument names, nargs etc. Use it!
|
||||
|
||||
"""
|
||||
from xml.dom import minidom
|
||||
from xml.dom.minidom import Element
|
||||
@@ -60,17 +62,16 @@ class XmlDumper(object):
|
||||
"""
|
||||
for value in values:
|
||||
tag = self.__document.createElement(tag_name)
|
||||
text = self.__document.createTextNode(value)
|
||||
text = self.__document.createTextNode(str(value))
|
||||
tag.appendChild(text)
|
||||
parent.appendChild(tag)
|
||||
|
||||
def start_command(self, command_name, command_help_text, command_args_text):
|
||||
def start_command(self, command_name, command_help_text):
|
||||
"""
|
||||
Starts manage command
|
||||
|
||||
:param command_name: command name
|
||||
:param command_help_text: command help
|
||||
:param command_args_text: command text for args
|
||||
|
||||
|
||||
"""
|
||||
@@ -78,9 +79,20 @@ class XmlDumper(object):
|
||||
self.__command_element = self.__document.createElement(XmlDumper.__command_info_tag)
|
||||
self.__command_element.setAttribute("name", command_name)
|
||||
self.__command_element.setAttribute("help", command_help_text)
|
||||
self.__command_element.setAttribute("args", command_args_text)
|
||||
self.__root.appendChild(self.__command_element)
|
||||
|
||||
def set_arguments(self, command_args_text):
|
||||
"""
|
||||
Adds "arguments help" to command.
|
||||
|
||||
TODO: Use real list of arguments instead of this text when people migrate to argparse (Dj. 1.8)
|
||||
|
||||
:param command_args_text: command text for args
|
||||
:type command_args_text str
|
||||
"""
|
||||
assert bool(self.__command_element), "Not in a a command"
|
||||
self.__command_element.setAttribute("args", command_args_text)
|
||||
|
||||
def add_command_option(self, long_opt_names, short_opt_names, help_text, argument_info):
|
||||
"""
|
||||
Adds command option
|
||||
@@ -93,10 +105,10 @@ class XmlDumper(object):
|
||||
:param short_opt_names: list of short opt names
|
||||
:param help_text: help text
|
||||
|
||||
:type long_opt_names list of str
|
||||
:type short_opt_names list of str
|
||||
:type long_opt_names iterable of str
|
||||
:type short_opt_names iterable of str
|
||||
:type help_text str
|
||||
:type argument_info tuple
|
||||
:type argument_info tuple or None
|
||||
"""
|
||||
assert isinstance(self.__command_element, Element), "Add option in command only"
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
This is an entry point of this helper.
|
||||
It fetches data from Django manage commands via _optparse module and report is via _xml module.
|
||||
It fetches data from Django manage commands delegating calles to _parser package report it via _xml module.
|
||||
See _xml module and readme.txt for more info.
|
||||
|
||||
Module can be called directly, but be sure env var DJANGO_SETTINGS_MODULE is set to something like "mysite.settings"
|
||||
"""
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
import django
|
||||
import _optparse
|
||||
|
||||
from _parser import parser
|
||||
import _xml
|
||||
|
||||
|
||||
__author__ = 'Ilya.Kazakevich'
|
||||
|
||||
# TODO: Support Django 1.8 as well, it uses argparse, not optparse
|
||||
version = LooseVersion(django.get_version())
|
||||
assert version < LooseVersion('1.8a'), "Only Django <1.8 is supported now"
|
||||
# Some django versions require setup
|
||||
if hasattr(django, 'setup'):
|
||||
django.setup()
|
||||
dumper = _xml.XmlDumper()
|
||||
_optparse.report_data(dumper)
|
||||
parser.report_data(dumper)
|
||||
print(dumper.xml)
|
||||
Reference in New Issue
Block a user