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

make asyncio.iscoroutinefunction a deprecated alias of inspect.iscoroutinefunction and remove asyncio.coroutines._is_coroutine #94912

Closed
graingert opened this issue Jul 17, 2022 · 34 comments
Labels
topic-asyncio type-feature A feature request or enhancement

Comments

@graingert
Copy link
Contributor

graingert commented Jul 17, 2022

currently asyncio.iscoroutinefunction and inspect.iscoroutinefunction behave differently in confusing and hard to document ways. It's possible to bring them into alignment but I think it would be better to make asyncio.iscoroutinefunction a deprecated alias of inspect.iscoroutinefunction and remove asyncio.coroutines._is_coroutine.

This is now possible with the recent removal of @asyncio.coroutine and support for AsyncMock and other duck-type functions in inspect.iscoroutinefunction

the only caveats are - what should happen to users of the asyncio.coroutines._is_coroutine mark? eg:

@types.coroutine
def _wrap_awaitable(awaitable):
"""Helper for asyncio.ensure_future().
Wraps awaitable (an object with __await__) into a coroutine
that will later be wrapped in a Task by ensure_future().
"""
return (yield from awaitable.__await__())
_wrap_awaitable._is_coroutine = _is_coroutine

and all of these:

Lib/test/test_asyncio/test_base_events.py:    m_socket.getaddrinfo._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        self.loop._add_reader._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        self.loop._add_writer._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        self.loop._add_reader._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        self.loop._add_writer._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        self.loop._add_reader._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        self.loop._add_writer._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        m_socket.getaddrinfo._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        m_socket.getaddrinfo._is_coroutine = False
Lib/test/test_asyncio/test_base_events.py:        self.loop._add_reader._is_coroutine = False
Lib/test/test_asyncio/test_selector_events.py:        self.loop.add_reader._is_coroutine = False
Lib/test/test_asyncio/test_subprocess.py:        protocol.connection_made._is_coroutine = False
Lib/test/test_asyncio/test_subprocess.py:        protocol.process_exited._is_coroutine = False
Lib/unittest/mock.py:    mock._is_coroutine = asyncio.coroutines._is_coroutine
Lib/unittest/mock.py:        # iscoroutinefunction() checks _is_coroutine property to say if an
Lib/unittest/mock.py:        self.__dict__['_is_coroutine'] = asyncio.coroutines._is_coroutine
@graingert graingert added the type-feature A feature request or enhancement label Jul 17, 2022
@graingert graingert changed the title make asyncio.iscoroutinefunction a deprecated alias of inspect.iscoroutinefunction and remove asyncio.coroutines._is_coroutine make asyncio.iscoroutinefunction a deprecated alias of inspect.iscoroutinefunction and remove asyncio.coroutines._is_coroutine Jul 17, 2022
@graingert
Copy link
Contributor Author

graingert commented Jul 17, 2022

changing _wrap_awaitable to be:

async def _wrap_awaitable(awaitable): 
   """Helper for asyncio.ensure_future(). 
  
   Wraps awaitable (an object with __await__) into a coroutine 
   that will later be wrapped in a Task by ensure_future(). 
   """ 
   return await awaitable

would have a few subtle changes - eg the if not called_wrap_awaitable check in asyncio.ensure_future(...) isn't needed anymore, and this would prevent wrapping objects that have .__dict__["__await__"] callables

@graingert
Copy link
Contributor Author

graingert commented Jul 17, 2022

I'm pretty sure all of the assignments in the test suite of protocol.process_exited._is_coroutine = False etc, are redundant or worse, invalidated by #94050 Edit: yes they are redundant see https://github.com/python/cpython/pull/94926/files#r922839081

the mock._is_coroutine = asyncio.coroutines._is_coroutine use in unittest.mock is also probably safe to remove?

@graingert
Copy link
Contributor Author

aha I've just checked and the protocol.process_exited._is_coroutine = False lines were made redundant in python/asyncio#459

@gvanrossum
Copy link
Member

So that's the ancient external asyncio project, which nobody should use any more. Does the corresponding code (or corresponding commits) exist in the stdlib version?

@graingert
Copy link
Contributor Author

So that's the ancient external asyncio project, which nobody should use any more. Does the corresponding code (or corresponding commits) exist in the stdlib version?

Yep, the fix was ported everywhere but the now redundant workarounds were left in, there's more info here https://github.com/python/cpython/pull/94926/files#r922839081

@kumaraditya303
Copy link
Contributor

Before making it a deprecated alias, it would be better to investigate:

  • How much code will be affected by this change?
  • How this interacts with cython coroutines?

@graingert
Copy link
Contributor Author

graingert commented Jul 18, 2022

Before making it a deprecated alias, it would be better to investigate:

* How much code will be affected by this change?

how about if I just add a deprecation notice and leave the implementation alone?

