Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for ignoring args #115

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ __pycache__
.eggs
.coverage
dist
build
3 changes: 2 additions & 1 deletion tap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
from tap._version import __version__
from tap.tap import Tap
from tap.tapify import tapify
from tap.utils import TapIgnore

__all__ = ['ArgumentError', 'ArgumentTypeError', 'Tap', 'tapify', '__version__']
__all__ = ['ArgumentError', 'ArgumentTypeError', 'Tap', 'TapIgnore', 'tapify', '__version__']
14 changes: 12 additions & 2 deletions tap/tap.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
as_python_object,
fix_py36_copy,
enforce_reproducibility,
PathLike
PathLike,
TapIgnore,
)

if sys.version_info >= (3, 10):
Expand Down Expand Up @@ -169,13 +170,22 @@ def _add_argument(self, *name_or_flags, **kwargs) -> None:

# Description
if variable in self.class_variables:
kwargs['help'] += ' ' + self.class_variables[variable]['comment']
comment = self.class_variables[variable]['comment']
# Ignore attributes with comment starting with "tap: ignore"
if comment.startswith('tap: ignore'):
return
kwargs['help'] += ' ' + comment

# Set other kwargs where not provided
if variable in self._annotations:
# Get type annotation
var_type = self._annotations[variable]

# ignore the variables annotated by TapIgnore
# e.g., var_ignore: TapIgnore[str]
if get_origin(var_type) == TapIgnore:
return

# If type is not explicitly provided, set it if it's one of our supported default types
if 'type' not in kwargs:
# Unbox Union[type] (Optional[type]) and set var_type = type
Expand Down
17 changes: 16 additions & 1 deletion tap/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@
PRIMITIVES = (str, int, float, bool)
PathLike = Union[str, os.PathLike]

# use some hacks to implement the hint in IDE, like ClassVar
if sys.version_info >= (3, 9):
from typing import ClassVar
TapIgnore = ClassVar
TapIgnore._name = "TapIgnore"
else:
# for python version <= 3.8, the above method not supported,
# so for now, TapIgnore[T] -> T not supported
from typing import TypeVar, Generic
T = TypeVar("T")
class TapIgnore(Generic[T]): ...

def check_output(command: List[str], suppress_stderr: bool = True, **kwargs) -> str:
"""Runs subprocess.check_output and returns the result as a string.
Expand Down Expand Up @@ -129,7 +140,7 @@ def type_to_str(type_annotation: Union[type, Any]) -> str:
return type_annotation.__name__

# Typing type
return str(type_annotation).replace('typing.', '')
return str(type_annotation).replace('typing.', '').replace('tap.utils.', '')


def get_argument_name(*name_or_flags) -> str:
Expand Down Expand Up @@ -497,6 +508,10 @@ def get_origin(tp: Any) -> Any:
if origin is None:
origin = tp

# fix typing_inspect not supporting TapIgnore
if hasattr(tp, '__origin__') and tp.__origin__ == TapIgnore:
origin = TapIgnore

if sys.version_info >= (3, 10) and isinstance(origin, UnionType):
origin = UnionType

Expand Down
23 changes: 21 additions & 2 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import pickle
import sys
from tempfile import TemporaryDirectory
from typing import Any, Iterable, List, Literal, Optional, Set, Tuple, Union
from typing import Any, Dict, Iterable, List, Literal, Optional, Set, Tuple, Union
import unittest
from unittest import TestCase

from tap import Tap
from tap import Tap, TapIgnore


# Suppress prints from SystemExit
Expand Down Expand Up @@ -330,6 +330,14 @@ class IntegrationDefaultTap(Tap):
# TODO: move these elsewhere since we don't support them as defaults
# arg_other_type_required: Person
# arg_other_type_default: Person = Person('tap')
arg_ignore: str # tap: ignore
arg_tapignore: TapIgnore
arg_tapignore_int: TapIgnore[int]
arg_tapignore_bool: TapIgnore[bool]
arg_tapignore_str: TapIgnore[str]
arg_tapignore_union: TapIgnore[Union[int, str]]
arg_tapignore_dict: TapIgnore[Dict[int, int]]
arg_tapignore_customed: TapIgnore[Person]


class SubclassTests(TestCase):
Expand Down Expand Up @@ -403,6 +411,17 @@ def test_get_default_args(self) -> None:
self.assertEqual(args.arg_tuple_bool, (True, True, True, False))
self.assertEqual(args.arg_tuple_multi, (1.2, 1, 'hi', True, 1.3))

# test for ignored args
self.assertFalse(hasattr(args, 'arg_ignore'))
self.assertFalse(hasattr(args, 'arg_tapignore'))
self.assertFalse(hasattr(args, 'arg_tapignore_int'))
self.assertFalse(hasattr(args, 'arg_tapignore_bool'))
self.assertFalse(hasattr(args, 'arg_tapignore_str'))
self.assertFalse(hasattr(args, 'arg_tapignore_union'))
self.assertFalse(hasattr(args, 'arg_tapignore_dict'))
self.assertFalse(hasattr(args, 'arg_tapignore_customed'))


def test_set_default_args(self) -> None:
arg_untyped = 'yes'
arg_str = 'goodbye'
Expand Down
3 changes: 3 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
UnpicklableObject,
as_python_object,
enforce_reproducibility,
TapIgnore,
)


Expand Down Expand Up @@ -134,6 +135,8 @@ def test_type_to_str(self) -> None:
self.assertEqual(type_to_str(Set[int]), 'Set[int]')
self.assertEqual(type_to_str(Dict[str, int]), 'Dict[str, int]')
self.assertEqual(type_to_str(Union[List[int], Dict[float, bool]]), 'Union[List[int], Dict[float, bool]]')
self.assertEqual(type_to_str(TapIgnore), "TapIgnore")
self.assertEqual(type_to_str(TapIgnore[int]), "TapIgnore[int]")


def class_decorator(cls):
Expand Down