Skip to content

Commit

Permalink
feat: allow @staticmethod to be used with @corgyparser
Browse files Browse the repository at this point in the history
  • Loading branch information
jayanthkoushik committed Nov 25, 2021
1 parent 1532b15 commit 17c2115
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 8 deletions.
9 changes: 9 additions & 0 deletions corgy/_corgy.py
Expand Up @@ -598,6 +598,9 @@ def corgyparser(var_name: str) -> Callable[[Callable[[str], Any]], _CorgyParser]
This decorator is only available on Python 3.9 or higher.
To use a custom function for parsing an argument with `Corgy`, use this decorator.
Parsing functions must be static, and should only accept a single string argument.
Decorating the function with `@staticmethod` is optional, but prevents type errors.
`@corgyparser` must be the final decorator in the decorator chain.
Args:
var_name: The argument associated with the decorated parser.
Expand All @@ -607,8 +610,10 @@ def corgyparser(var_name: str) -> Callable[[Callable[[str], Any]], _CorgyParser]
class A(Corgy):
time: tuple[int, int, int]
@corgyparser("time")
@staticmethod
def parse_time(s):
return tuple(map(int, s.split(":")))
"""
if not isinstance(var_name, str):
raise TypeError(
Expand All @@ -617,6 +622,10 @@ def parse_time(s):
)

def wrapper(var_name, fparse):
if isinstance(fparse, staticmethod):
fparse = fparse.__func__
if not callable(fparse):
raise TypeError("corgyparser can only decorate static functions")
return _CorgyParser(var_name, fparse)

return partial(wrapper, var_name)
4 changes: 4 additions & 0 deletions docs/corgy.md
Expand Up @@ -277,6 +277,9 @@ Decorate a function as a custom parser for a variable.
**NOTE**: This decorator is only available on Python 3.9 or higher.

To use a custom function for parsing an argument with `Corgy`, use this decorator.
Parsing functions must be static, and should only accept a single string argument.
Decorating the function with `@staticmethod` is optional, but prevents type errors.
`@corgyparser` must be the final decorator in the decorator chain.


* **Parameters**
Expand All @@ -290,6 +293,7 @@ Example:
class A(Corgy):
time: tuple[int, int, int]
@corgyparser("time")
@staticmethod
def parse_time(s):
return tuple(map(int, s.split(":")))
```
Expand Down
41 changes: 33 additions & 8 deletions tests/test_corgy.py
Expand Up @@ -723,8 +723,6 @@ class C(Corgy):
class TestCorgyCustomParsers(unittest.TestCase):
"""Tests to check usage of the @corgyparser decorator."""

# pylint: disable=no-self-argument, unused-variable

def test_corgyparser_raises_if_not_passed_name(self):
with self.assertRaises(TypeError):

Expand All @@ -735,19 +733,19 @@ def spam():
def test_corgy_raises_if_corgyparser_target_invalid(self):
with self.assertRaises(TypeError):

class A(Corgy):
class _(Corgy):
x: int

@corgyparser("y")
def parsex(s: str): # type: ignore
def parsex(s: str): # type: ignore # pylint: disable=no-self-argument
return 0

def test_add_args_handles_corgyparser(self):
class C(Corgy):
x: Annotated[int, "x"]

@corgyparser("x")
def parsex(s: str): # type: ignore
def parsex(s: str): # type: ignore # pylint: disable=no-self-argument
return 0

parser = argparse.ArgumentParser()
Expand All @@ -762,7 +760,7 @@ class C(Corgy):
x: int = 1

@corgyparser("x")
def parsex(s: str): # type: ignore
def parsex(s: str): # type: ignore # pylint: disable=no-self-argument
return 0

parser = argparse.ArgumentParser()
Expand All @@ -777,7 +775,7 @@ class C(Corgy):
x: int

@corgyparser("x")
def parsex(s: str): # type: ignore
def parsex(s: str): # type: ignore # pylint: disable=no-self-argument
return 0

getattr(C, "__parsers")["x"] = MagicMock()
Expand All @@ -793,7 +791,7 @@ class C(Corgy):
x: int

@corgyparser("x")
def parsex(s: str): # type: ignore
def parsex(s: str): # type: ignore # pylint: disable=no-self-argument
return -1

parser = argparse.ArgumentParser()
Expand All @@ -802,3 +800,30 @@ def parsex(s: str): # type: ignore

args = C.parse_from_cmdline(parser)
self.assertEqual(args.x, -1)

def test_corgyparser_allows_decorating_staticmethod(self):
class C(Corgy):
x: int

@corgyparser("x")
@staticmethod
def parsex(s: str):
return 0

parser = argparse.ArgumentParser()
orig_parse_args = argparse.ArgumentParser.parse_args
parser.parse_args = lambda: orig_parse_args(parser, ["--x", "test"])

c = C.parse_from_cmdline(parser)
self.assertEqual(c.x, 0)

def test_corgyparser_raises_if_decorating_non_staticmethod(self):
with self.assertRaises(TypeError):

class _(Corgy):
x: int

@corgyparser("x")
@classmethod
def parsex(cls, s: str):
return 0

0 comments on commit 17c2115

Please sign in to comment.