* How this interacts with cython coroutines?

inspect.iscoroutinefunction now uses inspect._signature_is_functionlike and so it should 'just work', I've let them know and will work on a cython test case to make sure

@gvanrossum
Copy link
Member

how about if I just add a deprecation notice and leave the implementation alone?

That's rarely a good strategy. Many people don't read the docs and keep using the function (if they are using it) and so once the deprecated function is deleted their code breaks without warning.

@graingert
Copy link
Contributor Author

graingert commented Jul 18, 2022

how about if I just add a deprecation notice and leave the implementation alone?

That's rarely a good strategy. Many people don't read the docs and keep using the function (if they are using it) and so once the deprecated function is deleted their code breaks without warning.

The alternative is asyncio.iscoroutinefunction starts returning False when it used to return True, which will break user code in a silent way

@gvanrossum
Copy link
Member

I thought we were talking about putting a warning in it? It should only warn when it's going to return True but the inspect function would return False.

@graingert
Copy link
Contributor Author

It should only warn when it's going to return True but the inspect function would return False.

ok I've pushed that change

ngnpope added a commit to ngnpope/django that referenced this issue Oct 28, 2022
Python 3.11 has removed support for `@asyncio.coroutine` and with it
removed the documentation references for `asyncio.iscoroutinefunction()`
such that the intersphinx reference is unavailable.

The implementation of `asyncio.iscoroutinefunction()` still exists,
however:

https://github.com/python/cpython/blob/a1092f62492a3fcd6195bea94eccf8d5a300acb1/Lib/asyncio/coroutines.py#L21-L24

It looks as though it will be removed in the future:

python/cpython#94912

As we still need to support Python < 3.11 where `@asyncio.coroutine` may
be used we will likely need to vendor this until Python 3.11 is the
minimum supported version when `inspect.iscoroutinefunction()` can be
used directly instead. That will likely not be required until 3.12
though.
@carltongibson
Copy link
Contributor

carltongibson commented Oct 29, 2022

the only caveats are - what should happen to users of the asyncio.coroutines._is_coroutine mark?

The _is_coroutine marker (for later use with asyncio.iscorountinefunction()) is leveraged by asgiref and Django to mark sync functions that return an awaitable, and are otherwise not detectable as such.

Is there an official way to do this? (I need to dig into the inspect version of the check to see if we can mimic what we're doing already, but on the initial pass substituting in inspect fails).

If not can we pause before removing it?

@gvanrossum had #67707 (comment) — which suggests just calling the thing and seeing if it returned a coroutine object, but in Django's case we have a middleware and view functions which we're adapting for later use. Having a way of saying, No this is a coroutine function just by inspecting is pretty essential. 😬

Thanks!

@gvanrossum
Copy link
Member

gvanrossum commented Oct 31, 2022

the only caveats are - what should happen to users of the asyncio.coroutines._is_coroutine mark?

The _is_coroutine marker (for later use with asyncio.iscorountinefunction()) is leveraged by asgiref and Django to mark sync functions that return an awaitable, and are otherwise not detectable as such.

Is there an official way to do this? (I need to dig into the inspect version of the check to see if we can mimic what we're doing already, but on the initial pass substituting in inspect fails).

The question is, what do those libraries do differently when the result is true or false? Asyncio itself only uses asyncio.iscoroutinefunction() in a few places where it wants to fail earlier, with a clearer error message, when the user passes something that isn't going to work -- in particular, places that require a callback function to be passed where users are particularly likely to be confused and pass in a coroutine.

But I'm guessing you have a different use in mind.

If not can we pause before removing it?

I'm happy to pause this work until you come back with an answer to the above question (but don't wait until 3.12 is deemed feature complete please).

@gvanrossum had #67707 (comment) — which suggests just calling the thing and seeing if it returned a coroutine object, but in Django's case we have a middleware and view functions which we're adapting for later use. Having a way of saying, No this is a coroutine function just by inspecting is pretty essential. 😬

Let me see if I understand this. You have some code that essentially wants to tread

async def callback1(...): ...

differently from

def callback2(...): ...

Maybe if it's an async def you want to await it while if it's a plain function you want to use loop.call_soon() on it; or maybe something else, (it might help if I knew what).

But now there's a special case

async def _real_callback3(...): ...

def callback3(...):
    return _real_callback3(...)

and you want to treat callback3 the same as callback1, which currently you can accomplish by adding

callback3._is_coroutine = asyncio._is_coroutine

But why?

Couldn't you just write

async def _real_callback3(...): ...

async def callback3(...):
    return await _real_callback3(...)

?

@carltongibson
Copy link
Contributor

Hi @gvanrossum — Thank you for your thoughtful reply, very insightful. 😊

… don't wait until 3.12 is deemed feature complete please.

Absolutely. Let's see if we can resolve this now, not least because selfishly
we'd need a migration strategy if this goes away.

Your initial analysis looks correct:

async def callback1(...): ...


async def _real_callback3(...): ...

def callback3(...):
    return _real_callback3(...)

... you want to treat callback3 the same as callback1, which currently you can accomplish by adding

callback3._is_coroutine = asyncio._is_coroutine

This is exactly what we're doing.

But why?

Paraphrasing:

Couldn't you just write the sync wrapper function as async def?

The question is, what do those libraries do differently when the result is true or false?

Let me give you the examples.

Django Views

So, Django being a web-framework, views are callables that turn requests into responses.

At its simplest, you declare a function to do this:

def view(request, *args, **kwargs): ...

You can also define equivalent async views:

async def view(request, *args, **kwargs): ...

Both types of view can be run under both WSGI and ASGI:

  • Under WSGI async def views are run using asgiref's async_to_sync helper, which is a bit like asyncio.run().
  • The dual of that, under ASGI def views are run using asgiref's sync_to_async helper, which handles the run_in_executor manoeuvre to dispatch the sync function in a thread pool, but also ensures that the correct thread is used where we have e.g. ORM operations in play.

So far so good, if you have an async def view iscoroutinefunction() should correctly identify it.

But Django also allows you to define class-based views. Normally these define handlers per-HTTP verb, so:

from django.views import View


class MyView(View):
    def get(self, *args, **kwargs): ...

These can also define async handlers:

class MyView(View):
    async def get(self, *args, **kwargs): ...

In order to have per-request view instances, allowing you to store state on
self, Views are not directly callable but rather define an as_view()
classmethod that returns a wrapper function responsible for instantiating the
View instance and dispatch the right method handler, depending on the incoming
request.

There are several (sync) layers involved here — as_view() to the view callback to View.dispatch() to the method handler, such as get() — but the final execution by either the WSGI or the ASGI handler needs to know whether the method handler is sync or async in order to correctly dispatch it.

We're doing that currently by marking the wrapper function returned from as_view() with asyncio.coroutines._is_coroutine, since even though the wrapper is sync its ultimate return value is a coroutine that must be awaited.

In this case, I think we could branch on the definition of the returned view to make it async def.

My concern would be the amount of duplication, over simply setting an attribute on the function.

Django Middleware

The example with middleware is a little more complex.

Before calling a view Django passes the request through a series of middleware,
which act kind of like decorators, acting before and after the next item down
the chain, the last of which will in the end be the view itself.

Like views, middleware can be either sync or async, but they can also support
both calling patterns.

In order to avoid switching more than necessary between sync and async contexts
whilst processing the middleware chain, we adapt (again using the
asgiref.sync utilities) each middleware to the calling style of the callable
it wraps, before finally adapting the outer middleware to the WSGI or ASGI
handler as may be. This adaptation is done once at startup when loading the
middleware chain, which precludes calling the middleware and then awaiting it
if the return happens to be a coroutine.

For class based middleware (which unlike views are single instance callables)
supporting both async and sync modes we use the
asyncio.coroutines._is_coroutine marker to know to switch to the async
implementation in __call__. (This one seems like a bit of a sticker.)

asgiref.sync

The SyncToAsync class marks itself with asyncio.coroutines._is_coroutine.
The class has an async def __call__() but that's not sufficient to get through asyncio.iscoroutinefunction().

Commenting out the mark:

>>> import asyncio
>>> from asgiref.sync import SyncToAsync
>>> def f():
...    print(1)
... 
>>> s = SyncToAsync(f)
>>> asyncio.iscoroutinefunction(s)
False  # Not the actual behaviour, which sets the _is_coroutine marker

Whether this behaviour is essential, I cannot immediately say, but the
asgiref test suite has an explicit check that a SyncToAsync instance is
correctly detected as a coroutine.

Thoughts then...

Hopefully that gives you an insight into how we got here. Needing to identify a
coroutine function, no matter how it came to be, seem(s|ed) quite important to
be able to integrate async into Django.

Looking at the Django middleware and asgiref.sync cases,
possibly we could rewrite, but I suspect (at least in the Django case) we'd
need to put a shim in place to look for a marker before calling the stdlib
implementation.

OK, that's do-able.

But I wonder, is it that we're doing this wrong? Is it really not a thing to
have a sync function returning an awaitable? 🤔

The question is, what do those libraries do differently when the result is true or false?

They use the result to adapt either ourselves or the function to the match the required calling style.

Why is this necessary? Because we're dealing with both sync and async code and we can't say upfront which it's going to be.

Could we re-write it? Yeah, maybe. Likely, maybe, even. But having a marker has
proven pretty handy so far. It would be quite disruptive (at the least) if it
were to disappear.

Thanks for considering. Really happy to take input 🙂

🎁

//cc @andrewgodwin

@gvanrossum
Copy link
Member

Some quick thoughts:

The root of the "problem" is something that seems more prevailing in web frameworks than in the core of Python, i.e., introspecting something and taking a different action based on the outcome. A very old example is web template libraries that can name objects that may or may not be callable, and have no explicit "call it" syntax -- they just implicitly call when the object is callable.

In the core we prefer explicit over implicit, or we'd use a method that each type can redefine. But the web world is different, and I'm okay whit that.

I think that the way forward is a decorator that sets the marker, and that decorator ought to be part of the inspect API, so that inspect can look for it. Would you be willing to help design such an API and implement it?

@andrewgodwin
Copy link

Just to extend from what Carlton said on this - as the original author of all the async piping through Django, the key thing I needed to know when I first designed all this is if a callable is going to return a coroutine or not without calling it, as if it's a synchronous function then we've already accidentally triggered the side-effects if we call it. While I like the simplicity of the Python design of "asynchronous callables are just callables that return a coroutine", this is one case where it's quite hard to duck-type as you can't inspect the object without potential side-effects.

It's essentially missing type information - we could use type hints at runtime to, in theory, perform the same marking, and we could also indeed just vendor our own mechanism for this and continue placing an attribute on the callable object (after all, most of what is happening here is Django view classes being inspected by Django request flows, so we do control both ends).

You are totally correct that in a nice, clean implementation from a blank slate, we'd have something explicit like two different kinds of URL router or middleware definition, or maybe even just make it all coroutine-by-default - alas, backwards compatibility meant we had to continue some of Django's existing design patterns to avoid making a huge amount of required upgrade work for people.

When you propose a decorator that sets a marker, are you thinking something similar to _is_coroutine and just adding a nicer interface? I'd personally be happy to help design and/or vet something in inspect that allows us to reliably mark and determine if something is about to return a coroutine or not.

@gvanrossum
Copy link
Member

When you propose a decorator that sets a marker, are you thinking something similar to _is_coroutine and just adding a nicer interface? I'd personally be happy to help design and/or vet something in inspect that allows us to reliably mark and determine if something is about to return a coroutine or not.

Yes, that's exactly what I was thinking. We need to think a bit carefully about deprecating asyncio.iscouroutinefunction().

@carltongibson
Copy link
Contributor

Thanks for your understanding @gvanrossum.

A very old example is web template libraries that can name objects that may or may not be callable, and have no explicit "call it" syntax -- they just implicitly call when the object is callable.

Yes. The Django template language ≈ does this. 😊

Would you be willing to help design such an API and implement it?

Ha. Sneaky. 🙂 Yes, OK, fair's fair — I'm happy to input as well.

/me looks at https://peps.python.org/pep-0693/#schedule 🤔

@gvanrossum
Copy link
Member

This shouldn't require a PEP or anything like that. Just think through the API a bit. You could start by writing the code and reason back from what you learned by trying to do that.

@carltongibson
Copy link
Contributor

I can certainly begin. Let me track down the tests for inspect.iscoroutinefunction() and see if I can add some failing cases. I'll open a draft PR to discuss.

@gvanrossum
Copy link
Member

Great. Please add me to the PR.

gvanrossum pushed a commit that referenced this issue Dec 18, 2022
…99247)

