Skip to content

strengthen pyright detection#38

Open
robin523790 wants to merge 8 commits into
timrid:mainfrom
robin523790:feature/strengthen-pyright-detection
Open

strengthen pyright detection#38
robin523790 wants to merge 8 commits into
timrid:mainfrom
robin523790:feature/strengthen-pyright-detection

Conversation

@robin523790

@robin523790 robin523790 commented Jul 2, 2026

Copy link
Copy Markdown
import construct as cs
import construct_typed as cst

@dataclasses.dataclass
class MyClass(cst.DataclassMixin):
    my_param: bytes = cst.csfield(cs.Bytes(30), doc="""test""")

c = MyClass()

Above code did not trigger a Pyright error but failed at runtime.

This PR introduces @overloads to csfield() that allow Pyright to ensure that:

  • fields that are not provided now raise errors
  • Const fields that are provided now raise errors (as these must only be provided in the declaration)
  • Default fields can be provided but do not have to (as the default value may be overriden)

This PR includes the migration to uv and the version bump to 0.8.0 found here: #37.

BREAKING CHANGES
This PR introduces breaking changes, thus the version has been bumped to 0.8.0.

  • Default fields are only treated as optional if they have an explicit (and redundant) default= marker. A wrapper method csdefault_field() has been provided for convenience.
class MyClass(cst.DataclassMixin):
    my_default_1: bytes = cst.csfield(cs.Default(cs.Bytes(30), bytes(30)), doc="""test""", default=bytes(30))
    my_default_2: bytes = cst.csdefault_field(cs.Bytes(5), bytes(5), doc="""test""")
  • Any fields following a Default field must have the kw_only= marker and must be provided by keyword.
class MyClass(cst.DataclassMixin):
    my_default: bytes = cst.csdefault_field(cs.Bytes(30), bytes(30), doc="""test""")
    my_param: bytes = cst.csfield(cs.Bytes(5), doc="""test""", kw_only=True)

c1 = MyClass(b"Hello")  # WRONG
c2 = MyClass(my_param=b"World")  # correct

@robin523790 robin523790 force-pushed the feature/strengthen-pyright-detection branch from 07bd2e1 to 9653daa Compare July 15, 2026 09:42
@timrid timrid requested a review from Copilot July 15, 2026 09:53

Copilot AI left a comment

Copy link
Copy Markdown

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 strengthens static type checking (Pyright) around construct_typed dataclass fields by refining typing behavior for csfield()/Default/Const/Computed patterns, aiming to prevent cases that previously passed type checking but failed at runtime.

Changes:

  • Added a set of @overload signatures for csfield() and introduced csdefault_field() to better model Default-field optionality and forbidden init parameters for Const/Computed/Pass-like constructs.
  • Updated Construct stubs to improve typing for Computed and singleton constructs (Tell, Pass, Terminated) used by the new overloads.
  • Expanded/adjusted tests to cover Default/Computed/Padded behavior and updated expected values accordingly.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
tests/test_typed.py Updates/adds tests intended to validate Pyright behavior for const/default/computed/padded fields.
construct-stubs/core.pyi Refines stub typing for Computed and makes Tell/Pass/Terminated usable as distinct singleton types.
construct_typed/dataclass_struct.py Adds overload-driven typing for csfield() and adds csdefault_field() to model Default-field semantics.
construct_typed/__init__.py Exports csdefault_field from the package API.

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

Comment thread construct_typed/dataclass_struct.py Outdated
Comment thread tests/test_typed.py Outdated
Comment thread tests/test_typed.py
Comment on lines +124 to +137
orig_subcon = subcon

# Rename subcon, if doc or parsed are available
if (doc is not None) or (parsed is not None):
if doc is not None:
doc = textwrap.dedent(doc).strip("\n")
subcon = cs.Renamed(subcon, newdocs=doc, newparsed=parsed)

if orig_subcon.flagbuildnone is True:
init = False
default = None
else:
init = True
default = dataclasses.MISSING

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@timrid This looks bogus to me. What do you think?

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