Skip to content

Conversation

@an-swe
Copy link

@an-swe an-swe commented Nov 5, 2025

Add PEP 561 compliant type stub files to enable static type checkers (Pylance, Pyright, mypy) to resolve dynamically created enums.

The sc2.data module creates enums at runtime using enum.Enum() with protobuf descriptors, which are invisible to static analysis tools.

an-swe and others added 2 commits November 4, 2025 20:08
Add PEP 561 compliant type stub files to enable static type checkers
(Pylance, Pyright, mypy) to resolve dynamically created enums.

The sc2.data module creates enums at runtime using enum.Enum() with
protobuf descriptors, which are invisible to static analysis tools.
@BurnySc2
Copy link
Owner

Very interesting addition!

Is the (empty) py.typed file and the change in pyproject.toml required?
Using pylance, I'm also experiencing issues when accessing the .value of enums, see screenshot.
image

I experimented a bit and it does seem that the protobuf message implementation differs a bit from python enum values.
E.g. common_pb.Race.Terran does return 1, so indeed the actual attributes are mapped to integers.

I added a commit starting stubs for s2clientprotocol in general, let me know what you think about it.
This could address issues for python-sc2 developers especially for position.py.
I'm wondering if there should be a separate package s2clientprotocol-stubs because if I ship the stubs (from s2clientprotocol folder) with this library, it could create issues if other libraries also do that.

image image

@an-swe
Copy link
Author

an-swe commented Nov 10, 2025

Hi @BurnySc2 ,

Thanks for pushing that commit, your fix for the protobuf enums (using : int) is perfect.

To answer your questions: Yes, the py.typed file and the pyproject.toml changes are both required by PEP 561 to make sure the stubs are packaged and recognized by type checkers.

Regarding the separate s2clientprotocol-stubs package, you make a valid point. However, I'd prefer we merge this as-is.

Shipping the stubs directly with this library will immensely help developers by providing clear typing and IntelliSense right out of the box. The practical benefit seems to outweigh the potential for a future conflict, which we can always address later if it actually becomes a problem.

@BurnySc2
Copy link
Owner

Thanks for your quick feedback.

Alright, I'll ship stubs with this package. Once conflicts with other packages come up (my guess is, that it is unlikely that others will put that much effort into stubs), one will have to resolve them.

I'll add more stubs for the s2clientprotocol over the next couple days and then would like to ask you to review, if it works properly with your mypy setup (I guess that's what you are using). I'll also ask people from discord to review the PR then.
I'll write another comment once I think I'm done.

@chadspratt
Copy link

in client.py, should color: tuple[float, float] be [float, float, float]?
in game_data.py:239, seems weird to me that it does self.race.value == Race.Zerg instead of what it previously was, or self.race.value == Race.Zerg.value. but maybe i don't understand python enums well enough

@BurnySc2
Copy link
Owner

BurnySc2 commented Nov 18, 2025

@an-swe I believe I did enough (probably too many) changes related to type hinting and stubs. The mpyq package could need stubs but that doesn't seem that important.
The PR should be ready for review. I put the link to this PR in the discord channel for other bot authors to review / test out and I give it a few days, maybe weeks, before I merge it and then push a new version to pypi.
There are still a lot of type hints remaining that need to be added, especially main.py and sc2process.py.

Edit:
Perhaps the enums can be changed from Enum to IntEnum instead (or in a separate PR), then they would behave similar to protobuf enums, is my guess:

from enum import Enum, IntEnum
class Test1(Enum):
    A = 1
    B = 2
class Test2(IntEnum):
    A = 1
    B = 2

print(Test1.A)
print(Test1.A.name)
print(Test1.A.value)
print(Test1.A == 1)
""" Output:
Test1.A
A
1
False
"""

print(Test2.A)
print(Test2.A.name)
print(Test2.A.value)
print(Test2.A == 1)
""" Output:
Test1.A
A
1
True
"""

@an-swe
Copy link
Author

an-swe commented Nov 19, 2025

@BurnySc2 Huge thanks for the effort on this! The stubs look great.

Regarding IntEnum: I think that's a great idea, but we probably want to do it in a fresh PR. Since changing to IntEnum affects runtime logic (comparisons against integers), I wouldn't want to risk breaking things inside this already large PR.

Aside from the failing CI checks, this looks ready to me.

I will try testing this stub change on my IDE later this week.

@BurnySc2
Copy link
Owner

Related: #168

@BurnySc2
Copy link
Owner

@an-swe Hey, any progress on the review? I'd like to merge this within the next 2 weeks. Of course there are holidays now so I can understand if you are busy.

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