Skip to content

More or less complete type stub#15

Merged
asuiu merged 6 commits into
masterfrom
type_stub
Nov 4, 2025
Merged

More or less complete type stub#15
asuiu merged 6 commits into
masterfrom
type_stub

Conversation

@MorariMaxim
Copy link
Copy Markdown
Collaborator

@MorariMaxim MorariMaxim commented Nov 4, 2025

Type stub for type checkers(pyright mostly, since using it on strict mode revealed the type errors)

I tried to make the stub reflect the internal structure as much as possible

Summary by CodeRabbit

  • New Features
    • Public, fully-typed streaming API with many functional transforms, stream-aware list/set/dict types, progress/parallel helpers, and serialization/IO utilities.
  • Bug Fixes
    • Improved preservation of original tracebacks for mapped and parallel operations.
  • Tests
    • Added/updated tests to validate traceback preservation and exception handling across mapping helpers.
  • Chores
    • Project version and minimum Python requirement bumped; CI matrix and environment/dev dependency specs cleaned up.

Copilot AI review requested due to automatic review settings November 4, 2025 14:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds type stubs (.pyi file) for the streamerate library to provide type annotations for better IDE support and static type checking. The stub file defines type signatures for all public classes and methods in the streamerate package.

Key changes:

  • Created streamerate/__init__.pyi with complete type annotations
  • Defined type signatures for the main classes: stream, slist, sset, and sdict
  • Added proper type variable definitions and method overloads for complex method signatures

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread streamerate/__init__.pyi Outdated
Comment on lines +113 to +116
def keyBy(self, keyfunc: Callable[[_K], _V]) -> stream[Tuple[_K, _V]]: ...
@overload
def keyBy(self) -> stream[Tuple[_K, _K]]: ...
def keyBy(self, keyfunc: Callable[[_K], _V] = ...) -> stream[Tuple[_K, _V]]: ...
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type is incorrect. When keyfunc is provided, it maps _K to _V and returns Tuple[_V, _K] (key, value), not Tuple[_K, _V]. Based on the implementation at line 716 of streams.py which does (keyfunc(h), h), the key comes first. Change the return type to stream[Tuple[_V, _K]].

Suggested change
def keyBy(self, keyfunc: Callable[[_K], _V]) -> stream[Tuple[_K, _V]]: ...
@overload
def keyBy(self) -> stream[Tuple[_K, _K]]: ...
def keyBy(self, keyfunc: Callable[[_K], _V] = ...) -> stream[Tuple[_K, _V]]: ...
def keyBy(self, keyfunc: Callable[[_K], _V]) -> stream[Tuple[_V, _K]]: ...
@overload
def keyBy(self) -> stream[Tuple[_K, _K]]: ...
def keyBy(self, keyfunc: Callable[[_K], _V] = ...) -> stream[Tuple[_V, _K]]: ...

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, there is a wrong inline type hint then in sdict

Comment thread streamerate/__init__.pyi Outdated
@overload
def keyBy(self, keyfunc: Callable[[_K], _V]) -> stream[Tuple[_K, _V]]: ...
@overload
def keyBy(self) -> stream[Tuple[_K, _K]]: ...
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type is incorrect. When keyfunc is omitted (using identity function), the method returns Tuple[_K, _K] where both elements are the same value. However, the non-overloaded signature at line 116 should use the same type variable pattern as line 113. This signature should not exist as a separate overload since the default parameter handling covers this case.

Suggested change
def keyBy(self) -> stream[Tuple[_K, _K]]: ...
# Removed redundant overload for keyBy with no arguments

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, I think redundant

Comment thread streamerate/__init__.pyi Outdated
@overload
def min(self) -> _K: ...
@overload
def min(self, key: Callable[[_K], _V]) -> _V: ...
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type is incorrect. The built-in min() function with a key parameter returns the minimum element from the iterable, not the minimum key value. When a key function is provided, it should return _K, not _V. The key function is only used for comparison purposes.

Suggested change
def min(self, key: Callable[[_K], _V]) -> _V: ...
def min(self, key: Callable[[_K], _V]) -> _K: ...

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right

Comment thread streamerate/__init__.pyi Outdated
@overload
def max(self) -> _K: ...
@overload
def max(self, key: Callable[[_K], _V]) -> _V: ...
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type is incorrect. The built-in max() function with a key parameter returns the maximum element from the iterable, not the maximum key value. When a key function is provided, it should return _K, not _V. The key function is only used for comparison purposes.

