-
-
Notifications
You must be signed in to change notification settings - Fork 953
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
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
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. |
There was a problem hiding this 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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is different
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is different
@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 |
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. |
# Conflicts: # falcon/request.py
# Conflicts: # falcon/typing.py # falcon/util/uri.py
not sure what's up with coverage. |
There was a problem hiding this 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.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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: