classification
Title: Default to emitting FutureWarning for provisional APIs
Type: enhancement Stage: needs patch
Components: Versions: Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: dstufft, eric.snow, ethan.furman, habnabit, hawkowl, ncoghlan, ned.deily, r.david.murray, serhiy.storchaka
Priority: normal Keywords:

Created on 2017-10-10 01:47 by ncoghlan, last changed 2017-10-18 19:43 by ethan.furman.

Messages (32)
msg304006 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-10 01:47
Chatting to Donald Stufft and Amber Brown about the way we currently handle provisional APIs, I think they have a legitimate concern that we may not be gathering adequately informed consent from users when they depend on APIs that we've reserved the right to change in the future.

So what I'm now wondering is whether or not it may be worth amending PEP 411 to say that all provisional modules should include an import-time snippet like the following:

    import __main__
    if not getattr(__main__, f"use_provisional_{__name__}"):
        import warnings
        warnings.warn(FutureWarning, f"The {__name__} module is currently a provisional API. New features might be added and API may change even between minor releases if deemed necessary.")

The exact wording of the warning would match the caveat used in that module's API documentation (the variant above comes from the "typing" module)

The idea here would be that *application* developers would get a FutureWarning whenever a provisional module was imported, unless they set "use_provisional_<module>" in __main__.

Storing the marker attribute in __main__ would be intended to serve two purposes:

- it's always going to be imported anyway, so the runtime cost of the new snippet in the provisional modules will be low
- it sends a clear indication to library authors that they *shouldn't* be setting the flag implicitly as a side-effect of import (framework authors, by contrast, may decide to set it, since they're more in control of the overall application behaviour than library authors)
msg304007 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-10 01:50
Another advantage of this approach: anyone running their tests in a "no warnings allowed" configuration would get a hard failure when dependencies on a provisional API were unexpectedly introduced (e.g. when updating a third party library), rather than silently acquiring a potential future compatibility issue.
msg304008 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-10 01:53
This is also fairly similar to Rust's approach of using feature flags to explicitly opt-in to unstable features: https://doc.rust-lang.org/unstable-book/
msg304009 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2017-10-10 03:58
While I understand the sentiment, having this warning pop up every time you import a provisional module while developing code feels like harassment of the very users we'd like to try out the provisional APIs.

It's also too course-grained -- most of the time a provisional module is pretty stable but has a few gray areas that are still under development.

For those people who are writing software for the ages, maybe this can be an opt-in warning? I won't object to that.
msg304010 - (view) Author: Donald Stufft (dstufft) * (Python committer) Date: 2017-10-10 04:24
The fundamental problem is that unless you're closely following the development of python-dev, it's really really easy to use a provisional module without knowing that you are.

Take asyncio for example, as far as I can tell the documentation that noted it was provisional was a single, near invisible (light grey on a white background) call out at the very top of a single page in the documentation. This is made especially hard when you have people giving talks at PyCon encouraging people to use this, libraries of the code that don't mention that asyncio is provisional, so unless you're directly using asyncio you might not even be aware that they're using a provisional API at all.

This has the effect that by including these APIs and make the fact they are provisional practically unnoticeable, it pushes the pain of dealing with changes onto library authors who have their own users asking them to integrate with these features. Putting library authors in the position of either having to opt-in to a provisional API and hope it doesn't change, having to scramble to keep up with changes in new minor releases, or having to relitigate why they're not going to support a provisional API.

Ironically, as we found with web APIs, releasing provisional APIs can actually make things worse for end users, because people will expect the usage of the APIs as if they were covered under the normal contract, but the developers of those APIs feel empowered to make more drastic changes. The long development time of CPython makes this even worse (it was ~2 years from the release of 3.4.0 and 3.6.0 for asyncio to become non provisional, longer if you count alpha/beta/etc) because people want to use the new stuff, but they have to wait a fairly long time to be "allowed" to do it, all the while the general ecosystem consensus is "just do it and ignore the warning, if you noticed the warning at all".