Suggested change
def max(self, key: Callable[[_K], _V]) -> _V: ...
def max(self, key: Callable[[_K], _V]) -> _K: ...

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 4, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a comprehensive public type stub streamerate/__init__.pyi; adjusts exception re-raising in streamerate/streams.py; updates tests to assert preserved tracebacks and a modified exception signature; bumps package Python minimum and version; removes several environment/requirements entries; trims CI matrix to Python 3.10–3.13.

Changes

Cohort / File(s) Summary
Public type stubs
streamerate/__init__.pyi
New, large type-stub declaring public API: stream, slist, sset, sdict, defaultstreamdict, buffered-stream types, helpers (ItrFromFunc, TqdmMapper), utility functions (smap, sfilter, iter_except) and populated __all__. Declarative only (no implementations).
Runtime exception handling
streamerate/streams.py
Adds _MapException.get_adjusted_exception() and replaces direct exc_info-based re-raises with get_adjusted_exception() calls so exceptions are re-raised with preserved type/value/traceback across mapping/worker paths.
Tests (traceback behavior)
streamerate/tests/test_Streams.py
Renamed a test method, added/updated tests asserting correct traceback lines for various map modes, changed SomeCustomException constructor to accept an extra parameter and updated all raises/expectations accordingly.
CI workflow
.github/workflows/python-package.yml
Removed Python 3.8 and 3.9 from the test matrix; CI now targets Python 3.10–3.13.
Packaging / deps
pyproject.toml
Bumped version to 1.2.1 and raised Python requirement to >=3.10; adjusted dependency and dev-dependency constraints for 3.10+ compatibility.
Removed environment files / deps
environment.yml, requirements-dev.txt, requirements.txt
Removed the Conda environment spec and cleared/removed several dependencies (including eventlet, pydantic, tblib, throttlex, tqdm) and dev requirement entries.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant MapWorker
  participant _MapException
  Note right of MapWorker: mapping function runs and raises
  Caller->>MapWorker: submit element for mapping
  MapWorker-->>_MapException: capture exc_info and wrap
  MapWorker-->>Caller: propagate wrapper
  Caller->>_MapException: call get_adjusted_exception()
  _MapException-->>Caller: returns exception reconstructed with original traceback
  Note over Caller,_MapException: re-raise uses reconstructed exception to preserve traceback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

  • Review focus:
    • Completeness and correctness of the public type-stub signatures (generics, overloads, all).
    • Correct behavior and edge cases in _MapException.get_adjusted_exception() and all replaced raise sites.
    • Tests: ensure updated exception constructor usage and asserted traceback lines match actual frames.
    • Packaging/CI impact from Python minimum bump and removed dependency entries.

Poem

🐇 I hopped through stubs with nimble paws,

I lined each stream with careful laws,
Tracebacks kept their proper face,
CI hopped up to a newer base,
A rabbit cheers for tidy space.

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'More or less complete type stub' directly reflects the main objective stated in the PR description. The PR's primary purpose is adding a comprehensive type stub file (streamerate/init.pyi) for type checkers, which is clearly captured by the title.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch type_stub

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76c7117 and 931f4b0.

