Skip to content

Commit

Permalink
more DRY code cleanup within the tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jorenham committed Feb 26, 2024
1 parent 6392e31 commit 3f17eb6
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 30 deletions.
35 changes: 33 additions & 2 deletions tests/helpers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import functools
import inspect
from types import ModuleType
from typing import Protocol, cast

from optype import CanBool, CanLt


def is_protocol(cls: type) -> bool:
"""Based on `typing_extensions.is_protocol`."""
Expand All @@ -19,7 +20,6 @@ def is_runtime_protocol(cls: type) -> bool:
return is_protocol(cls) and getattr(cls, '_is_runtime_protocol', False)


@functools.cache
def get_protocol_members(cls: type) -> frozenset[str]:
"""
A variant of `typing_extensions.get_protocol_members()` that doesn't
Expand Down Expand Up @@ -106,3 +106,34 @@ def get_callable_members(module: ModuleType) -> frozenset[str]:
and callable(cls := getattr(module, name))
and not is_protocol(cls)
})


def pascamel_to_snake(
pascamel: str,
start: CanLt[int, CanBool] = 0,
/,
) -> str:
"""Converts 'CamelCase' or 'pascalCase' to 'snake_case'."""
assert pascamel.isidentifier()

snake = ''.join(
f'_{char}' if i > start and char.isupper() else char
for i, char in enumerate(pascamel)
).lower()
assert snake.isidentifier()
assert snake[0] != '_'
assert snake[-1] != '_'

return snake


def is_dunder(name: str, /) -> bool:
"""Whether the name is a valid `__dunder_name__`."""
return (
len(name) > 4 # noqa: PLR2004
and name.isidentifier()
and name.islower()
and name[:2] == name[-2:] == '__'
and name[2].isalpha()
and name[-3].isalpha()
)
59 changes: 31 additions & 28 deletions tests/test_protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
get_callable_members,
get_protocol_members,
get_protocols,
is_dunder,
is_protocol,
is_runtime_protocol,
pascamel_to_snake,
)


Expand Down Expand Up @@ -84,33 +86,34 @@ def test_name_matches_dunder(cls: type):
assert members

member_count = len(members)
super_count = sum(map(is_protocol, cls.mro()[1:-1]))
parents = list(filter(is_protocol, cls.mro()[1:]))

# this test should probably be split up...

if member_count > 1:
assert super_count == member_count
return

# convert CamelCase to to snake_case (ignoring the first char, which
# could be an async (A), augmented (I), or reflected (R) binop name prefix)
member_expect = ''.join(
f'_{c}' if i > 1 and c.isupper() else c
for i, c in enumerate(name.removeprefix(prefix))
).lower()
# sanity checks (a bit out-of-scope, but humankind will probably survive)
assert member_expect.isidentifier()
assert '__' not in member_expect
assert member_expect[0] != '_'
assert member_expect[-1] != '_'

# remove potential trailing arity digit
if member_expect[-1].isdigit():
member_expect = member_expect[:-1]
# another misplaced check (ah well, let's hope the extinction event is fun)
assert not member_expect[-1].isdigit()

member = next(iter(members))
if member[:2] == member[-2:] == '__':
# add some thunder... or was is döner...? wait, no; dunder!.
member_expect = f'__{member_expect}__'

assert member == member_expect
# ensure #parent protocols == #members (including inherited)
assert len(parents) == member_count

members_concrete = set(members)
for parent in parents:
members_concrete.difference_update(get_protocol_members(parent))

assert not members_concrete
else:
# remove the `Can`, `Has`, or `Does` prefix
stem = name.removeprefix(prefix)
# strip the arity digit if exists
if stem[-1].isdigit():
stem = stem[:-1]
assert stem[-1].isalpha()

# the `1` arg ensures that any potential leading `A`, `I` or `R` chars
# won't have a `_` directly after (i.e. considers `stem[:2].lower()`).
member_predict = pascamel_to_snake(stem, 1)
member_expect = next(iter(members))

# prevent comparing apples with oranges: paint the apples orange!
if is_dunder(member_expect):
member_predict = f'__{member_predict}__'

assert member_predict == member_expect

0 comments on commit 3f17eb6

Please sign in to comment.