Ultimately, the provisional status doesn't reduce the pain of getting APIs wrong, it just shifts the pain from CPython's developers to the software developers writing stuff that depends on CPython but that they don't directly control the execution environment (e.g., all the OSS libraries and applications on PyPI). A proposal like this helps alleviate some of that pain by creating additional barriers to entry to make end users less likely to use it unless they understand what they're asking for.
msg304011 - (view) Author: Amber Brown (hawkowl) Date: 2017-10-10 04:57
Donald hits it on the head for me.

As long as the code is not covered by the same API deprecation contract that the rest of the standard library is, it should make it obvious when attempting to use it. I can be relatively certain that a lot of people were not aware that the potential scope of asyncio changes included what it potentially could have, and I personally wasn't aware that typing was a provisional module (and got burnt by incompatible changes in 3.6). There was a small note in the docs, but when I was learning from cattrs's docs and not CPython's, how was I supposed to know?

A warning is low cost, there's a way to switch it off, and it potentially informs users that they need to be aware of its provisional status, especially if it's a dependency of a dependency.
msg304019 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-10 07:03
I think if someone is going to be put off by a FutureWarning, then that indicates they're not yet familiar with just what the provisional API status means, and those are exactly the folks we *want* to scare away from provisional APIs (or features in third party libraries and frameworks that depend on them).

After all, if "Set 'use_provisional_<module> = True' in __main__" is more of an inconvenience than someone is prepared to tolerate to enable warning-free access to a still evolving feature, imagine how upset they'd be if we actually *did* make a breaking change to that API.

I did realise my draft warning was missing a bit though, which was to explain how to turn it off:

    import __main__
    _feature_flag = f"use_provisional_{__name__}"
    if not getattr(__main__, _feature_flag):
        import warnings
        _provisional_msg = f"The {__name__} module is currently a provisional API - see documentation for details. Set '__main__.{_feature_flag} = True' to disable this warning."
        warnings.warn(FutureWarning, _provisional_msg)

I also revised the draft message to account for Guido's observation about even "provisional" APIs being mostly stable by directing folks to the module documentation for details. That way the maintainers of the provisional API don't need to duplicate their explanation of how provisional the module actually is (e.g. the typing docs make it clear that feature additions and API changes are currently in scope for maintenance releases, but outright removal isn't listed as a possible outcome).

Folks that want to always opt-in to various features in their REPL sessions can then set them via PYTHONSTARTUP.

I'll also note here why I'm proposing this as a per-process flag rather than a per-module one:

- checking a feature flag in __main__ is easy, checking a flag in the "importing module" is hard
- module caching means only the first import would actually run the code to emit the warning any way
- we know from experience that having to set per-module __future__ flags to access backwards incompatible syntax changes genuinely *is* annoying (to the point where we'll implement clever tricks like those Yury came up with for native coroutines to avoid needing them)
msg304029 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-10-10 12:49
I think there needs to be an easy way to turn off the warnings while running tests, as well.  I don't want to be bothered by those messages when testing parts of my package that are consciously using the provisional features.

But really, I agree with Guido: if this were opt in, and set on by default in test harnesses like unittest, that would make sense to me.  Otherwise I think we'll be revisiting some of the pain that caused us to make deprecation warnings silent by default.

If you don't have tests for your code you should be expecting it to break eventually anyway :)
msg304030 - (view) Author: Amber Brown (hawkowl) Date: 2017-10-10 12:51
What is the point of an opt-in warning, when the entire point of the proposed warning is letting people know that they may be using something they are not fully educated or informed about the ramifications of using? If you know to turn on the warning, you know to check the list of provisional modules, or know that provisional modules exist in the first place.
msg304031 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-10-10 12:58
That's why I said set on by default by the test harnesses. The opt in would be done by the standard testing tools, not directly by the programmer.  That's how deprecation warnings work now.
msg304032 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-10 13:02
I don't think that a special way for silencing FutureWarning is needed. You can use an existing mechanism.

    import warnings
    warnings.filterwarnings('ignore', '', FutureWarning, 'typing')
msg304033 - (view) Author: Amber Brown (hawkowl) Date: 2017-10-10 13:05
So you're proposing a coordinated effort across the half dozen, possibly more, test runners to enable some flags, so CPython doesn't log a single message, possibly two, that you're using unsupported experimental software with no backwards compatibility guarantee?

I think the warnings control setting there could be a good solution for your "I don't care, don't show the warning" use case, but I also think explicit disabling per module is a better solution to recommend to users (since we might know that we're using typing, and we say "we are okay with using typing", but might not realise that we're silencing a new provisional module used by an updated library sometime in the future).
msg304034 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-10 13:09
Deprecation warnings were different: we turned those off by default because currently working applications could start emitting console warnings simply because an end user ran them on a newer version of Python.

With future warnings, we're instead trying to detect cases where:

* a user has heard about a cool new Python standard library API, starts trying it out, but has no idea yet that it isn't covered by our regular backwards compatibility assurances. If they're a beginner or a corporation, this is setting them up for potential problems without any clear indication they might be doing something they may want to reconsider
* you upgraded a dependency and it started relying on a provisional API, and you'd prefer to stick with the old version rather than risking relying on the provisional interface

Neither of those situations can be encountered simply by running an existing *application* on a newer version of Python (assuming the application bundles all of its dependencies other than the runtime itself).

Neither of them is helped by an opt-in flag either, since the scenarios we're trying to detect include the affected user either:

1. Not knowing about provisional APIs at all;
2. Not knowing that *that particular* API is provisional; or
3. Not knowing that a dependency on that particular API has been introduced

Since the warning would be emitted through the regular warnings machinery, all those options would be available to turn it off, but there'd also be the even more convenient option of just setting "use_provisional_typing = True" in __main__ and leaving the warnings settings alone.

It's clear to me from both Guido's and David's confusion that this idea is going to need to go through the PEP process though, which is probably a reasonable thing to do anyway (since one of the outcomes would be an amendment to PEP 411 to include the programmatic warning mechanism).
msg304035 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-10-10 13:09
I imagine we could make it controlled by the same setting that controls deprecation warnings, with some way to differentiate them if you really need to.  I forget exactly how that warning control works, so maybe that would be awkward, but I wouldn't be surprised if it was relatively easy.  That way test runners would automatically be enabling the warning without having to do anything.  (If a test runner isn't already automatically enabling deprecation warnings, then I doubt it will want to automatically enable future warnings).
msg304040 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-10-10 13:19
Nick says: "Neither of those situations can be encountered simply by running an existing *application* on a newer version of Python". 

I fail to see the operational difference between running an application on a newer version of Python and doing a pip --upgrade or yum update and then running the application.  So that argument boils down to "this warning should be on by default", which is what we are discussing :)

I'm happy to leave this to the PEP process.
msg304041 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-10 13:20
A note regarding *only* using the warnings module to turn things off:

The problem I have with that is that the UX is relatively clumsy, and hence runs into the concern Guido mentions above: "having this warning pop up every time you import a provisional module while developing code feels like harassment of the very users we'd like to try out the provisional APIs."

By contrast, "To rely on this provisional feature without getting a runtime FutureWarning, set this application level feature flag in your __main__ module" feels like exactly the right level of affirmative agreement to me - the equivalent of clicking through a confirmation dialog, or ticking an "I agree" check box on a form.

Libraries registering that agreement on behalf of their users would then always be inappropriate, while whether or not it was appropriate for a framework to do it would depend on the framework and the feature.
msg304042 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-10 13:25
The deprecation warnings weren't turned off due to Python developers running things in virtual environments - they were turned off due to Linux distros upgrading their system Python runtimes, and Linux ISVs getting an influx of new bug reports from their users.

So the cases of relevance were ones where all the app dependencies were bundled *except* the runtime itself.

By contrast, one of the things we *want* to warn about in this case is folks acquiring transitive dependencies on provisional APIs that they may not want to depend on.
msg304055 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2017-10-10 15:48
I have no bandwidth for this discussion, but so far I am not convinced. What happened to "consenting adults"?

> [...] the entire point of the proposed warning letting people know that they may be using something they are not fully educated or informed about the ramifications of using

That describes most of the Python experience. :-)

I think you all should lighten up. It really sounds like a classic case of someone who was bitten badly by some obscure aspect of an API and then overreacts.

If the warning in the docs does not grab enough attention, change the text or formatting of that warning.
msg304057 - (view) Author: Donald Stufft (dstufft) * (Python committer) Date: 2017-10-10 16:12
> What happened to "consenting adults"?

Making sure they're actually consenting when getting into something that might potentially bite them in the ass seems like a sane and thoughtful thing to do to me.
msg304097 - (view) Author: Amber Brown (hawkowl) Date: 2017-10-11 00:35
> What happened to "consenting adults"?

Consent does not mean that by using Python, users fully consent to using modules that they may not be aware will, to paraphrase Donald, come back to bite them in the ass.

Consent requires multiple things:

- Acknowledgement of the benefits involved
- Acknowledgement of the risks involved
- Positive affirmation that these things are accepted.

