Skip to content

Commit

Permalink
Added context manager support.
Browse files Browse the repository at this point in the history
  • Loading branch information
epsy committed Feb 19, 2015
1 parent 615482d commit 0a294ad
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 23 deletions.
18 changes: 13 additions & 5 deletions clize/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,13 @@ def coerce_value(self, arg, ba):
`ValueError`.
"""
try:
return self.typ(arg)
ret = self.typ(arg)
try:
type(ret).__enter__
except AttributeError:
return ret
else:
return ba.exitstack.enter_context(ret)
except ValueError as e:
exc = errors.BadArgumentFormat(self.typ, arg)
exc.__cause__ = e
Expand Down Expand Up @@ -822,14 +828,14 @@ def convert_parameter(cls, param):



def read_arguments(self, args, name='unnamed'):
def read_arguments(self, args, name, exitstack):
"""Returns a `.CliBoundArguments` instance for this CLI signature
bound to the given arguments.
:param sequence args: The CLI arguments, minus the script name.
:param str name: The script name.
"""
return CliBoundArguments(self, args, name)
return CliBoundArguments(self, args, name, exitstack)

def __str__(self):
return ' '.join(
Expand Down Expand Up @@ -938,7 +944,7 @@ class CliBoundArguments(object):
"""


def __init__(self, sig, args, name):
def __init__(self, sig, args, name, exitstack):
self.sig = sig
self.name = name
self.in_args = tuple(args)
Expand All @@ -947,6 +953,7 @@ def __init__(self, sig, args, name):
self.args = []
self.kwargs = {}
self.meta = {}
self.exitstack = exitstack

self.sticky = None
self.posarg_only = False
Expand All @@ -968,7 +975,8 @@ def __init__(self, sig, args, name):
try:
param = next(posparam)
except StopIteration:
exc = errors.TooManyArguments(self.in_args[i:])
exc = errors.TooManyArguments(
self.in_args[i:])
exc.__cause__ = None
raise exc
elif arg == '--':
Expand Down
11 changes: 7 additions & 4 deletions clize/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import itertools
import shutil

import contextlib2
from sigtools.modifiers import annotate, autokwoargs
from sigtools.specifiers import forwards_to_method, signature
from sigtools.signatures import mask
Expand Down Expand Up @@ -196,18 +197,20 @@ def _process_alt(self, alt):
yield param

def __call__(self, *args):
with errors.SetUserErrorContext(cli=self, pname=args[0]):
func, name, posargs, kwargs = self.read_commandline(args)
with contextlib2.ExitStack() as stack:
stack.enter_context(
errors.SetUserErrorContext(cli=self, pname=args[0]))
func, name, posargs, kwargs = self.read_commandline(args, stack)
return func(*posargs, **kwargs)

def read_commandline(self, args):
def read_commandline(self, args, stack):
"""Reads the command-line arguments from args and returns a tuple
with the callable to run, the name of the program, the positional
and named arguments to pass to the callable.
:raises: `.ArgumentError`
"""
ba = self.signature.read_arguments(args[1:], args[0])
ba = self.signature.read_arguments(args[1:], args[0], stack)
func, post, posargs, kwargs = ba
name = ' '.join([args[0]] + post)
if func or self.pass_name:
Expand Down
15 changes: 8 additions & 7 deletions clize/tests/extra/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def annotated_sigtests(self, sig_info, in_args, args, kwargs):
sig_str, annotation, str_rep = sig_info
sig = support.s(sig_str, locals={'a': annotation})
csig = parser.CliSignature.from_signature(sig)
ba = csig.read_arguments(in_args)
ba = util.read_arguments(csig, in_args)
self.assertEqual(ba.args, args)
self.assertEqual(ba.kwargs, kwargs)

Expand All @@ -47,7 +47,7 @@ def annotated_sigerror_tests(self, sig_info, in_args,
sig_str, annotation, str_rep = sig_info
sig = support.s(sig_str, locals={'a': annotation})
csig = parser.CliSignature.from_signature(sig)
self.assertRaises(exc, csig.read_arguments, in_args)
self.assertRaises(exc, util.read_arguments, csig, in_args)


@check_repr
Expand Down Expand Up @@ -216,7 +216,7 @@ class MappedTests(object):
def test_show_list(self):
sig = support.s('par:a', locals={'a': RepTests.mapped_basic[1]})
csig = parser.CliSignature.from_signature(sig)
ba = csig.read_arguments(['list'])
ba = util.read_arguments(csig, ['list'])
par = csig.positional[0]
self.assertEqual(par.show_list, ba.func)
self.assertEqual(
Expand All @@ -229,7 +229,7 @@ def test_show_list_alt(self):
sig = support.s('par:a',
locals={'a': RepTests.mapped_alternate_list[1]})
csig = parser.CliSignature.from_signature(sig)
ba = csig.read_arguments(['options'])
ba = util.read_arguments(csig, ['options'])
par = csig.positional[0]
self.assertEqual(par.show_list, ba.func)
self.assertEqual(
Expand Down Expand Up @@ -296,7 +296,7 @@ class OneOfTests(object):
def test_show_list(self):
sig = support.s('par:a', locals={'a': RepTests.oneof_help[1]})
csig = parser.CliSignature.from_signature(sig)
ba = csig.read_arguments(['list'])
ba = util.read_arguments(csig, ['list'])
par = csig.positional[0]
self.assertEqual(par.show_list, ba.func)
self.assertEqual(
Expand Down Expand Up @@ -369,12 +369,13 @@ def test_message(self):
csig = parser.CliSignature.from_signature(sig)

try:
csig.read_arguments(('--par=1',))
util.read_arguments(csig, ('--par=1',))
except errors.NotEnoughValues as e:
self.assertEqual(e.message, "Received too few values for --par")

try:
csig.read_arguments(('--par=1', '--par=2', '--par=3', '--par=4'))
util.read_arguments(csig,
('--par=1', '--par=2', '--par=3', '--par=4'))
except errors.TooManyValues as e:
self.assertEqual(e.message, "Received too many values for --par")

Expand Down
10 changes: 5 additions & 5 deletions clize/tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from sigtools import support, modifiers, specifiers

from clize import parser, errors, util
from clize.tests.util import testfunc
from clize.tests.util import testfunc, read_arguments


@testfunc
Expand Down Expand Up @@ -120,7 +120,7 @@ def noop_converter(param, annotations):
def signaturetests(self, sig_str, str_rep, args, posargs, kwargs):
sig = support.s(sig_str, locals={'P': parser.Parameter})
csig = parser.CliSignature.from_signature(sig)
ba = csig.read_arguments(args)
ba = read_arguments(csig, args)
self.assertEqual(str(csig), str_rep)
self.assertEqual(ba.args, posargs)
self.assertEqual(ba.kwargs, kwargs)
Expand Down Expand Up @@ -221,7 +221,7 @@ def conv(param, annotations):
def extraparamstests(self, sig_str, extra, args, posargs, kwargs, func):
sig = support.s(sig_str)
csig = parser.CliSignature.from_signature(sig, extra=extra)
ba = csig.read_arguments(args)
ba = read_arguments(csig, args)
self.assertEqual(ba.args, posargs)
self.assertEqual(ba.kwargs, kwargs)
self.assertEqual(ba.func, func)
Expand Down Expand Up @@ -285,7 +285,7 @@ def sigerrortests(self, sig_str, args, exc_typ):
sig = support.s(sig_str)
csig = parser.CliSignature.from_signature(sig)
try:
csig.read_arguments(args)
read_arguments(csig, args)
except exc_typ:
pass
except: #pragma: no cover
Expand Down Expand Up @@ -315,7 +315,7 @@ def func(*args):
csig = parser.CliSignature.from_signature(
specifiers.signature(func))
try:
csig.read_arguments(())
read_arguments(csig, ())
except errors.MissingRequiredArguments:
pass
except: # pragma: no cover
Expand Down
22 changes: 21 additions & 1 deletion clize/tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import shutil
import unittest

from sigtools import modifiers
from six.moves import cStringIO

from clize.tests import util
from clize import runner, errors

Expand Down Expand Up @@ -132,7 +134,7 @@ def func4(): pass
try:
runner.Clize.get_cli(
iter([iter([func1, func2]), iter([func3, func4])]))
except ValueError as e:
except ValueError:
pass
else:
self.fail("AttributeError not raised")
Expand Down Expand Up @@ -368,3 +370,21 @@ def func1(): return '1'
self.assertTrue(stderr.getvalue())
self.assertFalse(stdout.getvalue())

def test_close(self):
class Mgr(object):
def __init__(self):
self.closed = False

def __enter__(self):
pass

def __exit__(self, typ, val, tb):
self.closed = True
m = Mgr()
def coerc(arg):
return m
@modifiers.annotate(arg=coerc)
def func(arg):
pass
runner.run(func, args=['test', 'arg'], exit=False)
self.assertTrue(m.closed)
7 changes: 7 additions & 0 deletions clize/tests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from functools import partial
import unittest

import contextlib2

class Tests(unittest.TestCase):
def assertRaises(self, _exc, _func, *args, **kwargs):
try:
Expand Down Expand Up @@ -38,3 +40,8 @@ def testfunc(test_func):
return partial(build_sigtests, test_func)

repeated_test = partial(build_sigtests, None)


def read_arguments(sig, args):
with contextlib2.ExitStack() as stack:
return sig.read_arguments(args, 'test', stack)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
url='https://github.com/epsy/clize',
author='Yann Kaiser',
author_email='kaiser.yann@gmail.com',
install_requires=['six', 'sigtools >= 0.1b2'],
install_requires=['six', 'sigtools >= 0.1b2', 'contextlib2'],
extras_require={
':python_version in "2.6"': ['ordereddict'],
},
Expand Down

0 comments on commit 0a294ad

Please sign in to comment.