This introduces a new decorator `@inspect.markcoroutinefunction`,
which, applied to a sync function, makes it appear async to
`inspect.iscoroutinefunction()`.
carljm added a commit to carljm/cpython that referenced this issue Dec 19, 2022
* main:
  pythongh-89727: Fix os.walk RecursionError on deep trees (python#99803)
  Docs: Don't upload CI artifacts (python#100330)
  pythongh-94912: Added marker for non-standard coroutine function detection (python#99247)
  Correct CVE-2020-10735 documentation (python#100306)
  pythongh-100272: Fix JSON serialization of OrderedDict (pythonGH-100273)
  pythongh-93649: Split tracemalloc tests from _testcapimodule.c (python#99551)
  Docs: Use `PY_VERSION_HEX` for version comparison (python#100179)
  pythongh-97909: Fix markup for `PyMethodDef` members (python#100089)
  pythongh-99240: Reset pointer to NULL when the pointed memory is freed in argument parsing (python#99890)
  pythongh-99240: Reset pointer to NULL when the pointed memory is freed in argument parsing (python#99890)
  pythonGH-98831: Add DECREF_INPUTS(), expanding to DECREF() each stack input (python#100205)
  pythongh-78707: deprecate passing >1 argument to `PurePath.[is_]relative_to()` (pythonGH-94469)
iritkatriel added a commit to iritkatriel/cpython that referenced this issue Dec 28, 2022
* Correct CVE-2020-10735 documentation (python#100306)

* pythongh-94912: Added marker for non-standard coroutine function detection (python#99247)

This introduces a new decorator `@inspect.markcoroutinefunction`,
which, applied to a sync function, makes it appear async to
`inspect.iscoroutinefunction()`.

* Docs: Don't upload CI artifacts (python#100330)

* pythongh-89727: Fix os.walk RecursionError on deep trees (python#99803)

Use a stack to implement os.walk iteratively instead of recursively to
avoid hitting recursion limits on deeply nested trees.

* pythongh-69929: re docs: Add more specific definition of \w (python#92015)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>

* pythongh-89051: Add ssl.OP_LEGACY_SERVER_CONNECT (python#93927)

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Christian Heimes <christian@python.org>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
Fixes python#89051

* pythongh-88211: Change lower-case and upper-case to match recommendations in imaplib docs (python#99625)

* pythongh-100348: Fix ref cycle in `asyncio._SelectorSocketTransport` with `_read_ready_cb` (python#100349)

* pythongh-99925: Fix inconsistency in `json.dumps()` error messages (pythonGH-99926)

* Clarify that every thread has its own default context in contextvars (python#99246)

* pythongh-99576: Fix cookiejar file that was not truncated for some classes (pythonGH-99616)

Co-authored-by: Łukasz Langa <lukasz@langa.pl>

* pythongh-100188: Reduce misses in BINARY_SUBSCR_(LIST/TUPLE)_INT (python#100189)

Don't specialize if the index is negative.

* pythongh-99991: improve docs on str.encode and bytes.decode (python#100198)

Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM>

* pythongh-91081: Add note on WeakKeyDictionary behavior when deleting a replaced entry (python#91499)

Co-authored-by: Pieter Eendebak <P.T.eendebak@tudelft.nl>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>

* pythongh-85267: Improvements to inspect.signature __text_signature__ handling (python#98796)

This makes a couple related changes to inspect.signature's behaviour
when parsing a signature from `__text_signature__`.

First, `inspect.signature` is documented as only raising ValueError or
TypeError. However, in some cases, we could raise RuntimeError.  This PR
changes that, thereby fixing python#83685.

(Note that the new ValueErrors in RewriteSymbolics are caught and then
reraised with a message)

Second, `inspect.signature` could randomly drop parameters that it
didn't understand (corresponding to `return None` in the `p` function).
This is the core issue in python#85267. I think this is very surprising
behaviour and it seems better to fail outright.

Third, adding this new failure broke a couple tests. To fix them (and to
e.g. allow `inspect.signature(select.epoll.register)` as in python#85267), I
add constant folding of a couple binary operations to RewriteSymbolics.

(There's some discussion of making signature expression evaluation
arbitrary powerful in python#68155. I think that's out of scope. The
additional constant folding here is pretty straightforward, useful, and
not much of a slippery slope)

Fourth, while python#85267 is incorrect about the cause of the issue, it turns
out if you had consecutive newlines in __text_signature__, you'd get
`tokenize.TokenError`.

Finally, the `if name is invalid:` code path was dead, since
`parse_name` never returned `invalid`.

* pythonGH-100363: Speed up `asyncio.get_running_loop` (python#100364)

* pythonGH-100133: fix `asyncio` subprocess losing `stderr` and `stdout` output (python#100154)

* pythongh-100374: Fixed a bug in socket.getfqdn() (pythongh-100375)

* pythongh-100129: Add tests for pickling all builtin types and functions (pythonGH-100142)

* Remove unused variable from `dis._find_imports` (python#100396)

* pythongh-78878: Fix crash when creating an instance of `_ctypes.CField` (python#14837)

* pythonGH-69564: Clarify use of octal format of mode argument in help(os.chmod) (python#20621)

Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>

* pythonGH-99554: Pack location tables more effectively (pythonGH-99556)

* Correct typo in typing.py (python#100423)

In the docstring of `ParamSpec`, the name of `P = ParamSpec('P')` was
mistakenly written as `'T'`.

* pythongh-99761: Add `_PyLong_IsPositiveSingleDigit` function to check for single digit integers  (python#100064)

* pythonGH-99770: Make the correct call specialization fail kind show up in the stats (pythonGH-99771)

* pythongh-78997: fix bad rebase of moved test file (python#100424)

* pythongh-100344: Add C implementation for `asyncio.current_task` (python#100345)

Co-authored-by: pranavtbhat

* pythonGH-99554: Trim trailing whitespace (pythonGH-100435)



Automerge-Triggered-By: GH:brandtbucher

* pythongh-85432: Harmonise parameter names between C and pure-Python implementations of `datetime.time.strftime`, `datetime.datetime.fromtimestamp` (python#99993)

* pythongh-57762: fix misleading tkinter.Tk docstring (python#98837)

Mentioned as a desired change by terryjreedy on the corresponding issue,
since Tk is not a subclass of Toplevel.

* pythongh-48496: Added example and link to faq for UnboundLocalError in reference (python#93068)

* Fix typo in 3.12 What's New (python#100449)

* pythongh-76963: PEP3118 itemsize of an empty ctypes array should not be 0 (pythonGH-5576)

The itemsize returned in a memoryview of a ctypes array is now computed from the item type, instead of dividing the total size by the length and assuming that the length is not zero.

* pythonGH-100459: fix copy-paste errors in specialization stats (pythonGH-100460)

* pythongh-99110: Initialize `frame->previous` in init_frame to fix segmentation fault when accessing `frame.f_back` (python#100182)

* pythongh-98712: Clarify "readonly bytes-like object" semantics in C arg-parsing docs (python#98710)

* pythongh-92216: improve performance of `hasattr` for type objects (pythonGH-99979)

* pythongh-100288: Specialise LOAD_ATTR_METHOD for managed dictionaries (pythonGH-100289)

* Revert "pythongh-100288: Specialise LOAD_ATTR_METHOD for managed dictionaries (pythonGH-100289)" (python#100468)

This reverts commit c3c7848.

* pythongh-94155: Reduce hash collisions for code objects (python#100183)

* Uses a better hashing algorithm to get better dispersion and remove commutativity.

* Incorporates `co_firstlineno`, `Py_SIZE(co)`, and bytecode instructions.

* This is now the entire set of criteria used in `code_richcompare`, except for `_PyCode_ConstantKey` (which would incorporate the types of `co_consts` rather than just their values).

* pythongh-83076: 3.8x speed improvement in (Async)Mock instantiation (python#100252)

* pythongh-99482: remove `jython` compatibility parts from stdlib and tests (python#99484)

* bpo-40447: accept all path-like objects in compileall.compile_file (python#19883)

Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>

* pythonGH-100425: Improve accuracy of builtin sum() for float inputs (pythonGH-100426)

* pythongh-68320, pythongh-88302 - Allow for private `pathlib.Path` subclassing (pythonGH-31691)

Users may wish to define subclasses of `pathlib.Path` to add or modify
existing methods. Before this change, attempting to instantiate a subclass
raised an exception like:

    AttributeError: type object 'PPath' has no attribute '_flavour'

Previously the `_flavour` attribute was assigned as follows:

    PurePath._flavour        = xxx not set!! xxx
    PurePosixPath._flavour   = _PosixFlavour()
    PureWindowsPath._flavour = _WindowsFlavour()

This change replaces it with a `_pathmod` attribute, set as follows:

    PurePath._pathmod        = os.path
    PurePosixPath._pathmod   = posixpath
    PureWindowsPath._pathmod = ntpath

Functionality from `_PosixFlavour` and `_WindowsFlavour` is moved into
`PurePath` as underscored-prefixed classmethods. Flavours are removed.

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Brett Cannon <brett@python.org>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Eryk Sun <eryksun@gmail.com>

* pythongh-99947: Ensure unreported errors are chained for SystemError during import (pythonGH-99946)

* Add "strict" to dotproduct(). Add docstring. Factor-out common code. (pythonGH-100480)

* pythongh-94808: improve test coverage of number formatting (python#99472)

* pythongh-100454: Start running SSL tests with OpenSSL 3.1.0-beta1 (python#100456)

* pythongh-100268: Add is_integer method to int (python#100439)

This improves the lives of type annotation users of `float` - which type checkers implicitly treat as `int|float` because that is what most code actually wants. Before this change a `.is_integer()` method could not be assumed to exist on things annotated as `: float` due to the method not existing on both types.

* pythongh-77771: Add enterabs example in sched (python#92716)

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>

* pythonGH-91166: Implement zero copy writes for `SelectorSocketTransport` in asyncio (python#31871)

Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>

* pythonGH-91166: Implement zero copy writes for `SelectorSocketTransport` in asyncio (python#31871)

Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>

* Misc Itertools recipe tweaks (pythonGH-100493)

* pythongh-100357: Convert several functions in `bltinsmodule` to AC (python#100358)

* Remove wrong comment about `repr` in `test_unicode` (python#100495)

* pythongh-99908: Tutorial: Modernize the 'data-record class' example (python#100499)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>

* pythongh-100474: Fix handling of dirs named index.html in http.server (pythonGH-100475)



If you had a directory called index.html or index.htm within a directory, it would cause http.server to return a 404 Not Found error instead of the directory listing. This came about due to not checking that the index was a regular file.

I have also added a test case for this situation.

Automerge-Triggered-By: GH:merwok

* pythongh-100287: Fix unittest.mock.seal with AsyncMock (python#100496)

* pythongh-99535: Add test for inheritance of annotations and update documentation (python#99990)

* pythongh-100428: Make float documentation more accurate (python#100437)

Previously, the grammar did not accept `float("10")`.
Also implement mdickinson's suggestion of removing the indirection.

* [Minor PR] Quotes in documentation changed into code blocks (python#99536)

Minor formatting fix in documentation

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>

* pythongh-100472: Fix docs claim that compileall parameters could be bytes (python#100473)

* pythongh-100519: simplification to `eff_request_host` in cookiejar.py (python#99588)

`IPV4_RE` includes a `.`, and the `.find(".") == -1` included here is already testing to make sure there's no dot, so this part of the expression is tautological. Instead use more modern `in` syntax to make it clear what the check is doing here. The simplified implementation more clearly matches the wording in RFC 2965.

Co-authored-by: hauntsaninja <hauntsaninja@gmail.com>

* pythongh-99308: Clarify re docs for byte pattern group names (python#99311)

* pythongh-92446: Improve argparse choices docs; revert bad change to lzma docs (python#94627)

Based on the definition of the collections.abc classes, it is more accurate to use "sequence" instead of "container" when describing argparse choices.

A previous attempt at fixing this in python#92450 was mistaken; this PR reverts that change.

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>

* Fix name of removed `inspect.Signature.from_builtin` method in 3.11.0a2 changelog (python#100525)

* pythongh-100520: Fix `rst` markup in `configparser`  docstrings (python#100524)

* pythongh-99509: Add `__class_getitem__` to `multiprocessing.queues.Queue` (python#99511)

* pythongh-94603: micro optimize list.pop (pythongh-94604)

* Remove `NoneType` redefinition from `clinic.py` (python#100551)

* pythongh-100553: Improve accuracy of sqlite3.Row iter test (python#100555)

* pythonGH-98831: Modernize a ton of simpler instructions (python#100545)

* load_const and load_fast aren't families for now
* Don't decref unmoved names
* Modernize GET_ANEXT
* Modernize GET_AWAITABLE
* Modernize ASYNC_GEN_WRAP
* Modernize YIELD_VALUE
* Modernize POP_EXCEPT (in more than one way)
* Modernize PREP_RERAISE_STAR
* Modernize LOAD_ASSERTION_ERROR
* Modernize LOAD_BUILD_CLASS
* Modernize STORE_NAME
* Modernize LOAD_NAME
* Modernize LOAD_CLASSDEREF
* Modernize LOAD_DEREF
* Modernize STORE_DEREF
* Modernize COPY_FREE_VARS (mark it as done)
* Modernize LIST_TO_TUPLE
* Modernize LIST_EXTEND
* Modernize SET_UPDATE
* Modernize SETUP_ANNOTATIONS
* Modernize DICT_UPDATE
* Modernize DICT_MERGE
* Modernize MAP_ADD
* Modernize IS_OP
* Modernize CONTAINS_OP
* Modernize CHECK_EXC_MATCH
* Modernize IMPORT_NAME
* Modernize IMPORT_STAR
* Modernize IMPORT_FROM
* Modernize JUMP_FORWARD (mark it as done)
* Modernize JUMP_BACKWARD (mark it as done)

Signed-off-by: Filipe Laíns <lains@archlinux.org>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Co-authored-by: Jeremy Paige <ucodery@gmail.com>
Co-authored-by: Carlton Gibson <carlton@noumenal.es>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
Co-authored-by: Jon Burdo <jon@jonburdo.com>
Co-authored-by: Stanley <46876382+slateny@users.noreply.github.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Co-authored-by: Brad Wolfe <brad.wolfe@gmail.com>
Co-authored-by: Richard Kojedzinszky <rkojedzinszky@users.noreply.github.com>
Co-authored-by: František Nesveda <fnesveda@users.noreply.github.com>
Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
Co-authored-by: Dennis Sweeney <36520290+sweeneyde@users.noreply.github.com>
Co-authored-by: Bisola Olasehinde <horlasehinde@gmail.com>
Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM>
Co-authored-by: Pieter Eendebak <P.T.eendebak@tudelft.nl>
Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Co-authored-by: Dominic Socular <BBH@awsl.rip>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Hai Shi <shihai1992@gmail.com>
Co-authored-by: amaajemyfren <32741226+amaajemyfren@users.noreply.github.com>
Co-authored-by: Brandt Bucher <brandtbucher@microsoft.com>
Co-authored-by: david-why <david_why@outlook.com>
Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com>
Co-authored-by: penguin_wwy <940375606@qq.com>
Co-authored-by: Eli Schwartz <eschwartz93@gmail.com>
Co-authored-by: Itamar Ostricher <itamarost@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Co-authored-by: Bill Fisher <william.w.fisher@gmail.com>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Ken Jin <kenjin@python.org>
Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Filipe Laíns <lains@riseup.net>
Co-authored-by: Raymond Hettinger <rhettinger@users.noreply.github.com>
Co-authored-by: Barney Gale <barney.gale@gmail.com>
Co-authored-by: Brett Cannon <brett@python.org>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Eryk Sun <eryksun@gmail.com>
Co-authored-by: Sebastian Berg <sebastianb@nvidia.com>
Co-authored-by: Illia Volochii <illia.volochii@gmail.com>
Co-authored-by: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com>
Co-authored-by: James Frost <git@frost.cx>
Co-authored-by: MonadChains <monadchains@gmail.com>
Co-authored-by: Bart Broere <mail@bartbroere.eu>
Co-authored-by: Glyph <code@glyph.im>
Co-authored-by: hauntsaninja <hauntsaninja@gmail.com>
Co-authored-by: Ilya Kulakov <kulakov.ilya@gmail.com>
Co-authored-by: Guy Yagev <yourlefthandman8@gmail.com>
Co-authored-by: Jakub Kuczys <me@jacken.men>
carltongibson added a commit to carltongibson/cpython that referenced this issue Jan 11, 2023
…rker.

The initial implementation did not correctly identify explicitly
marked class instances.

Follow up to 532aa4e
gvanrossum pushed a commit that referenced this issue Jan 11, 2023
…100935)

The initial implementation did not correctly identify explicitly
marked class instances.

Follow up to 532aa4e
@kumaraditya303
Copy link
Contributor

@gvanrossum What's the status of this?

@gvanrossum
Copy link
Member

We have not yet deprecated asyncio.iscoroutinefunction, so I think there's still a little bit of work left to do. That seems a minor update that we could do before the 3.12 feature freeze in 2 weeks. Maybe @carltongibson would like to try?

@carltongibson
Copy link
Contributor

Hi @gvanrossum — I would love to but, I have family issues severely limiting my time at the moment. I cannot commit to being able to look at this in the timeframe. (Sorry)

@gvanrossum
Copy link
Member

@carltongibson No worries, family comes first! Between myself, @kumaraditya303 and @willingc I think we can get it done.

@Lancetnik
Copy link

@carltongibson @gvanrossum looks like I am too late here, but, please, answer the question: is inspect.markcoroutinefunction the best choice?

From my point inspect is not the right place for this method: all inspect functions check something via is... or get specific information about an object by get.... The markcoroutinefunction is just the one function in this module to change smth.

functools with its wraps and update_wrapper looks like a better choice to place markcoroutinefunction.

Also, marking a decorator as an async function in the following case with wraps by default makes some sense

from functools import wraps

def decorator(func)
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs(
    return wrapper

@decorator
async def some_func(): ...

Thus we can implement this functionality in the update_wrapper function (like an option or smth).

@gvanrossum
Copy link
Member

We are way too late for feature freeze, and I don't feel this is important enough to warrant changing course. Anyway, I'd rather have the code that adds the flag and the code that checks the flag in the same place, rather than having them spread across two quite unrelated modules.

@Lancetnik
Copy link

Okay, this is just a thought, not a problem. But what are you thinking about adding similar functionality to the wraps?

Maybe via a parameter (but the default behavior is better I think):

@wraps(func, mark_async=True) 
....

This decorator is just a sugar for decorators writing, so it can be very helpfull to add this feature here too

@gvanrossum
Copy link
Member

I think it's better to have two decorators. At least two of the Zen phrases would apply: EIBTI and TOOWTDI.

@Lancetnik
Copy link

But we have a lot of inspect.iscoroutinefunction based tools: all of these tools use the same decorator to wrap both async and sync functions.

If we were to decorate the function before using them, we might break this check in a not-so-obvious way.
However, almost all users still use wraps when writing their own decorators. If this decorator automatically labels wrappers with the appropriate type, library developers will be guaranteed that the function that comes into their decorator is defined by inspect.iscoroutinefunction correctly.

@gvanrossum
Copy link
Member

Let's see what reports we get from actual users of 3.12 once it is released.

@carltongibson
Copy link
Contributor

This seems equivalent to the discussion on #100317.

If you're expecting to wrap an async def function, you should return an async def function from the decorator:

import inspect
from functools import wraps


def decorator(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper


@decorator
async def some_func():
    pass


assert inspect.iscoroutinefunction(some_func)

(That already works)

It's only in those (presumably rare) case where you need a sync function to return an async function (and in Django's case that's only where we need to handle both types on the same code path, and it's not recommended™ if you can avoid it) that you need markcorountinefunction at all. In those cases it seems reasonable to have to explicitly use the marker.

@Lancetnik
Copy link

Yep, I am as a OSS developer talking about cases when u need to wrap any function by sync function. It's not the Django only case: FastAPI, tenacity, loguru, etc uses the one decorator to wrap any function
So it could be a better decision to implement this functionality in the regular 'wraps', not in the special function

@kumaraditya303
Copy link
Contributor

Superseded by #122858

@kumaraditya303 kumaraditya303 closed this as not planned Won't fix, can't repro, duplicate, stale Aug 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-asyncio type-feature A feature request or enhancement
Projects
Status: Done
Development

No branches or pull requests

7 participants