Skip to content

Commit ee97cbe

Browse files
committed
🛠️added pre-commit and some formatting helpers params in pyproject.toml, sorting out some stricter mypy and linting.
1 parent db5ccd2 commit ee97cbe

7 files changed

Lines changed: 327 additions & 92 deletions

File tree

‎.pre-commit-config.yaml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
repos:
2+
- repo: local
3+
hooks:
4+
- id: ruff-format
5+
name: ruff format
6+
entry: uv run ruff format .
7+
language: system
8+
pass_filenames: false
9+
10+
- id: ruff-check
11+
name: ruff check
12+
entry: uv run ruff check .
13+
language: system
14+
pass_filenames: false
15+
16+
- id: mypy
17+
name: mypy
18+
entry: uv run mypy .
19+
language: system
20+
pass_filenames: false

‎pyproject.toml‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,26 @@ build-backend = "hatchling.build"
1818
[dependency-groups]
1919
dev = [
2020
"mypy>=1.19.1",
21+
"pre-commit>=4.5.1",
2122
"pydocstyle>=6.3.0",
2223
"pytest>=9.0.2",
2324
"ruff>=0.14.9",
2425
]
26+
27+
[tool.ruff]
28+
line-length = 88
29+
30+
[tool.ruff.lint]
31+
select = ["E", "F", "I", "B", "UP"]
32+
33+
[tool.mypy]
34+
warn_unused_configs = true
35+
warn_return_any = true
36+
no_implicit_optional = true
37+
strict_equality = true
38+
disallow_untyped_defs = true
39+
check_untyped_defs = true
40+
pretty = true
41+
show_error_codes = true
42+
mypy_path = "src"
43+

‎src/bseqgen/base.py‎

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
"""core representation for binary sequences"""
2+
23
from __future__ import annotations
3-
from collections.abc import Sequence
4+
5+
import math
6+
from collections.abc import Iterator, Sequence
47
from enum import StrEnum
58
from itertools import groupby
6-
import math
7-
from typing import Iterator, TYPE_CHECKING, Any, Self
9+
from typing import TYPE_CHECKING, Any, Literal, Self
10+
811
__all__ = ("Direction", "BinarySequence")
912

1013

@@ -25,19 +28,15 @@ def __init__(self, bits: Sequence[int | str] | str) -> None:
2528
self.bits: tuple[int, ...] = self._validate_bits(bits)
2629

2730
@staticmethod
28-
def _validate_bits(
29-
input_bits: Sequence[int | str] | str
30-
) -> tuple[int, ...]:
31+
def _validate_bits(input_bits: Sequence[int | str] | str) -> tuple[int, ...]:
3132
"""Validate input bit sequences."""
3233
if (input_bits is None) or (not input_bits):
3334
raise ValueError("Input bits cannot be None or empty.")
3435

3536
try:
3637
bits_list: tuple[int, ...] = tuple(int(bit) for bit in input_bits)
3738
except (TypeError, ValueError) as e:
38-
raise TypeError(
39-
"Bits must be an iterable of 0 and 1 values."
40-
) from e
39+
raise TypeError("Bits must be an iterable of 0 and 1 values.") from e
4140

4241
if any(bit not in (0, 1) for bit in bits_list):
4342
raise ValueError("Bit sequence must only contain 0 or 1.")
@@ -64,12 +63,7 @@ def __str__(self) -> str:
6463
return self.bit_string
6564

6665
def __repr__(self) -> str:
67-
return (
68-
f"{type(self).__name__}("
69-
f"length={self.length}, "
70-
f"preview='{str(self)}'"
71-
f")"
72-
)
66+
return f"{type(self).__name__}(length={self.length}, preview='{str(self)}')"
7367

7468
def __eq__(self, other: object) -> bool:
7569
if not isinstance(other, BinarySequence):
@@ -135,7 +129,7 @@ def as_bytes(self) -> bytes:
135129
zero_padding_len: int = (-len(bit_str)) % 8
136130
bit_str_padded: str = ("0" * zero_padding_len) + bit_str
137131
byte_conversion: bytes = int(bit_str_padded, 2).to_bytes(
138-
len(bit_str_padded)//8, "big"
132+
len(bit_str_padded) // 8, "big"
139133
)
140134

141135
return byte_conversion
@@ -177,7 +171,7 @@ def entropy(self) -> float:
177171
if self.length == 0:
178172
return 0.0
179173

180-
p1 = self.ones/self.length
174+
p1 = self.ones / self.length
181175
p0 = 1.0 - p1
182176

183177
entropy = 0.0
@@ -188,7 +182,7 @@ def entropy(self) -> float:
188182
return round(entropy, 5)
189183

