Skip to content

DM-55832: Clean up some type annotations - #154

Merged
timj merged 22 commits into
mainfrom
tickets/DM-55832
Aug 14, 2026
Merged

DM-55832: Clean up some type annotations#154
timj merged 22 commits into
mainfrom
tickets/DM-55832

Conversation

@timj

@timj timj commented Aug 14, 2026

Copy link
Copy Markdown
Member

Checklist

  • ran Jenkins
  • added a release note for user-visible changes to doc/changes

@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 61.83206% with 50 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.31%. Comparing base (e719ee8) to head (6404814).
⚠️ Report is 23 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
python/lsst/resources/gs.py 6.25% 15 Missing ⚠️
python/lsst/resources/http.py 66.66% 6 Missing ⚠️
python/lsst/resources/s3.py 50.00% 5 Missing ⚠️
.../resources/_resourceHandles/_baseResourceHandle.py 69.23% 0 Missing and 4 partials ⚠️
python/lsst/resources/s3utils.py 69.23% 4 Missing ⚠️
python/lsst/resources/_resourcePath.py 72.72% 3 Missing ⚠️
python/lsst/resources/dav.py 72.72% 3 Missing ⚠️
python/lsst/resources/davutils.py 62.50% 3 Missing ⚠️
python/lsst/resources/file.py 57.14% 3 Missing ⚠️
python/lsst/resources/proxied.py 40.00% 3 Missing ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #154      +/-   ##
==========================================
- Coverage   83.38%   83.31%   -0.08%     
==========================================
  Files          36       36              
  Lines        7995     8025      +30     
  Branches      960      972      +12     
==========================================
+ Hits         6667     6686      +19     
- Misses       1032     1043      +11     
  Partials      296      296              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@timj
timj requested a review from TallJimbo August 14, 2026 19:14
@timj
timj force-pushed the tickets/DM-55832 branch 2 times, most recently from 13290aa to 6583d78 Compare August 14, 2026 19:19
echo "Enable upload"
echo "skip=false" >> "${GITHUB_OUTPUT}"
fi

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume the eventual plan is to move this somewhere reusable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes. It's on @mwittgen 's todo list.... so currently we have to copy it to every pypi repo to make sure we don't upload something to PyPI every weekly.

Comment thread python/lsst/resources/_resourcePath.py Outdated
Comment thread python/lsst/resources/file.py Outdated
*,
encoding: str | None = None,
) -> Generator[IO, None, None]:
) -> Generator[ResourceHandleProtocol, None, None]:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If ResourceHandleProtocol doesn't implement typing.IO[Any] that might be a real problem. Or at least I'd like to understand what's missing.

Or is this a case where ResourceHandleProtocol has extra methods and the caller might want to use them?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

ResourceHandleProtocol seems to define all the methods you would expect an IO to support without saying it is an IO. Maybe that was intentional but seems like it would be preferable for it to be an IO.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It seems like:

  1. A protocol can not inherit from IO
  2. We could have BaseResourceHandle inherit from IO
  3. There is a mismatch between isatty as a property vs a method.
  4. The readlines signature is also wrong.
  5. There is no __iter__.
  6. The write() methods need to accept memoryview and bytearray and they don't.

The isatty is the one that breaks everything.

@natelust what was your reasoning for not quite having an IO?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure this was it, but I think back when this was written, there was some dispute (or maybe just confusion on our end) about whether typing.IO or io.IOBase (or one of its subclasses) was the annotation we wanted to satisfy.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Either way isatty() is always a method and not a property.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We likely should fix all this but not on this ticket.

@timj
timj force-pushed the tickets/DM-55832 branch 2 times, most recently from fae1334 to f098913 Compare August 14, 2026 19:39
timj and others added 19 commits August 14, 2026 13:14
They don't recognize abstractproperty as being special.
Also fixes the S3 boto.client annotation which was also wrong
The encoding parameter was declared third positionally, where the
AbstractFileSystem base class declares block_size. A caller passing
block_size positionally would have bound it to encoding instead.
Move encoding after the base class parameters and make it keyword-only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ResourcePath.__new__ dispatches on scheme and so is declared as
returning the base class. ty follows that declaration and reports
subclass attributes as unresolved; mypy resolves the constructor to
the subclass and rejects a cast as redundant. A ty-specific
suppression is the only form both checkers accept.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The requests stubs declare AuthBase.__call__ with a parameter named r,
which callers may pass by keyword, so an override naming it req is not
a valid substitute.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
defusedxml.ElementTree does not re-export Element, so eTree.Element was
unresolvable for type checkers. defusedxml hardens the parser but builds
trees from the standard library element type, so import Element directly
for the annotations. Parsing still goes through eTree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Traversable.open is overloaded on Literal["r"] and Literal["rb"], so a
variable mode could not select an overload and needed a call-overload
suppression. Branch on the mode explicitly instead, which also normalizes
modes such as "br" that zipfile.Path rejects but pathlib.Path accepts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The boto3 section only matched the top level module, so importing
boto3.s3.transfer still required an inline suppression. Widen it to
boto3.* to match the existing botocore.* handling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ResourcePath.open documents encoding as being ignored for binary IO, but
FileResourceHandle and the prefer_file_temporary path both forwarded it
into the builtin open(), which raises ValueError for a binary mode. Drop
it in binary mode, as is already done for newline, so that all schemes
agree.

Covered by the shared GenericReadWriteTestCase.test_open so every scheme
is exercised.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
defusedxml ships no type information, so importing it as eTree left every
XML call typed as Any, and the conditional fallback import of the stdlib
module was reported as a redefinition by mypy in some environments.

Give type checkers the standard library module that defusedxml hardens and
mirrors, while the runtime continues to prefer defusedxml when available.
This also makes the defusedxml mypy override unnecessary.
backoff, moto and aiohttp all ship a py.typed marker, so their
ignore_missing_imports overrides no longer have any effect.

The wsgidav override is retained. It is reported as unused only because
CI type checks the python directory, and wsgidav is imported solely by
the tests.
@timj
timj force-pushed the tickets/DM-55832 branch from f098913 to 483f700 Compare August 14, 2026 20:14
The fsspec HTTP backend in the http module imports aiohttp directly, but
it was only ever installed as a transitive dependency of s3fs during
testing and was absent entirely when type checking.

List it in the https extra and in requirements.txt so that it is present
wherever the code that uses it is exercised or checked.
@timj
timj merged commit b52b75b into main Aug 14, 2026
24 of 27 checks passed
@timj
timj deleted the tickets/DM-55832 branch August 14, 2026 21:44
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.

2 participants