Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Typed ops.pebble.py #773

Merged
merged 6 commits into from
Jun 24, 2022
Merged

Typed ops.pebble.py #773

merged 6 commits into from
Jun 24, 2022

Conversation

PietroPasotti
Copy link
Contributor

@PietroPasotti PietroPasotti commented Jun 9, 2022

Added types to pebble.py

  • several minor API changes (List[str] --> Iterable[str], adding conversions and adapting error messages where necessary)
  • small changes to model.py to remove the # type: ignore hanging around the pebble calls

Checklist

  • Have any types changed? If so, have the type annotations been updated?
  • [NA] If this code exposes model data, does it do so thoughtfully, in a way that aids understanding?
  • [NA] Do error messages, if any, present charm authors or operators with enough information to solve the problem?

QA steps

tox -e static

Changelog

  • typed ops/pebble.py
  • integrated the new types with ops/model.py (previously types were ignored when pebble calls were involved)

@PietroPasotti
Copy link
Contributor Author

@benhoyt I noticed some inconsistencies in how Client methods check (or don't) inputs.
Many methods used to be typed List[str], are now Iterable[str].
Some methods raise TypeErrors if the input value is not iterable (and/or if the generated values aren't strings), but some others do not. This feels like a good time to consider adding a little utility _try_cast_list_of_strings(obj, allow_none=False) that raises uniformly for all the api methods where Iterable[str] is required? Do you agree?

@PietroPasotti
Copy link
Contributor Author

@benhoyt I noticed some inconsistencies in how Client methods check (or don't) inputs. Many methods used to be typed List[str], are now Iterable[str]. Some methods raise TypeErrors if the input value is not iterable (and/or if the generated values aren't strings), but some others do not. This feels like a good time to consider adding a little utility _try_cast_list_of_strings(obj, allow_none=False) that raises uniformly for all the api methods where Iterable[str] is required? Do you agree?

Also, I swapped around some arguments to make it more transparent that socket_path:str is a required argument throughout, because the type hints were inconsistent and I thought that eventually an error would be raised if it was None. Can you please double check that?

ops/pebble.py Outdated Show resolved Hide resolved
Copy link
Contributor

@pengale pengale left a comment

Choose a reason for hiding this comment

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

Overall, looks good. Had one minor comment.

I like how many "fixmes" are going away :-)

ops/model.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@benhoyt benhoyt left a comment

Choose a reason for hiding this comment

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

Thanks for your work on this. I've left a lot of comments. Overall I'm happy to move in the direction of checking the type annotations -- we're using (some) type annotations, so I agree we should check them otherwise they'll get stale. And as you've noted, some or inconsistent already (list vs iterable).

That said, I think this PR as it stands is too invasive and complex for what we need. We're primarily concerned about "typing" the public API, right? In which case I'd much prefer to limit this to add types to just the public parts. I also don't think we should refactor, add helper functions, and so on in this PR -- if needed we can refactor in subsequent PRs. I'm concerned the complexity of this change will introduce subtle changes (I've noted a couple in my comments).

So I think we should reduce the changes here and limit the scope of this PR to just typing (the public parts of the API). IMO we should get it type checking in a fairly minimal and non-invasive way, then if we want to add more typing or do further refactoring, we can do that later in other PRs.

ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@benhoyt benhoyt left a comment

Choose a reason for hiding this comment

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

This is significantly lighter and less invasive now, thanks! I'd probably still prefer the complicated internal I/O stuff to be untyped, but that's more of a preference.

Yeah, Python text vs bytes I/O is a pain in the neck. I remember writing this same API in the Go client versus this Python one, and the Go io.Reader and io.Writer abstractions (and Go's str being essentially equivalent to Python's bytes) made this soooo much easier in Go. In Python it's so hard to tell what a given "file-like object" supports, whether it's a reader or writer, when it returns str vs bytes, and so on.

I don't think it's so much that it requires refactoring, but just that Python's I/O is kinda messy and vague. I pored over the documentation at the time, but still had to trial-and-error a lot of stuff.

In any case, thanks for your efforts on this, and for the updates.

ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
Copy link
Contributor

@rwcarlsen rwcarlsen left a comment

Choose a reason for hiding this comment

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

Looks pretty good. Just some mostly minor suggestions below.

ops/model.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated
'last-added': str,
'last-shown': Optional[str],
'expire-after': str,
'repeat-after': str})
Copy link
Contributor

@rwcarlsen rwcarlsen Jun 14, 2022

Choose a reason for hiding this comment

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

I agree that we should probably move the specific case types (e.g. from_dict ones) next to where they are used. Repeating the if TYPE_CHECKING is okay.

ops/pebble.py Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Show resolved Hide resolved
ops/pebble.py Outdated
Comment on lines 1799 to 1801
deadline = time.time() + timeout if timeout is not None else None

while timeout is None or time.time() < deadline:
while timeout is None or time.time() < deadline: # type: ignore
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a bit confusing. pyright is too dumb to figure out that deadline and timeout are both either None or not None together. But this also basically means that deadline could just have if timeout is not None else 0 and then we don't need any type annotations anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, true, again I tried to refrain from introducing innocent-seeming refactors as part of this PR, fearing to add subtle bugs. In this specific case it does look very innocent, so... @benhoyt can you help confirm?

ops/pebble.py Outdated Show resolved Hide resolved
ops/pebble.py Outdated Show resolved Hide resolved
@PietroPasotti
Copy link
Contributor Author

@rwcarlsen RE: moving the from_dict types closer to where they are used: I'd like that too, but the problem is that there is some cross-referencing going on (even recursive types, possibly). So I'd rather have them all in one place to make that easier.

I think in the future we could think about moving all of the types to stub files, to keep the source clean.

@jnsgruk jnsgruk merged commit 5543d22 into canonical:main Jun 24, 2022
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants