Skip to content

Commit

Permalink
Fixed bug in handle_py3_kw_only_args
Browse files Browse the repository at this point in the history
- Fixed a few bugs in initial implementation, added unit test.
- Fixed some very minor doc issue.
  • Loading branch information
lowell80 committed Mar 21, 2021
1 parent 7507a19 commit acd1425
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -11,6 +11,7 @@ zdist/
build.cache
.release_name
.release_path
docs/source/_build/

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down Expand Up @@ -113,4 +114,3 @@ ENV/

# mypy
.mypy_cache/
zdist/
2 changes: 1 addition & 1 deletion ksconf/builder/__init__.py
Expand Up @@ -90,7 +90,7 @@ def run(self, executable, *args, **kw_only):
The process will run withing the working directory of the build folder.
:param str executable: Executable to launch for a build step.
:param str *args: Additional arguments for the new process.
:param str args: Additional argument(s) for the new process.
:param str cwd: Optional kw arg to change the working directory. This
defaults to the build folder.
"""
Expand Down
26 changes: 18 additions & 8 deletions ksconf/util/__init__.py
Expand Up @@ -65,32 +65,42 @@ def debug_traceback(): # pragma: no cover
traceback.print_exc(level)


def handle_py3_kw_only_args(kw, *args):
def handle_py3_kw_only_args(kw, *default_args):
""" Python 2.7 workaround for Python 3 style kw arg after '*' arg.
Example Python 3 syntax:
.. code-block:: py
def f(arg, *args, a=True, b=False): ...
def f(arg, *args, a=True, b=False):
...
Example Python 2 syntax:
Example Python 2 syntax with this helper function:
.. code-block:: py
def f(arg, *args, **kw_only):
a, b = handle_py3_kw_only_args(kw_only, ("a", True), ("b", False))
...
:param dict kw: keyword arguments provided to the calling function. Be aware
that this dict will be empty after this function is done.
:param tuple default_args: pairs of keyword argument to the caller function
in argument (arg_name, default_value) order.
:raises TypeError: if ``kw`` contains any keys not defined in ``args``
This mirrors Python's native behavior when an unexpected
argument is passed to a function.
"""
out = []
for arg_name, arg_default in args:
if arg_name in kw:
for arg_name, arg_default in default_args:
try:
out.append(kw.pop(arg_name))
else:
except KeyError:
out.append(arg_default)
if args:
if kw:
import inspect
caller = inspect.currentframe().f_back.f_code.co_name
# Should all unexpected args be reported? feels like this good enough
raise TypeError("{} got an unexpected keyword argument '{}'"
.format(caller, args.keys()[0]))
.format(caller, list(kw)[0]))
return out
13 changes: 13 additions & 0 deletions tests/test_util.py
Expand Up @@ -24,6 +24,19 @@ def test_bwlist(self):
self.assertTrue(match_bwlist("hotdog", bwlist))
self.assertTrue(match_bwlist("bake", bwlist))

def test_handle_p3koa(self):
from ksconf.util import handle_py3_kw_only_args
kw = {"a": 1, "b": 2}
a, b, c = handle_py3_kw_only_args(kw, ("a", None), ("b", 3), ("c", 99))
self.assertEqual(a, 1)
self.assertEqual(b, 2)
self.assertEqual(c, 99)

# Should raise 'unexpected argument' here because 'a' is not defined
kw = {a: 1}
with self.assertRaises(TypeError):
c = handle_py3_kw_only_args(kw, ("c", 99))


class KsconfMiscIternalsTest(unittest.TestCase):

Expand Down

0 comments on commit acd1425

Please sign in to comment.