• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

basilisp-lang / basilisp / 26408720365

25 May 2026 03:47PM UTC coverage: 97.379% (-1.5%) from 98.896%
26408720365

Pull #1338

github

web-flow
Merge ef449721d into 416c61480
Pull Request #1338: Native LazySeq implementation with PyO3

1093 of 1113 branches covered (98.2%)

Branch coverage included in aggregate %.

24 of 28 new or added lines in 3 files covered. (85.71%)

1 existing line in 1 file now uncovered.

9162 of 9418 relevant lines covered (97.28%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.64
/src/basilisp/lang/interfaces.py
1
import itertools
1✔
2
from abc import ABC, abstractmethod
1✔
3
from collections.abc import (
1✔
4
    Callable,
5
    Hashable,
6
    Iterable,
7
    Iterator,
8
    Mapping,
9
    Sequence,
10
    Sized,
11
)
12
from typing import (
1✔
13
    AbstractSet,
14
    Any,
15
    Final,
16
    Generic,
17
    Optional,
18
    Protocol,
19
    TypeVar,
20
    Union,
21
    overload,
22
)
23

24
from typing_extensions import Self, Unpack
1✔
25

26
from basilisp.lang.obj import LispObject as _LispObject
1✔
27
from basilisp.lang.obj import PrintSettings, seq_lrepr
1✔
28

29
T = TypeVar("T")
1✔
30

31

32
class IDeref(Generic[T], ABC):
1✔
33
    """``IDeref`` types are reference container types which return their contained
34
    value via :lpy:fn:`deref` .
35

36
    .. seealso::
37

38
       :py:class:`IBlockingDeref`"""
39

40
    __slots__ = ()
1✔
41

42
    @abstractmethod
1✔
43
    def deref(self) -> T | None:
1✔
44
        raise NotImplementedError()
45

46

47
class IBlockingDeref(IDeref[T]):
1✔
48
    """``IBlockingDeref`` types are reference container types which may block returning
49
    their contained value. The contained value can be fetched with a timeout and default
50
    via :lpy:fn:`deref` .
51

52
    .. seealso::
53

54
       :py:class:`IDeref`"""
55

56
    __slots__ = ()
1✔
57

58
    @abstractmethod
1✔
59
    def deref(
1✔
60
        self, timeout: float | None = None, timeout_val: T | None = None
61
    ) -> T | None:
62
        raise NotImplementedError()
63

64

65
class ICounted(Sized, ABC):
1✔
66
    """``ICounted`` is a marker interface for types can produce their length in
67
    constant time.
68

69
    All the builtin collections are ``ICounted``, except Lists whose length is
70
    determined by counting all the elements in the list in linear time.
71

72
    .. seealso::
73

74
       :lpy:fn:`counted?`"""
75

76
    __slots__ = ()
1✔
77

78

79
K = TypeVar("K")
1✔
80
V = TypeVar("V")
1✔
81

82

83
class IIndexed(ICounted, Generic[V], ABC):
1✔
84
    """``IIndexed`` is an interface for types can be accessed by index.
85

86
    Of the builtin collections, only Vectors are ``IIndexed`` . ``IIndexed`` types
87
    respond ``True`` to the :lpy:fn:`indexed?` predicate.
88

89
    .. seealso::
90

91
       :lpy:fn:`indexed?`, :lpy:fn:`nth`"""
92

93
    __slots__ = ()
1✔
94

95
    NTH_SENTINEL = object()
1✔
96

97
    @abstractmethod
1✔
98
    def nth(self, k: int, notfound: V | None = NTH_SENTINEL) -> V | None:  # type: ignore[assignment]
1✔
99
        raise NotImplementedError()
100

101

102
T_ExceptionInfo = TypeVar("T_ExceptionInfo", bound="IPersistentMap")
1✔
103

104

105
class IExceptionInfo(Exception, Generic[T_ExceptionInfo], ABC):
1✔
106
    """``IExceptionInfo`` types are exception types which contain an optional
107
    :py:class:`IPersistentMap` data element of contextual information about the thrown
108
    exception.
109

110
    .. seealso::
111

112
       :lpy:fn:`ex-data`"""
113

114
    __slots__ = ()
1✔
115

116
    @property
1✔
117
    @abstractmethod
1✔
118
    def data(self) -> T_ExceptionInfo:
1✔
119
        raise NotImplementedError()
120

121

122
class IMapEntry(Generic[K, V], ABC):
1✔
123
    """``IMapEntry`` values are produced :lpy:fn:`seq` ing over any
124
    :py:class:`IAssociative` (such as a Basilisp map).
125

126
    .. seealso::
127

128
       :lpy:fn:`key` , :lpy:fn:`val` , :lpy:fn:`map-entry?`"""
129

130
    __slots__ = ()
1✔
131

132
    @property
1✔
133
    @abstractmethod
1✔
134
    def key(self) -> K:
1✔
135
        raise NotImplementedError()
136

137
    @property
1✔
138
    @abstractmethod
1✔
139
    def value(self) -> V:
1✔
140
        raise NotImplementedError()
141

142

143
class IMeta(ABC):
1✔
144
    """``IMeta`` types can optionally include a map of metadata.
145

146
    Persistent data types metadata cannot be mutated, but many of these data types
147
    also implement :py:class:`IWithMeta` which allows creating a copy of the structure
148
    with new metadata.
149

150
    .. seealso::
151

152
       :lpy:fn:`meta`"""
153

154
    __slots__ = ()
1✔
155

156
    @property
1✔
157
    @abstractmethod
1✔
158
    def meta(self) -> Optional["IPersistentMap"]:
1✔
159
        raise NotImplementedError()
160

161

162
class IWithMeta(IMeta):
1✔
163
    """``IWithMeta`` are :py:class:`IMeta` types which can create copies of themselves
164
    with new metadata.
165

166
    .. seealso::
167

168
       :lpy:fn:`with-meta`"""
169

170
    __slots__ = ()
1✔
171

172
    @abstractmethod
1✔
173
    def with_meta(self, meta: "Optional[IPersistentMap]") -> Self:
1✔
174
        raise NotImplementedError()
175

176

177
class INamed(ABC):
1✔
178
    """``INamed`` instances are symbolic identifiers with a name and optional
179
    namespace.
180

181
    .. seealso::
182

183
       :lpy:fn:`name` , :lpy:fn:`namespace`"""
184

185
    __slots__ = ()
1✔
186

187
    @property
1✔
188
    @abstractmethod
1✔
189
    def name(self) -> str:
1✔
190
        raise NotImplementedError()
191

192
    @property
1✔
193
    @abstractmethod
1✔
194
    def ns(self) -> str | None:
1✔
195
        raise NotImplementedError()
196

197
    @classmethod
1✔
198
    @abstractmethod
1✔
199
    def with_name(cls, name: str, ns: str | None = None) -> Self:
1✔
200
        """Create a new instance of this INamed with `name` and optional `ns`."""
201
        raise NotImplementedError()
202

203

204
ILispObject = _LispObject
1✔
205

206

207
class IReference(IMeta):
1✔
208
    """``IReference`` types are mutable reference containers which allow mutation of
209
    the associated metadata.
210

211
    .. seealso::
212

213
       :lpy:fn:`alter-meta!` , :lpy:fn:`reset-meta!`"""
214

215
    __slots__ = ()
1✔
216

217
    @abstractmethod
1✔
218
    def alter_meta(
1✔
219
        self, f: Callable[..., Optional["IPersistentMap"]], *args
220
    ) -> Optional["IPersistentMap"]:
221
        raise NotImplementedError()
222

223
    @abstractmethod
1✔
224
    def reset_meta(
1✔
225
        self, meta: Optional["IPersistentMap"]
226
    ) -> Optional["IPersistentMap"]:
227
        raise NotImplementedError()
228

229

230
RefValidator = Callable[[T], bool]
1✔
231
RefWatchKey = Hashable
1✔
232
RefWatcher = Callable[[RefWatchKey, "IRef", T, T], None]
1✔
233

234

235
class IRef(IDeref[T]):
1✔
236
    """``IRef`` types are mutable reference containers which support validation of the
237
    contained value and watchers which are notified when the contained value changes.
238

239
    .. seealso::
240

241
       :lpy:fn:`add-watch` , :lpy:fn:`remove-watch` , :lpy:fn:`get-validator` ,
242
       :lpy:fn:`set-validator!`"""
243

244
    __slots__ = ()
1✔
245

246
    @abstractmethod
1✔
247
    def add_watch(self, k: RefWatchKey, wf: RefWatcher[T]) -> "IReference":
1✔
248
        raise NotImplementedError()
249

250
    @abstractmethod
1✔
251
    def remove_watch(self, k: RefWatchKey) -> "IReference":
1✔
252
        raise NotImplementedError()
253

254
    @abstractmethod
1✔
255
    def get_validator(self) -> RefValidator[T] | None:
1✔
256
        raise NotImplementedError()
257

258
    @abstractmethod
1✔
259
    def set_validator(self, vf: RefValidator[T] | None = None) -> None:
1✔
260
        raise NotImplementedError()
261

262

263
class IReversible(Generic[T]):
1✔
264
    """``IReversible`` types can produce a sequences of their elements in reverse in
265
    constant time.
266

267
    Of the builtin collections, only Vectors are ``IReversible``.
268

269
    .. seealso::
270

271
       :lpy:fn:`reversible?`"""
272

273
    __slots__ = ()
1✔
274

275
    @abstractmethod
1✔
276
    def rseq(self) -> "ISeq[T]":
1✔
277
        raise NotImplementedError()
278

279

280
class ISeqable(Iterable[T]):
1✔
281
    """``ISeqable`` types can produce sequences of their elements, but are not
282
    :py:class:`ISeq` .
283

284
    All the builtin collections are ``ISeqable``, except Lists which directly implement
285
    :py:class:`ISeq` .
286

287
    .. seealso::
288

289
       :ref:`seqs` , :lpy:fn:`seq` , :lpy:fn:`seqable?`"""
290

291
    __slots__ = ()
1✔
292

293
    @abstractmethod
1✔
294
    def seq(self) -> "Optional[ISeq[T]]":
1✔
295
        raise NotImplementedError()
296

297

298
class ISequential(ABC):
1✔
299
    """``ISequential`` is a marker interface for sequential types.
300

301
    Lists and Vectors are both considered ``ISequential``.
302

303
    .. seealso::
304

305
       :lpy:fn:`sequential?`"""
306

307
    __slots__ = ()
1✔
308

309

310
class ILookup(Generic[K, V], ABC):
1✔
311
    """``ILookup`` types allow accessing contained values by a key or index.
312

313
    .. seealso::
314

315
       :lpy:fn:`get`"""
316

317
    __slots__ = ()
1✔
318

319
    @abstractmethod
1✔
320
    def val_at(self, k: K, default: V | None = None) -> V | None:
1✔
321
        raise NotImplementedError()
322

323

324
class IPending(ABC):
1✔
325
    """``IPending`` types are types which may represent deferred computations such
326
    as a future or promise.
327

328
    .. seealso::
329

330
       :lpy:fn:`realized?`, :lpy:fn:`delay`, :lpy:fn:`future`, :lpy:fn:`promise`"""
331

332
    __slots__ = ()
1✔
333

334
    @property
1✔
335
    @abstractmethod
1✔
336
    def is_realized(self) -> bool:
1✔
337
        raise NotImplementedError()
338

339

340
class IPersistentCollection(ISeqable[T]):
1✔
341
    """``IPersistentCollection`` types support both fetching empty variants of an
342
    existing persistent collection and creating a new collection with additional
343
    members.
344

345
    .. seealso::
346

347
       :lpy:fn:`conj` , :lpy:fn:`empty` , :lpy:fn:`coll?`"""
348

349
    __slots__ = ()
1✔
350

351
    @abstractmethod
1✔
352
    def cons(self: Self, *elems: T) -> Self:
1✔
353
        raise NotImplementedError()
354

355
    @abstractmethod
1✔
356
    def empty(self) -> "IPersistentCollection[T]":
1✔
357
        raise NotImplementedError()
358

359

360
class IAssociative(ILookup[K, V], IPersistentCollection[IMapEntry[K, V]]):
1✔
361
    """``IAssociative`` types support a persistent data structure variant of
362
    associative operations.
363

364
    .. seealso::
365

366
       :lpy:fn:`assoc` , :lpy:fn:`contains?`, :lpy:fn:`find` , :lpy:fn:`associative?`
367
    """
368

369
    __slots__ = ()
1✔
370

371
    @abstractmethod
1✔
372
    def assoc(self: Self, *kvs) -> Self:
1✔
373
        raise NotImplementedError()
374

375
    @abstractmethod
1✔
376
    def contains(self, k: K) -> bool:
1✔
377
        raise NotImplementedError()
378

379
    @abstractmethod
1✔
380
    def entry(self, k: K) -> IMapEntry[K, V] | None:
1✔
381
        raise NotImplementedError()
382

383

384
class IPersistentStack(IPersistentCollection[T]):
1✔
385
    """``IPersistentStack`` types support a persistent data structure variant of
386
    classical stack operations.
387

388
    .. seealso::
389

390
       :lpy:fn:`pop` , :lpy:fn:`peek`
391
    """
392

393
    __slots__ = ()
1✔
394

395
    @abstractmethod
1✔
396
    def peek(self) -> T | None:
1✔
397
        raise NotImplementedError()
398

399
    @abstractmethod
1✔
400
    def pop(self: Self) -> Self:
1✔
401
        raise NotImplementedError()
402

403

404
class IProxy(ABC):
1✔
405
    """``IProxy`` is a marker interface for proxy types.
406

407
    All types created by ``proxy`` are automatically marked with ``IProxy``.
408

409
    .. seealso::
410

411
       :ref:`proxies`, :lpy:fn:`proxy`, :lpy:fn:`proxy-mappings`, :lpy:fn:`proxy-super`,
412
       :lpy:fn:`construct-proxy`, :lpy:fn:`init-proxy`, :lpy:fn:`update-proxy`,
413
       :lpy:fn:`get-proxy-class`"""
414

415
    __slots__ = ()
1✔
416

417
    @abstractmethod
1✔
418
    def _get_proxy_mappings(self) -> "IPersistentMap[str, Callable]":
1✔
419
        raise NotImplementedError()
420

421
    @abstractmethod
1✔
422
    def _set_proxy_mappings(
1✔
423
        self, proxy_mappings: "IPersistentMap[str, Callable]"
424
    ) -> None:
425
        raise NotImplementedError()
426

427
    @abstractmethod
1✔
428
    def _update_proxy_mappings(
1✔
429
        self, proxy_mappings: "IPersistentMap[str, Callable]"
430
    ) -> None:
431
        raise NotImplementedError()
432

433

434
T_key = TypeVar("T_key")
1✔
435
V_contra = TypeVar("V_contra", contravariant=True)
1✔
436

437

438
class ReduceFunction(Protocol[T, V_contra]):
1✔
439
    @overload
1✔
440
    def __call__(self) -> T: ...
1✔
441

442
    @overload
1✔
443
    def __call__(self, init: T, val: V_contra) -> T: ...
1✔
444

445
    def __call__(self, *args, **kwargs): ...
1✔
446

447

448
ReduceKVFunction = Callable[[T, T_key, V_contra], T]
1✔
449

450

451
class IReduce(ABC):
1✔
452
    """``IReduce`` types define custom implementations of ``reduce``.
453

454
    Only vectors are ``IReduce`` by default, providing faster iteration than relying on
455
    ``seq``.
456

457
    .. seealso::
458

459
       :lpy:fn:`reduce`
460
    """
461

462
    REDUCE_SENTINEL: Final = object()
1✔
463

464
    __slots__ = ()
1✔
465

466
    @overload
1✔
467
    def reduce(self, f: ReduceFunction[T, V_contra]) -> T: ...
1✔
468

469
    @overload
1✔
470
    def reduce(self, f: ReduceFunction[T, V_contra], init: T) -> T: ...
1✔
471

472
    @abstractmethod
1✔
473
    def reduce(self, f, init=REDUCE_SENTINEL):
1✔
474
        raise NotImplementedError()
475

476

477
class IReduceKV(ABC):
1✔
478
    """``IReduceKV`` types define custom implementations of ``reduce-kv``.
479

480
    Both vectors and maps are ``IReduceKV`` by default, providing faster iteration than
481
    relying on ``seq``. Maps iterate over the key-value pairs as expected, and vectors
482
    iterate over the index-item pairs of the vector.
483

484
    .. seealso::
485

486
       :lpy:fn:`reduce-kv`
487
    """
488

489
    __slots__ = ()
1✔
490

491
    @abstractmethod
1✔
492
    def reduce_kv(self: Self, f: ReduceKVFunction, init: T):
1✔
493
        raise NotImplementedError()
494

495

496
class IPersistentList(ISequential, IPersistentStack[T]):
1✔
497
    """``IPersistentList`` is a marker interface for a singly-linked list."""
498

499
    __slots__ = ()
1✔
500

501

502
class IPersistentMap(ICounted, Mapping[K, V], IAssociative[K, V]):
1✔
503
    """``IPersistentMap`` types support creating and modifying persistent maps.
504

505
    .. seealso::
506

507
       :lpy:fn:`conj` , :lpy:fn:`dissoc` , :lpy:fn:`map?`"""
508

509
    __slots__ = ()
1✔
510

511
    @abstractmethod
1✔
512
    def cons(
1✔
513
        self: Self, *elems: Union[IMapEntry[K, V], "IPersistentMap[K, V]", None]
514
    ) -> Self:
515
        raise NotImplementedError()
516

517
    @abstractmethod
1✔
518
    def dissoc(self: Self, *ks: K) -> Self:
1✔
519
        raise NotImplementedError()
520

521

522
class IPersistentSet(AbstractSet[T], ICounted, IPersistentCollection[T]):
1✔
523
    """``IPersistentSet`` types support creating and modifying persistent sets.
524

525
    .. seealso::
526

527
       :lpy:fn:`disj` , :lpy:fn:`set?`"""
528

529
    __slots__ = ()
1✔
530

531
    @abstractmethod
1✔
532
    def disj(self: Self, *elems: T) -> Self:
1✔
533
        raise NotImplementedError()
534

535

536
class IPersistentVector(
1✔
537
    Sequence[T],
538
    IAssociative[int, T],
539
    IIndexed,
540
    IReversible[T],
541
    ISequential,
542
    IPersistentStack[T],
543
):
544
    """``IPersistentVector`` types support creating and modifying persistent vectors.
545

546
    .. seealso::
547

548
       :lpy:fn:`vector?`"""
549

550
    __slots__ = ()
1✔
551

552
    @abstractmethod
1✔
553
    def assoc(self: Self, *kvs) -> Self:
1✔
554
        raise NotImplementedError()
555

556
    @abstractmethod
1✔
557
    def cons(self: Self, *elems: T) -> Self:  # type: ignore[override]
1✔
558
        raise NotImplementedError()
559

560
    @abstractmethod
1✔
561
    def seq(self) -> "Optional[ISeq[T]]":  # type: ignore[override]
1✔
562
        raise NotImplementedError()
563

564

565
T_tcoll = TypeVar("T_tcoll", bound="ITransientCollection")
1✔
566

567

568
# Including ABC as a base seems to cause catastrophic meltdown.
569
class IEvolveableCollection(Generic[T_tcoll]):
1✔
570
    """``IEvolveableCollection`` types support creating transient variants of persistent
571
    data structures which can be modified efficiently and then returned back into
572
    persistent data structures once modification is complete.
573

574
    .. seealso::
575

576
       :lpy:fn:`transient`"""
577

578
    @abstractmethod
1✔
579
    def to_transient(self) -> T_tcoll:
1✔
580
        raise NotImplementedError()
581

582

583
class ITransientCollection(Generic[T]):
1✔
584
    """``ITransientCollection`` types support efficient modification of otherwise
585
    persistent data structures.
586

587
    .. seealso::
588

589
       :lpy:fn:`conj!` , :lpy:fn:`persistent!`"""
590

591
    __slots__ = ()
1✔
592

593
    @abstractmethod
1✔
594
    def cons_transient(self: T_tcoll, *elems: T) -> "T_tcoll":
1✔
595
        raise NotImplementedError()
596

597
    @abstractmethod
1✔
598
    def to_persistent(self: T_tcoll) -> "IPersistentCollection[T]":
1✔
599
        raise NotImplementedError()
600

601

602
class ITransientAssociative(ILookup[K, V], ITransientCollection[IMapEntry[K, V]]):
1✔
603
    """``ITransientAssociative`` types are the transient counterpart of
604
    :py:class:`IAssociative` types.
605

606
    .. seealso::
607

608
       :lpy:fn:`assoc!` , :lpy:fn:`contains?`, :lpy:fn:`find`"""
609

610
    __slots__ = ()
1✔
611

612
    @abstractmethod
1✔
613
    def assoc_transient(self, *kvs) -> Self:
1✔
614
        raise NotImplementedError()
615

616
    @abstractmethod
1✔
617
    def contains_transient(self, k: K) -> bool:
1✔
618
        raise NotImplementedError()
619

620
    @abstractmethod
1✔
621
    def entry_transient(self, k: K) -> IMapEntry[K, V] | None:
1✔
622
        raise NotImplementedError()
623

624

625
class ITransientMap(ICounted, ITransientAssociative[K, V]):
1✔
626
    """``ITransientMap`` types are the transient counterpart of
627
    :py:class:`IPersistentMap` types.
628

629
    .. seealso::
630

631
       :lpy:fn:`dissoc!`"""
632

633
    __slots__ = ()
1✔
634

635
    @abstractmethod
1✔
636
    def cons_transient(
1✔
637
        self, *elems: Union[IMapEntry[K, V], "IPersistentMap[K, V]", None]
638
    ) -> Self:
639
        raise NotImplementedError()
640

641
    @abstractmethod
1✔
642
    def dissoc_transient(self, *ks: K) -> Self:
1✔
643
        raise NotImplementedError()
644

645

646
class ITransientSet(ICounted, ITransientCollection[T]):
1✔
647
    """``ITransientSet`` types are the transient counterpart of
648
    :py:class:`IPersistentSet` types.
649

650
    .. seealso::
651

652
       :lpy:fn:`disj!`"""
653

654
    __slots__ = ()
1✔
655

656
    @abstractmethod
1✔
657
    def disj_transient(self, *elems: T) -> Self:
1✔
658
        raise NotImplementedError()
659

660

661
T_tvec = TypeVar("T_tvec", bound="ITransientVector")
1✔
662

663

664
class ITransientVector(
1✔
665
    ITransientAssociative[int, T],
666
    IIndexed,
667
):
668
    """``ITransientVector`` types are the transient counterpart of
669
    :py:class:`IPersistentVector` types."""
670

671
    __slots__ = ()
1✔
672

673
    @abstractmethod
1✔
674
    def assoc_transient(self: T_tvec, *kvs) -> T_tvec:
1✔
675
        raise NotImplementedError()
676

677
    @abstractmethod
1✔
678
    def cons_transient(self: T_tvec, *elems: T) -> T_tvec:  # type: ignore[override]
1✔
679
        raise NotImplementedError()
680

681
    @abstractmethod
1✔
682
    def pop_transient(self: T_tvec) -> T_tvec:
1✔
683
        raise NotImplementedError()
684

685

686
class IRecord(ILispObject):
1✔
687
    """``IRecord`` is a marker interface for types :lpy:form:`def` 'ed by
688
    :lpy:fn:`defrecord` forms.
689

690
    All types created by ``defrecord`` are automatically marked with ``IRecord``.
691

692
    .. seealso::
693

694
       :ref:`data_types_and_records` , :lpy:fn:`defrecord` , :lpy:fn:`record?`
695
    """
696

697
    __slots__ = ()
1✔
698

699
    @classmethod
1✔
700
    @abstractmethod
1✔
701
    def create(cls, m: IPersistentMap) -> "IRecord":
1✔
702
        """Class method constructor from an :py:class:`IPersistentMap` instance."""
703
        raise NotImplementedError()
704

705
    def _lrepr(self, **kwargs: Unpack[PrintSettings]) -> str:
1✔
706
        return self._record_lrepr(kwargs)
1✔
707

708
    @abstractmethod
1✔
709
    def _record_lrepr(self, kwargs: PrintSettings) -> str:
1✔
710
        """Translation method converting Python keyword arguments into a
711
        Python dict.
712

713
        Basilisp methods and functions cannot formally accept Python keyword
714
        arguments, so this method is called by `_lrepr` with the keyword
715
        arguments cast to a Python dict."""
716
        raise NotImplementedError()
717

718

719
def seq_equals(s1: Union["ISeq", ISequential], s2: Any) -> bool:
1✔
720
    """Return True if two sequences contain exactly the same elements in the same
721
    order. Return False if one sequence is shorter than the other."""
722
    assert isinstance(s1, (ISeq, ISequential))
1✔
723

724
    if not isinstance(s2, (ISeq, ISequential)):
1✔
725
        return NotImplemented
1✔
726

727
    sentinel = object()
1✔
728
    for e1, e2 in itertools.zip_longest(s1, s2, fillvalue=sentinel):  # type: ignore[arg-type]
1✔
729
        if bool(e1 is sentinel) or bool(e2 is sentinel):
1✔
730
            return False
1✔
731
        if e1 != e2:
1✔
732
            return False
1✔
733
    return True
1✔
734

735

736
T_inner = TypeVar("T_inner")
1✔
737

738

739
class ISeq(ILispObject, IPersistentCollection[T]):
1✔
740
    """``ISeq`` types represent a potentially infinite sequence of elements.
741

742
    .. seealso::
743

744
       :ref:`seqs` , :lpy:fn:`lazy-seq` , :lpy:fn:`seq` , :lpy:fn:`first` ,
745
       :lpy:fn:`rest` , :lpy:fn:`next` , :lpy:fn:`second` , :lpy:fn:`seq?` ,
746
       :lpy:fn:`nfirst` , :lpy:fn:`fnext` , :lpy:fn:`nnext` , :lpy:fn:`empty?` ,
747
       :lpy:fn:`seq?`
748
    """
749

750
    __slots__ = ()
1✔
751

752
    class _SeqIter(Iterator[T_inner]):
1✔
753
        """Stateful iterator for sequence types.
754

755
        This is primarily useful for avoiding blowing the stack on a long (or infinite)
756
        sequence. It is not safe to use `yield` statements to iterate over sequences,
757
        since they accrete one Python stack frame per sequence element."""
758

759
        __slots__ = ("_cur",)
1✔
760

761
        def __init__(self, seq: "ISeq[T_inner]"):
1✔
762
            self._cur = seq
1✔
763

764
        def __next__(self):
1✔
765
            if not self._cur:
1✔
UNCOV
766
                raise StopIteration
×
767
            v = self._cur.first
1✔
768
            if self._cur.is_empty:
1✔
769
                raise StopIteration
1✔
770
            self._cur = self._cur.rest
1✔
771
            return v
1✔
772

773
        def __repr__(self):  # pragma: no cover
774
            return repr(self._cur)
775

776
    @property
1✔
777
    @abstractmethod
1✔
778
    def is_empty(self) -> bool:
1✔
779
        raise NotImplementedError()
780

781
    @property
1✔
782
    @abstractmethod
1✔
783
    def first(self) -> T | None:
1✔
784
        raise NotImplementedError()
785

786
    @property
1✔
787
    @abstractmethod
1✔
788
    def rest(self) -> "ISeq[T]":
1✔
789
        raise NotImplementedError()
790

791
    @abstractmethod
1✔
792
    def cons(self, *elem: T) -> "ISeq[T]":
1✔
793
        raise NotImplementedError()
794

795
    def seq(self) -> "Optional[ISeq[T]]":
1✔
796
        return self
1✔
797

798
    def _lrepr(self, **kwargs: Unpack[PrintSettings]):
1✔
799
        return seq_lrepr(iter(self), "(", ")", **kwargs)
1✔
800

801
    def __eq__(self, other):
1✔
802
        if self is other:
1✔
803
            return True
1✔
804
        return seq_equals(self, other)
1✔
805

806
    def __hash__(self):
1✔
807
        return hash(tuple(self))
1✔
808

809
    def __iter__(self):
1✔
810
        return self._SeqIter(self)
1✔
811

812

813
class IType(ABC):
1✔
814
    """``IType`` is a marker interface for types :lpy:form:`def` 'ed by
815
    :lpy:fn:`deftype` forms.
816

817
    All types created by ``deftype`` are automatically marked with ``IType``.
818

819
    .. seealso::
820

821
       :ref:`data_types_and_records`"""
822

823
    __slots__ = ()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc