[Python-Dev] Re: str() vs format(): trivia question
20.04.21 22:01, Guido van Rossum пише: > So to be clear, that one user wants f"{Color.RED}" to return "1" and not > " Color.RED" (or something like that). The user should write f"{int(Color.RED)}" or f"{Color.RED.value}". I have also an idea to support of additional conversion characters, so the use could write f"{Color.RED!i}". Opened a discussion for this on Python-ideas. https://mail.python.org/archives/list/python-id...@python.org/thread/3AALXB6A7EN6UY635MF5O2SFHZVFA5NM/ ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/UB4PDYSREAEUXAEUVCIZQA4G5KJPFEVD/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: str() vs format(): trivia question
20.04.21 17:56, Ethan Furman пише: > urllib.urlencode currently uses `str()` on its non-bytes objects before > encoding the result. This causes a compatibility break when integer > module constants are converted to IntEnum, as `str(IntEnum.MEMBER)` no > longer returns the integer representation; however, `format()` does > still return the integer representation. > > The fix is to add a separate branch to check if the argument is an Enum, > and use the value if so -- but it got me wondering: in general, are > there differences between calling str() vs calling format() on Python > objects? format() without format specifier and str() should return the same value in general, otherwise it will confuse users. But str() for enum should in general return a symbolic name, not the attached value which is an implementation detail. This is the purpose of enums. I think that __format__ should return the same as __str__ by default. This can break some user code, but custom __str__ can break it as well. Some breakage is inevitable when we convert some constants in the stdlib to enums. If user want to get an integer representation of an enum member, it should use IntEnum.MEMBER.value or int(IntEnum.MEMBER). ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/TGWLHZA7F6OW35IAGTQBLTAURRQMACCP/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: Keeping Python a Duck Typed Language.
Am 20.04.2021 um 19:03 schrieb Mark Shannon: PEP 544 supports structural typing, but to declare a structural type you must inherit from Protocol. That smells a lot like nominal typing to me. I'm not sure what inheriting from Protocol has to do with nominal typing, even though I would personally prefer to have a separate keyword for declaring protocols. But current typing philosophy is to prefer structural over nominal typing: * Use abstract types like "Iterable" or "Sequence" over concrete types list "list". * Use protocols instead of instances where it makes sense (although pragmatism very often means using an existing concrete class instead of defining a protocol). * We are slowly replacing typing.IO et al. with more restricted protocols. Personally I think that the typing infrastructure should move even more towards structural typing than it does at the moment and there is certainly a lot of room to grow. But overall I feel that typing is moving towards allowing more duck typing than it does at the moment. - Sebastian ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/4SDOHDJENVLNDZVJYZN47JSHWJMN4SHV/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: [python-committers] PEP 563 and Python 3.10.
While I have not been involved in the release process for like 15 years or more, I would like to point out that breaking changes mean the distros are less likely to ship them, and be less likely to trust updates. Trying to get RH &c to stop shipping 1.5.2 was a huge effort. Always, any time when you might need to break compat it's a huge risk. On Wed, 21 Apr 2021, 4:58 am Thomas Wouters, wrote: > > (Starting a new thread so as not to derail any of the ongoing discussions.) > > Thanks, everyone, for your thoughts on Python 3.10 and the impact of PEP > 563 (postponed evaluation of annotations) becoming the default. The > Steering Council has considered the issue carefully, along with many of the > proposed alternatives and solutions, and we’ve decided that at this point, > we simply can’t risk the compatibility breakage of PEP 563. We need to roll > back the change that made stringified annotations the default, at least for > 3.10. (Pablo is already working on this.) > > To be clear, we are not reverting PEP 563 itself. The future import will > keep working like it did since Python 3.7. We’re delaying making PEP 563 > string-based annotations the default until Python 3.11. This will give us > time to find a solution that works for everyone (or to find a feasible > upgrade path for users who currently rely on evaluated annotations). Some > considerations that led us to this decision: > > - PEP 563’s default change is clearly too disruptive to downstream users > and third-party libraries to happen right now. We can’t risk breaking even > a small subset of the FastAPI/pydantic users, not to mention other uses of > evaluated type annotations that we’re not aware of yet. > - PEP 563 provides no warning to users of the feature it’s disabling. > Without that, we can’t expect users to be aware of the upcoming breakage. > The lack of a warning was by design, and made sense in a world where type > annotations were only consumed by static type checkers --- but that’s not > actually the situation we’re in. There are clearly existing real-world, > run-time uses of type annotations that would be adversely affected by this > change. > - Originally, PEP 563 was scheduled to take effect in Python 4, and this > changed recently (after the discussion in the Language Summit of 2020). > It's possible that third-party libraries and users didn’t plan to react in > the current time frame as they were not aware of this change in timing. > - There isn’t enough time to properly discuss PEP 649 or any of the > alternatives before the beta 1 deadline, and we really need to make sure we > don’t compound errors here. We need to look for a long term solution, > which isn’t possible while still maintaining the release deadlines of > Python 3.10. That means we’re also deferring PEP 649 to Python 3.11. > > In the Steering Council’s unanimous opinion, rolling back the default flip > for stringified annotations in Python 3.10 is the least disruptive of all > the options. > > We need to continue discussing the issue and potential solutions, since > this merely postpones the problem until 3.11. (For the record, postponing > the change further is not off the table, either, for example if the final > decision is to treat evaluated annotations as a deprecated feature, with > warnings on use.) > > For what it’s worth, the SC is also considering what we can do to reduce > the odds of something like this happening again, but that’s a separate > consideration, and a multi-faceted one at that. > > For the Steering Council, > Thomas. > -- > Thomas Wouters > > Hi! I'm an email virus! Think twice before sending your email to help me > spread! > ___ > python-committers mailing list -- python-committ...@python.org > To unsubscribe send an email to python-committers-le...@python.org > https://mail.python.org/mailman3/lists/python-committers.python.org/ > Message archived at > https://mail.python.org/archives/list/python-committ...@python.org/message/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ > Code of Conduct: https://www.python.org/psf/codeofconduct/ > ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/ULSWULZIBTYQ5GRBTLRQGNKQ525S7WJC/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST]
Removing two concepts and preserving semantics simplifies the matter for users. People need less to memorize and less to learn. Or am I missing something here? Couldn’t we achieve our goal without these two new classes? Splitting, wrapping and exception handling semantics are perfectly fine and serve their purposes. So, of course this still needs to be implemented. > On 20. Apr 2021, at 23:26, Irit Katriel wrote: > > I don’t see what this simplifies. We still need to implement split, and to > worry about wrapping or not wrapping BaseExceptions and we still need to > define exception handling semantics (except/except*). > > >> On 20 Apr 2021, at 22:12, srku...@mail.de wrote: >> >> So, forgive me my relatively simple mental model about ExceptionGroup. I >> still try to create one for daily use. >> >> As noted in the discussion, an EG provides a way to collect exceptions from >> different sources and raise them as a bundle. They have no apparent relation >> up until this point in time (for whatever reason they have been separate and >> for whatever reason they are bundled now). The result would be a tree graph >> in any case. >> >> A usual datastructure for a tree is to store all child nodes at the parent >> node. >> >> That was the idea behind the content of BaseException.__group__: it’s the >> list of child exceptions bundled at a specific point in time and raise as >> such a bundle. So all exceptions could become EGs with the additional >> semantics you‘ve described in the PEP. >> >> Illustrative Example: > bundle_exc.__group__ >> [IOError(123), RuntimerError(‘issue somewhere’)] >> >> I was wondering what of the PEP could be removed to make it simpler and more >> acceptable/less confusing (also looking at reactions from Twitter etc.) and >> I found these additional classes to be a part of it. Additionally, I fail to >> see how to access these bundled exceptions in an easy manner like __cause__ >> and __context__. (As the PEP also referring to them). So, I removed the >> classes and added a regular attribute. >> >> The reason I brought this up what the section “rejected ideas” didn’t showed >> anything in this direction (or I managed to missed that). >> >> Sven >> >>> On 20. Apr 2021, at 22:05, Irit Katriel wrote: >>> >>> Hi Sven, >>> >>> I don’t follow. What would the value of __group__ be and how would it work? >>> >>> Irit >>> > On 20 Apr 2021, at 20:44, srku...@mail.de wrote: Hi Irit, reading this subthread specifically, I just got a wild idea and I couldn‘t find any related information in the PEP: Why not extending BaseException by __group__ among __cause__ and __context__? Would this reduce some of the added complexity and thus increase broader acceptance? Cheers, Sven >> ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/OD6IDT5HGDULGLVVQ2HF7WJXNQM7RA3K/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST]
On Wed, Apr 21, 2021 at 11:50 AM srku...@mail.de wrote: > > Removing two concepts and preserving semantics simplifies the matter for > users. People need less to memorize and less to learn. > > Or am I missing something here? Couldn’t we achieve our goal without these > two new classes? No, we can't. What you are proposing would make it very hard for users to understand at a glance if what you have in an innocently looking `except Exception` is correct or not. In my async/await code I'd have to always check the `__group__` attribute to make sure it's not an exception group in disguise. So while you're "simplifying" the proposal by removing a couple of types, you're complicating it in all other places. Besides, I don't think that adding the ExceptionGroup type is a controversial idea that needs any simplification. Yury ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/IXHLXQYTEFPRC2GLFGAM6GHCYAAQG4DQ/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: Keeping Python a Duck Typed Language.
Thanks Mark for posting this. I know some of us are uneasy about the pace of the typing train On Tue, Apr 20, 2021 at 11:20 AM Nathaniel Smith wrote: > > If you guarded your code with `isinstance(foo, Sequence)` then I could > > not use it with my `Foo` even if my `Foo` quacked like a sequence. I was > > forced to use nominal typing; inheriting from Sequence, or explicitly > > registering as a Sequence. > > You say this like it's a bad thing, but how is this avoidable, even in > principle? Structural typing lets you check whether Foo is duck-shaped > -- has appropriate attribute names, etc. But quacking like a duck is > harder: you also have to implement the Sequence behavioral contract, > and realistically the only way to know that is if the author of Foo > tells you. > But that's not what duck typing is (at least to me :-) ) For a given function, I need the passed in object to quack (and yes, I need that quack to sound like a duck) -- but I usually don't care whether that object waddles like a duck. So yes, isinstance(obj, Sequence) is really the only way to know that obj is a Sequence in every important way -- but if you only need it to do one or two things like a Sequence, then you don't care. And this is not uncommon -- I suspect it's very rare for a single function to use most of the methods of a given ABC (or protocol, or whatever). And a lot of the standard library works exactly this way. Two examples (chosen arbitrarily, I just happen to have thought about how they work): json.load() simply calls ``fp.read()``, and passes the result on down to json.loads(). That's it -- no checking of anything. If fp does not have a read() method, you get an AttributeError. If fp has a read() method, but it returns something other than a string, then you get some other Exception. And if it returns a string, but that string isn't valid JSON, you get yet another kind of error. In short, json.load(fp, ...) requires fp to have a read() method that returns a valid JSON string. But it doesn't check, nor does it need to, if it's getting an actual io.TextIOBase object. Is that the right one? I'm not totally sure, which I kind of think makes my point -- I've been using "file-like" objects for years (decades) without worrying about it. Example 2: The str.translate method takes: "a mapping of Unicode ordinals to Unicode ordinals, strings, or None" Ok, then you need to pass in a Mapping, yes? Well, no you don't. The docs go on to say: The table must implement lookup/indexing via __getitem__, for instance a dictionary or list. Ah -- so we don't need a Mapping -- we need anything indexable by an integer that contains "ordinals, strings, or None". What the heck ABC could we use for that? The ABCs do have an already complex hierarchy of containers, but there is no "Indexable", (quacks) and certainly no "indexable and returns these particular things. (quacks a certain way). (maybe there's something in the typing module that would work for static typing -- I have no idea). I'm pretty sure this particular API was designed to accommodate the old py2 str.translate, which took a length-256 sequence, while also accommodating full Unicode, which would have required a 2^32 length sequence to do the same thing :-) But again -- this is duck typing, built into the stdlib, and it works just fine. Granted, until PEP 563 (kind of) , there has been nothing that weakens or disallows such duck typing -- those of us that want to write fully duck-typed code can continue to do so. But there is the "culture" of Python -- and it has been very much shifting toward more typing -- A recent publication (sorry can't find it now -- my google fu is failing me) examined code on PyPi and found a lot of type hints -- many of which were apparently not being used with a static type checker. So why were they there? And I've seen a lot more isinstance(Some_ABC) code lately as well. >From looking at the work of my beginning students, I can tell that they are seeing examples out there that use more typing, to the point that they think it's a best practice (or maybe even required?). Maybe it is -- but if the community is moving that way, we should be honest about it. > I'm not even sure that this *is* nominal typing. You could just as > well argue that "the operation `isinstance(..., Sequence)` returns > `True`" is just another of the behavioral constraints that are > required to quack like a sequence. > I'm not sure of the definition of "nominal" typing -- but it absolutely is NOT duck typing (As Luciano pointed out, Alex Martelli coined the term Goose Typing for this). The big distinction is whether we want to know if the object is a duck, or if we only need it to do one or two things like a duck. -CHB -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython ___ Py
[Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST]
On Tue, Apr 20, 2021 at 2:15 PM srku...@mail.de wrote: > > So, forgive me my relatively simple mental model about ExceptionGroup. I > still try to create one for daily use. > > As noted in the discussion, an EG provides a way to collect exceptions from > different sources and raise them as a bundle. They have no apparent relation > up until this point in time (for whatever reason they have been separate and > for whatever reason they are bundled now). The result would be a tree graph > in any case. > > A usual datastructure for a tree is to store all child nodes at the parent > node. > > That was the idea behind the content of BaseException.__group__: it’s the > list of child exceptions bundled at a specific point in time and raise as > such a bundle. So all exceptions could become EGs with the additional > semantics you‘ve described in the PEP. > > Illustrative Example: > >>> bundle_exc.__group__ > [IOError(123), RuntimerError(‘issue somewhere’)] > > I was wondering what of the PEP could be removed to make it simpler and more > acceptable/less confusing (also looking at reactions from Twitter etc.) and I > found these additional classes to be a part of it. Additionally, I fail to > see how to access these bundled exceptions in an easy manner like __cause__ > and __context__. (As the PEP also referring to them). So, I removed the > classes and added a regular attribute. This seems more confusing to me too. Instead of having a single ExceptionGroup class, you're suggesting that all exceptions should effectively become ExceptionGroups. I know what ExceptionGroup([ValueError]) means -- it means there was a ValueError, and it got put in a group, so it should probably be handled the same way as a ValueError. I have no idea what a KeyError([ValueError]) would mean. Is that a KeyError or a ValueError? Adding flexibility doesn't necessarily make things simpler :-) -n -- Nathaniel J. Smith -- https://vorpus.org ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/F6LVP7CLQGIAZ2PI2SL6FN3VXF2MVY2M/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: Keeping Python a Duck Typed Language
I think what Christopher says is fair. My question (with little understanding of the underlying infrastructure) is can we define a 'type' that communicates that? Something like 'object that implements .read', or '.upper and .__iter__ for a spring-like object's? toonarmycaptain Original message From: python-dev-requ...@python.org Date: 4/21/21 2:44 PM (GMT-06:00) To: python-dev@python.org Subject: Python-Dev Digest, Vol 213, Issue 128 Send Python-Dev mailing list submissions to python-dev@python.org To subscribe or unsubscribe via the World Wide Web, visit https://mail.python.org/mailman3/lists/python-dev.python.org/ or, via email, send a message with subject or body 'help' to python-dev-requ...@python.org You can reach the person managing the list at python-dev-ow...@python.org When replying, please edit your Subject line so it is more specific than "Re: Contents of Python-Dev digest..." Today's Topics: 1. Re: PEP 654: Exception Groups and except* [REPOST] (srku...@mail.de) 2. Re: PEP 654: Exception Groups and except* [REPOST] (Yury Selivanov) 3. Re: Keeping Python a Duck Typed Language. (Christopher Barker) -- Message: 1 Date: Wed, 21 Apr 2021 20:48:29 +0200 From: "srku...@mail.de" Subject: [Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST] To: Irit Katriel Cc: Irit Katriel , python-dev , Yury Selivanov Message-ID: <04512110-64b5-4ab3-ae54-ae6889c77...@mail.de> Content-Type: text/plain; charset=utf-8 Removing two concepts and preserving semantics simplifies the matter for users. People need less to memorize and less to learn. Or am I missing something here? Couldn’t we achieve our goal without these two new classes? Splitting, wrapping and exception handling semantics are perfectly fine and serve their purposes. So, of course this still needs to be implemented. > On 20. Apr 2021, at 23:26, Irit Katriel wrote: > > I don’t see what this simplifies. We still need to implement split, and to > worry about wrapping or not wrapping BaseExceptions and we still need to > define exception handling semantics (except/except*). > > >> On 20 Apr 2021, at 22:12, srku...@mail.de wrote: >> >> So, forgive me my relatively simple mental model about ExceptionGroup. I >> still try to create one for daily use. >> >> As noted in the discussion, an EG provides a way to collect exceptions from >> different sources and raise them as a bundle. They have no apparent relation >> up until this point in time (for whatever reason they have been separate and >> for whatever reason they are bundled now). The result would be a tree graph >> in any case. >> >> A usual datastructure for a tree is to store all child nodes at the parent >> node. >> >> That was the idea behind the content of BaseException.__group__: it’s the >> list of child exceptions bundled at a specific point in time and raise as >> such a bundle. So all exceptions could become EGs with the additional >> semantics you‘ve described in the PEP. >> >> Illustrative Example: > bundle_exc.__group__ >> [IOError(123), RuntimerError(‘issue somewhere’)] >> >> I was wondering what of the PEP could be removed to make it simpler and more >> acceptable/less confusing (also looking at reactions from Twitter etc.) and >> I found these additional classes to be a part of it. Additionally, I fail to >> see how to access these bundled exceptions in an easy manner like __cause__ >> and __context__. (As the PEP also referring to them). So, I removed the >> classes and added a regular attribute. >> >> The reason I brought this up what the section “rejected ideas” didn’t showed >> anything in this direction (or I managed to missed that). >> >> Sven >> >>> On 20. Apr 2021, at 22:05, Irit Katriel wrote: >>> >>> Hi Sven, >>> >>> I don’t follow. What would the value of __group__ be and how would it work? >>> >>> Irit >>> > On 20 Apr 2021, at 20:44, srku...@mail.de wrote: Hi Irit, reading this subthread specifically, I just got a wild idea and I couldn‘t find any related information in the PEP: Why not extending BaseException by __group__ among __cause__ and __context__? Would this reduce some of the added complexity and thus increase broader acceptance? Cheers, Sven >> -- Message: 2 Date: Wed, 21 Apr 2021 11:58:23 -0700 From: Yury Selivanov Subject: [Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST] To: "srku...@mail.de" Cc: Irit Katriel , Irit Katriel , python-dev , Yury Selivanov Message-ID: Content-Type: text/plain; charset="UTF-8" On Wed, Apr 21, 2021 at 11:50 AM srku...@mail.de wrote: > > Removing two concepts and preserving semantics simplifies the matter for > users. People need less to memorize and less to learn. > > Or am I mis
[Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST]
On Tue, Apr 20, 2021 at 3:15 AM Irit Katriel wrote: > On Tue, Apr 20, 2021 at 2:48 AM Nathaniel Smith wrote: >> >> >> The problem is that most of the time, even if you're using concurrency >> internally so multiple things *could* go wrong at once, only one thing >> actually *does* go wrong. So it's unfortunate if some existing code is >> prepared for a specific exception that it knows can be raised, that >> exact exception is raised... and the existing code fails to catch it >> because now it's wrapped in an EG. > > Yes, this was discussed at length on this list. Raising an exception group is > an API-breaking change. If a function starts raising exception groups its > callers need to be prepared for that. Realistically we think exception groups > will be raised by new APIs. We tried and were unable to define exception > group semantics for except that would be reasonable and backwards compatible. > That's why we added except*. Sure. This was in my list of reasons why the backwards compatibility tradeoffs are forcing us into awkward compromises. I only elaborated on it b/c in your last email you said you didn't understand why this was a problem :-). And except* is definitely useful. But I think there are options for 'except' that haven't been considered fully. Saying that you have to make a new API every time you start using concurrency inside a function is extremely restrictive. The whole point of structured concurrency (and structured programming in general) is that function callers don't need to know about the control flow decisions inside a function. So right now, the EG proposal is like saying that if you every have a function that doesn't contain a 'for' loop, and then you want to add a 'for' loop, then you have to define a whole new API instead of modifying the existing one. I absolutely get why the proposal looks like that. I'm just making the point that we should make sure we've exhausted all our options before settling for that as a compromise. >> > It is easy enough to write a denormalize() function in traceback.py that >> > constructs this from the current EG structure, if you need it (use the >> > leaf_generator from the PEP). I'm not sure I see why we should trouble the >> > interpreter with this. >> >> In the current design, once an exception is wrapped in an EG, then it >> can never be unwrapped, because its traceback information is spread >> across the individual exception + the EG tree around it. This is >> confusing to users ("how do I check errno?"), and makes the design >> more complicated (the need for topology-preserving .split(), the >> inability to define a sensible EG.__iter__, ...). The advantage of >> making the denormalized form the native form is that now the leaf >> exceptions would be self-contained objects like they are now, so you >> don't need EG nesting at all, and users can write intuitive code like: >> >> except OSError as *excs: >> remainder = [exc for exc in excs if exc.errno != ...] >> if remainder: >> raise ExceptionGroup(remainder) > > > We have this precise example in the PEP: >match, rest = excs.split(lambda e: e.errno != ...) > > You use split() instead of iteration for that. split() preserves all > __context__, __cause__ and __traceback__ information, on all leaf and > non-leaf exceptions. Well, yeah, I know, I'm the one who invented split() :-). My point was to compare these two options: in the flat EG example, most Python users could write and read that code without knowing anything except "there can be multiple exceptions now". It's all old, well-known constructs used in the obvious way. For the .split() version, you have to write a lambda (which is allowed to access parts of the exception object, but not all of it!), and use this idiosyncratic method that only makes sense if you know about EG tree structures. That's a lot more stuff that users have to understand and hold in their head. Or here's another example. Earlier you suggested: > If you are using concurrency internally and don't want to raise EGs > externally, then surely you will catch EGs, select one of the exceptions to > raise and throw away all the others But with nested EGs, this is difficult, because you *can't* just pull out one of the exceptions to raise. The leaf exceptions aren't standalone objects; you need some obscure traceback manipulation to do this. I guarantee that users will get this wrong, even if we provide the tools, because the explanation about when and why you need the tools is complicated and people won't internalize it. With flat EGs, this is trivial: it's just `raise ExceptionGroup[the_selected_exception_index]`. >> > For display purposes, it is probably nicer to look at a normalized >> > traceback where common parts are not repeated. >> >> Yeah, I agree; display code would want to re-normalize before >> printing. But now it's only the display code that needs to care about >> figuring out shared parts of the traceback, rathe
[Python-Dev] Re: Keeping Python a Duck Typed Language.
As demonstrated, protocols don't get us there because duck typing isn't a matter of having an object exhibit all of the attributes of a duck, but rather some subset of attributes to be used by the consumer. I want this duck to quack; someone else will want it to waddle. I don't see how type hints could reasonably support "file like object" in the duck type sense (unless the consumer were to specify the exact attributes of the duck it's interested in, which I fear would become a tedious type writing style). I too have sensed static typing driving the typing development agenda in Python recently, causing other typing methods to take a back seat, so to speak. I add my voice to those requesting Python handle other typing methods. Barring an innovation to allow a "subset" of a type to be declared in a type hint, I would conclude that static typing and duck typing are diametrically opposed. If we agree that both are valuable, developers could build consensus on that point, and work to ensure that one does not move forward at the expense of the other. Paul On Wed, 2021-04-21 at 12:36 -0700, Christopher Barker wrote: > Thanks Mark for posting this. I know some of us are uneasy about the > pace of the typing train > > On Tue, Apr 20, 2021 at 11:20 AM Nathaniel Smith > wrote: > > > If you guarded your code with `isinstance(foo, Sequence)` then I > > could > > > not use it with my `Foo` even if my `Foo` quacked like a > > sequence. I was > > > forced to use nominal typing; inheriting from Sequence, or > > explicitly > > > registering as a Sequence. > > > > You say this like it's a bad thing, but how is this avoidable, even > > in > > principle? Structural typing lets you check whether Foo is duck- > > shaped > > -- has appropriate attribute names, etc. But quacking like a duck > > is > > harder: you also have to implement the Sequence behavioral > > contract, > > and realistically the only way to know that is if the author of Foo > > tells you. > > > > > But that's not what duck typing is (at least to me :-) ) For a given > function, I need the passed in object to quack (and yes, I need that > quack to sound like a duck) -- but I usually don't care whether that > object waddles like a duck. > > So yes, isinstance(obj, Sequence) is really the only way to know that > obj is a Sequence in every important way -- but if you only need it > to do one or two things like a Sequence, then you don't care. > > And this is not uncommon -- I suspect it's very rare for a single > function to use most of the methods of a given ABC (or protocol, or > whatever). > > And a lot of the standard library works exactly this way. Two > examples (chosen arbitrarily, I just happen to have thought about how > they work): > > json.load() simply calls ``fp.read()``, and passes the result on down > to json.loads(). That's it -- no checking of anything. > > If fp does not have a read() method, you get an AttributeError. If fp > has a read() method, but it returns something other than a string, > then you get some other Exception. And if it returns a string, but > that string isn't valid JSON, you get yet another kind of error. > > In short, json.load(fp, ...) requires fp to have a read() method that > returns a valid JSON string. But it doesn't check, nor does it need > to, if it's getting an actual io.TextIOBase object. Is that the right > one? I'm not totally sure, which I kind of think makes my point -- > I've been using "file-like" objects for years (decades) without > worrying about it. > > Example 2: > > The str.translate method takes: > > "a mapping of Unicode ordinals to Unicode ordinals, strings, or None" > > Ok, then you need to pass in a Mapping, yes? Well, no you don't. The > docs go on to say: > > The table must implement lookup/indexing via __getitem__, for > instance a > dictionary or list. > > Ah -- so we don't need a Mapping -- we need anything indexable by an > integer that contains "ordinals, strings, or None". What the heck ABC > could we use for that? > > The ABCs do have an already complex hierarchy of containers, but > there is no "Indexable", (quacks) and certainly no "indexable and > returns these particular things. (quacks a certain way). (maybe > there's something in the typing module that would work for static > typing -- I have no idea). > > I'm pretty sure this particular API was designed to accommodate the > old py2 str.translate, which took a length-256 sequence, while also > accommodating full Unicode, which would have required a 2^32 length > sequence to do the same thing :-) > > But again -- this is duck typing, built into the stdlib, and it works > just fine. > > Granted, until PEP 563 (kind of) , there has been nothing that > weakens or disallows such duck typing -- those of us that want to > write fully duck-typed code can continue to do so. > > But there is the "culture" of Python -- and it has been very much > shifting toward more typing -- A recent publication (sorry can't fi
[Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST]
On Wed, Apr 21, 2021 at 11:22 PM Nathaniel Smith wrote: > > > Saying that you have to make a new API every time you start using > concurrency inside a function is extremely restrictive. You don't need a new API when you start using concurrency inside a function. You need a new API when you start raising exception groups from a function because that changes the function's external behaviour. -1 from me on the denormalized traceback idea. I've said what I had to say about it in my previous email. Cheers Irit ___ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/HVPVPRSTJMI7KOFXQLJJCRUPC2R4IE42/ Code of Conduct: http://python.org/psf/codeofconduct/
[Python-Dev] Re: Keeping Python a Duck Typed Language.
El mié, 21 abr 2021 a las 15:30, Paul Bryan () escribió: > As demonstrated, protocols don't get us there because duck typing isn't a > matter of having an object exhibit all of the attributes of a duck, but > rather some subset of attributes to be used by the consumer. I want this > duck to quack; someone else will want it to waddle. I don't see how type > hints could reasonably support "file like object" in the duck type sense > (unless the consumer were to specify the exact attributes of the duck it's > interested in, which I fear would become a tedious type writing style). ' > This approach (a Protocol that declares exactly what you need the file-like object to do) is in fact what we've been doing in typeshed, the repository that provides type hints for the standard library. For those unfamiliar, it would look something like this: from typing import Protocol class SupportsRead(Protocol): def read(self) -> bytes: ... def uses_a_file(f: SupportsRead) -> None: f.read() That's somewhat tedious, to be sure, but it is working duck typing. > > I too have sensed static typing driving the typing development agenda in > Python recently, causing other typing methods to take a back seat, so to > speak. I add my voice to those requesting Python handle other typing > methods. > > Barring an innovation to allow a "subset" of a type to be declared in a > type hint, I would conclude that static typing and duck typing are > diametrically opposed. If we agree that both are valuable, developers could > build consensus on that point, and work to ensure that one does not move > forward at the expense of the other. > What would you suggest? Should the syntax for declaring Protocols be more concise? > > Paul > > On Wed, 2021-04-21 at 12:36 -0700, Christopher Barker wrote: > > Thanks Mark for posting this. I know some of us are uneasy about the pace > of the typing train > > On Tue, Apr 20, 2021 at 11:20 AM Nathaniel Smith wrote: > > > If you guarded your code with `isinstance(foo, Sequence)` then I could > > not use it with my `Foo` even if my `Foo` quacked like a sequence. I was > > forced to use nominal typing; inheriting from Sequence, or explicitly > > registering as a Sequence. > > You say this like it's a bad thing, but how is this avoidable, even in > principle? Structural typing lets you check whether Foo is duck-shaped > -- has appropriate attribute names, etc. But quacking like a duck is > harder: you also have to implement the Sequence behavioral contract, > and realistically the only way to know that is if the author of Foo > tells you. > > > But that's not what duck typing is (at least to me :-) ) For a given > function, I need the passed in object to quack (and yes, I need that quack > to sound like a duck) -- but I usually don't care whether that object > waddles like a duck. > > So yes, isinstance(obj, Sequence) is really the only way to know that obj > is a Sequence in every important way -- but if you only need it to do one > or two things like a Sequence, then you don't care. > > And this is not uncommon -- I suspect it's very rare for a single function > to use most of the methods of a given ABC (or protocol, or whatever). > > And a lot of the standard library works exactly this way. Two examples > (chosen arbitrarily, I just happen to have thought about how they work): > > json.load() simply calls ``fp.read()``, and passes the result on down to > json.loads(). That's it -- no checking of anything. > > If fp does not have a read() method, you get an AttributeError. If fp has > a read() method, but it returns something other than a string, then you get > some other Exception. And if it returns a string, but that string isn't > valid JSON, you get yet another kind of error. > > In short, json.load(fp, ...) requires fp to have a read() method that > returns a valid JSON string. But it doesn't check, nor does it need to, if > it's getting an actual io.TextIOBase object. Is that the right one? I'm not > totally sure, which I kind of think makes my point -- I've been using > "file-like" objects for years (decades) without worrying about it. > > Example 2: > > The str.translate method takes: > > "a mapping of Unicode ordinals to Unicode ordinals, strings, or None" > > Ok, then you need to pass in a Mapping, yes? Well, no you don't. The docs > go on to say: > > The table must implement lookup/indexing via __getitem__, for instance a > dictionary or list. > > Ah -- so we don't need a Mapping -- we need anything indexable by an > integer that contains "ordinals, strings, or None". What the heck ABC could > we use for that? > > The ABCs do have an already complex hierarchy of containers, but there is > no "Indexable", (quacks) and certainly no "indexable and returns these > particular things. (quacks a certain way). (maybe there's something in the > typing module that would work for static typing -- I have no idea). > > I'm pretty sure this particular API was designed to accommodate the old >
[Python-Dev] Re: Keeping Python a Duck Typed Language.
I agree, that's duck typing with a protocol, and precisely the tedious type style I would want to avoid. I don't know what would be a good suggestion. Something where you reference a "fully equipped" type and cherry pick the attributes you want? Something like `Protocol[Duck, ("quack", "waddle")]`? Paul On Wed, 2021-04-21 at 16:13 -0700, Jelle Zijlstra wrote: > > > El mié, 21 abr 2021 a las 15:30, Paul Bryan () > escribió: > > As demonstrated, protocols don't get us there because duck typing > > isn't a matter of having an object exhibit all of the attributes of > > a duck, but rather some subset of attributes to be used by the > > consumer. I want this duck to quack; someone else will want it to > > waddle. I don't see how type hints could reasonably support "file > > like object" in the duck type sense (unless the consumer were to > > specify the exact attributes of the duck it's interested in, which > > I fear would become a tedious type writing style). ' > > > > > This approach (a Protocol that declares exactly what you need the > file-like object to do) is in fact what we've been doing in typeshed, > the repository that provides type hints for the standard library. For > those unfamiliar, it would look something like this: > > from typing import Protocol > > class SupportsRead(Protocol): > def read(self) -> bytes: ... > > def uses_a_file(f: SupportsRead) -> None: > f.read() > > That's somewhat tedious, to be sure, but it is working duck typing. > > > > > I too have sensed static typing driving the typing development > > agenda in Python recently, causing other typing methods to take a > > back seat, so to speak. I add my voice to those requesting Python > > handle other typing methods. > > > > Barring an innovation to allow a "subset" of a type to be declared > > in a type hint, I would conclude that static typing and duck typing > > are diametrically opposed. If we agree that both are valuable, > > developers could build consensus on that point, and work to ensure > > that one does not move forward at the expense of the other. > > > > What would you suggest? Should the syntax for declaring Protocols be > more concise? > > > > > Paul > > > > On Wed, 2021-04-21 at 12:36 -0700, Christopher Barker wrote: > > > Thanks Mark for posting this. I know some of us are uneasy about > > > the pace of the typing train > > > > > > On Tue, Apr 20, 2021 at 11:20 AM Nathaniel Smith > > > wrote: > > > > > If you guarded your code with `isinstance(foo, Sequence)` > > > > then I could > > > > > not use it with my `Foo` even if my `Foo` quacked like a > > > > sequence. I was > > > > > forced to use nominal typing; inheriting from Sequence, or > > > > explicitly > > > > > registering as a Sequence. > > > > > > > > You say this like it's a bad thing, but how is this avoidable, > > > > even in > > > > principle? Structural typing lets you check whether Foo is > > > > duck-shaped > > > > -- has appropriate attribute names, etc. But quacking like a > > > > duck is > > > > harder: you also have to implement the Sequence behavioral > > > > contract, > > > > and realistically the only way to know that is if the author of > > > > Foo > > > > tells you. > > > > > > > > > > > > > But that's not what duck typing is (at least to me :-) ) For a > > > given function, I need the passed in object to quack (and yes, I > > > need that quack to sound like a duck) -- but I usually don't care > > > whether that object waddles like a duck. > > > > > > So yes, isinstance(obj, Sequence) is really the only way to know > > > that obj is a Sequence in every important way -- but if you only > > > need it to do one or two things like a Sequence, then you don't > > > care. > > > > > > And this is not uncommon -- I suspect it's very rare for a single > > > function to use most of the methods of a given ABC (or protocol, > > > or whatever). > > > > > > And a lot of the standard library works exactly this way. Two > > > examples (chosen arbitrarily, I just happen to have thought about > > > how they work): > > > > > > json.load() simply calls ``fp.read()``, and passes the result on > > > down to json.loads(). That's it -- no checking of anything. > > > > > > If fp does not have a read() method, you get an AttributeError. > > > If fp has a read() method, but it returns something other than a > > > string, then you get some other Exception. And if it returns a > > > string, but that string isn't valid JSON, you get yet another > > > kind of error. > > > > > > In short, json.load(fp, ...) requires fp to have a read() method > > > that returns a valid JSON string. But it doesn't check, nor does > > > it need to, if it's getting an actual io.TextIOBase object. Is > > > that the right one? I'm not totally sure, which I kind of think > > > makes my point -- I've been using "file-like" objects for years > > > (decades) without worrying about it. > > > > > > Example 2: > > > > > > The str.translate method takes:
[Python-Dev] Re: PEP 654: Exception Groups and except* [REPOST]
On Wed, Apr 21, 2021 at 3:26 PM Nathaniel Smith wrote: > On Tue, Apr 20, 2021 at 3:15 AM Irit Katriel > wrote: > > On Tue, Apr 20, 2021 at 2:48 AM Nathaniel Smith wrote: > >> > >> > >> The problem is that most of the time, even if you're using concurrency > >> internally so multiple things *could* go wrong at once, only one thing > >> actually *does* go wrong. So it's unfortunate if some existing code is > >> prepared for a specific exception that it knows can be raised, that > >> exact exception is raised... and the existing code fails to catch it > >> because now it's wrapped in an EG. > > > > Yes, this was discussed at length on this list. Raising an exception > group is an API-breaking change. If a function starts raising exception > groups its callers need to be prepared for that. Realistically we think > exception groups will be raised by new APIs. We tried and were unable to > define exception group semantics for except that would be reasonable and > backwards compatible. That's why we added except*. > > Sure. This was in my list of reasons why the backwards compatibility > tradeoffs are forcing us into awkward compromises. I only elaborated > on it b/c in your last email you said you didn't understand why this > was a problem :-). And except* is definitely useful. But I think there > are options for 'except' that haven't been considered fully. > Do you have any suggestions, or are you just telling us to think harder? Because we've already thought as hard as we could and within all the constraints (backwards compatibility and otherwise) we just couldn't think of a better one. > Saying that you have to make a new API every time you start using > concurrency inside a function is extremely restrictive. The whole > point of structured concurrency (and structured programming in > general) is that function callers don't need to know about the control > flow decisions inside a function. So right now, the EG proposal is > like saying that if you every have a function that doesn't contain a > 'for' loop, and then you want to add a 'for' loop, then you have to > define a whole new API instead of modifying the existing one. > That analogy doesn't fly. If "what exceptions can this raise" is not part of the function spec (so users who catch exceptions are already flying blind or guessing based on what they've seen happen in the past), then you can certainly switch to using EGs, and this *is* like adding a for-loop. But if the exceptions that a function raises *are* part of its spec, and users are catching them, then switching to concurrency (without catching and converting EGs in the function) *is* an API change, akin to taking a function that returns an int and changing it to (sometimes?) returning a list of ints. > I absolutely get why the proposal looks like that. I'm just making the > point that we should make sure we've exhausted all our options before > settling for that as a compromise. > But if you're not able to make constructive proposals I have to conclude that we *have* exhausted our options, and we should move on. > >> > It is easy enough to write a denormalize() function in traceback.py > that constructs this from the current EG structure, if you need it (use the > leaf_generator from the PEP). I'm not sure I see why we should trouble the > interpreter with this. > >> > >> In the current design, once an exception is wrapped in an EG, then it > >> can never be unwrapped, because its traceback information is spread > >> across the individual exception + the EG tree around it. This is > >> confusing to users ("how do I check errno?"), and makes the design > >> more complicated (the need for topology-preserving .split(), the > >> inability to define a sensible EG.__iter__, ...). The advantage of > >> making the denormalized form the native form is that now the leaf > >> exceptions would be self-contained objects like they are now, so you > >> don't need EG nesting at all, and users can write intuitive code like: > >> > >> except OSError as *excs: > >> remainder = [exc for exc in excs if exc.errno != ...] > >> if remainder: > >> raise ExceptionGroup(remainder) > > > > > > We have this precise example in the PEP: > >match, rest = excs.split(lambda e: e.errno != ...) > > > > You use split() instead of iteration for that. split() preserves all > __context__, __cause__ and __traceback__ information, on all leaf and > non-leaf exceptions. > > Well, yeah, I know, I'm the one who invented split() :-). My point was > to compare these two options: in the flat EG example, most Python > users could write and read that code without knowing anything except > "there can be multiple exceptions now". It's all old, well-known > constructs used in the obvious way. > > For the .split() version, you have to write a lambda (which is allowed > to access parts of the exception object, but not all of it!), and use > this idiosyncratic method that only makes sense if you know about EG > tree s
[Python-Dev] Re: Keeping Python a Duck Typed Language.
My personal motivating example for PEP 637 was shorthand for protocols. `x: Protocol[foo=int, bar=Callable[str, int]]` could say x has attribute foo which is an int and method bar from str to int. On Wed, Apr 21, 2021 at 4:23 PM Paul Bryan wrote: > I agree, that's duck typing with a protocol, and precisely the tedious > type style I would want to avoid. > > I don't know what would be a good suggestion. Something where you > reference a "fully equipped" type and cherry pick the attributes you want? > Something like `Protocol[Duck, ("quack", "waddle")]`? > > Paul > > > On Wed, 2021-04-21 at 16:13 -0700, Jelle Zijlstra wrote: > > > > El mié, 21 abr 2021 a las 15:30, Paul Bryan () escribió: > > As demonstrated, protocols don't get us there because duck typing isn't a > matter of having an object exhibit all of the attributes of a duck, but > rather some subset of attributes to be used by the consumer. I want this > duck to quack; someone else will want it to waddle. I don't see how type > hints could reasonably support "file like object" in the duck type sense > (unless the consumer were to specify the exact attributes of the duck it's > interested in, which I fear would become a tedious type writing style). ' > > > This approach (a Protocol that declares exactly what you need the > file-like object to do) is in fact what we've been doing in typeshed, the > repository that provides type hints for the standard library. For those > unfamiliar, it would look something like this: > > from typing import Protocol > > class SupportsRead(Protocol): > def read(self) -> bytes: ... > > def uses_a_file(f: SupportsRead) -> None: > f.read() > > That's somewhat tedious, to be sure, but it is working duck typing. > > > > I too have sensed static typing driving the typing development agenda in > Python recently, causing other typing methods to take a back seat, so to > speak. I add my voice to those requesting Python handle other typing > methods. > > Barring an innovation to allow a "subset" of a type to be declared in a > type hint, I would conclude that static typing and duck typing are > diametrically opposed. If we agree that both are valuable, developers could > build consensus on that point, and work to ensure that one does not move > forward at the expense of the other. > > What would you suggest? Should the syntax for declaring Protocols be more > concise? > > > > Paul > > On Wed, 2021-04-21 at 12:36 -0700, Christopher Barker wrote: > > Thanks Mark for posting this. I know some of us are uneasy about the pace > of the typing train > > On Tue, Apr 20, 2021 at 11:20 AM Nathaniel Smith wrote: > > > If you guarded your code with `isinstance(foo, Sequence)` then I could > > not use it with my `Foo` even if my `Foo` quacked like a sequence. I was > > forced to use nominal typing; inheriting from Sequence, or explicitly > > registering as a Sequence. > > You say this like it's a bad thing, but how is this avoidable, even in > principle? Structural typing lets you check whether Foo is duck-shaped > -- has appropriate attribute names, etc. But quacking like a duck is > harder: you also have to implement the Sequence behavioral contract, > and realistically the only way to know that is if the author of Foo > tells you. > > > But that's not what duck typing is (at least to me :-) ) For a given > function, I need the passed in object to quack (and yes, I need that quack > to sound like a duck) -- but I usually don't care whether that object > waddles like a duck. > > So yes, isinstance(obj, Sequence) is really the only way to know that obj > is a Sequence in every important way -- but if you only need it to do one > or two things like a Sequence, then you don't care. > > And this is not uncommon -- I suspect it's very rare for a single function > to use most of the methods of a given ABC (or protocol, or whatever). > > And a lot of the standard library works exactly this way. Two examples > (chosen arbitrarily, I just happen to have thought about how they work): > > json.load() simply calls ``fp.read()``, and passes the result on down to > json.loads(). That's it -- no checking of anything. > > If fp does not have a read() method, you get an AttributeError. If fp has > a read() method, but it returns something other than a string, then you get > some other Exception. And if it returns a string, but that string isn't > valid JSON, you get yet another kind of error. > > In short, json.load(fp, ...) requires fp to have a read() method that > returns a valid JSON string. But it doesn't check, nor does it need to, if > it's getting an actual io.TextIOBase object. Is that the right one? I'm not > totally sure, which I kind of think makes my point -- I've been using > "file-like" objects for years (decades) without worrying about it. > > Example 2: > > The str.translate method takes: > > "a mapping of Unicode ordinals to Unicode ordinals, strings, or None" > > Ok, then you need to pass in a Mapping, yes? Well, no you
[Python-Dev] Re: Keeping Python a Duck Typed Language.
Luciano, Thank you for such a thoughtful and eloquent response. Your wisdom is definitely appreciated, and I agree this is an opportunity to go forward with more clarity. I'm so proud of the Python dev community for their thoughtful and respectful conversation. All, Without the efforts of Larry, Guido, Mark, Lukasz, Luciano, the pydantic/FastAPI folks, the Steering Council and their willingness listen and problem solve, the outcome would have been far less appealing and useful. Thank you! Carol On Wed, Apr 21, 2021 at 4:26 PM Paul Bryan wrote: > I agree, that's duck typing with a protocol, and precisely the tedious > type style I would want to avoid. > > I don't know what would be a good suggestion. Something where you > reference a "fully equipped" type and cherry pick the attributes you want? > Something like `Protocol[Duck, ("quack", "waddle")]`? > > Paul > > > On Wed, 2021-04-21 at 16:13 -0700, Jelle Zijlstra wrote: > > > > El mié, 21 abr 2021 a las 15:30, Paul Bryan () escribió: > > As demonstrated, protocols don't get us there because duck typing isn't a > matter of having an object exhibit all of the attributes of a duck, but > rather some subset of attributes to be used by the consumer. I want this > duck to quack; someone else will want it to waddle. I don't see how type > hints could reasonably support "file like object" in the duck type sense > (unless the consumer were to specify the exact attributes of the duck it's > interested in, which I fear would become a tedious type writing style). ' > > > This approach (a Protocol that declares exactly what you need the > file-like object to do) is in fact what we've been doing in typeshed, the > repository that provides type hints for the standard library. For those > unfamiliar, it would look something like this: > > from typing import Protocol > > class SupportsRead(Protocol): > def read(self) -> bytes: ... > > def uses_a_file(f: SupportsRead) -> None: > f.read() > > That's somewhat tedious, to be sure, but it is working duck typing. > > > > I too have sensed static typing driving the typing development agenda in > Python recently, causing other typing methods to take a back seat, so to > speak. I add my voice to those requesting Python handle other typing > methods. > > Barring an innovation to allow a "subset" of a type to be declared in a > type hint, I would conclude that static typing and duck typing are > diametrically opposed. If we agree that both are valuable, developers could > build consensus on that point, and work to ensure that one does not move > forward at the expense of the other. > > What would you suggest? Should the syntax for declaring Protocols be more > concise? > > > > Paul > > On Wed, 2021-04-21 at 12:36 -0700, Christopher Barker wrote: > > Thanks Mark for posting this. I know some of us are uneasy about the pace > of the typing train > > On Tue, Apr 20, 2021 at 11:20 AM Nathaniel Smith wrote: > > > If you guarded your code with `isinstance(foo, Sequence)` then I could > > not use it with my `Foo` even if my `Foo` quacked like a sequence. I was > > forced to use nominal typing; inheriting from Sequence, or explicitly > > registering as a Sequence. > > You say this like it's a bad thing, but how is this avoidable, even in > principle? Structural typing lets you check whether Foo is duck-shaped > -- has appropriate attribute names, etc. But quacking like a duck is > harder: you also have to implement the Sequence behavioral contract, > and realistically the only way to know that is if the author of Foo > tells you. > > > But that's not what duck typing is (at least to me :-) ) For a given > function, I need the passed in object to quack (and yes, I need that quack > to sound like a duck) -- but I usually don't care whether that object > waddles like a duck. > > So yes, isinstance(obj, Sequence) is really the only way to know that obj > is a Sequence in every important way -- but if you only need it to do one > or two things like a Sequence, then you don't care. > > And this is not uncommon -- I suspect it's very rare for a single function > to use most of the methods of a given ABC (or protocol, or whatever). > > And a lot of the standard library works exactly this way. Two examples > (chosen arbitrarily, I just happen to have thought about how they work): > > json.load() simply calls ``fp.read()``, and passes the result on down to > json.loads(). That's it -- no checking of anything. > > If fp does not have a read() method, you get an AttributeError. If fp has > a read() method, but it returns something other than a string, then you get > some other Exception. And if it returns a string, but that string isn't > valid JSON, you get yet another kind of error. > > In short, json.load(fp, ...) requires fp to have a read() method that > returns a valid JSON string. But it doesn't check, nor does it need to, if > it's getting an actual io.TextIOBase object. Is that the right one? I'm not > totally sure, which