Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(typing): type request module #2271

Merged
merged 22 commits into from
Aug 26, 2024
Merged

Conversation

CaselIT
Copy link
Member

@CaselIT CaselIT commented Aug 14, 2024

Type sync and async request modules.

Also move the attribute docs next to the actual attribute declaration, to improve usability with editors.

Some additional odd and end typing improvements

Also incorporate #2205:

Co-authored-by: Dave Tapley <dave@tapley.com>

@CaselIT CaselIT mentioned this pull request Aug 14, 2024
13 tasks
@CaselIT CaselIT requested a review from vytas7 August 14, 2024 21:52
Copy link

codecov bot commented Aug 14, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (df2debe) to head (9f8231b).
Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #2271   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           63        63           
  Lines         7249      7326   +77     
  Branches      1268      1275    +7     
=========================================
+ Hits          7249      7326   +77     

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

Copy link
Member Author

@CaselIT CaselIT left a comment

Choose a reason for hiding this comment

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

@vytas7 I've marked a couple of point where I've done something slightly different. Honestly not sure we gain much by doing that change

# gets a None back on the first reference to property, it
# probably isn't going to access the property again (TBD).
if self._cached_if_match is None:
if self._cached_if_match is MISSING:
Copy link
Member Author

Choose a reason for hiding this comment

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

this is different

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer MISSING than None.

def if_none_match(self):
if self._cached_if_none_match is None:
def if_none_match(self) -> Optional[List[Union[ETag, Literal['*']]]]:
if self._cached_if_none_match is MISSING:
Copy link
Member Author

Choose a reason for hiding this comment

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

this too

# gets a None back on the first reference to property, it
# probably isn't going to access the property again (TBD).
if self._cached_if_match is None:
if self._cached_if_match is MISSING:
Copy link
Member Author

Choose a reason for hiding this comment

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

this is different


