PY-11855 Run manage.py task improvements

Options now fetched from python and returned to Java
This commit is contained in:
Ilya.Kazakevich
2015-02-11 15:37:32 +03:00
parent 7a8a4c15bd
commit b496b88b94
16 changed files with 361 additions and 25 deletions
@@ -24,17 +24,26 @@ def report_data(dumper):
command = utility.fetch_command(command_name)
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_help_text=str(command.usage("").replace("%prog", command_name)),
# TODO: support subcommands
command_args_text=str(command.args))
for opt in command.option_list:
opt_type = opt.type if opt.type in Option.TYPES else "" # Empty for unknown
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(
opt_type=opt_type,
choices=opt.choices,
long_opt_names=opt._long_opts,
short_opt_names=opt._short_opts,
help_text=opt.help,
num_of_args=opt.nargs)
argument_info=(num_of_args, opt_type) if num_of_args else None)
dumper.close_command()
@@ -6,13 +6,17 @@ It does not have schema (yet!) but here is XML format it uses.
<commandInfo-array> -- root
<commandInfo args="args description" help="human readable text" name="command name"> -- info about command
<option help="option help" numberOfArgs="number of values (nargs)" type="option type: Option.TYPES"> -- one entry for each option
<option help="option help" numberOfArgs="number of values (nargs)" type="option_type (see below)"> -- one entry for each option
<longNames>--each-for-one-long-opt-name</longNames>
<shortNames>-each-for-one-short-name</shortNames>
<choices>--each-for-one-available-value</choices>
</option>
</commandInfo>
</commandInfo-array>
"option_type" is only set if "numberOfArgs" > 0, and it can be: "int" (means integer),
"choices" (means opt can have one of the values, provided in choices) or "str" that means "string" (option may have any value)
Classes like DjangoCommandsInfo is used on Java side.
"""
@@ -77,35 +81,45 @@ class XmlDumper(object):
self.__command_element.setAttribute("args", command_args_text)
self.__root.appendChild(self.__command_element)
def add_command_option(self, opt_type, choices, long_opt_names, short_opt_names, help_text, num_of_args):
def add_command_option(self, long_opt_names, short_opt_names, help_text, argument_info):
"""
Adds command option
:param opt_type: "string", "int", "long", "float", "complex", "choice"
:param choices: list of choices for "choice" type
:param argument_info: None if option does not accept any arguments or tuple of (num_of_args, type_info) \
where num_of_args is int > 0 and type_info is str, representing type (only "int" and "string" are supported) \
or list of available types in case of choices
:param long_opt_names: list of long opt names
:param short_opt_names: list of short opt names
:param help_text: help text
:param num_of_args: number of arguments
:type opt_type str
:type choices list of string
:type long_opt_names list of str
:type short_opt_names list of str
:type help_text str
:type num_of_args int
:type argument_info tuple
"""
assert isinstance(self.__command_element, Element), "Add option in command only"
option = self.__document.createElement("option")
option.setAttribute("type", opt_type)
if choices:
self.__create_text_array(option, "choices", choices)
option = self.__document.createElement("option")
opt_type_to_report = None
num_of_args = 0
if argument_info:
(num_of_args, type_info) = argument_info
if isinstance(type_info, list):
self.__create_text_array(option, "choices", type_info)
opt_type_to_report = "choices"
else:
opt_type_to_report = "int" if str(type_info) == "int" else "str"
if long_opt_names:
self.__create_text_array(option, "longNames", long_opt_names)
if short_opt_names:
self.__create_text_array(option, "shortNames", short_opt_names)
if opt_type_to_report:
option.setAttribute("type", opt_type_to_report)
option.setAttribute("help", help_text)
if num_of_args:
option.setAttribute("numberOfArgs", str(num_of_args))
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
package com.jetbrains.python.commandInterface.command;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
package com.jetbrains.python.commandInterface.command;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.Nullable;
@@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
package com.jetbrains.python.commandInterface.command;
import com.intellij.openapi.module.Module;
import com.jetbrains.python.commandLineParser.CommandLineParseResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Command with arguments
* Command with arguments and options
*
* @author Ilya.Kazakevich
*/
@@ -47,6 +49,12 @@ public interface Command {
@NotNull
ArgumentsInfo getArgumentsInfo();
/**
* @return command options
*/
@NotNull
List<Option> getOptions();
/**
* Execute command
*
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
package com.jetbrains.python.commandInterface.command;
import com.google.common.base.Preconditions;
import com.intellij.openapi.util.Pair;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
package com.jetbrains.python.commandInterface.command;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.Nullable;
@@ -0,0 +1,92 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.command;
import com.google.common.base.Preconditions;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Command option
* @author Ilya.Kazakevich
*/
public final class Option {
@NotNull
private final List<String> myLongNames = new ArrayList<String>();
@NotNull
private final List<String> myShortNames = new ArrayList<String>();
@Nullable
private final Pair<Integer, OptionArgumentInfo> myArgumentAndQuantity;
@NotNull
private final String myHelp;
/**
*
* @param argumentAndQuantity if option accepts argument, there should be pair of [argument_quantity, its_type_info]
* @param help option help
* @param shortNames option short names
* @param longNames option long names
*/
public Option(@Nullable final Pair<Integer, OptionArgumentInfo> argumentAndQuantity,
@NotNull final String help,
@NotNull final Collection<String> shortNames,
@NotNull final Collection<String> longNames) {
Preconditions.checkArgument(argumentAndQuantity == null || argumentAndQuantity.first > 0, "Illegal args and quantity: " + argumentAndQuantity);
myArgumentAndQuantity = argumentAndQuantity;
myShortNames.addAll(shortNames);
myLongNames.addAll(longNames);
myHelp = help;
}
/**
* @return Option long names
*/
@NotNull
public List<String> getLongNames() {
return Collections.unmodifiableList(myLongNames);
}
/**
* @return Option short names
*/
@NotNull
public List<String> getShortNames() {
return Collections.unmodifiableList(myShortNames);
}
/**
*
* @return if option accepts argument -- pair of [argument_quantity, its_type_info]. Null otherwise.
*/
@Nullable
Pair<Integer, OptionArgumentInfo> getArgumentAndQuantity() {
return myArgumentAndQuantity;
}
/**
* @return Option help
*/
@NotNull
public String getHelp() {
return myHelp;
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.command;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Information about option argument
*
* @author Ilya.Kazakevich
*/
public interface OptionArgumentInfo {
/**
* Validates argument value
*
* @param value value to validate
* @return true if valid
*/
boolean isValid(@NotNull String value);
/**
* @return list of available values (if argument is based on list of choices), or null if any value is accepted (but should be validated)
*/
@Nullable
List<String> getAvailableValues();
}
@@ -0,0 +1,31 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.command;
/**
* Argument type to be used with {@link OptionTypedArgumentInfo}
* @author Ilya.Kazakevich
*/
public enum OptionArgumentType {
/**
* String (actually, anything)
*/
STRING,
/**
* Integer or long
*/
INTEGER
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.command;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* For options, whose argument is based on list of choices
* @author Ilya.Kazakevich
*/
public final class OptionChoiceBasedArgumentInfo implements OptionArgumentInfo {
@NotNull
private final List<String> myChoices = new ArrayList<String>();
/**
* @param choices available choices
*/
public OptionChoiceBasedArgumentInfo(@NotNull final Collection<String> choices) {
myChoices.addAll(choices);
}
@Override
public boolean isValid(@NotNull final String value) {
return myChoices.contains(value);
}
@Nullable
@Override
public List<String> getAvailableValues() {
return Collections.unmodifiableList(myChoices);
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.command;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* For options, whose argument is based on certain type.
*
* @author Ilya.Kazakevich
* @see com.jetbrains.python.commandInterface.command.OptionArgumentType
*/
public final class OptionTypedArgumentInfo implements OptionArgumentInfo {
@NotNull
private final OptionArgumentType myType;
/**
* @param type type argument(s) of this option may have
*/
public OptionTypedArgumentInfo(@NotNull final OptionArgumentType type) {
myType = type;
}
@Override
public boolean isValid(@NotNull final String value) {
// We only check integer for now
if (myType == OptionArgumentType.INTEGER) {
try {
// We just parse it to get exception
//noinspection ResultOfMethodCallIgnored
Integer.parseInt(value);
}
catch (final NumberFormatException ignored) {
return false;
}
}
return true;
}
@Nullable
@Override
public List<String> getAvailableValues() {
return null;
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;
package com.jetbrains.python.commandInterface.command;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Command with arguments and options.
* Each {@link com.jetbrains.python.commandInterface.command.Command} may have one or more positional {@link com.jetbrains.python.commandInterface.command.Argument arguments}
* and several {@link com.jetbrains.python.commandInterface.command.Option options}.
* @author Ilya.Kazakevich
*/
package com.jetbrains.python.commandInterface.command;
@@ -23,6 +23,9 @@ import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriv
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkInfo;
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ParseInfo;
import com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.SuggestionInfo;
import com.jetbrains.python.commandInterface.command.Argument;
import com.jetbrains.python.commandInterface.command.ArgumentsInfo;
import com.jetbrains.python.commandInterface.command.Command;
import com.jetbrains.python.commandLineParser.CommandLineParseResult;
import com.jetbrains.python.commandLineParser.CommandLineParser;
import com.jetbrains.python.commandLineParser.CommandLinePartType;
@@ -16,12 +16,14 @@
/**
* {@link com.jetbrains.python.commandInterface.chunkDriverBasedPresenter.ChunkDriver} implementation based on idea of
* {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.Command command} and its {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.Argument arguments}.
* {@link com.jetbrains.python.commandInterface.command command, option and argument}.
*
* See {@link com.jetbrains.python.commandInterface.commandBasedChunkDriver.CommandBasedChunkDriver} as entry point.
* It parses command line using {@link com.jetbrains.python.commandLineParser.CommandLineParser} and finds matching command and arguments
* provided by user
*
* @see com.jetbrains.python.commandInterface.command
* @see com.jetbrains.python.commandInterface.command.Command
* @author Ilya.Kazakevich
*/
package com.jetbrains.python.commandInterface.commandBasedChunkDriver;