first commit
This commit is contained in:
parent
2b3470af3b
commit
82d8186062
35 changed files with 526 additions and 0 deletions
179
examples/Hangman/hangman.py
Normal file
179
examples/Hangman/hangman.py
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Hangman example
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from random import choice
|
||||||
|
import unicodedata
|
||||||
|
import re
|
||||||
|
from os import unlink
|
||||||
|
from os.path import isfile
|
||||||
|
from tiramisu import RegexpOption, OptionDescription, Config, IntOption, UnicodeOption, BoolOption, ParamOption, Params
|
||||||
|
from tiramisu.storage import storage_type
|
||||||
|
from tiramisu.storage.sqlite3.storage import SETTING
|
||||||
|
from tiramisu_parser import TiramisuParser
|
||||||
|
|
||||||
|
|
||||||
|
LANG = 'fr_FR'
|
||||||
|
DICT_FILE = '/usr/share/myspell/{}.dic'.format(LANG)
|
||||||
|
WORD_REGEXP = re.compile(r'^[a-z]{7,12}$')
|
||||||
|
PROPOSALS_LEN = 27
|
||||||
|
NB_PROPOSALS = 6
|
||||||
|
|
||||||
|
|
||||||
|
def remove_accent(word):
|
||||||
|
"""remove all accent"""
|
||||||
|
word = unicodedata.normalize('NFD', word)
|
||||||
|
return word.encode('ascii', 'ignore').decode()
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_word():
|
||||||
|
"""get line randomly in myspell file
|
||||||
|
"""
|
||||||
|
with open(DICT_FILE, 'r') as file_content:
|
||||||
|
word = choice(file_content.readlines()).strip()
|
||||||
|
if word.endswith('/S.'):
|
||||||
|
word = word[:-3]
|
||||||
|
if word.endswith('/X.'):
|
||||||
|
word = word[:-3]
|
||||||
|
if word.endswith('/F.'):
|
||||||
|
word = word[:-3]
|
||||||
|
if word.endswith('/a0p+') or word.endswith('/d0p+') or word.endswith('/a3p+'):
|
||||||
|
word = word[:-5]
|
||||||
|
if not WORD_REGEXP.search(remove_accent(word)):
|
||||||
|
return get_random_word()
|
||||||
|
return word
|
||||||
|
|
||||||
|
|
||||||
|
def display_uncomplete_word(word, *proposals):
|
||||||
|
"""display response with proposals
|
||||||
|
"""
|
||||||
|
if display_proposals_left(display_misses(word, *proposals)) == 0:
|
||||||
|
return word
|
||||||
|
display = ['-'] * len(word)
|
||||||
|
for idx, char in enumerate(remove_accent(word)):
|
||||||
|
if char in proposals:
|
||||||
|
display[idx] = word[idx]
|
||||||
|
return ''.join(display)
|
||||||
|
|
||||||
|
|
||||||
|
def display_misses(word, *proposals):
|
||||||
|
"""display all proposals
|
||||||
|
"""
|
||||||
|
ret = list(set(proposals) - set(list(remove_accent(word))))
|
||||||
|
if None in ret:
|
||||||
|
ret.remove(None)
|
||||||
|
ret.sort()
|
||||||
|
return ' '.join(ret)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_misses(misses):
|
||||||
|
if display_proposals_left(misses) == 0:
|
||||||
|
raise ValueError('No more guest possible')
|
||||||
|
|
||||||
|
|
||||||
|
def display_proposals_left(misses):
|
||||||
|
if not misses:
|
||||||
|
return NB_PROPOSALS
|
||||||
|
return max(NB_PROPOSALS - len(misses.split(' ')), 0)
|
||||||
|
|
||||||
|
|
||||||
|
def display_proposal(word, *proposals):
|
||||||
|
if display_uncomplete_word(word, *proposals) == word:
|
||||||
|
return False
|
||||||
|
return display_proposals_left(display_misses(word, *proposals)) != 0
|
||||||
|
|
||||||
|
|
||||||
|
class ProposalOption(RegexpOption):
|
||||||
|
__slots__ = tuple()
|
||||||
|
_regexp = re.compile(r'^[a-z]$')
|
||||||
|
_display_name = 'proposal'
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
options = []
|
||||||
|
proposal = None
|
||||||
|
word = UnicodeOption('word',
|
||||||
|
'Word',
|
||||||
|
properties=('hidden', 'force_store_value'),
|
||||||
|
callback=get_random_word)
|
||||||
|
proposals = [ParamOption(word)]
|
||||||
|
for idx in range(PROPOSALS_LEN):
|
||||||
|
requires = [{'option': 'self',
|
||||||
|
'expected': None,
|
||||||
|
'action': 'hidden',
|
||||||
|
'inverse': True}]
|
||||||
|
if proposal is not None:
|
||||||
|
display = BoolOption('display{}'.format(idx),
|
||||||
|
'Display {}'.format(idx),
|
||||||
|
properties=('hidden',),
|
||||||
|
callback=display_proposal,
|
||||||
|
callback_params=Params(tuple(proposals)))
|
||||||
|
options.append(display)
|
||||||
|
requires.append({'option': proposal,
|
||||||
|
'expected': None,
|
||||||
|
'action': 'disabled'})
|
||||||
|
requires.append({'option': display,
|
||||||
|
'expected': False,
|
||||||
|
'action': 'disabled'})
|
||||||
|
|
||||||
|
proposal = ProposalOption('guess{}'.format(idx),
|
||||||
|
'Guess {}'.format(idx),
|
||||||
|
requires=requires,
|
||||||
|
properties=('positional',))
|
||||||
|
#FIXME maximum recursion ...
|
||||||
|
#if proposals:
|
||||||
|
# proposal.impl_add_consistency('not_equal', proposals[0])
|
||||||
|
|
||||||
|
proposals.append(ParamOption(proposal, True))
|
||||||
|
options.append(proposal)
|
||||||
|
#
|
||||||
|
proposal_word = UnicodeOption('proposal_word',
|
||||||
|
'Word',
|
||||||
|
properties=('frozen',),
|
||||||
|
callback=display_uncomplete_word,
|
||||||
|
callback_params=Params(tuple(proposals)))
|
||||||
|
misses = UnicodeOption('misses',
|
||||||
|
'Misses',
|
||||||
|
properties=('frozen',),
|
||||||
|
callback=display_misses,
|
||||||
|
callback_params=Params(tuple(proposals)),
|
||||||
|
validator=validate_misses)
|
||||||
|
proposals_left = IntOption('proposals_left',
|
||||||
|
'Proposals left',
|
||||||
|
properties=('frozen',),
|
||||||
|
callback=display_proposals_left,
|
||||||
|
callback_params=Params(ParamOption(misses)))
|
||||||
|
#descr = OptionDescription('proposals',
|
||||||
|
# 'Suggesting letters',
|
||||||
|
# options)
|
||||||
|
storage_type.set('sqlite3')
|
||||||
|
config = Config(OptionDescription('root', 'root', [word, proposal_word, misses, proposals_left] + options), persistent=True, session_id='hangman')
|
||||||
|
parser = TiramisuParser()
|
||||||
|
parser.add_arguments(config)
|
||||||
|
try:
|
||||||
|
parser.parse_args()
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
config = parser.get_config()
|
||||||
|
filename = '{}/tiramisu.db'.format(SETTING.dir_database)
|
||||||
|
lost = False
|
||||||
|
for name in ['proposal_word', 'misses', 'proposals_left']:
|
||||||
|
option = config.option(name)
|
||||||
|
try:
|
||||||
|
value = option.value.get()
|
||||||
|
print('{}: {}'.format(option.option.doc(), value))
|
||||||
|
except ValueError as err:
|
||||||
|
lost = True
|
||||||
|
err.prefix = ''
|
||||||
|
print(option.option.doc(), str(err))
|
||||||
|
if isfile(filename):
|
||||||
|
unlink(filename)
|
||||||
|
if not lost and \
|
||||||
|
config.option('proposal_word').value.get() == config.forcepermissive.option('word').value.get():
|
||||||
|
print('You win')
|
||||||
|
if isfile(filename):
|
||||||
|
unlink(filename)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
1
test/data/compare/10_positional/argparse.py
Normal file
1
test/data/compare/10_positional/argparse.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_argument("echo", help="echo the string you use here")
|
1
test/data/compare/10_positional/tiramisu.py
Normal file
1
test/data/compare/10_positional/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(StrOption('echo', 'echo the string you use here', properties=('mandatory', 'positional')))
|
1
test/data/compare/10_positional_default/argparse.py
Normal file
1
test/data/compare/10_positional_default/argparse.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_argument("echo", help="echo the string you use here", default='blah', nargs='?')
|
1
test/data/compare/10_positional_default/tiramisu.py
Normal file
1
test/data/compare/10_positional_default/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(StrOption('echo', 'echo the string you use here', properties=('mandatory', 'positional'), default='blah'))
|
2
test/data/compare/10_positional_int/argparse.py
Normal file
2
test/data/compare/10_positional_int/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument("square", help="display a square of a given number",
|
||||||
|
type=int)
|
1
test/data/compare/10_positional_int/tiramisu.py
Normal file
1
test/data/compare/10_positional_int/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(IntOption('square', 'display a square of a given number', properties=('mandatory', 'positional')))
|
1
test/data/compare/10_positional_list/argparse.py
Normal file
1
test/data/compare/10_positional_list/argparse.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_argument("echo", help="echo the string you use here", nargs='+')
|
1
test/data/compare/10_positional_list/tiramisu.py
Normal file
1
test/data/compare/10_positional_list/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(StrOption('echo', 'echo the string you use here', properties=('mandatory', 'positional'), multi=True))
|
1
test/data/compare/20_bool/argparse.py
Normal file
1
test/data/compare/20_bool/argparse.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_argument('--verbosity', help='increase output verbosity', action='store_true')
|
1
test/data/compare/20_bool/tiramisu.py
Normal file
1
test/data/compare/20_bool/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(BoolOption('verbosity', 'increase output verbosity', default=False))
|
1
test/data/compare/20_bool_true/argparse.py
Normal file
1
test/data/compare/20_bool_true/argparse.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_argument('--verbosity', help='increase output verbosity', action='store_false')
|
1
test/data/compare/20_bool_true/tiramisu.py
Normal file
1
test/data/compare/20_bool_true/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(BoolOption('verbosity', 'increase output verbosity', default=True))
|
2
test/data/compare/20_choice/argparse.py
Normal file
2
test/data/compare/20_choice/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('--door', help='Door numbers', choices=['1', '2', '3'])
|
||||||
|
|
1
test/data/compare/20_choice/tiramisu.py
Normal file
1
test/data/compare/20_choice/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(ChoiceOption('door', 'Door numbers', ('1', '2', '3')))
|
2
test/data/compare/20_int/argparse.py
Normal file
2
test/data/compare/20_int/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('--int', help='integer', type=int)
|
||||||
|
|
1
test/data/compare/20_int/tiramisu.py
Normal file
1
test/data/compare/20_int/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(IntOption('int', 'integer'))
|
2
test/data/compare/20_string/argparse.py
Normal file
2
test/data/compare/20_string/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('--foo', help='foo help')
|
||||||
|
|
2
test/data/compare/20_string/tiramisu.py
Normal file
2
test/data/compare/20_string/tiramisu.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_arguments(StrOption('foo', 'foo help'))
|
||||||
|
|
2
test/data/compare/20_string_list/argparse.py
Normal file
2
test/data/compare/20_string_list/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('--foo', help='foo help', nargs='*')
|
||||||
|
|
2
test/data/compare/20_string_list/tiramisu.py
Normal file
2
test/data/compare/20_string_list/tiramisu.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_arguments(StrOption('foo', 'foo help', multi=True))
|
||||||
|
|
2
test/data/compare/30_choice_int/argparse.py
Normal file
2
test/data/compare/30_choice_int/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('--door', help='Door numbers', choices=[1, 2, 3])
|
||||||
|
|
1
test/data/compare/30_choice_int/tiramisu.py
Normal file
1
test/data/compare/30_choice_int/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments(ChoiceOption('door', 'Door numbers', (1, 2, 3)))
|
2
test/data/compare/30_string_default/argparse.py
Normal file
2
test/data/compare/30_string_default/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('--foo', help='foo help', default='default', nargs='?')
|
||||||
|
|
2
test/data/compare/30_string_default/tiramisu.py
Normal file
2
test/data/compare/30_string_default/tiramisu.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_arguments(StrOption('foo', 'foo help', 'default'))
|
||||||
|
|
1
test/data/compare/30_string_short/argparse.py
Normal file
1
test/data/compare/30_string_short/argparse.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_argument('-f', '--foo', help='foo help')
|
3
test/data/compare/30_string_short/tiramisu.py
Normal file
3
test/data/compare/30_string_short/tiramisu.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
str_long = StrOption('foo', 'foo help')
|
||||||
|
str_short = SymLinkOption('f', str_long)
|
||||||
|
parser.add_arguments([str_long, str_short])
|
2
test/data/compare/40_multi_bool/argparse.py
Normal file
2
test/data/compare/40_multi_bool/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('-v', help='increase output verbosity', action='store_true')
|
||||||
|
parser.add_argument('-s', help='second argument', action='store_true')
|
1
test/data/compare/40_multi_bool/tiramisu.py
Normal file
1
test/data/compare/40_multi_bool/tiramisu.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
parser.add_arguments([BoolOption('v', 'increase output verbosity', default=False), BoolOption('s', 'second argument', default=False)])
|
3
test/data/compare/40_positional_optional/argparse.py
Normal file
3
test/data/compare/40_positional_optional/argparse.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
parser.add_argument("echo", help="echo the string you use here")
|
||||||
|
parser.add_argument('--verbosity', help='increase output verbosity', action='store_true')
|
||||||
|
|
3
test/data/compare/40_positional_optional/tiramisu.py
Normal file
3
test/data/compare/40_positional_optional/tiramisu.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
parser.add_arguments([StrOption('echo', 'echo the string you use here', properties=('mandatory', 'positional')),
|
||||||
|
BoolOption('verbosity', 'increase output verbosity', default=False)])
|
||||||
|
|
2
test/data/compare/40_short_long/argparse.py
Normal file
2
test/data/compare/40_short_long/argparse.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
parser.add_argument('-v', help='increase output verbosity', action='store_true')
|
||||||
|
parser.add_argument('-i', '--int', help='integer', type=int)
|
4
test/data/compare/40_short_long/tiramisu.py
Normal file
4
test/data/compare/40_short_long/tiramisu.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
int_long = IntOption('int', 'integer')
|
||||||
|
parser.add_arguments([BoolOption('v', 'increase output verbosity', default=False),
|
||||||
|
int_long,
|
||||||
|
SymLinkOption('i', int_long)])
|
145
test/test_simple.py
Normal file
145
test/test_simple.py
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
from tiramisu import StrOption, BoolOption, IntOption, ChoiceOption, OptionDescription, SymLinkOption
|
||||||
|
from py.test import raises, fixture
|
||||||
|
from io import StringIO
|
||||||
|
import sys
|
||||||
|
from os import listdir
|
||||||
|
from os.path import join, isdir
|
||||||
|
from contextlib import redirect_stdout, redirect_stderr
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
#from pouet import TiramisuParser
|
||||||
|
from tiramisu_parser import TiramisuParser
|
||||||
|
|
||||||
|
|
||||||
|
DATA_DIR = 'test/data/compare'
|
||||||
|
TEST_DIRS = []
|
||||||
|
|
||||||
|
|
||||||
|
for test in listdir(DATA_DIR):
|
||||||
|
test_file = join(DATA_DIR, test)
|
||||||
|
if isdir(test_file):
|
||||||
|
TEST_DIRS.append(test_file)
|
||||||
|
|
||||||
|
TEST_DIRS.sort()
|
||||||
|
# TEST_DIRS.remove('test/data/compare/10_positional_list')
|
||||||
|
# TEST_DIRS = ['test/data/compare/50_conditional_disable']
|
||||||
|
|
||||||
|
|
||||||
|
@fixture(scope="module", params=TEST_DIRS)
|
||||||
|
def test_list(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
|
def import_subfile_and_test(filename, parser, arg):
|
||||||
|
parser_dict = []
|
||||||
|
parser_system_err = []
|
||||||
|
f = StringIO()
|
||||||
|
with redirect_stderr(f):
|
||||||
|
exec(open(filename).read())
|
||||||
|
# print('arg', arg)
|
||||||
|
try:
|
||||||
|
parser_dict.append(parser.parse_args(arg).__dict__)
|
||||||
|
except SystemExit as err:
|
||||||
|
parser_system_err.append(str(err))
|
||||||
|
else:
|
||||||
|
parser_system_err.append(None)
|
||||||
|
parser_error = f.getvalue()
|
||||||
|
f = StringIO()
|
||||||
|
with redirect_stdout(f):
|
||||||
|
parser.print_help()
|
||||||
|
parser_help = f.getvalue()
|
||||||
|
return parser_dict, parser_system_err, parser_error, parser_help
|
||||||
|
|
||||||
|
|
||||||
|
def test_files(test_list):
|
||||||
|
args = [[],
|
||||||
|
# 10_positional
|
||||||
|
['bar'], ['foo', 'bar'],
|
||||||
|
# 10_positional_int
|
||||||
|
['4'],
|
||||||
|
# 20_bool
|
||||||
|
['--verbosity'], ['--verbosity', 'arg'],
|
||||||
|
# 20_string
|
||||||
|
['--foo'], ['--foo', '--bar'], ['--foo', 'a'],
|
||||||
|
['--foo', 'a', '--foo', 'b'],
|
||||||
|
# 20_int
|
||||||
|
['--int', '3'], ['--int', 'a'],
|
||||||
|
# 20 choice
|
||||||
|
['--door', 'a'], ['--door', '1'],
|
||||||
|
# 30_string_short
|
||||||
|
['-f', 'b'], ['--foo', 'c', '-f', 'b'],
|
||||||
|
# 40 multi_bool
|
||||||
|
['-v'], ['-v', '-s'], ['-vs'],
|
||||||
|
# 40_short_long
|
||||||
|
['-v', '--foo', '1'], ['-vf', '2'], ['-vf'], ['-vf', '-v'],
|
||||||
|
# 40_positional_optional
|
||||||
|
['bar', '--verbosity'], ['--verbosity', 'bar'],
|
||||||
|
]
|
||||||
|
for arg in args:
|
||||||
|
tiramparser = TiramisuParser('prog.py')
|
||||||
|
tiramparser_dict, tiramparser_system_err, tiramparser_error, tiramparser_help = import_subfile_and_test(test_list + '/tiramisu.py',
|
||||||
|
tiramparser, arg)
|
||||||
|
#
|
||||||
|
argparser = ArgumentParser('prog.py')
|
||||||
|
argparser_dict, argparser_system_err, argparser_error, argparser_help = import_subfile_and_test(test_list + '/argparse.py',
|
||||||
|
argparser, arg)
|
||||||
|
#print(tiramparser_dict)
|
||||||
|
#print(tiramparser_system_err)
|
||||||
|
#print(tiramparser_error)
|
||||||
|
#print(tiramparser_help)
|
||||||
|
#print('-----')
|
||||||
|
#print(argparser_dict)
|
||||||
|
#print(argparser_system_err)
|
||||||
|
#print(argparser_error)
|
||||||
|
#print(argparser_help)
|
||||||
|
assert tiramparser_dict == argparser_dict
|
||||||
|
assert tiramparser_error == argparser_error
|
||||||
|
assert tiramparser_help == argparser_help
|
||||||
|
assert tiramparser_system_err == argparser_system_err
|
||||||
|
|
||||||
|
|
||||||
|
#FIXME --verbose sans --quiet
|
||||||
|
#parser = argparse.ArgumentParser(description="calculate X to the power of Y")
|
||||||
|
#group = parser.add_mutually_exclusive_group()
|
||||||
|
#group.add_argument("-v", "--verbose", action="store_true")
|
||||||
|
#group.add_argument("-q", "--quiet", action="store_true")
|
||||||
|
#parser.add_argument("x", type=int, help="the base")
|
||||||
|
#parser.add_argument("y", type=int, help="the exponent")
|
||||||
|
#args = parser.parse_args()
|
||||||
|
#answer = args.x**args.y
|
||||||
|
|
||||||
|
|
||||||
|
#FIXME --sum ?
|
||||||
|
#parser = argparse.ArgumentParser(description='Process some integers.')
|
||||||
|
#parser.add_argument('integers', metavar='N', type=int, nargs='+',
|
||||||
|
# help='an integer for the accumulator')
|
||||||
|
#parser.add_argument('--sum', dest='accumulate', action='store_const',
|
||||||
|
# const=sum, default=max,
|
||||||
|
# help='sum the integers (default: find the max)')
|
||||||
|
#args = parser.parse_args()
|
||||||
|
#print(args.accumulate(args.integers))
|
||||||
|
|
||||||
|
|
||||||
|
# +++++++++++++++++++++++++++++ nargs
|
||||||
|
#FIXME longueur fixe
|
||||||
|
#>>> parser = argparse.ArgumentParser()
|
||||||
|
#>>> parser.add_argument('--foo', nargs=2)
|
||||||
|
#>>> parser.add_argument('bar', nargs=1)
|
||||||
|
#>>> parser.parse_args('c --foo a b'.split())
|
||||||
|
#Namespace(bar=['c'], foo=['a', 'b'])
|
||||||
|
|
||||||
|
|
||||||
|
#FIXME const
|
||||||
|
#>>> parser = argparse.ArgumentParser()
|
||||||
|
#>>> parser.add_argument('--foo', nargs='?', const='c', default='d')
|
||||||
|
#>>> parser.add_argument('bar', nargs='?', default='d')
|
||||||
|
#>>> parser.parse_args(['XX', '--foo', 'YY'])
|
||||||
|
#Namespace(bar='XX', foo='YY')
|
||||||
|
#>>> parser.parse_args(['XX', '--foo'])
|
||||||
|
#Namespace(bar='XX', foo='c')
|
||||||
|
#>>> parser.parse_args([])
|
||||||
|
#Namespace(bar='d', foo='d')
|
||||||
|
|
||||||
|
#FIXME ? | * | +
|
||||||
|
# * == list
|
||||||
|
# + == list + mandatory
|
149
tiramisu_parser.py
Normal file
149
tiramisu_parser.py
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
from typing import Union, List
|
||||||
|
from argparse import ArgumentParser, Namespace, SUPPRESS
|
||||||
|
from tiramisu import Option, OptionDescription, Config, BoolOption, StrOption, IntOption, \
|
||||||
|
ChoiceOption, SymLinkOption
|
||||||
|
from tiramisu.error import PropertiesOptionError
|
||||||
|
|
||||||
|
|
||||||
|
class TiramisuNamespace(Namespace):
|
||||||
|
def _populate(self):
|
||||||
|
for tiramisu_key, tiramisu_value in self._config.value.dict().items():
|
||||||
|
option = self._config.option(tiramisu_key)
|
||||||
|
if not isinstance(option.option.get(), SymLinkOption):
|
||||||
|
if tiramisu_value == [] and option.option.ismulti() and option.owner.isdefault():
|
||||||
|
tiramisu_value = None
|
||||||
|
super().__setattr__(tiramisu_key, tiramisu_value)
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
self._config = config
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if key == '_config':
|
||||||
|
super().__setattr__(key, value)
|
||||||
|
return
|
||||||
|
self._config.property.read_write()
|
||||||
|
option = self._config.option(key)
|
||||||
|
if option.option.ismulti() and value is not None and not isinstance(value, list):
|
||||||
|
value = [value]
|
||||||
|
option.value.set(value)
|
||||||
|
|
||||||
|
def __getattribute__(self, key):
|
||||||
|
if key == '__dict__' and hasattr(self, '_config'):
|
||||||
|
self._config.property.read_only()
|
||||||
|
self._populate()
|
||||||
|
self._config.property.read_write()
|
||||||
|
return super().__getattribute__(key)
|
||||||
|
|
||||||
|
|
||||||
|
class TiramisuParser(ArgumentParser):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.config = None
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _match_arguments_partial(self, actions, arg_string_pattern):
|
||||||
|
# used only when check first proposal for first value
|
||||||
|
# we have to remove all actions with propertieserror
|
||||||
|
# so only first settable option will be returned
|
||||||
|
actions_pop = []
|
||||||
|
for idx, action in enumerate(actions):
|
||||||
|
if self.config.unrestraint.option(action.dest).property.get(only_raises=True):
|
||||||
|
actions_pop.append(idx)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
for idx in actions_pop:
|
||||||
|
actions.pop(0)
|
||||||
|
return super()._match_arguments_partial(actions, arg_string_pattern)
|
||||||
|
|
||||||
|
def add_argument(self, *args, **kwargs):
|
||||||
|
if args == ('-h', '--help'):
|
||||||
|
super().add_argument(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError('do not use add_argument')
|
||||||
|
|
||||||
|
def add_arguments(self, tiramisu: Union[Config, Option, List[Option], OptionDescription]) -> None:
|
||||||
|
if not isinstance(tiramisu, Config):
|
||||||
|
if not isinstance(tiramisu, OptionDescription):
|
||||||
|
if isinstance(tiramisu, Option):
|
||||||
|
tiramisu = [tiramisu]
|
||||||
|
tiramisu = OptionDescription('root', 'root', tiramisu)
|
||||||
|
tiramisu = Config(tiramisu)
|
||||||
|
self.config = tiramisu
|
||||||
|
actions = {}
|
||||||
|
for obj in tiramisu.unrestraint.option.list():
|
||||||
|
if obj.option.properties(only_raises=True) or 'frozen' in obj.option.properties():
|
||||||
|
continue
|
||||||
|
option = obj.option
|
||||||
|
tiramisu_option = option.get()
|
||||||
|
name = option.name()
|
||||||
|
if name.startswith(self.prefix_chars):
|
||||||
|
raise ValueError('name cannot startswith "{}"'.format(self.prefix_chars))
|
||||||
|
properties = obj.property.get()
|
||||||
|
kwargs = {'help': option.doc(),
|
||||||
|
'default': SUPPRESS}
|
||||||
|
if 'positional' in properties:
|
||||||
|
#if not 'mandatory' in properties:
|
||||||
|
# raise ValueError('"positional" argument must be "mandatory" too')
|
||||||
|
args = [name]
|
||||||
|
if option.requires():
|
||||||
|
kwargs['nargs'] = '?'
|
||||||
|
else:
|
||||||
|
if len(name) == 1 and 'longargument' not in properties:
|
||||||
|
args = [self.prefix_chars + name]
|
||||||
|
else:
|
||||||
|
args = [self.prefix_chars * 2 + name]
|
||||||
|
if 'mandatory' in properties:
|
||||||
|
kwargs['required'] = True
|
||||||
|
if isinstance(tiramisu_option, BoolOption):
|
||||||
|
if 'mandatory' in properties:
|
||||||
|
raise ValueError('"mandatory" property is not allowed for BoolOption')
|
||||||
|
#if not isinstance(option.default(), bool):
|
||||||
|
# raise ValueError('default value is mandatory for BoolOption')
|
||||||
|
if option.default() is False:
|
||||||
|
action = 'store_true'
|
||||||
|
else:
|
||||||
|
action = 'store_false'
|
||||||
|
kwargs['action'] = action
|
||||||
|
else:
|
||||||
|
if option.default() not in [None, []]:
|
||||||
|
#kwargs['default'] = kwargs['const'] = option.default()
|
||||||
|
#kwargs['action'] = 'store_const'
|
||||||
|
kwargs['nargs'] = '?'
|
||||||
|
if option.ismulti():
|
||||||
|
if 'mandatory' in properties:
|
||||||
|
kwargs['nargs'] = '+'
|
||||||
|
else:
|
||||||
|
kwargs['nargs'] = '*'
|
||||||
|
if isinstance(tiramisu_option, StrOption):
|
||||||
|
pass
|
||||||
|
elif isinstance(tiramisu_option, IntOption):
|
||||||
|
kwargs['type'] = int
|
||||||
|
elif isinstance(tiramisu_option, SymLinkOption):
|
||||||
|
tiramisu_option = tiramisu_option.impl_getopt()
|
||||||
|
actions[tiramisu_option.impl_getname()][0].insert(0, args[0])
|
||||||
|
continue
|
||||||
|
elif isinstance(tiramisu_option, ChoiceOption):
|
||||||
|
kwargs['choices'] = obj.value.list()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
#raise NotImplementedError('not supported yet')
|
||||||
|
actions[option.name()] = (args, kwargs)
|
||||||
|
for args, kwargs in actions.values():
|
||||||
|
super().add_argument(*args, **kwargs)
|
||||||
|
|
||||||
|
def parse_args(self, *args, **kwargs):
|
||||||
|
kwargs['namespace'] = TiramisuNamespace(self.config)
|
||||||
|
try:
|
||||||
|
namespaces = super().parse_args(*args, **kwargs)
|
||||||
|
except PropertiesOptionError as err:
|
||||||
|
# import traceback
|
||||||
|
# traceback.print_exc()
|
||||||
|
if err.proptype == ('mandatory',):
|
||||||
|
self.error('the following arguments are required: {}'.format(err._option_bag.option.impl_getname()))
|
||||||
|
else:
|
||||||
|
self.error('unexpected error: {}'.format(err))
|
||||||
|
del namespaces.__dict__['_config']
|
||||||
|
return namespaces
|
||||||
|
|
||||||
|
def get_config(self):
|
||||||
|
return self.config
|
Loading…
Reference in a new issue