The spate of PyCon talks on provisional modules (the half dozen asyncio at PyCon US last year, and the 2-3+ asyncio talks at every conference I've been to since 2014) very much has given the community the first item, and as people have expressed interest in the benefits, acknowledge them.

But, CPython does not like at all admitting clearly or explicitly the risks involved. From multiple discussions with Nick, an author of the provisional API PEP, it has come clear to me that the intended role of provisional software is to allow CPython to ship not-production-ready software for testing and API usability testing in an experimental capacity. How many people, from reading the single line in the docs (which, remember, is not the only path where people learned how to use asyncio in 3.4/3.5, and is absent from module documentation), did not know this?

Upon discussing this issue with others, a few people admitted that they had no idea that provisional APIs existed, that asyncio was one, that typing is one, and they had no idea where to look to see if they were using a provisional module in their code or their dependencies.

So, it appears that CPython is failing item #2 here, by not adequately informing users of the risks. Yes, the warning should be improved (and ought to be made bright red, not grey, and actually use the word "warning", not note), but we should also take a step to protect the users that may not learn about the module through official documentation, and in some cases, may never reference it.

Only then can we confidently say that the user is potentially a "consenting adult". (Which is maybe interesting language to use nowadays considering Python's growth areas contain a lot of school-age education...)
msg304103 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2017-10-11 03:08
I'm still trying to understand whether there's a specific event (or set of events) that's triggered this issue. There is a lot of talk about people can be misled but not a single specific example of someone who actually got in trouble because a provisional API they were actually using changed.

Given the passion I read in some of the comments it shouldn't be hard to collect such stories?

As with every other change proposed to Python, unless there's a clear indication that there is an actual problem, I'm not inclined to try to solve it preemptively (since the proposed action also may *introduce* new problems). Note that I'm not asking for proof that some people don't know what provisional means -- I'm looking for evidence of actual situations where someone got bitten.

Also I don't think that people who didn't read the docs have much of a leg to stand on. There are plenty of situations where subtle aspects of APIs are not guaranteed to be stable (e.g. calling a function with a value that the docs say is invalid but that is not actively rejected by some version). And nobody can expect that a talk (no matter how clearly presented) is a substitute for reading the docs -- a talk on a complex API like asyncio or typing cannot possibly cover the whole API (I know, I've tried :-).

That said, we should absolutely change the warnings in the docs.
msg304108 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-11 06:27
A note regarding applicability if we did make a change to our approach:

* asyncio & pathlib are already non-provisional, so wouldn't be affected.

* typing is still provisional, but introducing a new FutureWarning after two releases without one would be problematic, so we should probably leave it alone and only apply a policy change (if we make one) to *new* provisional APIs

* the main open proposal we have for a new provisional API in 3.7 is for the interpreters module in PEP 554 (as we don't expect either execution contexts or data classes to need to be provisional). Defaulting to emitting a FutureWarning for that would be appropriate, since we already expect there to be changes over time in the way it interacts with the threading module. Accordingly, I've added Eric Snow to the nosy list here, since it's his PEP that would be most directly affected (it would need to define a new "__main__.use_provisional_interpreters" feature flag, and emit a FutureWarning on import if that wasn't set).


The specific cases of our current approach to handling provisional APIs causing problems for others that came up in the original discussion were:

* third party libraries (e.g. Twisted, Django) being pressured by their users to add asyncio support while asyncio was still provisional. This pushed the burden of explaining what CPython's provisional API status actually means on to the developers of those libraries, including the implication that the community support period for any particular version of the provisional API may end up being measured in months rather than the years typically implied by inclusion in the CPython standard library.
* developers & users of the cattrs package getting caught by surprise when backwards incompatible changes were made to the typing module

So this RFE is about ensuring that we have *adequately informed* consent when people opt-in to the more demanding requirements of using provisional APIs (i.e. you need to be able to reliably track the latest version, and if you and your users aren't in a position to commit to that, it's desirable to either not support them at all, or else treat support for them as an optional add-on until they're declared stable).

It's also about helping to ensure that when folks do have questions about what it means for a Python API to be provisional, they come to *us* (the CPython core developers that are actually working on those provisional APIs) with their questions, rather than hassling third party library developers that are just trying to keep up with the related changes.

For syntactic changes, we already have __future__ imports as a way for people to declare informed consent and adopt features early.

We don't currently have a comparable mechanism for provisional APIs - we're just assuming that everyone advocating for early adoption of the API will also be including the appropriate caveats around the fact that it's provisional, and that this may place additional burdens on both end users attempting to use it, and third party libraries attempting to support it.

We've had 5 years of experience with the provisional API R&D model now, and while some of the uses haven't caused any ecosystem level problems that we're aware of (e.g. pathlib), two of the more high profile uses (asyncio & typing) *have* caused problems, and at least some of those problems can be attributed to the fact that it's easy for people to learn of and start using a provisional API without ever looking at the standard library module documentation for it:

- they may be following something they saw in a talk
- they may be following an answer from Stack Overflow
- they may be following a blog post or 3rd party guide rather than the standard library documentation
- they may be following the documentation for a 3rd party libary or framework

As a result, merely importing a provisional API doesn't really qualify as giving appropriately informed consent, while silencing a FutureWarning that's emitted by default *does*: the act of silencing the warning means that either they've read the warning themselves and decided to silence it, or else they've silenced it based on someone else's advice.

This differs from DeprecationWarnings, since we know from experience (most recently with the initially proposed warnings in the locale coercion PEP) that any kind of new output on stderr can sometimes break working code. Making those opt-in thus meant that we could continue adding them as needed, but developers would be in control of when and where they actually appear (e.g. only in their test systems, instead of on end user's machines).

With FutureWarnings for provisional APIs, we'd be aiming to encourage a couple of key behaviours:

- for direct use in scripts and application code, set the feature flag when first adding an import of the provisional API, and have this guidance be a standard part of all third party introductions to using the provisional API
- for third party libraries, either only import provisional APIs when their own users explicitly opt-in to using related experimental features, or else inform their users that they should set the feature flag appropriately when depending on the library
msg304124 - (view) Author: Amber Brown (hawkowl) Date: 2017-10-11 09:54
> * asyncio & pathlib are already non-provisional, so wouldn't be affected.

I was reading this and actually said "wait what I didn't know pathlib was provisional", and went back to check. The warning for it was grey next to a pair of yellow ones, no wonder I didn't see it.

I guess we can all agree that the provisional warning should be modified to be more obvious, at least. What's required in doing that? Just editing what's there, or is there some form of template that is used?
msg304299 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2017-10-13 00:43
OK, so the Django and Twisted developers had to resist some pressure from their users. These are mature projects and if they can't resist pressure from users something is wrong with those projects, and just pointing users to PEP 411 would have been a sufficient answer. (In reality of course, some users come across feeling excessively entitled, but that issue goes well beyond this.)

I recall what happened to cattrs (they came to our tracker: https://github.com/python/typing/issues/345). Ironically the cattrs package itself advertises itself as experimental, so this seems fair game. From skimming the issue they took it in stride (also, we fixed it in the next release).

I really don't think we should add annoying runtime behavior just to warn off to people who don't read the official docs. I don't actually know how that has to be done, but surely there are some Sphinx experts in the core-dev group?

Regarding the interpreters package, it feels like something of a different magnitude (due to the extensive interpreter changes and the implications for 3rd party extensions). But I really don't want this thread to be distracted by that.
msg304301 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2017-10-13 01:21
Besides making the provisional warning more noticeable in module doc pages, perhaps we should have a "Provisional Package / API" section somewhere in the release's docset: a liat of all provisionals and perhaps a list of formerly provisional, now stable items that have transitioned since the previous feature release.  An obvious place would be in the What's New doc and it could be linked to from various places, like the Glossary, the release PEP, and/or the Release Notes.
msg304302 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-13 01:27
If a proposed standard library API is sufficiently stable that the folks proposing it are reluctant to emit a runtime warning about any remaining stability risks, then I think it's reasonable to expect them to propose it as non-provisional and accept the related backwards compatibility obligations.

We have past examples of our being able to cope with that approach, such as when contextlib.nested turned out to be broken at a design level, so we deprecated it, removed it, and replaced it with a combination of contextlib.ExitStack and native support for multiple context managers in with statements.

Framing that in different terms: with PEP 411 as currently written, the benefits of instability accrue to the API publisher and willing early adopters, while the costs appear as negative externalities affecting folks that would prefer not to care about the API at all until it is covered by the regular backwards compatibility guarantees.

This RFE proposes to internalise some of those costs (in the form of a required runtime warning for any future provisional APIs), such that API publishers ask themselves "Do I *really* need this whole API to be provisional? Can I restructure it so only selected clearly identifiable parts are provisional or private, and the rest are covered by regular stability guarantees?" and early adopters ask themselves "Do I really want to make this a *required* dependency? Perhaps I can make it optional somehow, so folks that aren't using these features won't get the warning?"
msg304303 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2017-10-13 02:32
I am at a loss for words.

On Oct 12, 2017 6:27 PM, "Nick Coghlan" <report@bugs.python.org> wrote:

>
> Nick Coghlan <ncoghlan@gmail.com> added the comment:
>
> If a proposed standard library API is sufficiently stable that the folks
> proposing it are reluctant to emit a runtime warning about any remaining
> stability risks, then I think it's reasonable to expect them to propose it
> as non-provisional and accept the related backwards compatibility
> obligations.
>
> We have past examples of our being able to cope with that approach, such
> as when contextlib.nested turned out to be broken at a design level, so we
> deprecated it, removed it, and replaced it with a combination of
> contextlib.ExitStack and native support for multiple context managers in
> with statements.
>
> Framing that in different terms: with PEP 411 as currently written, the
> benefits of instability accrue to the API publisher and willing early
> adopters, while the costs appear as negative externalities affecting folks
> that would prefer not to care about the API at all until it is covered by
> the regular backwards compatibility guarantees.
>
> This RFE proposes to internalise some of those costs (in the form of a
> required runtime warning for any future provisional APIs), such that API
> publishers ask themselves "Do I *really* need this whole API to be
> provisional? Can I restructure it so only selected clearly identifiable
> parts are provisional or private, and the rest are covered by regular
> stability guarantees?" and early adopters ask themselves "Do I really want
> to make this a *required* dependency? Perhaps I can make it optional
> somehow, so folks that aren't using these features won't get the warning?"
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <https://bugs.python.org/issue31742>
> _______________________________________
>
msg304309 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-13 05:39
Keep in mind that I'm not proposing that we retroactively change our approach to managing any currently provisional APIs that were proposed and implemented under the current version of PEP 411.

Instead I'm merely suggesting that the policy for any *future* provisional APIs be amended to require a runtime feature flag and FutureWarning by default, with the possible disposition of such proposals then being:

1. Rejected (as with any proposal)
2. Accepted with the feature flag and runtime warning in place
3. The proposal is reformulated to include both non-provisional and provisional parts, with the feature flag and warning applying only to use of the latter elements
4. The proposal is reformulated to offer an entirely non-provisional public API without a feature flag or warning (perhaps with a private "_machinery" or "_internals" submodule to better enable third party tinkering and experimentation)
5. The submitter of the proposal successfully makes the case that their proposal is simultaneously stable enough that it doesn't need a feature flag or runtime warning and *un*stable enough that it shouldn't be expected to offer the standard library's usual backwards compatibility guarantees

I personally think the final option will be tricky enough to justify that most folks won't even bother trying, and will instead opt for one side or the other (i.e. the feature-flag-and-warning, or a non-provisional API)

However, it's an option I'd consider reasonable to retain in recognition of the fact that it's been used without problems in the past (e.g. it's plausible that pathlib could credibly have made that case, since the provisional status wasn't due to potential API stability within pathlib itself, it was due to doubts about how well it would be able to integrate with other parts of the standard library. It went unprovisional once the introduction of the filesystem path protocol allowed those integration concerns to be fully resolved).
msg304347 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2017-10-13 17:03
I think this ought to be a new PEP which supersedes PEP 411. I think it should simply *offer* the option of using a feature flag rather than prescribing it. It could also explain that feature flags could cover an entire module or only the unstable parts. I would be happy to accept something like that.

I am still unclear as to how you are proposing to implement the "feature flag". Making this an attribute of `__main__` doesn't feel right (I've seen programs with different main entry point). However I do think it could be global state, similar to what `warnings` does (maybe it should just be a warning).

I don't like the parallel with `__future__` imports because those are for *stable* APIs that use backwards incompatible syntax (typically a new keyword).

Thinking back on my experiences with asyncio and typing, I have a feeling that the provisional status was *mostly* used to introduce *new* APIs at bugfix releases. We were in general pretty careful with changes to the documented APIs, with some exceptions for where the design was broken, and we sometimes pushed backwards incompatibilities to feature releases (which get more vetting by users than bugfix releases). But in both cases the API surface was sufficiently large that we simply didn't know in which areas details would have to change in the future, and we didn't want to be stuck with backwards compatibility hacks long-term. (The worst thing is when the perfect name for an API is found to require an incompatible signature change -- if you solve it by using a different the API will forever look ugly or confusing or weird.)

I know there have been times for both asyncio and typing where we wished they weren't in the stdlib at all -- mostly because we had users stuck with a CPython version (e.g. 3.5.1) that was missing an important addition to the API. But all in all I think that for both libraries the advantages have well outweighed the disadvantages. And a required warning on import would have really bothered me.
msg304348 - (view) Author: Aaron Gallagher (habnabit) Date: 2017-10-13 17:09
>Storing the marker attribute in __main__ [...]

Can I request please not using __main__ for this? setuptools console_scripts are very common, which is a case where __main__ will be a generated (i.e. not user-controllable) file. Making application code import __main__ to set the flag would be brittle.
msg304372 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-10-14 04:22
OK, I'll head down the path of creating a new procedural PEP to supersede PEP 411 (I'll try to get the locals() semantic clarification PEP out of the way first, though).

I'll make "Where to put the feature flags?" an open question, as my rationale for proposing __main__ was three-fold:

1. In regular scripts, it makes feature flags as easy to set as possible, since you can just do "use_provisional_interpreters = True" without any import at all
2. In applications, "import __main__; use_provisional_interpreters = True" isn't markedly more brittle as a process-global state storage location than any other module name (as long as the feature flag names are prefixed to minimise the risk of name collisions)
3. Using an existing always imported module makes the runtime cost of managing the feature flags as close to zero as possible

However, you'd also get most of those benefits with an even lower risk of name collisions by co-opting "sys" for the same purpose.

Silencing the warning via the feature flag:

    import sys
    sys.use_provisional_interpreters = True
    import interpreters


Silencing the warning via the warnings module:

    from warnings import filterwarnings
    filterwarnings("ignore", module="interpreters", category=FutureWarning)
    import interpreters

Emitting the warning:

    import sys
    _feature_flag = f"use_provisional_{__name__}"
    if not getattr(sys, _feature_flag):
        import warnings
        _provisional_msg = (
            f"The {__name__} module is currently a provisional API - see documentation for details. "
            f"Set 'sys.{_feature_flag} = True' before importing the API to disable this warning."
        )
        warnings.warn(FutureWarning, _provisional_msg)
msg304417 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2017-10-15 05:19
OK, I'll wait for the new PEP and ignore the ticket.
History
Date User Action Args
2017-10-18 19:43:36ethan.furmansetnosy: + ethan.furman
2017-10-15 05:20:09gvanrossumsetnosy: - gvanrossum
2017-10-15 05:19:56gvanrossumsetmessages: + msg304417
2017-10-14 04:22:19ncoghlansetmessages: + msg304372
2017-10-13 17:09:12habnabitsetnosy: + habnabit
messages: + msg304348
2017-10-13 17:03:53gvanrossumsetmessages: + msg304347
2017-10-13 05:39:42ncoghlansetmessages: + msg304309
2017-10-13 02:32:01gvanrossumsetmessages: + msg304303
2017-10-13 01:27:30ncoghlansetmessages: + msg304302
2017-10-13 01:21:47ned.deilysetmessages: + msg304301
2017-10-13 00:43:37gvanrossumsetmessages: + msg304299
2017-10-12 21:24:30ned.deilysetnosy: + ned.deily
2017-10-11 09:54:04hawkowlsetmessages: + msg304124
2017-10-11 06:27:57ncoghlansetnosy: + eric.snow
messages: + msg304108
2017-10-11 03:08:34gvanrossumsetmessages: + msg304103
2017-10-11 00:35:38hawkowlsetmessages: + msg304097
2017-10-10 16:12:35dstufftsetmessages: + msg304057
2017-10-10 15:48:43gvanrossumsetmessages: + msg304055
2017-10-10 13:25:56ncoghlansetmessages: + msg304042
2017-10-10 13:20:44ncoghlansetmessages: + msg304041
2017-10-10 13:19:38r.david.murraysetmessages: + msg304040
2017-10-10 13:09:55r.david.murraysetmessages: + msg304035
2017-10-10 13:09:52ncoghlansetmessages: + msg304034
2017-10-10 13:05:35hawkowlsetmessages: + msg304033
2017-10-10 13:02:41serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg304032
2017-10-10 12:58:42r.david.murraysetmessages: + msg304031
2017-10-10 12:51:43hawkowlsetmessages: + msg304030
2017-10-10 12:49:43r.david.murraysetnosy: + r.david.murray
messages: + msg304029
2017-10-10 07:03:55ncoghlansetmessages: + msg304019
2017-10-10 04:57:15hawkowlsetnosy: + hawkowl
messages: + msg304011
2017-10-10 04:24:28dstufftsetmessages: + msg304010
2017-10-10 03:58:29gvanrossumsetmessages: + msg304009
2017-10-10 01:53:30ncoghlansetmessages: + msg304008
2017-10-10 01:50:02ncoghlansetmessages: + msg304007
2017-10-10 01:47:27ncoghlancreate