📒 Files selected for processing (1)
  • streamerate/__init__.pyi (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build (windows-latest, 3.8)
  • GitHub Check: build (macos-latest, 3.8)
  • GitHub Check: build (windows-latest, 3.13)

Comment thread streamerate/__init__.pyi
Comment on lines +148 to +151
def toSet(self) -> sset[_K]: ...
def toList(self) -> slist[_K]: ...
def sorted(self, key: Any = None, reverse: bool = False) -> slist[_K]: ...
def toMap(self: stream[Tuple[_T, _V]]) -> sdict[_T, _V]: ...
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore the cmp parameter on sorted.

The runtime API still accepts a cmp callable, so omitting it here makes type checkers reject valid calls. Please add the parameter back in the stub.

Apply this diff:

-    def sorted(self, key: Any = None, reverse: bool = False) -> slist[_K]: ...
+    def sorted(
+        self,
+        key: Optional[Callable[[_K], Any]] = None,
+        cmp: Optional[Callable[[_K, _K], int]] = None,
+        reverse: bool = False,
+    ) -> slist[_K]: ...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def toSet(self) -> sset[_K]: ...
def toList(self) -> slist[_K]: ...
def sorted(self, key: Any = None, reverse: bool = False) -> slist[_K]: ...
def toMap(self: stream[Tuple[_T, _V]]) -> sdict[_T, _V]: ...
def toSet(self) -> sset[_K]: ...
def toList(self) -> slist[_K]: ...
def sorted(
self,
key: Optional[Callable[[_K], Any]] = None,
cmp: Optional[Callable[[_K, _K], int]] = None,
reverse: bool = False,
) -> slist[_K]: ...
def toMap(self: stream[Tuple[_T, _V]]) -> sdict[_T, _V]: ...
🤖 Prompt for AI Agents
In streamerate/__init__.pyi around lines 148 to 151, the stub for sorted omits
the legacy cmp parameter while the runtime still accepts a cmp callable; restore
the parameter by updating the sorted signature to include cmp:
Optional[Callable[[Any, Any], int]] = None (after key and before reverse or with
appropriate placement), and adjust imports/typing if needed so type checkers
accept calls using cmp without breaking existing annotations.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cant find any cmp

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the implementation of the sorted method in the streamerate codebase
# First, find Python source files (not stubs)
fd -e py -E "*.pyi" | head -20

# Search for the sorted method definition
rg -n -A5 "def sorted" --type py -g "!*.pyi"

Length of output: 580


@MorariMaxim You're absolutely right, and I apologize for the confusion.

I've verified the actual implementation in streamerate/streams.py (line 861), and the sorted method only accepts key and reverse parameters:

def sorted(self, key=None, reverse=False):
    return slist(sorted(self, key=key, reverse=reverse))

There is no cmp parameter in the implementation. My previous suggestion was incorrect—the stub is actually correct as-is. The cmp parameter was removed from Python 3's sorted() built-in, and this library doesn't support it either.

Would you like me to resolve my previous comment since it's not valid?

Comment thread streamerate/__init__.pyi
Comment on lines +197 to +199
def zip(self) -> stream[object]: ...
def distinct(self, key: Optional[Callable[[_K], _V]] = None) -> stream[_K]: ...
def unique(self, key: Optional[Callable[[_K], _V]] = None) -> stream[_K]: ...
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Model zip as a true var-args zipper.

The concrete implementation forwards to itertools.zip, so callers can (and do) pass additional iterables. The current stub forbids that and drops tuple typing. Please expose the var-args and tuple result.

Apply this diff:

-    def zip(self) -> stream[object]: ...
+    def zip(self, *iterables: Iterable[Any]) -> stream[Tuple[Any, ...]]: ...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def zip(self) -> stream[object]: ...
def distinct(self, key: Optional[Callable[[_K], _V]] = None) -> stream[_K]: ...
def unique(self, key: Optional[Callable[[_K], _V]] = None) -> stream[_K]: ...
def zip(self, *iterables: Iterable[Any]) -> stream[Tuple[Any, ...]]: ...
def distinct(self, key: Optional[Callable[[_K], _V]] = None) -> stream[_K]: ...
def unique(self, key: Optional[Callable[[_K], _V]] = None) -> stream[_K]: ...
🤖 Prompt for AI Agents
In streamerate/__init__.pyi around lines 197–199, the zip stub is currently
non-variadic and returns stream[object]; change it to model zip as a true
var-args zipper that accepts any number of additional iterables and returns a
stream of tuples containing the zipped elements. Replace the current signature
with a variadic-iterables version (e.g. def zip(self, *others: Iterable[Any]) ->
stream[Tuple[Any, ...]]) or, if you want precise typing, use PEP 646
TypeVarTuple and Unpack to express def zip(self, *others: Iterable[Unpack[Ts]])
-> stream[Tuple[_K, Unpack[Ts]]]; ensure required typing imports (TypeVarTuple,
Unpack, Tuple, Iterable, Any) are present and fall back to Tuple[Any, ...] when
PEP 646 is not used.

Copy link
Copy Markdown
Collaborator Author

@MorariMaxim MorariMaxim Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are talking about implementation here, its fixed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current state of the zip method in the stub file
cd /tmp/repos/asuiu/streamerate
git checkout type_stub
grep -n -A 1 "def zip" streamerate/__init__.pyi || echo "Pattern not found"

Length of output: 278


🏁 Script executed:

#!/bin/bash
# Check the latest commits on the PR branch and the current state of the file
git log --oneline -5 type_stub 2>/dev/null || git log --oneline -5

# Show the exact current content around the zip method
sed -n '195,200p' streamerate/__init__.pyi

Length of output: 649


🏁 Script executed:

#!/bin/bash
# Check the actual implementation file to see what zip does
if [ -f streamerate/__init__.py ]; then
    echo "=== Checking actual implementation of zip method ==="
    grep -n -A 10 "def zip" streamerate/__init__.py | head -20
else
    echo "Implementation file not found"
fi

# Also check if there are any other Python files in streamerate
ls -la streamerate/

Length of output: 408


🏁 Script executed:

#!/bin/bash
# Check the actual zip implementation in streams.py
echo "=== Checking zip method in streams.py ==="
grep -n -B 2 -A 10 "def zip" streamerate/streams.py

# Also check what __init__.py imports/exports
echo ""
echo "=== Checking __init__.py ==="
cat streamerate/__init__.py

Length of output: 853


@MorariMaxim, you're absolutely right, and I apologize for the incorrect suggestion.

I reviewed the actual implementation in streams.py (line 1183):

def zip(self) -> "stream[_V]":
    return stream(zip(*(self.toList())))

The method indeed takes no parameters - it unpacks the stream's own elements into Python's builtin zip(). Your stub signature correctly reflects this. My earlier suggestion to add *iterables: Iterable[Any] was wrong.

The only minor refinement you might consider is the return type - stream[Tuple[Any, ...]] would be slightly more precise than stream[object], but that's optional and the current signature is functionally correct.

Thank you for the correction! 🐰

 /\___/\
( o   o )
(  =^=  )  "You're right,
(        )  my mistake!"
(         )
(          )))))))

asuiu
asuiu previously approved these changes Nov 4, 2025
Copy link
Copy Markdown
Owner

@asuiu asuiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
pyproject.toml (1)

19-21: Minor oddity in gevent constraint syntax.

The gevent dependency uses a matrix syntax with a redundant Python constraint and an unusual upper bound:

gevent = [
    {version = ">=24.2.1", python = ">=3.10,<=4.0.0"}
]

The python = ">=3.10,<=4.0.0" is redundant since the project already requires python >= 3.10. Additionally, <=4.0.0 is unusual when the project constraint is <4.0.0. Simplify to:

gevent = ">=24.2.1"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f20f2d and e39306f.

📒 Files selected for processing (1)
  • pyproject.toml (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build (macos-latest, 3.11)
  • GitHub Check: build (macos-latest, 3.10)
  • GitHub Check: build (windows-latest, 3.11)
🔇 Additional comments (4)
pyproject.toml (4)

16-16: Verify Python 3.10+ minimum is reflected across all tooling and CI.

This is a breaking change that bumps the minimum from (presumably) 3.9 to 3.10. Ensure this is:

  • Documented in release notes or migration guide
  • Reflected in the CI workflow matrix (the AI summary mentions trimming to 3.10–3.13)
  • Tested across all target Python versions

Please confirm that CI workflows have been updated to reflect the new Python 3.10–3.13 matrix and that all dependencies in pyproject.toml are compatible with Python 3.10+.


25-27: Conditional Python constraints in dev dependencies are acceptable but could be clearer.

Both pandas and pydantic are now conditional on Python >= 3.10. Since these are dev dependencies, this is acceptable given the project's new minimum. However, consider documenting why these specific versions require Python 3.10+ (e.g., if they dropped support for earlier versions).

Also applies to: 32-34


8-8: Version consistency note between tool.poetry and tool.commitizen.

Line 8 documents that the poetry version is "kept in sync with tool.commitizen config via automation," but the commitizen version on line 85 is "1.0.5" while the poetry version is "1.2.1". If these should be in sync, verify the automation is working correctly. If they serve different purposes (e.g., commitizen tracks conventional commits schema version rather than package version), clarify the comment on line 8.

Also applies to: 85-85


28-28: pyxtension patch bump is safe.

The minor version bump from 1.17.0 to 1.17.1 is a patch update and should be backward compatible. ✓

@asuiu asuiu merged commit 1f1203e into master Nov 4, 2025
18 checks passed
@asuiu asuiu deleted the type_stub branch November 4, 2025 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants