Skip to content

Commit

Permalink
bug - Fix method descriptor bug (#9)
Browse files Browse the repository at this point in the history
* 🐛 - Fix method descriptor bug

* 🔖 - Bump up version to 1.0.2
  • Loading branch information
isac322 authored Oct 25, 2021
1 parent 51a589d commit e8dd2cc
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 79 deletions.
23 changes: 15 additions & 8 deletions flake8_force_keyword_arguments/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import inspect
from enum import Enum
from types import ModuleType
from typing import Iterable, Optional, Set
from typing import Any, Callable, Iterable, Optional, Set


class QualifierOption(str, Enum):
Expand Down Expand Up @@ -67,12 +67,13 @@ def _list_pos_only_callables(

def does_callable_have_poa_more_than(o: object, poa_threshold: int) -> bool:
"""POA: Positional Only Arguments"""
sig = get_invocation_signature(o)
if sig is None:
func = get_inspectable_function(o)
if func is None:
return False

sig = inspect.signature(func)
params = tuple(sig.parameters.values())
if len(params) > 0 and params[0].name in ('self', 'cls'):
if inspect.ismethoddescriptor(func) and len(params) > 0:
params = params[1:]

poa_count = 0
Expand All @@ -85,22 +86,28 @@ def does_callable_have_poa_more_than(o: object, poa_threshold: int) -> bool:
return poa_count >= poa_threshold


def get_invocation_signature(o: object) -> Optional[inspect.Signature]:
def get_inspectable_function(o: object) -> Optional[Callable[..., Any]]:
try:
return inspect.signature(o) # type: ignore[arg-type]
inspect.signature(o) # type: ignore[arg-type]
except (ValueError, TypeError):
pass
else:
return o # type: ignore

if inspect.isclass(o):
try:
return inspect.signature(o.__init__) # type: ignore[misc]
inspect.signature(o.__init__) # type: ignore[misc]
except (ValueError, TypeError):
return None
else:
return o.__init__ # type: ignore[no-any-return,misc]
elif callable(o):
try:
return inspect.signature(o.__call__)
inspect.signature(o.__call__)
except (ValueError, TypeError):
return None
else:
return o.__call__
return None


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "flake8-force-keyword-arguments"
version = "1.0.1"
version = "1.0.2"
description = "A flake8 extension that is looking for function calls and forces to use keyword arguments if there are more than X arguments"
authors = ["Viktor Chaptsev <viktor@chaptsev.ru>", "Byeonghoon Yoo <bh322yoo@gmail.com>"]
maintainers = ["Byeonghoon Yoo <bh322yoo@gmail.com>"]
Expand Down
87 changes: 17 additions & 70 deletions tests/test_util.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import ast
import inspect
import sys
from functools import partial
from textwrap import dedent
from typing import Set

import pytest
import sys

from flake8_force_keyword_arguments import util

Expand Down Expand Up @@ -33,79 +32,24 @@ def test_get_invocation_line(source_code: str, expected: str) -> None:


@pytest.mark.parametrize(
('obj', 'expected'),
('obj', 'expected_attribute_name'),
(
(1, None),
('qwr', None),
(
int,
inspect.Signature(
(
inspect.Parameter(
'self',
kind=inspect.Parameter.POSITIONAL_ONLY,
),
inspect.Parameter(
'args',
kind=inspect.Parameter.VAR_POSITIONAL,
),
inspect.Parameter(
'kwargs',
kind=inspect.Parameter.VAR_KEYWORD,
),
)
),
),
(
getattr,
inspect.Signature(
(
inspect.Parameter(
'args',
kind=inspect.Parameter.VAR_POSITIONAL,
),
inspect.Parameter(
'kwargs',
kind=inspect.Parameter.VAR_KEYWORD,
),
)
),
),
(
type('TestClass', (), dict(__init__=lambda self, a, b: None)),
inspect.Signature(
(
inspect.Parameter(
'a',
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
),
inspect.Parameter(
'b',
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
),
)
),
),
(
type('TestClass', (), dict(__init__=lambda self: None)),
inspect.Signature(()),
),
(
lambda c: None,
inspect.Signature((inspect.Parameter('c', kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),)),
),
(
partial(lambda c, d: None, 1),
inspect.Signature((inspect.Parameter('d', kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),)),
),
(
type('TestClass', (), dict(__call__=lambda self, a: None))(),
inspect.Signature((inspect.Parameter('a', kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),)),
),
(int, '__init__'),
(getattr, '__call__'),
(type('TestClass', (), dict(__init__=lambda self, a, b: None)), '$self$'),
(lambda c: None, '$self$'),
(type('TestClass', (), dict(__call__=lambda self, a: None))(), '$self$'),
),
)
def test_get_invocation_signature(obj: object, expected: inspect.Signature) -> None:
assert util.get_invocation_signature(obj) == expected
def test_get_inspectable_function(obj, expected_attribute_name):
if expected_attribute_name == '$self$':
assert util.get_inspectable_function(obj) == obj
elif expected_attribute_name is None:
assert util.get_inspectable_function(obj) is None
else:
assert util.get_inspectable_function(obj) == getattr(obj, expected_attribute_name)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -137,6 +81,9 @@ def test_get_invocation_signature(obj: object, expected: inspect.Signature) -> N
(type('TestClass', (), dict(__call__=lambda self, a: None))(), 0, True),
(type('TestClass', (), dict(__call__=lambda self, a: None))(), 1, False),
(type('TestClass', (), dict(__call__=lambda self, a: None))(), 2, False),
(issubclass, 0, True),
(issubclass, 1, True),
(issubclass, 2, True),
),
)
def test_does_callable_have_poa_more_than(obj: object, threshold: int, expected: bool) -> None:
Expand Down

0 comments on commit e8dd2cc

Please sign in to comment.