(See also: RFC 7232, Section 3.2)
"""
if self._cached_if_none_match is MISSING:
Copy link
Member Author

Choose a reason for hiding this comment

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

this is different

@CaselIT CaselIT marked this pull request as draft August 14, 2024 22:37
@CaselIT CaselIT marked this pull request as ready for review August 15, 2024 09:22
@CaselIT
Copy link
Member Author

CaselIT commented Aug 15, 2024

@vytas7 it's ready for review. We need to decide what to do with python 3.7

I vote to drop it. We can do best effort for 3.8 in 4.0, but let's keep the mention that it may be dropped in following 4.x releases

@vytas7
Copy link
Member

vytas7 commented Aug 15, 2024

@vytas7 it's ready for review. We need to decide what to do with python 3.7

I vote to drop it. We can do best effort for 3.8 in 4.0, but let's keep the mention that it may be dropped in following 4.x releases

Yes, all that is already described in the upcoming release notes: Changes to Supported Platforms.

Re 3.7, cannot we just do Protocol = object in typing.py for 3.7, and things would just work? We could just write that typing is unsupported on 3.7. And then drop 3.7 and maybe 3.8 in Falcon 4.1.

@vytas7 vytas7 changed the title Type request module feat(typing): type request module Aug 15, 2024
@CaselIT
Copy link
Member Author

CaselIT commented Aug 15, 2024

Re 3.7, cannot we just do Protocol = object in typing.py for 3.7, and things would just work? We could just write that typing is unsupported on 3.7. And then drop 3.7 and maybe 3.8 in Falcon 4.1.

I'm not really sold on doing this. Since I don't think type checkers will like it. In my opinion we either do something like https://github.com/falconry/falcon/blob/7341f96eab52f98b16080f2f458fa5fd3799f4f5/falcon/_typing_extensions.py or we drop it.
Doing the aliasing seems an hack

@CaselIT
Copy link
Member Author

CaselIT commented Aug 24, 2024

not sure what's up with coverage.

Copy link
Member

@vytas7 vytas7 left a comment

Choose a reason for hiding this comment

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

Looks great, thanks! 👍

I would just like to make a decision on that MISSING constant before proceeding -- either we need to document it as part of the public interface, or underscore it.

@CaselIT CaselIT requested a review from vytas7 August 25, 2024 21:06
@CaselIT
Copy link
Member Author

CaselIT commented Aug 25, 2024

re missing, I've moved it to the typing module, so that we can decide with all the rest when we are done with the typing what to make public and not to

@@ -184,7 +184,8 @@

# NOTE(kgriffs): Special singleton to be used internally whenever using
# None would be ambiguous.
_UNSET = object()
_UNSET = object() # TODO: remove once replaced with missing
Copy link
Member

Choose a reason for hiding this comment

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

"Unset" sounds better to me than "missing" in the general case. Or maybe, for things request, "missing" sounds good too, but for response properties it would feel odd, like for resp.media it is more logical to say it is either set or unset.

Copy link
Member Author

Choose a reason for hiding this comment

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

In general a value can be missing. That's where the name comes from. I'm not attached to it by any means, it's just what I use at work (that I think I've copied from some other project)

We can leave a todo in the enum to rename and do that once the _UNSET object has been removed, so we avoid the confusion?

Copy link
Member

Choose a reason for hiding this comment

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

That works for me, but regardless of the name, let's underscore it for any runtime usage (i.e. outside of type annotations), so we can change it at any point as we wish.



_T = TypeVar('_T')
MISSING = _Missing.MISSING
Copy link
Member

Choose a reason for hiding this comment

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

Could we underscore this constant too then, if we are using it outside of this module?

Copy link
Member Author

Choose a reason for hiding this comment

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

my idea was to cleanup all the typing aliases at the same time in a final PR once all things are typed

_cookies_collapsed: Optional[Dict[str, str]] = None
_cached_if_match: MissingOr[Optional[List[Union[ETag, Literal['*']]]]] = MISSING
_cached_if_none_match: MissingOr[Optional[List[Union[ETag, Literal['*']]]]] = (
MISSING
Copy link
Member

Choose a reason for hiding this comment

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

Could we keep None for now? This PR isn't the smallest to follow, so ideally we should minimize the introduced runtime changes outside of typing. We probably need to profile things again otherwise (although these cached headers are probably not the most/important sensitive part; we should move the ones that are less frequently used to being cached in some _state attribute to increase perf even more).

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think we need to profile things for this change, since a comparison using is has the same time.

missing is used when the final value can include none. in those cases having a default of None is suboptimal like mentioned in the previous TODO.

I can create a new PR for this, but seemed so small a change that made sense to do inline with the typing one

Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is true for cythonized code, but since these methods are not very critical for core performance of the framework, I agree it is no big deal.

Just for reference

_OBJECT = object()


def func1(obj):
    return obj is None


def func2(obj):
    return obj is _OBJECT

⬇️

.....................
func1 (None is None): Mean +- std dev: 31.5 ns +- 0.4 ns
.....................
func1 (_OBJECT is None): Mean +- std dev: 32.0 ns +- 2.0 ns
.....................
func2 (None is _OBJECT): Mean +- std dev: 37.0 ns +- 0.6 ns
.....................
func2 (_OBJECT is _OBJECT): Mean +- std dev: 36.9 ns +- 1.0 ns

This is because is None in Cython can be implemented as checking a C pointer vs NULL, whereas a global or local constant still needs to be resolved from name first.

But as said, I don't think this difference is enough to bother. But I do want to underscore these constants when used in runtime, so _Missing, _MISSING, _UNSET, etc.

Copy link
Member Author

Choose a reason for hiding this comment

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

as mentioned my idea was to worry about the public/private in a final cleanup PR. but if you prefer it here I can change it here.

personally I think all the properties could be re-written using a memorizing property since request has dict in any case

Copy link
Member

Choose a reason for hiding this comment

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

OK, but let's not forget about that cleanup then!

Copy link
Member Author

Choose a reason for hiding this comment

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

roger. Should we also try the momoizing prop approach?

I think it would end up faster both in init than in use

Copy link
Member

Choose a reason for hiding this comment

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

Dunno, we could maybe focus on getting 4.0 out the door first, and then circle back on rebuilding benchmarks, Codspeed, and optimizations.

I was thinking to add a private ._state attribute, object or dict (whatever suits better), and then store less used cached header properties there. Also store stuff like the state of dependent middleware which we would deprecate and provide a shim for.

Copy link
Member Author

Choose a reason for hiding this comment

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

if we keep __dict__ in request, memoized properties are all faster, since they avoid the property after the first run

Copy link
Member

Choose a reason for hiding this comment

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

Re cleanup/privatizing, I added a note on #1457.

Copy link
Member Author

Choose a reason for hiding this comment

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

but sure let's just keep it in mind

@vytas7 vytas7 merged commit 0bd3dc2 into falconry:master Aug 26, 2024
37 checks passed
@CaselIT CaselIT deleted the type_request branch August 26, 2024 09:18
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