Commit f019888c authored by realead's avatar realead Committed by Stefan Behnel

Replace custom command line parser with argparse (GH-3001)

parent 78e417e5
......@@ -120,33 +120,8 @@ def run_distutils(args):
def create_args_parser():
from argparse import ArgumentParser, Action
class ParseDirectivesAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
old_directives = dict(getattr(namespace, self.dest,
Options.get_directive_defaults()))
directives = Options.parse_directive_list(
values, relaxed_bool=True, current_settings=old_directives)
setattr(namespace, self.dest, directives)
class ParseOptionsAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
options = dict(getattr(namespace, self.dest, {}))
for opt in values.split(','):
if '=' in opt:
n, v = opt.split('=', 1)
v = v.lower() not in ('false', 'f', '0', 'no')
else:
n, v = opt, True
options[n] = v
setattr(namespace, self.dest, options)
class ParseCompileTimeEnvAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
old_env = dict(getattr(namespace, self.dest, {}))
new_env = Options.parse_compile_time_env(values, current_settings=old_env)
setattr(namespace, self.dest, new_env)
from argparse import ArgumentParser
from ..Compiler.CmdLine import ParseDirectivesAction, ParseOptionsAction, ParseCompileTimeEnvAction
parser = ArgumentParser()
......
......@@ -5,222 +5,222 @@
from __future__ import absolute_import
import os
import sys
from argparse import ArgumentParser, Action, SUPPRESS
from . import Options
usage = """\
Cython (https://cython.org/) is a compiler for code written in the
Cython language. Cython is based on Pyrex by Greg Ewing.
Usage: cython [options] sourcefile.{pyx,py} ...
Options:
-V, --version Display version number of cython compiler
-l, --create-listing Write error messages to a listing file
-I, --include-dir <directory> Search for include files in named directory
(multiple include directories are allowed).
-o, --output-file <filename> Specify name of generated C file
-t, --timestamps Only compile newer source files
-f, --force Compile all source files (overrides implied -t)
-v, --verbose Be verbose, print file names on multiple compilation
-p, --embed-positions If specified, the positions in Cython files of each
function definition is embedded in its docstring.
--cleanup <level> Release interned objects on python exit, for memory debugging.
Level indicates aggressiveness, default 0 releases nothing.
-w, --working <directory> Sets the working directory for Cython (the directory modules
are searched from)
--gdb Output debug information for cygdb
--gdb-outdir <directory> Specify gdb debug information output directory. Implies --gdb.
-D, --no-docstrings Strip docstrings from the compiled module.
-a, --annotate Produce a colorized HTML version of the source.
--annotate-fullc Produce a colorized HTML version of the source which
includes entire generated C/C++-code.
--annotate-coverage <cov.xml> Annotate and include coverage information from cov.xml.
--line-directives Produce #line directives pointing to the .pyx source
--cplus Output a C++ rather than C file.
--embed[=<method_name>] Generate a main() function that embeds the Python interpreter.
-2 Compile based on Python-2 syntax and code semantics.
-3 Compile based on Python-3 syntax and code semantics.
--3str Compile based on Python-3 syntax and code semantics without
assuming unicode by default for string literals under Python 2.
--lenient Change some compile time errors to runtime errors to
improve Python compatibility
--capi-reexport-cincludes Add cincluded headers to any auto-generated header files.
--fast-fail Abort the compilation on the first error
--warning-errors, -Werror Make all warnings into errors
--warning-extra, -Wextra Enable extra warnings
-X, --directive <name>=<value>[,<name=value,...] Overrides a compiler directive
-E, --compile-time-env name=value[,<name=value,...] Provides compile time env like DEF would do.
"""
# The following experimental options are supported only on MacOSX:
# -C, --compile Compile generated .c file to .o file
# --link Link .o file to produce extension module (implies -C)
# -+, --cplus Use C++ compiler for compiling and linking
# Additional .o files to link may be supplied when using -X."""
def bad_usage():
sys.stderr.write(usage)
sys.exit(1)
class ParseDirectivesAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
old_directives = dict(getattr(namespace, self.dest,
Options.get_directive_defaults()))
directives = Options.parse_directive_list(
values, relaxed_bool=True, current_settings=old_directives)
setattr(namespace, self.dest, directives)
def parse_command_line(args):
pending_arg = []
def pop_arg():
if not args or pending_arg:
bad_usage()
if '=' in args[0] and args[0].startswith('--'): # allow "--long-option=xyz"
name, value = args.pop(0).split('=', 1)
pending_arg.append(value)
return name
return args.pop(0)
def pop_value(default=None):
if pending_arg:
return pending_arg.pop()
elif default is not None:
return default
elif not args:
bad_usage()
return args.pop(0)
def get_param(option):
tail = option[2:]
if tail:
return tail
class ParseOptionsAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
options = dict(getattr(namespace, self.dest, {}))
for opt in values.split(','):
if '=' in opt:
n, v = opt.split('=', 1)
v = v.lower() not in ('false', 'f', '0', 'no')
else:
n, v = opt, True
options[n] = v
setattr(namespace, self.dest, options)
class ParseCompileTimeEnvAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
old_env = dict(getattr(namespace, self.dest, {}))
new_env = Options.parse_compile_time_env(values, current_settings=old_env)
setattr(namespace, self.dest, new_env)
class ActivateAllWarningsAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
directives = getattr(namespace, 'compiler_directives', {})
directives.update(Options.extra_warnings)
namespace.compiler_directives = directives
class SetLenientAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
namespace.error_on_unknown_names = False
namespace.error_on_uninitialized = False
class SetGDBDebugAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
namespace.gdb_debug = True
namespace.output_dir = os.curdir
class SetGDBDebugOutputAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
namespace.gdb_debug = True
namespace.output_dir = values
class SetAnnotateCoverageAction(Action):
def __call__(self, parser, namespace, values, option_string=None):
namespace.annotate = True
namespace.annotate_coverage_xml = values
def create_cython_argparser():
description = "Cython (https://cython.org/) is a compiler for code written in the "\
"Cython language. Cython is based on Pyrex by Greg Ewing."
parser = ArgumentParser(description=description, argument_default=SUPPRESS)
parser.add_argument("-V", "--version", dest='show_version', action='store_const', const=1,
help='Display version number of cython compiler')
parser.add_argument("-l", "--create-listing", dest='use_listing_file', action='store_const', const=1,
help='Write error messages to a listing file')
parser.add_argument("-I", "--include-dir", dest='include_path', action='append',
help='Search for include files in named directory '
'(multiple include directories are allowed).')
parser.add_argument("-o", "--output-file", dest='output_file', action='store', type=str,
help='Specify name of generated C file')
parser.add_argument("-t", "--timestamps", dest='timestamps', action='store_const', const=1,
help='Only compile newer source files')
parser.add_argument("-f", "--force", dest='timestamps', action='store_const', const=0,
help='Compile all source files (overrides implied -t)')
parser.add_argument("-v", "--verbose", dest='verbose', action='count',
help='Be verbose, print file names on multiple compilation')
parser.add_argument("-p", "--embed-positions", dest='embed_pos_in_docstring', action='store_const', const=1,
help='If specified, the positions in Cython files of each '
'function definition is embedded in its docstring.')
parser.add_argument("--cleanup", dest='generate_cleanup_code', action='store', type=int,
help='Release interned objects on python exit, for memory debugging. '
'Level indicates aggressiveness, default 0 releases nothing.')
parser.add_argument("-w", "--working", dest='working_path', action='store', type=str,
help='Sets the working directory for Cython (the directory modules are searched from)')
parser.add_argument("--gdb", action=SetGDBDebugAction, nargs=0,
help='Output debug information for cygdb')
parser.add_argument("--gdb-outdir", action=SetGDBDebugOutputAction, type=str,
help='Specify gdb debug information output directory. Implies --gdb.')
parser.add_argument("-D", "--no-docstrings", dest='docstrings', action='store_false',
help='Strip docstrings from the compiled module.')
parser.add_argument('-a', '--annotate', action='store_const', const='default', dest='annotate',
help='Produce a colorized HTML version of the source.')
parser.add_argument('--annotate-fullc', action='store_const', const='fullc', dest='annotate',
help='Produce a colorized HTML version of the source '
'which includes entire generated C/C++-code.')
parser.add_argument("--annotate-coverage", dest='annotate_coverage_xml', action=SetAnnotateCoverageAction, type=str,
help='Annotate and include coverage information from cov.xml.')
parser.add_argument("--line-directives", dest='emit_linenums', action='store_true',
help='Produce #line directives pointing to the .pyx source')
parser.add_argument("-+", "--cplus", dest='cplus', action='store_const', const=1,
help='Output a C++ rather than C file.')
parser.add_argument('--embed', action='store_const', const='main',
help='Generate a main() function that embeds the Python interpreter. '
'Pass --embed=<method_name> for a name other than main().')
parser.add_argument('-2', dest='language_level', action='store_const', const=2,
help='Compile based on Python-2 syntax and code semantics.')
parser.add_argument('-3', dest='language_level', action='store_const', const=3,
help='Compile based on Python-3 syntax and code semantics.')
parser.add_argument('--3str', dest='language_level', action='store_const', const='3str',
help='Compile based on Python-3 syntax and code semantics without '
'assuming unicode by default for string literals under Python 2.')
parser.add_argument("--lenient", action=SetLenientAction, nargs=0,
help='Change some compile time errors to runtime errors to '
'improve Python compatibility')
parser.add_argument("--capi-reexport-cincludes", dest='capi_reexport_cincludes', action='store_true',
help='Add cincluded headers to any auto-generated header files.')
parser.add_argument("--fast-fail", dest='fast_fail', action='store_true',
help='Abort the compilation on the first error')
parser.add_argument("-Werror", "--warning-errors", dest='warning_errors', action='store_true',
help='Make all warnings into errors')
parser.add_argument("-Wextra", "--warning-extra", action=ActivateAllWarningsAction, nargs=0,
help='Enable extra warnings')
parser.add_argument('-X', '--directive', metavar='NAME=VALUE,...',
dest='compiler_directives', type=str,
action=ParseDirectivesAction,
help='Overrides a compiler directive')
parser.add_argument('-E', '--compile-time-env', metavar='NAME=VALUE,...',
dest='compile_time_env', type=str,
action=ParseCompileTimeEnvAction,
help='Provides compile time env like DEF would do.')
parser.add_argument('sources', nargs='*', default=[])
# TODO: add help
parser.add_argument("-z", "--pre-import", dest='pre_import', action='store', type=str, help=SUPPRESS)
parser.add_argument("--convert-range", dest='convert_range', action='store_true', help=SUPPRESS)
parser.add_argument("--no-c-in-traceback", dest='c_line_in_traceback', action='store_false', help=SUPPRESS)
parser.add_argument("--cimport-from-pyx", dest='cimport_from_pyx', action='store_true', help=SUPPRESS)
parser.add_argument("--old-style-globals", dest='old_style_globals', action='store_true', help=SUPPRESS)
# debug stuff:
from . import DebugFlags
for name in vars(DebugFlags):
if name.startswith("debug"):
option_name = name.replace('_', '-')
parser.add_argument("--" + option_name, action='store_true', help=SUPPRESS)
return parser
def parse_command_line_raw(parser, args):
# special handling for --embed and --embed=xxxx as they aren't correctly parsed
def filter_out_embed_options(args):
with_embed, without_embed = [], []
for x in args:
if x == '--embed' or x.startswith('--embed='):
with_embed.append(x)
else:
without_embed.append(x)
return with_embed, without_embed
with_embed, args_without_embed = filter_out_embed_options(args)
arguments, unknown = parser.parse_known_args(args_without_embed)
sources = arguments.sources
del arguments.sources
# unknown can be either debug, embed or input files or really unknown
for option in unknown:
if option.startswith('-'):
parser.error("unknown option " + option)
else:
return pop_arg()
sources.append(option)
# embed-stuff must be handled extra:
for x in with_embed:
if x == '--embed':
name = 'main' # default value
else:
name = x[len('--embed='):]
setattr(arguments, 'embed', name)
return arguments, sources
def parse_command_line(args):
parser = create_cython_argparser()
arguments, sources = parse_command_line_raw(parser, args)
options = Options.CompilationOptions(Options.default_options)
sources = []
while args:
if args[0].startswith("-"):
option = pop_arg()
if option in ("-V", "--version"):
options.show_version = 1
elif option in ("-l", "--create-listing"):
options.use_listing_file = 1
elif option in ("-+", "--cplus"):
options.cplus = 1
elif option == "--embed":
Options.embed = pop_value("main")
elif option.startswith("-I"):
options.include_path.append(get_param(option))
elif option == "--include-dir":
options.include_path.append(pop_value())
elif option in ("-w", "--working"):
options.working_path = pop_value()
elif option in ("-o", "--output-file"):
options.output_file = pop_value()
elif option in ("-t", "--timestamps"):
options.timestamps = 1
elif option in ("-f", "--force"):
options.timestamps = 0
elif option in ("-v", "--verbose"):
options.verbose += 1
elif option in ("-p", "--embed-positions"):
Options.embed_pos_in_docstring = 1
elif option in ("-z", "--pre-import"):
Options.pre_import = pop_value()
elif option == "--cleanup":
Options.generate_cleanup_code = int(pop_value())
elif option in ("-D", "--no-docstrings"):
Options.docstrings = False
elif option in ("-a", "--annotate"):
Options.annotate = "default"
elif option == "--annotate-fullc":
Options.annotate = "fullc"
elif option == "--annotate-coverage":
Options.annotate = True
Options.annotate_coverage_xml = pop_value()
elif option == "--convert-range":
Options.convert_range = True
elif option == "--line-directives":
options.emit_linenums = True
elif option == "--no-c-in-traceback":
options.c_line_in_traceback = False
elif option == "--gdb":
options.gdb_debug = True
options.output_dir = os.curdir
elif option == "--gdb-outdir":
options.gdb_debug = True
options.output_dir = pop_value()
elif option == "--lenient":
Options.error_on_unknown_names = False
Options.error_on_uninitialized = False
elif option == '-2':
options.language_level = 2
elif option == '-3':
options.language_level = 3
elif option == '--3str':
options.language_level = '3str'
elif option == "--capi-reexport-cincludes":
options.capi_reexport_cincludes = True
elif option == "--fast-fail":
Options.fast_fail = True
elif option == "--cimport-from-pyx":
Options.cimport_from_pyx = True
elif option in ('-Werror', '--warning-errors'):
Options.warning_errors = True
elif option in ('-Wextra', '--warning-extra'):
options.compiler_directives.update(Options.extra_warnings)
elif option == "--old-style-globals":
Options.old_style_globals = True
elif option == "--directive" or option.startswith('-X'):
if option.startswith('-X') and option[2:].strip():
x_args = option[2:]
else:
x_args = pop_value()
try:
options.compiler_directives = Options.parse_directive_list(
x_args, relaxed_bool=True,
current_settings=options.compiler_directives)
except ValueError as e:
sys.stderr.write("Error in compiler directive: %s\n" % e.args[0])
sys.exit(1)
elif option == "--compile-time-env" or option.startswith('-E'):
if option.startswith('-E') and option[2:].strip():
x_args = option[2:]
else:
x_args = pop_value()
try:
options.compile_time_env = Options.parse_compile_time_env(
x_args, current_settings=options.compile_time_env)
except ValueError as e:
sys.stderr.write("Error in compile-time-env: %s\n" % e.args[0])
sys.exit(1)
elif option.startswith('--debug'):
option = option[2:].replace('-', '_')
from . import DebugFlags
if option in dir(DebugFlags):
setattr(DebugFlags, option, True)
else:
sys.stderr.write("Unknown debug flag: %s\n" % option)
bad_usage()
elif option in ('-h', '--help'):
sys.stdout.write(usage)
sys.exit(0)
for name, value in vars(arguments).items():
if name.startswith('debug'):
from . import DebugFlags
if name in dir(DebugFlags):
setattr(DebugFlags, name, value)
else:
sys.stderr.write("Unknown compiler flag: %s\n" % option)
sys.exit(1)
parser.error("Unknown debug flag: %s\n" % name)
elif hasattr(Options, name):
setattr(Options, name, value)
else:
sources.append(pop_arg())
if pending_arg:
bad_usage()
setattr(options, name, value)
if options.use_listing_file and len(sources) > 1:
sys.stderr.write(
"cython: Only one source file allowed when using -o\n")
sys.exit(1)
parser.error("cython: Only one source file allowed when using -o\n")
if len(sources) == 0 and not options.show_version:
bad_usage()
parser.error("cython: Need at least one source file\n")
if Options.embed and len(sources) > 1:
sys.stderr.write(
"cython: Only one source file allowed when using -embed\n")
sys.exit(1)
parser.error("cython: Only one source file allowed when using -embed\n")
return options, sources
import os
import sys
import copy
from unittest import TestCase
try:
from StringIO import StringIO
......@@ -14,6 +15,9 @@ class CmdLineParserTest(TestCase):
def setUp(self):
backup = {}
for name, value in vars(Options).items():
# we need a deep copy of _directive_defaults, because they can be changed
if name == '_directive_defaults':
value = copy.deepcopy(value)
backup[name] = value
self._options_backup = backup
......@@ -22,6 +26,23 @@ class CmdLineParserTest(TestCase):
for name, orig_value in self._options_backup.items():
if getattr(Options, name, no_value) != orig_value:
setattr(Options, name, orig_value)
# strip Options from new keys that might have been added:
for name in vars(Options).keys():
if name not in self._options_backup:
delattr(Options, name)
def check_default_global_options(self, white_list=[]):
no_value = object()
for name, orig_value in self._options_backup.items():
if name not in white_list:
self.assertEqual(getattr(Options, name, no_value), orig_value, msg="error in option " + name)
def check_default_options(self, options, white_list=[]):
default_options = Options.CompilationOptions(Options.default_options)
no_value = object()
for name in default_options.__dict__.keys():
if name not in white_list:
self.assertEqual(getattr(options, name, no_value), getattr(default_options, name), msg="error in option " + name)
def test_short_options(self):
options, sources = parse_command_line([
......@@ -97,6 +118,31 @@ class CmdLineParserTest(TestCase):
self.assertEqual(Options.annotate_coverage_xml, 'cov.xml')
self.assertTrue(options.gdb_debug)
self.assertEqual(options.output_dir, '/gdb/outdir')
self.assertEqual(options.compiler_directives['wraparound'], False)
def test_embed_before_positional(self):
options, sources = parse_command_line([
'--embed',
'source.pyx',
])
self.assertEqual(sources, ['source.pyx'])
self.assertEqual(Options.embed, 'main')
def test_two_embeds(self):
options, sources = parse_command_line([
'--embed', '--embed=huhu',
'source.pyx',
])
self.assertEqual(sources, ['source.pyx'])
self.assertEqual(Options.embed, 'huhu')
def test_two_embeds2(self):
options, sources = parse_command_line([
'--embed=huhu', '--embed',
'source.pyx',
])
self.assertEqual(sources, ['source.pyx'])
self.assertEqual(Options.embed, 'main')
def test_no_annotate(self):
options, sources = parse_command_line([
......@@ -125,6 +171,345 @@ class CmdLineParserTest(TestCase):
])
self.assertEqual(Options.annotate, 'fullc')
def test_short_w(self):
options, sources = parse_command_line([
'-w', 'my_working_path',
'source.pyx'
])
self.assertEqual(options.working_path, 'my_working_path')
self.check_default_global_options()
self.check_default_options(options, ['working_path'])
def test_short_o(self):
options, sources = parse_command_line([
'-o', 'my_output',
'source.pyx'
])
self.assertEqual(options.output_file, 'my_output')
self.check_default_global_options()
self.check_default_options(options, ['output_file'])
def test_short_z(self):
options, sources = parse_command_line([
'-z', 'my_preimport',
'source.pyx'
])
self.assertEqual(Options.pre_import, 'my_preimport')
self.check_default_global_options(['pre_import'])
self.check_default_options(options)
def test_convert_range(self):
options, sources = parse_command_line([
'--convert-range',
'source.pyx'
])
self.assertEqual(Options.convert_range, True)
self.check_default_global_options(['convert_range'])
self.check_default_options(options)
def test_line_directives(self):
options, sources = parse_command_line([
'--line-directives',
'source.pyx'
])
self.assertEqual(options.emit_linenums, True)
self.check_default_global_options()
self.check_default_options(options, ['emit_linenums'])
def test_no_c_in_traceback(self):
options, sources = parse_command_line([
'--no-c-in-traceback',
'source.pyx'
])
self.assertEqual(options.c_line_in_traceback, False)
self.check_default_global_options()
self.check_default_options(options, ['c_line_in_traceback'])
def test_gdb(self):
options, sources = parse_command_line([
'--gdb',
'source.pyx'
])
self.assertEqual(options.gdb_debug, True)
self.assertEqual(options.output_dir, os.curdir)
self.check_default_global_options()
self.check_default_options(options, ['gdb_debug', 'output_dir'])
def test_3str(self):
options, sources = parse_command_line([
'--3str',
'source.pyx'
])
self.assertEqual(options.language_level, '3str')
self.check_default_global_options()
self.check_default_options(options, ['language_level'])
def test_capi_reexport_cincludes(self):
options, sources = parse_command_line([
'--capi-reexport-cincludes',
'source.pyx'
])
self.assertEqual(options.capi_reexport_cincludes, True)
self.check_default_global_options()
self.check_default_options(options, ['capi_reexport_cincludes'])
def test_fast_fail(self):
options, sources = parse_command_line([
'--fast-fail',
'source.pyx'
])
self.assertEqual(Options.fast_fail, True)
self.check_default_global_options(['fast_fail'])
self.check_default_options(options)
def test_cimport_from_pyx(self):
options, sources = parse_command_line([
'--cimport-from-pyx',
'source.pyx'
])
self.assertEqual(Options.cimport_from_pyx, True)
self.check_default_global_options(['cimport_from_pyx'])
self.check_default_options(options)
def test_Werror(self):
options, sources = parse_command_line([
'-Werror',
'source.pyx'
])
self.assertEqual(Options.warning_errors, True)
self.check_default_global_options(['warning_errors'])
self.check_default_options(options)
def test_warning_errors(self):
options, sources = parse_command_line([
'--warning-errors',
'source.pyx'
])
self.assertEqual(Options.warning_errors, True)
self.check_default_global_options(['warning_errors'])
self.check_default_options(options)
def test_Wextra(self):
options, sources = parse_command_line([
'-Wextra',
'source.pyx'
])
self.assertEqual(options.compiler_directives, Options.extra_warnings)
self.check_default_global_options()
self.check_default_options(options, ['compiler_directives'])
def test_warning_extra(self):
options, sources = parse_command_line([
'--warning-extra',
'source.pyx'
])
self.assertEqual(options.compiler_directives, Options.extra_warnings)
self.check_default_global_options()
self.check_default_options(options, ['compiler_directives'])
def test_old_style_globals(self):
options, sources = parse_command_line([
'--old-style-globals',
'source.pyx'
])
self.assertEqual(Options.old_style_globals, True)
self.check_default_global_options(['old_style_globals'])
self.check_default_options(options)
def test_directive_multiple(self):
options, source = parse_command_line([
'-X', 'cdivision=True',
'-X', 'c_string_type=bytes',
'source.pyx'
])
self.assertEqual(options.compiler_directives['cdivision'], True)
self.assertEqual(options.compiler_directives['c_string_type'], 'bytes')
self.check_default_global_options()
self.check_default_options(options, ['compiler_directives'])
def test_directive_multiple_v2(self):
options, source = parse_command_line([
'-X', 'cdivision=True,c_string_type=bytes',
'source.pyx'
])
self.assertEqual(options.compiler_directives['cdivision'], True)
self.assertEqual(options.compiler_directives['c_string_type'], 'bytes')
self.check_default_global_options()
self.check_default_options(options, ['compiler_directives'])
def test_directive_value_yes(self):
options, source = parse_command_line([
'-X', 'cdivision=YeS',
'source.pyx'
])
self.assertEqual(options.compiler_directives['cdivision'], True)
self.check_default_global_options()
self.check_default_options(options, ['compiler_directives'])
def test_directive_value_no(self):
options, source = parse_command_line([
'-X', 'cdivision=no',
'source.pyx'
])
self.assertEqual(options.compiler_directives['cdivision'], False)
self.check_default_global_options()
self.check_default_options(options, ['compiler_directives'])
def test_directive_value_invalid(self):
self.assertRaises(ValueError, parse_command_line, [
'-X', 'cdivision=sadfasd',
'source.pyx'
])
def test_directive_key_invalid(self):
self.assertRaises(ValueError, parse_command_line, [
'-X', 'abracadabra',
'source.pyx'
])
def test_directive_no_value(self):
self.assertRaises(ValueError, parse_command_line, [
'-X', 'cdivision',
'source.pyx'
])
def test_compile_time_env_short(self):
options, source = parse_command_line([
'-E', 'MYSIZE=10',
'source.pyx'
])
self.assertEqual(options.compile_time_env['MYSIZE'], 10)
self.check_default_global_options()
self.check_default_options(options, ['compile_time_env'])
def test_compile_time_env_long(self):
options, source = parse_command_line([
'--compile-time-env', 'MYSIZE=10',
'source.pyx'
])
self.assertEqual(options.compile_time_env['MYSIZE'], 10)
self.check_default_global_options()
self.check_default_options(options, ['compile_time_env'])
def test_compile_time_env_multiple(self):
options, source = parse_command_line([
'-E', 'MYSIZE=10', '-E', 'ARRSIZE=11',
'source.pyx'
])
self.assertEqual(options.compile_time_env['MYSIZE'], 10)
self.assertEqual(options.compile_time_env['ARRSIZE'], 11)
self.check_default_global_options()
self.check_default_options(options, ['compile_time_env'])
def test_compile_time_env_multiple_v2(self):
options, source = parse_command_line([
'-E', 'MYSIZE=10,ARRSIZE=11',
'source.pyx'
])
self.assertEqual(options.compile_time_env['MYSIZE'], 10)
self.assertEqual(options.compile_time_env['ARRSIZE'], 11)
self.check_default_global_options()
self.check_default_options(options, ['compile_time_env'])
def test_option_first(self):
options, sources = parse_command_line(['-V', 'file.pyx'])
self.assertEqual(sources, ['file.pyx'])
def test_file_inbetween(self):
options, sources = parse_command_line(['-V', 'file.pyx', '-a'])
self.assertEqual(sources, ['file.pyx'])
def test_option_trailing(self):
options, sources = parse_command_line(['file.pyx', '-V'])
self.assertEqual(sources, ['file.pyx'])
def test_multiple_files(self):
options, sources = parse_command_line([
'file1.pyx', '-V',
'file2.pyx', '-a',
'file3.pyx'
])
self.assertEqual(sources, ['file1.pyx', 'file2.pyx', 'file3.pyx'])
def test_debug_flags(self):
options, sources = parse_command_line([
'--debug-disposal-code', '--debug-coercion',
'file3.pyx'
])
from Cython.Compiler import DebugFlags
for name in ['debug_disposal_code', 'debug_temp_alloc', 'debug_coercion']:
self.assertEqual(getattr(DebugFlags, name), name in ['debug_disposal_code', 'debug_coercion'])
setattr(DebugFlags, name, 0) # restore original value
def test_gdb_overwrites_gdb_outdir(self):
options, sources = parse_command_line([
'--gdb-outdir=my_dir', '--gdb',
'file3.pyx'
])
self.assertEqual(options.gdb_debug, True)
self.assertEqual(options.output_dir, os.curdir)
self.check_default_global_options()
self.check_default_options(options, ['gdb_debug', 'output_dir'])
def test_gdb_first(self):
options, sources = parse_command_line([
'--gdb', '--gdb-outdir=my_dir',
'file3.pyx'
])
self.assertEqual(options.gdb_debug, True)
self.assertEqual(options.output_dir, 'my_dir')
self.check_default_global_options()
self.check_default_options(options, ['gdb_debug', 'output_dir'])
def test_coverage_overwrites_annotation(self):
options, sources = parse_command_line([
'--annotate-fullc', '--annotate-coverage=my.xml',
'file3.pyx'
])
self.assertEqual(Options.annotate, True)
self.assertEqual(Options.annotate_coverage_xml, 'my.xml')
self.check_default_global_options(['annotate', 'annotate_coverage_xml'])
self.check_default_options(options)
def test_coverage_first(self):
options, sources = parse_command_line([
'--annotate-coverage=my.xml', '--annotate-fullc',
'file3.pyx'
])
self.assertEqual(Options.annotate, 'fullc')
self.assertEqual(Options.annotate_coverage_xml, 'my.xml')
self.check_default_global_options(['annotate', 'annotate_coverage_xml'])
self.check_default_options(options)
def test_annotate_first_fullc_second(self):
options, sources = parse_command_line([
'--annotate', '--annotate-fullc',
'file3.pyx'
])
self.assertEqual(Options.annotate, 'fullc')
self.check_default_global_options(['annotate'])
self.check_default_options(options)
def test_annotate_fullc_first(self):
options, sources = parse_command_line([
'--annotate-fullc', '--annotate',
'file3.pyx'
])
self.assertEqual(Options.annotate, 'default')
self.check_default_global_options(['annotate'])
self.check_default_options(options)
def test_warning_extra_dont_overwrite(self):
options, sources = parse_command_line([
'-X', 'cdivision=True',
'--warning-extra',
'-X', 'c_string_type=bytes',
'source.pyx'
])
self.assertTrue(len(options.compiler_directives), len(Options.extra_warnings) + 1)
self.check_default_global_options()
self.check_default_options(options, ['compiler_directives'])
def test_errors(self):
def error(*args):
old_stderr = sys.stderr
......@@ -143,3 +528,4 @@ class CmdLineParserTest(TestCase):
error('--verbose=1')
error('--verbose=1')
error('--cleanup')
error('--debug-disposal-code-wrong-name', 'file3.pyx')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment