Skip to content

Commit

Permalink
Make is_nonstringy_sequence available in types. (#126)
Browse files Browse the repository at this point in the history
Gives `is_normal_sequence` a slightly better name and makes it available
to packages importing somacore.
  • Loading branch information
thetorpedodog authored Feb 14, 2023
1 parent 4a0d62a commit a74f44c
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 9 deletions.
10 changes: 3 additions & 7 deletions python-spec/src/somacore/query/axis.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from typing import Any, Optional, Sequence, Tuple
from typing import Optional, Sequence, Tuple

import attrs
import numpy as np
import pyarrow as pa
from typing_extensions import TypeGuard

from .. import options
from .. import types


def _canonicalize_coords(
Expand All @@ -22,7 +22,7 @@ def _canonicalize_coords(
raise TypeError(
f"query coordinates must be a sequence, not a single {type(in_coords)}"
)
if not _is_normal_sequence(in_coords):
if not types.is_nonstringy_sequence(in_coords):
raise TypeError(
"query coordinates must be a normal sequence, not `str` or `bytes`."
)
Expand All @@ -42,10 +42,6 @@ def _canonicalize_coord(coord: options.SparseDFCoord) -> options.SparseDFCoord:
raise TypeError(f"{type(coord)} object cannot be used as a coordinate.")


def _is_normal_sequence(it: Any) -> TypeGuard[Sequence]:
return not isinstance(it, (str, bytes)) and isinstance(it, Sequence)


@attrs.define(frozen=True, kw_only=True)
class AxisQuery:
"""Single-axis dataframe query with coordinates and a value filter.
Expand Down
14 changes: 12 additions & 2 deletions python-spec/src/somacore/types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
"""Type and interface declarations that are not specific to options."""

from typing import Optional, TypeVar
from typing_extensions import Protocol, Self, runtime_checkable
from typing import Any, Optional, TypeVar, Sequence
from typing_extensions import Protocol, Self, runtime_checkable, TypeGuard


def is_nonstringy_sequence(it: Any) -> TypeGuard[Sequence]:
"""Returns true if a sequence is a "normal" sequence and not str or bytes.
str and bytes are "weird" sequences because iterating them gives you
another str or bytes instance for each character, and when used as a
sequence is not what users want.
"""
return not isinstance(it, (str, bytes)) and isinstance(it, Sequence)


class Comparable(Protocol):
Expand Down
21 changes: 21 additions & 0 deletions python-spec/testing/test_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Any, Iterable
import unittest

from somacore import types


class TestTypes(unittest.TestCase):
def test_is_nonstringy_sequence(self):
seqs: Iterable[Any] = ([], (), range(10))
for seq in seqs:
with self.subTest(seq):
self.assertTrue(types.is_nonstringy_sequence(seq))

non_seqs: Iterable[Any] = (1, "hello", b"goodbye", (x for x in range(10)))
for non_seq in non_seqs:
with self.subTest(non_seq):
self.assertFalse(types.is_nonstringy_sequence(non_seq))

def test_slice(self):
self.assertIsInstance(slice(None), types.Slice)
self.assertNotIsInstance((1, 2), types.Slice)

0 comments on commit a74f44c

Please sign in to comment.