190184
@property
191-
def run_lengths(self):
185+
def run_lengths(self) -> list[tuple[int, int]]:
192186
"""Return list of run lengths [(digit, run count)].
193187
194188
E.g. 111001 >> [(1, 3), (0, 2), (1, 1)]
@@ -212,15 +206,15 @@ def to_length(self, n: int) -> BinarySequence:
212206
return BinarySequence(bits)
213207

214208
def shift(
215-
self,
216-
n: int,
217-
direction: Direction = Direction.LEFT
218-
) -> "BinarySequence":
219-
"""Shift sequence (circular)
209+
self,
210+
n: int,
211+
direction: Direction | Literal["left", "right"] = Direction.LEFT,
212+
) -> BinarySequence:
213+
"""Shift sequence (circular).
220214
221215
Args:
222216
n (int): How many bits to shift by.
223-
direction (Direction, optional): 'left' or 'right'.
217+
direction (Direction|Literal['left', 'right'], optional): 'left' or 'right'.
224218
Defaults to Direction.LEFT.
225219
226220
Returns:
@@ -236,8 +230,7 @@ def shift(
236230
if n < 0:
237231
n = -n
238232
direction = (
239-
Direction.RIGHT if direction == Direction.LEFT
240-
else Direction.LEFT
233+
Direction.RIGHT if direction == Direction.LEFT else Direction.LEFT
241234
)
242235

243236
match direction:
@@ -248,13 +241,13 @@ def shift(
248241

249242
raise ValueError(f"{direction} not a valid direction; 'left', 'right'")
250243

251-
def autocorr(self):
244+
def autocorr(self) -> Any:
252245
raise NotImplementedError("Auto-correlation coming soon.")
253246

254-
def crosscorr(self):
247+
def crosscorr(self) -> Any:
255248
raise NotImplementedError("Cross-correlation coming soon.")
256249

257-
def to_numpy(self, dtype: "NpDTypeInt | None" = None) -> "NpNDArrayInt":
250+
def to_numpy(self, dtype: NpDTypeInt | None = None) -> NpNDArrayInt:
258251
"""Convert BinarySequence to 1D NumPy array.
259252
260253
NumPy is an optional dependency, only required when calling the to_numpy
@@ -280,7 +273,7 @@ def to_numpy(self, dtype: "NpDTypeInt | None" = None) -> "NpNDArrayInt":
280273
return np.array(self.bits, dtype=out_dtype)
281274

282275
@classmethod
283-
def from_numpy(cls, np_array: "NpNDArrayInt") -> Self:
276+
def from_numpy(cls, np_array: NpNDArrayInt) -> Self:
284277
"""Convert 1D NumPy array to BinarySequence.
285278
286279
Args:
@@ -329,7 +322,7 @@ def xor(self, other: BinarySequence) -> BinarySequence:
329322
"""
330323
other_sequence = self._compatible_bits(other, "xor")
331324
return BinarySequence(
332-
tuple(a ^ b for a, b in zip(self.bits, other_sequence))
325+
tuple(a ^ b for a, b in zip(self.bits, other_sequence, strict=True))
333326
)
334327

335328
def bitwise_and(self, other: BinarySequence) -> BinarySequence:
@@ -343,7 +336,7 @@ def bitwise_and(self, other: BinarySequence) -> BinarySequence:
343336
"""
344337
other_sequence = self._compatible_bits(other, "and")
345338
return BinarySequence(
346-
tuple(a & b for a, b in zip(self.bits, other_sequence))
339+
tuple(a & b for a, b in zip(self.bits, other_sequence, strict=True))
347340
)
348341

349342
def bitwise_or(self, other: BinarySequence) -> BinarySequence:
@@ -357,8 +350,8 @@ def bitwise_or(self, other: BinarySequence) -> BinarySequence:
357350
"""
358351
other_sequence = self._compatible_bits(other, "or")
359352
return BinarySequence(
360-
tuple(a | b for a, b in zip(self.bits, other_sequence))
353+
tuple(a | b for a, b in zip(self.bits, other_sequence, strict=True))
361354
)
362355

363-
def hamming_distance(self):
356+
def hamming_distance(self) -> Any:
364357
raise NotImplementedError("Hamming distance coming soon")

‎src/bseqgen/random_seq.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"""Generate PRBS with Random Modules"""
2-
from .base import BinarySequence
2+
33
import random
44
from typing import TypeAlias
5+
6+
from .base import BinarySequence
7+
58
__all__ = ("random_sequence",)
69

710
Seed: TypeAlias = None | int | float | str | bytes | bytearray

0 commit comments

Comments
 (0)