Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum. We need new type IntFlags. It is like IntEnum, but has differences: 1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer. 2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass. 3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
2015-03-03 16:52 GMT+01:00 Serhiy Storchaka <storchaka@gmail.com>:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
By the way, since the PEP 446, sock.type may contain the flag SOCK_CLOEXEC ;-) Extract of test_socket.py: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.assertEqual(sock.family, socket.AF_INET) if hasattr(socket, 'SOCK_CLOEXEC'): self.assertIn(sock.type, (socket.SOCK_STREAM | socket.SOCK_CLOEXEC, socket.SOCK_STREAM)) Victor
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
This was in my original Enum code, but stripped out as not being needed at the time. If there is sufficient interest (and use-cases) I can add it back in. -- ~Ethan~
On 3 March 2015 at 13:40, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
This was in my original Enum code, but stripped out as not being needed at the time.
If there is sufficient interest (and use-cases) I can add it back in.
I think that would be nice. The problem is that it would be too late now for changing the behavior of "IntEnum" itself, right?
-- ~Ethan~
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
* Ethan Furman <ethan@stoneleaf.us> [2015-03-03 08:40:11 -0800]:
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
This was in my original Enum code, but stripped out as not being needed at the time.
If there is sufficient interest (and use-cases) I can add it back in.
I'd gladly use it - for example when communicating with embedded electronic devices. Florian -- http://www.the-compiler.org | me@the-compiler.org (Mail/XMPP) GPG: 916E B0C8 FD55 A072 | http://the-compiler.org/pubkey.asc I love long mails! | http://email.is-not-s.ms/
On 03.03.15 18:40, Ethan Furman wrote:
This was in my original Enum code, but stripped out as not being needed at the time.
If there is sufficient interest (and use-cases) I can add it back in.
This would be good. I need IntFlags even more than IntEnum. I made an implementation based on early implementations of IntEnum, but then IntEnum was changed too much and became incompatible with my extension. IntFlags should be a separate class, IntEnum is good with its restrictions.
On Wed, Mar 4, 2015 at 4:27 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
IntFlags should be a separate class, IntEnum is good with its restrictions.
Also, some operations that make sense for an IntEnum don't make sense for IntFlags (or FlagEnum or whatever it gets called), such as greater-than/less-than comparisons. Definitely a separate class IMO. ChrisA
On 03Mar2015 19:27, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 03.03.15 18:40, Ethan Furman wrote:
This was in my original Enum code, but stripped out as not being needed at the time.
If there is sufficient interest (and use-cases) I can add it back in.
This would be good. I need IntFlags even more than IntEnum.
I am also +1. I also probably use IntFlags (euqivalents) more often than IntEnums. It would have been nice for my PEP-418 demo implementation: https://bitbucket.org/cameron_simpson/css/src/tip/lib/python/cs/clockutils.p... I rolled my own _Clock_Flags there for this very reason.
I made an implementation based on early implementations of IntEnum, but then IntEnum was changed too much and became incompatible with my extension.
IntFlags should be a separate class, IntEnum is good with its restrictions.
I agree. I presume IntEnum|IntEnum might return an IntFlags? Or would that make for a TypeError or ValueError and one be expected to start with IntFlags? Cheers, Cameron Simpson <cs@zip.com.au> Trust the computer... the computer is your friend. - Richard Dominelli <dominel@panix.com>
On Wed, Mar 4, 2015 at 9:04 AM, Cameron Simpson <cs@zip.com.au> wrote:
I agree. I presume IntEnum|IntEnum might return an IntFlags? Or would that make for a TypeError or ValueError and one be expected to start with IntFlags?
That should be a sanity error, but since an IntEnum devolves to an int, it just produces a nonsensical integer. It definitely shouldn't become an IntFlags. There are two distinctly different use-cases here: class Color(IntEnum): black = 0 red = 1 green = 2 orange = 3 blue = 4 magenta = 5 cyan = 6 white = 7 class FileMode(IntFlags): owner_read = 0o400 owner_write= 0o200 owner_exec = 0o100 group_read = 0o040 group_write= 0o020 group_exec = 0o010 other_read = 0o004 other_write= 0o002 other_exec = 0o001 With colors, it makes no sense to combine them in any way (addition, bitwise or, etc). You can't put blue and cyan together and expect to get something usable. File modes are meant to be combined, and their values are deliberately chosen to make this possible. So IntEnum|IntEnum is most likely going to be combining values that were assigned sequentially or arbitrarily, which isn't likely to be very useful. ChrisA
On 03/03/2015 02:16 PM, Chris Angelico wrote:
On Wed, Mar 4, 2015 at 9:04 AM, Cameron Simpson <cs@zip.com.au> wrote:
I agree. I presume IntEnum|IntEnum might return an IntFlags? Or would that make for a TypeError or ValueError and one be expected to start with IntFlags?
That should be a sanity error, but since an IntEnum devolves to an int, it just produces a nonsensical integer. It definitely shouldn't become an IntFlags.
Agreed. IntEnums are just fancy ints, and will continue to behave as ints with the exception of how they are displayed. IntFlags, should such a thing come into being, can be more proprietary with its behavior. -- ~Ethan~
On 04Mar2015 09:16, Chris Angelico <rosuav@gmail.com> wrote:
On Wed, Mar 4, 2015 at 9:04 AM, Cameron Simpson <cs@zip.com.au> wrote:
I agree. I presume IntEnum|IntEnum might return an IntFlags? Or would that make for a TypeError or ValueError and one be expected to start with IntFlags?
That should be a sanity error, but since an IntEnum devolves to an int, it just produces a nonsensical integer. It definitely shouldn't become an IntFlags.
Yes, agreed. Brain fade. Sorry, Cameron Simpson <cs@zip.com.au>
That should be a sanity error, but since an IntEnum devolves to an int, it just produces a nonsensical integer. It definitely shouldn't become an IntFlags. There are two distinctly different use-cases here:
class Color(IntEnum): black = 0 red = 1 green = 2 orange = 3 blue = 4 magenta = 5 cyan = 6 white = 7
class FileMode(IntFlags): owner_read = 0o400 owner_write= 0o200 owner_exec = 0o100 group_read = 0o040 group_write= 0o020 group_exec = 0o010 other_read = 0o004 other_write= 0o002 other_exec = 0o001
With colors, it makes no sense to combine them in any way (addition, bitwise or, etc). You can't put blue and cyan together and expect to get something usable.
I don't see your point. With colours it would be exactly the same: red | blue == magenta and for your example: cyan | blue = cyan As with the later discussion on file flags, cyan is just a combination flag for green and blue. -Alexander
On Wed, Mar 4, 2015, at 13:37, Alexander Heger wrote:
I don't see your point. With colours it would be exactly the same:
red | blue == magenta
and for your example:
cyan | blue = cyan
As with the later discussion on file flags, cyan is just a combination flag for green and blue.
I think it was a badly chosen example - someone easily _could_ have a color enum that doesn't have these properties. Definitely there are enums that it doesn't make sense to combine together with bitwise ops.
On Tue, Mar 3, 2015, at 17:16, Chris Angelico wrote:
class Color(IntEnum): black = 0 red = 1 green = 2 orange = 3 blue = 4 magenta = 5 cyan = 6 white = 7
With colors, it makes no sense to combine them in any way (addition, bitwise or, etc). You can't put blue and cyan together and expect to get something usable.
Except for the part where green|blue==cyan... and if only you'd said yellow instead of orange it'd hold across the board. I assume you took these values from ANSI colors.
On Thu, Mar 5, 2015 at 6:48 AM, <random832@fastmail.us> wrote:
On Tue, Mar 3, 2015, at 17:16, Chris Angelico wrote:
class Color(IntEnum): black = 0 red = 1 green = 2 orange = 3 blue = 4 magenta = 5 cyan = 6 white = 7
With colors, it makes no sense to combine them in any way (addition, bitwise or, etc). You can't put blue and cyan together and expect to get something usable.
Except for the part where green|blue==cyan... and if only you'd said yellow instead of orange it'd hold across the board. I assume you took these values from ANSI colors.
Those are indeed the ANSI colors, but even though you might think that you can combine them, they don't really combine usefully in all cases. But sure. If you're bothered by the fact that blue+green == cyan, use a different example. Turn 'em into animals, cars, CPU models, whatever you like; the point of an IntEnum is usually just that the values are unique, _not_ that they can be combined in any meaningful way. ChrisA
Also +1. I think IntFlags should also have a named constructor that constructs an IntFlags instance from a set: reduce(lambda x, y: x | y, (c[key] for key in some_set)) Best, Neil On Tuesday, March 3, 2015 at 5:04:50 PM UTC-5, Cameron Simpson wrote:
On 03Mar2015 19:27, Serhiy Storchaka <stor...@gmail.com <javascript:>> wrote:
On 03.03.15 18:40, Ethan Furman wrote:
This was in my original Enum code, but stripped out as not being needed at the time.
If there is sufficient interest (and use-cases) I can add it back in.
This would be good. I need IntFlags even more than IntEnum.
I am also +1.
I also probably use IntFlags (euqivalents) more often than IntEnums. It would have been nice for my PEP-418 demo implementation:
https://bitbucket.org/cameron_simpson/css/src/tip/lib/python/cs/clockutils.p...
I rolled my own _Clock_Flags there for this very reason.
I made an implementation based on early implementations of IntEnum, but then IntEnum was changed too much and became incompatible with my extension.
IntFlags should be a separate class, IntEnum is good with its restrictions.
I agree. I presume IntEnum|IntEnum might return an IntFlags? Or would that make for a TypeError or ValueError and one be expected to start with IntFlags?
Cheers, Cameron Simpson <c...@zip.com.au <javascript:>>
Trust the computer... the computer is your friend. - Richard Dominelli <dom...@panix.com <javascript:>> _______________________________________________ Python-ideas mailing list Python...@python.org <javascript:> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
One of the big questions that (IIRC) derailed this last time and got it dropped from the enum stdlib design was: what does ~ do? Does it give you the 2's complement negative integer? What does that display as in the str and repr? And, if you add in conversion from an IntFlags to/from a set of separate values, as has been suggested again in this thread, how does that work? All of this is trivial when you're dealing with C fixed-size unsigned ints: ~READ means 15 of the 16 bits (all except the READ bit) are set. Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.) Sent from a random iPhone On Mar 3, 2015, at 7:52, Serhiy Storchaka <storchaka@gmail.com> wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.)
That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like this: from enum import IntEnum class Flags(IntEnum): RDWR = 3 RDONLY = 1 WRONLY = 2 CLOEXEC = 4 def flag_str(flg): names = [] for flag in Flags: if (flg&flag) == flag: flg -= flag names.append(str(flag)) return "|".join(names) print(flag_str(Flags.RDWR|Flags.CLOEXEC)) print(flag_str(Flags.RDONLY|Flags.CLOEXEC)) As long as the combined versions come up ahead of the others, they'll be used. Alternatively, if you prefer them _not_ to be used, just put them after the individual forms, and then the str() will expand them out. ChrisA
Why do you need these composite flags? On Wed, Mar 4, 2015 at 10:31 AM, Chris Angelico <rosuav@gmail.com> wrote:
Another issue that came up was that C flags often have "combined" names
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote: that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.)
That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like this:
from enum import IntEnum
class Flags(IntEnum): RDWR = 3 RDONLY = 1 WRONLY = 2 CLOEXEC = 4
def flag_str(flg): names = [] for flag in Flags: if (flg&flag) == flag: flg -= flag names.append(str(flag)) return "|".join(names)
print(flag_str(Flags.RDWR|Flags.CLOEXEC)) print(flag_str(Flags.RDONLY|Flags.CLOEXEC))
As long as the combined versions come up ahead of the others, they'll be used. Alternatively, if you prefer them _not_ to be used, just put them after the individual forms, and then the str() will expand them out.
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On Mar 4, 2015, at 7:44, Neil Girdhar <mistersheik@gmail.com> wrote:
Why do you need these composite flags?
Because the whole point of this proposal is to deal with C types (otherwise, who cares about the int value?). And most such C types define combined values--the motivating example, stat, has S_IRWXU, etc. If the Python code is less readable than the equivalent C code...
On Wed, Mar 4, 2015 at 10:31 AM, Chris Angelico <rosuav@gmail.com> wrote:
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.)
That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like this:
from enum import IntEnum
class Flags(IntEnum): RDWR = 3 RDONLY = 1 WRONLY = 2 CLOEXEC = 4
def flag_str(flg): names = [] for flag in Flags: if (flg&flag) == flag: flg -= flag names.append(str(flag)) return "|".join(names)
print(flag_str(Flags.RDWR|Flags.CLOEXEC)) print(flag_str(Flags.RDONLY|Flags.CLOEXEC))
As long as the combined versions come up ahead of the others, they'll be used. Alternatively, if you prefer them _not_ to be used, just put them after the individual forms, and then the str() will expand them out.
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 04.03.15 17:31, Chris Angelico wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.) That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote: this:
Yes, something like this, but with iterating flags in descended sorted order, and with special case for negative value.
On Thu, Mar 5, 2015 at 2:58 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 04.03.15 17:31, Chris Angelico wrote:
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.)
That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like this:
Yes, something like this, but with iterating flags in descended sorted order, and with special case for negative value.
Sure. Going automatically like that is a way of guaranteeing that the combined flags will be used, which is probably what you want most of the time anyway. And yes, I hadn't coped with negatives in that. ChrisA
On Mar 4, 2015, at 7:58, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 04.03.15 17:31, Chris Angelico wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.) That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote: this:
Yes, something like this, but with iterating flags in descended sorted order, and with special case for negative value.
I think the very fact that the two of you immediately knew which order was obvious, but you chose the opposite one as the obvious one, proves that it's not obvious. For the record, I think your version is better, because usually the C definitions define the combined values after the individual ones, and it would be nice to be able to mirror the C definitions (or, even better, auto generate the Python from the C header*) and get the desired results. I think you're also right that using signed 1's complement is the best way to handle negated flags, despite the tradeoffs (not being able to compare to negated C values, having a confusing numerical value in the repr, having silly str for silly cases), especially since that's what ~ already does with IntEnum (except that the result is just plain int, of course). But regardless, the point is that these questions don't have a single obvious answer; you have to think about them, decide what makes sense, and explain the tradeoffs and why one should win (and implement it). That's why this "simple" proposal didn't make it into 3.4's enum: because it's not actually simple, and everyone who insisted that it was disagreed, and someone (Guido or Eli?) finally told everyone to shut up, flags weren't going into 3.4, and put your competing implementations on PyPI and see which one people use. Do we really want to rehash all those arguments from scratch? --- * The parenthetical brings up another issue: if you look in your platform's sys/stat.h (or whichever header actually defines these things), S_IRWXG is probably not defined as 0o700, but as S_IRGRP | S_IWGRP | S_IXGRP. Can we do that in the enum definition? If not, it may be less readable than the C. (In fact, on many platforms, S_IRGRP is itself defined as something like _S_IREAD << _S_GRP, and S_IRWXG may be defined as (_S_IREAD | _S_IWRITE | _S_IEXEC) << _S_GRP. I think Linux took this even further and defined it as S_IRWXU >> (_S_USR - _S_GRP) or something silly. But at that point, not being able to clone the C no longer looks like a loss of readability...)
On Thu, Mar 5, 2015 at 10:42 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Mar 4, 2015, at 7:58, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 04.03.15 17:31, Chris Angelico wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.) That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote: this:
Yes, something like this, but with iterating flags in descended sorted order, and with special case for negative value.
I think the very fact that the two of you immediately knew which order was obvious, but you chose the opposite one as the obvious one, proves that it's not obvious.
Actually, we chose the same thing, only in slightly different ways. Serhiy suggested (in effect) sorting the flags by value and stepping through from highest to lowest, which enforces that the combined flags will be the ones picked. I suggested putting the responsibility onto the class author - if you want the combined ones to be used, place them first - which is like how aliasing works (the first one with a given value is used in str/repr, any others are aliases). That's a relatively minor point, and it depends on whether there'd ever be a time when you want to provide a combined flag that _isn't_ used in str/repr; if there is, you need my plan, but if not, go with the simpler route. ChrisA
On Mar 4, 2015, at 15:56, Chris Angelico <rosuav@gmail.com> wrote:
On Thu, Mar 5, 2015 at 10:42 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Mar 4, 2015, at 7:58, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 04.03.15 17:31, Chris Angelico wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.) That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote: this:
Yes, something like this, but with iterating flags in descended sorted order, and with special case for negative value.
I think the very fact that the two of you immediately knew which order was obvious, but you chose the opposite one as the obvious one, proves that it's not obvious.
Actually, we chose the same thing, only in slightly different ways. Serhiy suggested (in effect) sorting the flags by value and stepping through from highest to lowest, which enforces that the combined flags will be the ones picked. I suggested putting the responsibility onto the class author - if you want the combined ones to be used, place them first - which is like how aliasing works (the first one with a given value is used in str/repr, any others are aliases). That's a relatively minor point, and it depends on whether there'd ever be a time when you want to provide a combined flag that _isn't_ used in str/repr; if there is, you need my plan, but if not, go with the simpler route.
Except with yours, any time you copy the definition (whether by hand, or with an automated tool) from C, where the combined ones almost always come last, they won't be used; with his, they will. So, in the most common use case, you'll get the opposite result. (Of course if you always pick the _last_ instead of the first or the highest, which gives you the choice for the rare case, but the nice answer for the ubiquitous case. That breaks your analogy with aliases, but it does work the same way as normal class attributes, where the last value wins...)
Can I propose that instead of IntFlags, we have IntFields? class Permissions(IntFields): # Some Boolean flags (the bit index) owner_read = 0 owner_write = 1 owner_exec = 2 group_read = 3 group_write = 4 group_exec = 5 user_read = 6 user_write = 7 user_exec = 8 # Some named fields (can overlap) owner_flags = range(3) group_flags = range(3, 6) user_flags = range(6, 9) # Some field values (can overlap with flags and each other) regular_file = range(9), 0o755 character_file = range(9), 0o664 directory = range(9), 0o600 And then you can do the following operations: p = Permissions(Permissions.character_file) p.set(Permissions.regular_file) p.owner_write = True print(p.user_read) p.owner_flags = 6 Another example: class IEEE754(IntFields): fraction = range(23) exponent = range(23, 23+8) sign = 31 denormalized = 'exponent', 0 infinity = 'exponent', 255 In case it's not clear, there are two kinds of members: named fields and field values. Named fields are specified with a range instance or integer represent the bit range or index. Field values are specified with a pair, the first member of which is either a range, an integer, or a string (which would refer to a declared range); and a second member, which is the value. IntFields.set accepts a field value name, and sets the bits according to the value. IntFields.__setattr__ accepts a named field name; it sets the corresponding bits according to the value. IntFields.__getattr__ accepts a named field name or field value name There is no ~ or & operator. Something should probably be done for __or__. Best, Neil On Thu, Mar 5, 2015 at 4:25 AM, 'Andrew Barnert' via python-ideas < python-ideas@googlegroups.com> wrote:
On Mar 4, 2015, at 15:56, Chris Angelico <rosuav@gmail.com> wrote:
On Thu, Mar 5, 2015 at 10:42 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Mar 4, 2015, at 7:58, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 04.03.15 17:31, Chris Angelico wrote:
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.) That could probably be handled by going through the flags in iteration order. If the flag is present, emit it and move on. Something like
On Thu, Mar 5, 2015 at 2:17 AM, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote: this:
Yes, something like this, but with iterating flags in descended sorted order, and with special case for negative value.
I think the very fact that the two of you immediately knew which order was obvious, but you chose the opposite one as the obvious one, proves that it's not obvious.
Actually, we chose the same thing, only in slightly different ways. Serhiy suggested (in effect) sorting the flags by value and stepping through from highest to lowest, which enforces that the combined flags will be the ones picked. I suggested putting the responsibility onto the class author - if you want the combined ones to be used, place them first - which is like how aliasing works (the first one with a given value is used in str/repr, any others are aliases). That's a relatively minor point, and it depends on whether there'd ever be a time when you want to provide a combined flag that _isn't_ used in str/repr; if there is, you need my plan, but if not, go with the simpler route.
Except with yours, any time you copy the definition (whether by hand, or with an automated tool) from C, where the combined ones almost always come last, they won't be used; with his, they will. So, in the most common use case, you'll get the opposite result.
(Of course if you always pick the _last_ instead of the first or the highest, which gives you the choice for the rare case, but the nice answer for the ubiquitous case. That breaks your analogy with aliases, but it does work the same way as normal class attributes, where the last value wins...) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Actually, it would be cool to support nesting too, e.g., class SubPermissions(IntFields): read = 0 write = 1 exec_ = 2 class Permissions(IntFields): owner_flags = range(3), SubPermissions group_flags = range(3, 6), SubPermissions user_flags = range(6, 9), SubPermissions regular_file = range(9), 0o755 character_file = range(9), 0o664 directory = range(9), 0o600
On 05.03.15 01:42, Andrew Barnert wrote:
I think you're also right that using signed 1's complement is the best way to handle negated flags, despite the tradeoffs (not being able to compare to negated C values, having a confusing numerical value in the repr, having silly str for silly cases), especially since that's what ~ already does with IntEnum (except that the result is just plain int, of course).
Sorry, I don't understand your argument. Why you can't compare complemented IntFlags with complemented int? ~(os.OpenMode.O_CLOEXEC) == int(~os.OpenMode.O_CLOEXEC) == ~int(os.OpenMode.O_CLOEXEC) == posix.O_CLOEXEC IntFlags is an int subclass and behaves as plain int, except that it has special repr and results of bitwise operations preserve a type.
* The parenthetical brings up another issue: if you look in your platform's sys/stat.h (or whichever header actually defines these things), S_IRWXG is probably not defined as 0o700, but as S_IRGRP | S_IWGRP | S_IXGRP. Can we do that in the enum definition? If not, it may be less readable than the C. (In fact, on many platforms, S_IRGRP is itself defined as something like _S_IREAD << _S_GRP, and S_IRWXG may be defined as (_S_IREAD | _S_IWRITE | _S_IEXEC) << _S_GRP. I think Linux took this even further and defined it as S_IRWXU >> (_S_USR - _S_GRP) or something silly. But at that point, not being able to clone the C no longer looks like a loss of readability...)
Yes, of course you can define: class Permissions(enum.IntFlags): S_IRGRP = 0o0040 # read by group S_IWGRP = 0o0020 # write by group S_IXGRP = 0o0010 # execute by group S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP ... or what your like. I tried and this works.
On Mar 5, 2015, at 9:11, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 01:42, Andrew Barnert wrote:
I think you're also right that using signed 1's complement is the best way to handle negated flags, despite the tradeoffs (not being able to compare to negated C values, having a confusing numerical value in the repr, having silly str for silly cases), especially since that's what ~ already does with IntEnum (except that the result is just plain int, of course).
Sorry, I don't understand your argument. Why you can't compare complemented IntFlags with complemented int?
You can, but what you can't do is compare it with the values that you'll likely get from C, because -0o200001 != 0o37775777777 in Python, whereas they are equal in C.
~(os.OpenMode.O_CLOEXEC) == int(~os.OpenMode.O_CLOEXEC) == ~int(os.OpenMode.O_CLOEXEC) == posix.O_CLOEXEC
I certainly hope that's not true, or ~ is a no-op... But even if you add the missing ~, none of these is going to be == the value of an unsigned int holding ~O_CLOEXEC in C. Again, I already said I think you chose the best tradeoff, but there is a downside compared to treating the values as unsigned or to the other options people have proposed here; that's why it's a tradeoff rather than a one obvious choice.
On 04.03.15 17:17, Andrew Barnert wrote:
One of the big questions that (IIRC) derailed this last time and got it dropped from the enum stdlib design was: what does ~ do? Does it give you the 2's complement negative integer? What does that display as in the str and repr? And, if you add in conversion from an IntFlags to/from a set of separate values, as has been suggested again in this thread, how does that work? All of this is trivial when you're dealing with C fixed-size unsigned ints: ~READ means 15 of the 16 bits (all except the READ bit) are set.
IntFlags is just fancy int. int(~flags) == int(~int(flags)). Python supports ~ for arbitrary integers. ~x == -x-1 There are no problems with conversions from a set to IntFlags, but the conversion from IntFlags to set is not always possible.
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.)
This problem is nor so hard. My implementation was smart enough.
print(OpenMode.RDONLY | OpenMode.WRONLY | OpenMode.CLOEXEC) OpenMode.RDWR|OpenMode.CLOEXEC print(~(OpenMode.RDONLY | OpenMode.WRONLY | OpenMode.CLOEXEC)) ~(OpenMode.RDWR|OpenMode.CLOEXEC)
Of course the repr can be senseless if the value is senseless (such as RDONLY | ~WRONLY).
One of the big questions that (IIRC) derailed this last time and got it dropped from the enum stdlib design was: what does ~ do? Does it give you the 2's complement negative integer? What does that display as in the str and repr? And, if you add in conversion from an IntFlags to/from a set of separate values, as has been suggested again in this thread, how does that work? All of this is trivial when you're dealing with C fixed-size unsigned ints: ~READ means 15 of the 16 bits (all except the READ bit) are set.
IntFlags is just fancy int. int(~flags) == int(~int(flags)). Python supports ~ for arbitrary integers. ~x == -x-1
There are no problems with conversions from a set to IntFlags, but the conversion from IntFlags to set is not always possible.
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY), which is fine until you want a repr (in C, it's just going to print 3); does it have to be smart enough to show RDWR? (Or, worse, RDWR | CLOEXEC.)
This problem is nor so hard. My implementation was smart enough.
print(OpenMode.RDONLY | OpenMode.WRONLY | OpenMode.CLOEXEC) OpenMode.RDWR|OpenMode.CLOEXEC print(~(OpenMode.RDONLY | OpenMode.WRONLY | OpenMode.CLOEXEC)) ~(OpenMode.RDWR|OpenMode.CLOEXEC)
Of course the repr can be senseless if the value is senseless (such as RDONLY | ~WRONLY).
Wouldn't it be possible to for the class to determine what bits are all used from the constants defined, and then for the ~ operator to just invert those? It may require some sanity check for users only defining "combination constants" in the class such that the result could not be represented. class Stupid(IntFlags): CAT = 3 DOG = 6 in which case ~CAT would not make sense, but neither could CAT | DOG be represented. I suppose in such cases an error should be raised by the metaclass on class definition. -Alexander
It may require some sanity check for users only defining "combination constants" in the class such that the result could not be represented.
class Stupid(IntFlags): CAT = 3 DOG = 6
in which case ~CAT would not make sense, but neither could CAT | DOG be represented. I suppose in such cases an error should be raised by the metaclass on class definition.
-Alexander Personally, I find that CAT & DOG is almost invariably an error. :-)
On Wed, Mar 4, 2015, at 10:17, Andrew Barnert wrote:
One of the big questions that (IIRC) derailed this last time and got it dropped from the enum stdlib design was: what does ~ do? Does it give you the 2's complement negative integer? What does that display as in the str and repr? And, if you add in conversion from an IntFlags to/from a set of separate values, as has been suggested again in this thread, how does that work? All of this is trivial when you're dealing with C fixed-size unsigned ints: ~READ means 15 of the 16 bits (all except the READ bit) are set.
str should be ~(READ) obviously. And more generally ~(all|bits|that|are|not|set).
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY),
Nope. You've actually got a different, much worse, issue: traditionally, RDONLY is 0 (and should be printed if and only if 1 and 2 are not set), WRONLY is 1, RDWR is 2, and 3 is traditionally invalid and may have a platform-dependent meaning.
On Mar 4, 2015, at 11:52, random832@fastmail.us wrote:
On Wed, Mar 4, 2015, at 10:17, Andrew Barnert wrote:
One of the big questions that (IIRC) derailed this last time and got it dropped from the enum stdlib design was: what does ~ do? Does it give you the 2's complement negative integer? What does that display as in the str and repr? And, if you add in conversion from an IntFlags to/from a set of separate values, as has been suggested again in this thread, how does that work? All of this is trivial when you're dealing with C fixed-size unsigned ints: ~READ means 15 of the 16 bits (all except the READ bit) are set.
str should be ~(READ) obviously. And more generally ~(all|bits|that|are|not|set).
Think about how that extends to the result of |. Of course the answer depends on how you store ~ in the first place, but for most choices, str is not obvious. For example, using fixed-size unsigned with automatic highest-bit detection, for an enum with READ, WRITE, EXEC, STICKY, ~(READ) is the same value as (WRITE|EXEC|STICKY), so how does str know which to print? The one with the fewest flags? Some other rule? (The signed 1's comp choice actually has a reasonable answer here, it just means that you get silly results for silly values, which is fine...)
Another issue that came up was that C flags often have "combined" names that are ambiguous: RDWR = RDONLY | WRONLY),
Nope. You've actually got a different, much worse, issue: traditionally, RDONLY is 0 (and should be printed if and only if 1 and 2 are not set), WRONLY is 1, RDWR is 2, and 3 is traditionally invalid and may have a platform-dependent meaning.
Yeah, open flags are especially screwy, where flags & 3 has a special non-bitmapped meaning but the rest of the bits are flags. There are other cases where multiple ints, only some of which are bitmaps, are packed as separate bitfields into the same int (e.g., TCP/IP headers), but I don't know of any others that pretend to be a single bitmap even though they aren't, so that's really a unique problem, which can be ignored. Just think about stat results or mmap prot flags or anything else where read and write are separate bits. (PS, IIRC, Linux treats 3 as "open for fstat only", and you have that access on most files even if you can't do anything else to them, which was an accident left in place because lilo used it, which made lilo a pain to port to FreeBSD as part of a Linux repair kit...)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, Mar 4, 2015, at 18:19, Andrew Barnert wrote:
Think about how that extends to the result of |.
The value is either negative (is the complement of a finite set of bits) or it isn't. Remember, the underlying representation is an int. Of course the answer
depends on how you store ~ in the first place, but for most choices, str is not obvious. For example, using fixed-size unsigned with automatic highest-bit detection, for an enum with READ, WRITE, EXEC, STICKY, ~(READ) is the same value as (WRITE|EXEC|STICKY),
Why would that be the case?
Yeah, open flags are especially screwy, where flags & 3 has a special non-bitmapped meaning but the rest of the bits are flags. There are other cases where multiple ints, only some of which are bitmaps, are packed as separate bitfields into the same int (e.g., TCP/IP headers), but I don't know of any others that pretend to be a single bitmap even though they aren't, so that's really a unique problem, which can be ignored.
Technically file modes have the file type field (flags & 0xF000) which has some states that don't mean anything on common platforms.
Concerning ~, if you think of it as a set, then ~x should be the result of exlusive-oring x with an int containing all the valid bits, i.e. the value obtained by oring all the defined values together. This is different from what you would get in C, but it's self-consistent and the repr() would make sense. -- Greg
On Thu, Mar 5, 2015 at 3:30 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Concerning ~, if you think of it as a set, then ~x should be the result of exlusive-oring x with an int containing all the valid bits, i.e. the value obtained by oring all the defined values together.
Yes, that will work. Then we might want predefined constants for all set bits and zero. (Zero can be just calling MyBitSet() with no arguments). Then ~X is type(X).ALL_ONES - X. The main question is whether this operation makes much sense. I think in practice ~ is only used with flags to clear bits (X & ~Y), which can be done in one explicit operation. It may still be worth supporting ~ to allow painless transition from plain ints. Eugene
On Thu, Mar 5, 2015 at 2:07 PM, Eugene Toder <eltoder@gmail.com> wrote:
On Thu, Mar 5, 2015 at 3:30 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Concerning ~, if you think of it as a set, then ~x should be the result of exlusive-oring x with an int containing all the valid bits, i.e. the value obtained by oring all the defined values together.
Yes, that will work. Then we might want predefined constants for all set bits and zero. (Zero can be just calling MyBitSet() with no arguments). Then ~X is type(X).ALL_ONES - X. The main question is whether this operation makes much sense. I think in practice ~ is only used with flags to clear bits (X & ~Y), which can be done in one explicit operation. It may still be worth supporting ~ to allow painless transition from plain ints.
I wonder if it would be worth it to have an ALL value supported, which defaults to the other values ored together, but could be overridden for specific cases. This could be useful for compatibility with C code where there are not publicly defined values for some bits, but they have some meaning (possibly only for compatibility). If it were purely computed automatically, handling of cases where there are unused bits could get messy in some cases. Even if methods are implemented for clearing flags (the primary case for bit-wise not on a bit field), supporting a bit-wise not would still be extremely useful for compatibility with other languages. In terms of printing, I would expect that, for ease of reading, a negated flags would print with the negation (storing it as one-complement), however every other operation would collapse the negation into the full int value, losing the printing value. Something like:
READ = 1; WRITE = 2; EXECUTE = 4 # Obviously, different syntax. This would default ALL = 4 | 2 | 1, however it could be overridden in the special cases where other values have meaning. I would imagine the most common override would be to 0xFFFFFFFF, though I could also see others. a = READ | EXECUTE a (READ | EXECUTE) ~a # Stores it as a negation of the values. ~(READ | EXECUTE) b = EXECUTE | WRITE b # Reordered based on values. (WRITE | EXECUTE) b & ~a # Collapses the negation into the full bit field, resulting in a non-negated bit field. WRITE ~b | ~a (READ | WRITE)
On Thu, Mar 5, 2015 at 1:22 PM, Chris Angelico <rosuav@gmail.com> wrote:
As the other Chris says, it's basically the bit with name "n", and then we give it a much more convenient alias.
Now that I've joined the conversation, there are TWO other Chris's :).
For ~, I suggest either having an extra bit on the object that remembers negation or replacing the patterns a &= ~b with a.clear_flags(b) and a & ~b with a.without_flags(b) or something like that. On Tuesday, March 3, 2015 at 10:54:15 AM UTC-5, Serhiy Storchaka wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python...@python.org <javascript:> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mar 4, 2015, at 7:51, Neil Girdhar <mistersheik@gmail.com> wrote:
For ~, I suggest either having an extra bit on the object that remembers negation
Then what exactly happens when you & or | two values, one of which has the negation bit set? For example, using stat values, what is S_IRUSR & ~S_IXUSR? Or, worse, with |. You can end up with some bits set, some negatively set, and some set neither way. How are you going to represent that? In C, because the values are stored as unsigned fixed-size (say, 16-bit) ints and the ~ is just the usual 1's complement operator on unsigned int types, ~S_IXUSR is all the bits except 0o400 (that is 0o1377). There are multiple ways to solve this: * Use signed 1's complement (as I suggested in my original message, which Serhily ignored and suggested the same thing), but then the value of ~S_IXUSR is -0o401, which isn't likely to match == to a value you got from C (or to be obvious to someone using/debugging the code). * Scan the values and assume the highest bit seen is the max bit (as two other people suggested) and manually unsigned-1's-comp, but this is not only more complicated, it only provides the same values as C if you've used all 8/16/32/64 bits or you explicitly mask off & ALL_BITS at the end. * Require specifying the max bit somewhere in the definition, maybe defaulting to 32, or maybe using C++ enum rules (round the highest value up to 8, 16, 32, or 64 bits). * Store two separate ints, one for negated and one for non-negated. * Don't allow ~ at all (as you suggest at the end of the paragraph), using methods instead (although you only gave mutating methods, which is going to make a lot of C 1-liners turn into verbose 3-liners in Python). And if you're wondering why I keep harping on having the same value as C: if you don't care about that, you don't need to use an int as a bitset in the first place; the only reason this proposal is useful in the first place is that functions like stat return ints that have well-known meanings from C, and everyone knows how to manipulate them in C, and we want to be able to do the same thing in Python but with readable reprs and all the other benefits of a real enum type.
or replacing the patterns a &= ~b with a.clear_flags(b) and a & ~b with a.without_flags(b) or something like that.
On Tuesday, March 3, 2015 at 10:54:15 AM UTC-5, Serhiy Storchaka wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python...@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
This is exactly why we should not have & and ~. It creates the possibility to write meaningless code. Have methods that clear fields and methods that set them. I proposed an interface to IntFields in another message. Best, Neil On Wed, Mar 4, 2015 at 5:59 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Mar 4, 2015, at 7:51, Neil Girdhar <mistersheik@gmail.com> wrote:
For ~, I suggest either having an extra bit on the object that remembers negation
Then what exactly happens when you & or | two values, one of which has the negation bit set? For example, using stat values, what is S_IRUSR & ~S_IXUSR? Or, worse, with |. You can end up with some bits set, some negatively set, and some set neither way. How are you going to represent that?
In C, because the values are stored as unsigned fixed-size (say, 16-bit) ints and the ~ is just the usual 1's complement operator on unsigned int types, ~S_IXUSR is all the bits except 0o400 (that is 0o1377).
There are multiple ways to solve this:
* Use signed 1's complement (as I suggested in my original message, which Serhily ignored and suggested the same thing), but then the value of ~S_IXUSR is -0o401, which isn't likely to match == to a value you got from C (or to be obvious to someone using/debugging the code).
* Scan the values and assume the highest bit seen is the max bit (as two other people suggested) and manually unsigned-1's-comp, but this is not only more complicated, it only provides the same values as C if you've used all 8/16/32/64 bits or you explicitly mask off & ALL_BITS at the end.
* Require specifying the max bit somewhere in the definition, maybe defaulting to 32, or maybe using C++ enum rules (round the highest value up to 8, 16, 32, or 64 bits).
* Store two separate ints, one for negated and one for non-negated.
* Don't allow ~ at all (as you suggest at the end of the paragraph), using methods instead (although you only gave mutating methods, which is going to make a lot of C 1-liners turn into verbose 3-liners in Python).
(I did give a pair of methods one of which was not mutating)
And if you're wondering why I keep harping on having the same value as C: if you don't care about that, you don't need to use an int as a bitset in the first place; the only reason this proposal is useful in the first place is that functions like stat return ints that have well-known meanings from C, and everyone knows how to manipulate them in C, and we want to be able to do the same thing in Python but with readable reprs and all the other benefits of a real enum type.
or replacing the patterns a &= ~b with a.clear_flags(b) and a & ~b with a.without_flags(b) or something like that.
On Tuesday, March 3, 2015 at 10:54:15 AM UTC-5, Serhiy Storchaka wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python...@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 05Mar2015 05:04, Neil Girdhar <mistersheik@gmail.com> wrote:
This is exactly why we should not have & and ~. It creates the possibility to write meaningless code. Have methods that clear fields and methods that set them. I proposed an interface to IntFields in another message.
I can see the argument against ~, unless you preagree a 1's complelment bit range (or equivalent, whatever): feasible, given that you might refuse to accept "unknown" flags. With a preagreed range, "~" is perfectly meaningful. I do not see _any_ argument again "&". I would be very unhappy with an IntFlags that didn't accept "&" and "|", and immediately subclass it and never use the raw one again. I'm not sure I am a fan of "remembering negation". Keep it simple. Forbid (or document as undefined) ~ in the absense of a range agreement at setup time. Cheers, Cameron Simpson <cs@zip.com.au> Favourite proverb: Ein mann der motorrad fahrt ist fuer immer jung.
On Tue, Mar 3, 2015, at 10:52, Serhiy Storchaka wrote:
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
Any reason StatFlags shouldn't have a representation like "rwxr-x---" for 0750?
On 4 March 2015 at 19:47, Skip Montanaro <skip.montanaro@gmail.com> wrote:
On Wed, Mar 4, 2015 at 1:45 PM, <random832@fastmail.us> wrote:
Any reason StatFlags shouldn't have a representation like "rwxr-x---" for 0750?
I've never considered this before, but is this sort of thing portable to/meaningful on Windows?
Not really. Windows' actual ACL system is far too complex to fit into 9 bits. But the readable and writable bits are use to reflect the readonly flag (which is a separate value from the ACLs) of a file (so S_IREAD is always true, and S_IWRITE is true unless the file is readonly). So you only ever get "'-rw-rw-rw-'" or "'-r--r--r--'" Paul
On Wed, Mar 4, 2015, at 14:47, Skip Montanaro wrote:
On Wed, Mar 4, 2015 at 1:45 PM, <random832@fastmail.us> wrote:
Any reason StatFlags shouldn't have a representation like "rwxr-x---" for 0750?
I've never considered this before, but is this sort of thing portable to/meaningful on Windows?
No, but neither are 90% of the flags themselves, especially as actually implemented by the C runtime library (they're basically all hardcoded, except the write flags are turned off if a file is readonly, the exec flags are set based on a hardcoded list of filename extensions, and a handful of the file type bit states are implemented) If someone wants to do something meaningful on windows, they'll need a richer API than the one currently implemented in the os module.
I like the IntFlags concept, as long as it's clearly separated from IntEnum -- it seems that's the consensus now. I don't like the name IntFlags: BitFlags makes more sense to me, since the key feature is supporting bitwise operators. Calling it BitFlags has the additional advantage of making it very clear that it's not closely related to IntEnum. Best, Luciano On Tue, Mar 3, 2015 at 12:52 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Luciano Ramalho Twitter: @ramalhoorg Professor em: http://python.pro.br Twitter: @pythonprobr
Maybe even call it BitSet, and model the interface based on frozenset, except that every element is also a set of one. The operations will be: X | Y -- union X & Y -- intersection X ^ Y -- symmetric difference X - Y -- difference X in Y == (X & Y) == X len(X) -- number of set bits bool(X) -- any bits set isdisjoint, issubset, issuperset == not X & Y, not X - Y, not Y - X (Note no negation.) Eugene On Thu, Mar 5, 2015 at 11:15 AM, Luciano Ramalho <luciano@ramalho.org> wrote:
I like the IntFlags concept, as long as it's clearly separated from IntEnum -- it seems that's the consensus now.
I don't like the name IntFlags: BitFlags makes more sense to me, since the key feature is supporting bitwise operators.
Calling it BitFlags has the additional advantage of making it very clear that it's not closely related to IntEnum.
Best,
Luciano
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of
On Tue, Mar 3, 2015 at 12:52 PM, Serhiy Storchaka <storchaka@gmail.com> wrote: predefined
constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Luciano Ramalho Twitter: @ramalhoorg
Professor em: http://python.pro.br Twitter: @pythonprobr _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
The problem is that a set is by definition unordered, but the position of the bits in BitFlags is crucial, so the name BitSet may not give the right idea. On Thu, Mar 5, 2015 at 1:58 PM, Eugene Toder <eltoder@gmail.com> wrote:
Maybe even call it BitSet, and model the interface based on frozenset, except that every element is also a set of one. The operations will be: X | Y -- union X & Y -- intersection X ^ Y -- symmetric difference X - Y -- difference X in Y == (X & Y) == X len(X) -- number of set bits bool(X) -- any bits set isdisjoint, issubset, issuperset == not X & Y, not X - Y, not Y - X (Note no negation.)
Eugene
On Thu, Mar 5, 2015 at 11:15 AM, Luciano Ramalho <luciano@ramalho.org> wrote:
I like the IntFlags concept, as long as it's clearly separated from IntEnum -- it seems that's the consensus now.
I don't like the name IntFlags: BitFlags makes more sense to me, since the key feature is supporting bitwise operators.
Calling it BitFlags has the additional advantage of making it very clear that it's not closely related to IntEnum.
Best,
Luciano
On Tue, Mar 3, 2015 at 12:52 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
socket.AF_INET <AddressFamily.AF_INET: 2> socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is int, and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Luciano Ramalho Twitter: @ramalhoorg
Professor em: http://python.pro.br Twitter: @pythonprobr _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Luciano Ramalho Twitter: @ramalhoorg Professor em: http://python.pro.br Twitter: @pythonprobr
On Thu, Mar 5, 2015 at 9:03 AM, Luciano Ramalho <luciano@ramalho.org> wrote:
The problem is that a set is by definition unordered, but the position of the bits in BitFlags is crucial,
Is it? or is that an implementation detail? What I'm getting at is that if you use an integer to store bits, then the nth bit is, well, the nth bit in the value. But you could conceptually think of it as the bit with the name, 'n', in which case order no longer matters. -Chris
so the name BitSet may not give the right idea.
On Thu, Mar 5, 2015 at 1:58 PM, Eugene Toder <eltoder@gmail.com> wrote:
Maybe even call it BitSet, and model the interface based on frozenset, except that every element is also a set of one. The operations will be: X | Y -- union X & Y -- intersection X ^ Y -- symmetric difference X - Y -- difference X in Y == (X & Y) == X len(X) -- number of set bits bool(X) -- any bits set isdisjoint, issubset, issuperset == not X & Y, not X - Y, not Y - X (Note no negation.)
Eugene
On Thu, Mar 5, 2015 at 11:15 AM, Luciano Ramalho <luciano@ramalho.org> wrote:
I like the IntFlags concept, as long as it's clearly separated from IntEnum -- it seems that's the consensus now.
I don't like the name IntFlags: BitFlags makes more sense to me, since the key feature is supporting bitwise operators.
Calling it BitFlags has the additional advantage of making it very clear that it's not closely related to IntEnum.
Best,
Luciano
On Tue, Mar 3, 2015 at 12:52 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations.
> socket.AF_INET <AddressFamily.AF_INET: 2> > socket.socket() <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
But when integer constants are flags that should be ORed, IntEnum doesn't help, because the result of bitwise OR of two IntEnum instances is
int,
and this value can't be represented as IntEnum.
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
> print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH > stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Any thoughts?
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Luciano Ramalho Twitter: @ramalhoorg
Professor em: http://python.pro.br Twitter: @pythonprobr _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Luciano Ramalho Twitter: @ramalhoorg
Professor em: http://python.pro.br Twitter: @pythonprobr _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov
On Fri, Mar 6, 2015 at 4:11 AM, Chris Barker <chris.barker@noaa.gov> wrote:
On Thu, Mar 5, 2015 at 9:03 AM, Luciano Ramalho <luciano@ramalho.org> wrote:
The problem is that a set is by definition unordered, but the position of the bits in BitFlags is crucial,
Is it? or is that an implementation detail?
What I'm getting at is that if you use an integer to store bits, then the nth bit is, well, the nth bit in the value. But you could conceptually think of it as the bit with the name, 'n', in which case order no longer matters.
The position of bits is the values of things in the set. These two are more-or-less expressing the same concept: flags1 = {"ReadOnly", "AllowRead", "CloseOnExec"} flags2 = FLG_READONLY | FLG_ALLOWREAD | FLG_CLOEXEC The order of elements in the braced set is meaningless, just as the order of bitflags in the piped list is meaningless. The positions of bits in the piped set corresponds to the string names in the braced set. As the other Chris says, it's basically the bit with name "n", and then we give it a much more convenient alias. ChrisA
On Thu, Mar 5, 2015 at 12:03 PM, Luciano Ramalho <luciano@ramalho.org> wrote:
The problem is that a set is by definition unordered, but the position of the bits in BitFlags is crucial, so the name BitSet may not give the right idea.
The flags discussed so far are unordered as well. E.g. OpenMode.RDONLY | OpenMode.WRONLY | OpenMode.CLOEXEC == OpenMode.RDONLY | OpenMode.CLOEXEC | OpenMode.WRONLY or any other permutation. In other words, flags (aka bitmasks) are just an optimized representation of an (unordered) set of small integer values. Eugene
On Thu, Mar 5, 2015 at 2:14 PM, Eugene Toder <eltoder@gmail.com> wrote:
The flags discussed so far are unordered as well. E.g. OpenMode.RDONLY | OpenMode.WRONLY | OpenMode.CLOEXEC == OpenMode.RDONLY | OpenMode.CLOEXEC | OpenMode.WRONLY or any other permutation. In other words, flags (aka bitmasks) are just an optimized representation of an (unordered) set of small integer values.
I agree that they are unordered in the common use cases. But the kind of low-level code where such flags are used often involves building a byte or a word out of those flags to pass to a low-level C API. How do you make sure that the byte you will build makes sense if you don't care about the position, or the log2 of each flag value? So I definitely think ordering and positioning are to be preserved in a data structure intended to manage and combine bits. Best, Luciano -- Luciano Ramalho Twitter: @ramalhoorg Professor em: http://python.pro.br Twitter: @pythonprobr
On Thu, Mar 5, 2015 at 12:20 PM, Luciano Ramalho <luciano@ramalho.org> wrote:
On Thu, Mar 5, 2015 at 2:14 PM, Eugene Toder <eltoder@gmail.com> wrote:
The flags discussed so far are unordered as well. E.g. OpenMode.RDONLY | OpenMode.WRONLY | OpenMode.CLOEXEC == OpenMode.RDONLY | OpenMode.CLOEXEC | OpenMode.WRONLY or any other permutation. In other words, flags (aka bitmasks) are just an optimized representation of an (unordered) set of small integer values.
I agree that they are unordered in the common use cases. But the kind of low-level code where such flags are used often involves building a byte or a word out of those flags to pass to a low-level C API. How do you make sure that the byte you will build makes sense if you don't care about the position, or the log2 of each flag value? So I definitely think ordering and positioning are to be preserved in a data structure intended to manage and combine bits.
I think you conflate the two separate issues: ordering of the elements in the set, and the bit representation of the set. Flags don't preserve the former, but guarantee the later. Normal sets don't preserve the former too, and the latter doesn't make sense for them. BitSet would not preserve the former, but add the later in the way compatible with flags. Specifically, when you define a BitSet, you assign every possible element a specific bit position (i.e. integer value). When you perform bit operations the natural thing happens, and you get the expected bit representation. Eugene
Luciano Ramalho wrote:
The problem is that a set is by definition unordered, but the position of the bits in BitFlags is crucial, so the name BitSet may not give the right idea.
I think the "Bit" part of BitSet covers that. It's a set that's represented using bits, and bits have a position. Also, the elements of the set are conceptually ints, and ints are ordered. -- Greg
On 05.03.15 18:15, Luciano Ramalho wrote:
I don't like the name IntFlags: BitFlags makes more sense to me, since the key feature is supporting bitwise operators.
I chose this name because the concept IntFlags is very similar to the concept of Flags enums in C#. The Flags decorator in C# is as close to IntFlags as enums in C# close to IntEnum. The Int prefix is here because IntFlags is just an funny int (as IntEnum) and both IntFlags and IntEnum are purposed to replace int constants.
Calling it BitFlags has the additional advantage of making it very clear that it's not closely related to IntEnum.
But it is closely related to IntEnum. Both are int subclass and fully compatible with ints, both provides named constants, both have funny str and repr, both inherit common useful interface from Enum, both are purposed to replace integer constants.
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags. On Thu, Mar 5, 2015 at 12:26 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 18:15, Luciano Ramalho wrote:
I don't like the name IntFlags: BitFlags makes more sense to me, since the key feature is supporting bitwise operators.
I chose this name because the concept IntFlags is very similar to the concept of Flags enums in C#. The Flags decorator in C# is as close to IntFlags as enums in C# close to IntEnum. The Int prefix is here because IntFlags is just an funny int (as IntEnum) and both IntFlags and IntEnum are purposed to replace int constants.
Calling it BitFlags has the additional advantage of making it very
clear that it's not closely related to IntEnum.
But it is closely related to IntEnum. Both are int subclass and fully compatible with ints, both provides named constants, both have funny str and repr, both inherit common useful interface from Enum, both are purposed to replace integer constants.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/ topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On 05.03.15 19:29, Neil Girdhar wrote:
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags.
It looks too complicated for such simple case. And it has an interface incompatible with plain int.
Even if you constrain yourself to the BitFlags rather than the more general BitFields, I strongly disagree with the interface that people are proposing involving & and ~ operators. In general, good interface design reflects the way we think about objects — not their underlying representation. The fact is that a BitSet's main operations are set and clear individual bits. It is as if the BitFlags are a namedtuple with Boolean elements whose underlying storage happens to be an integer. Therefore, the interface that makes the most sense is member access: my_bit_flags.some_bit = True my_bit_flags.some_bit = False I don't see the justification for writing these as my_bit_flags |= TheBitFlagsClass.some_bit my_bit_flags &= ~TheBitFlagsClass.some_bit The second line is particularly terrible because it exposes you to making mistakes like: my_bit_flags &= TheBitFlagsClass.some_bit my_bit_flags |= ~TheBitFlagsClass.some_bit — both of which are meaningless. It also makes it hard to convert code between the alternate implementation of using a namedtuple. It should be easy to do that in my opinion. Best, Neil On Thu, Mar 5, 2015 at 12:57 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 19:29, Neil Girdhar wrote:
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags.
It looks too complicated for such simple case. And it has an interface incompatible with plain int.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/ topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
FYI: ctypes already has a BitFlags/BitFields mechanism. class Flags_bits(ctypes.LittleEndianStructure): _fields_ = [ ("logout", c_uint8, 1), ("userswitch", c_uint8, 1), ("suspend", c_uint8, 1), ("idle", c_uint8, 1), ] On Thu, Mar 5, 2015 at 11:26 PM, Neil Girdhar <mistersheik@gmail.com> wrote:
Even if you constrain yourself to the BitFlags rather than the more general BitFields, I strongly disagree with the interface that people are proposing involving & and ~ operators. In general, good interface design reflects the way we think about objects — not their underlying representation. The fact is that a BitSet's main operations are set and clear individual bits. It is as if the BitFlags are a namedtuple with Boolean elements whose underlying storage happens to be an integer. Therefore, the interface that makes the most sense is member access:
my_bit_flags.some_bit = True my_bit_flags.some_bit = False
I don't see the justification for writing these as
my_bit_flags |= TheBitFlagsClass.some_bit my_bit_flags &= ~TheBitFlagsClass.some_bit
The second line is particularly terrible because it exposes you to making mistakes like:
my_bit_flags &= TheBitFlagsClass.some_bit my_bit_flags |= ~TheBitFlagsClass.some_bit
— both of which are meaningless.
It also makes it hard to convert code between the alternate implementation of using a namedtuple. It should be easy to do that in my opinion.
Best,
Neil
On Thu, Mar 5, 2015 at 12:57 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 19:29, Neil Girdhar wrote:
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags.
It looks too complicated for such simple case. And it has an interface incompatible with plain int.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/ topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On Mar 5, 2015, at 20:26, Neil Girdhar <mistersheik@gmail.com> wrote:
Even if you constrain yourself to the BitFlags rather than the more general BitFields, I strongly disagree with the interface that people are proposing involving & and ~ operators. In general, good interface design reflects the way we think about objects — not their underlying representation.
But sometimes the object really is "an integer used as a set of bits in some C structure/protocol field/well-known API". For example, if we were designing os.open or mmap or whatever as a Pythonic interface, it wouldn't have a "flags" value that or's together multiple integers. We'd probably have separate keyword-only arguments for the less common flags, etc. But they weren't designed from scratch; they were designed to closely mirror the POSIX APIs. And that doesn't just mean simpler implementation, it means people who are familiar with those APIs know how to use them. It means the vast volumes of tutorials and sample code for opening file handles or mapping memory written for C applies to Python. And so on. So, the interface makes sense. So, one very good use for something like IntFlags is to allow people to keep using that C sample code (with trivial, easy-to-understand changes), but get better debugging, etc. when they do so--e.g., when you introspect an mmap object, it would be great if it could tell you that it was opened with PROT_READ | PROT_EXEC, instead of telling you "3", which you have to manually convert to bits and reverse-lookup in the docs or the module dict. Not allowing people to use C-style operations if they use named bits means that someone who wants the advantages of named bits has to rewrite their familiar C-style code. Sure, maybe the result will be more readable (although that's arguable; the suggested alternatives are pretty verbose--especially since people keep suggesting mutating-only APIs...), but it means many people will stick with plain ints rather than rewrite, and those who do rewrite will end up with code that doesn't look like the familiar code that everyone knows how to read from C.
The fact is that a BitSet's main operations are set and clear individual bits. It is as if the BitFlags are a namedtuple with Boolean elements whose underlying storage happens to be an integer.
In the case where you don't really care that the underlying storage is an integer, why use an integer in the first place? Why not use a namedtuple, or a set, or whatever else is appropriate? In the very rare case where you need to store a million of these things (and can't store them even more compactly with array or NumPy or similar), you can go get a third-party lib; the vast majority of the time, there's no advantage to using an integer. Except, of course, when the underlying representation is the whole point, because you're dealing with an API that's written in terms of integers.
Therefore, the interface that makes the most sense is member access:
my_bit_flags.some_bit = True my_bit_flags.some_bit = False
I don't see the justification for writing these as
my_bit_flags |= TheBitFlagsClass.some_bit my_bit_flags &= ~TheBitFlagsClass.some_bit
The second line is particularly terrible because it exposes you to making mistakes like:
my_bit_flags &= TheBitFlagsClass.some_bit my_bit_flags |= ~TheBitFlagsClass.some_bit
— both of which are meaningless.
No they're not. Put some real names instead of toy names there: readable = m.prot readable &= ProtFlags.Readable Now it's true iff m.prot includes the Readable flag. Of course usually you'd write this in a single line without mutation: readable = m.prot & ProtFlags.Readable But that just goes to show that the primary interface of bit flags is an immutable one; trying to force people to use mutating methods like set_bit and clear_bit is just getting in people's way. (And try to come up with a good name for the non-mutating operation that's obvious and reads like English and isn't approaching the ridiculous Apple level of verbosity you get in Cocoa methods like "bitSetWithBitClear:".)
It also makes it hard to convert code between the alternate implementation of using a namedtuple. It should be easy to do that in my opinion.
Best,
Neil
On Thu, Mar 5, 2015 at 12:57 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 19:29, Neil Girdhar wrote:
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags.
It looks too complicated for such simple case. And it has an interface incompatible with plain int.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Mar 6, 2015 at 4:28 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Mar 5, 2015, at 20:26, Neil Girdhar <mistersheik@gmail.com> wrote:
Even if you constrain yourself to the BitFlags rather than the more general BitFields, I strongly disagree with the interface that people are proposing involving & and ~ operators. In general, good interface design reflects the way we think about objects — not their underlying representation.
But sometimes the object really is "an integer used as a set of bits in some C structure/protocol field/well-known API".
You can always get that integer by casting to integer.
For example, if we were designing os.open or mmap or whatever as a Pythonic interface, it wouldn't have a "flags" value that or's together multiple integers. We'd probably have separate keyword-only arguments for the less common flags, etc. But they weren't designed from scratch; they were designed to closely mirror the POSIX APIs. And that doesn't just mean simpler implementation, it means people who are familiar with those APIs know how to use them. It means the vast volumes of tutorials and sample code for opening file handles or mapping memory written for C applies to Python. And so on. So, the interface makes sense.
I disagree that there is any need to follow the style of the "vast volumes of tutorials and sample code in C" when designing Python libraries. The goal is for the Python code to be as natural as possible. Member access, and building constants using | are natural. Using &~ to clear a bit is not natural; It is a coincidence of implementation that distracts from what is happening.
So, one very good use for something like IntFlags is to allow people to keep using that C sample code (with trivial, easy-to-understand changes), but get better debugging, etc. when they do so--e.g., when you introspect an mmap object, it would be great if it could tell you that it was opened with PROT_READ | PROT_EXEC, instead of telling you "3", which you have to manually convert to bits and reverse-lookup in the docs or the module dict.
Yes, totally agree.
Not allowing people to use C-style operations if they use named bits means that someone who wants the advantages of named bits has to rewrite their familiar C-style code. Sure, maybe the result will be more readable (although that's arguable; the suggested alternatives are pretty verbose--especially since people keep suggesting mutating-only APIs...), but it means many people will stick with plain ints rather than rewrite, and those who do rewrite will end up with code that doesn't look like the familiar code that everyone knows how to read from C.
I totally agree with you that there should not only be mutating-only functions. I agree that | should be used for comining bit fields or flags. However, the people who are "familiar with C" (including me) are frankly dying :) Pandering to the past really gets you nowhere. Try to be a bit idealistic so that new Python code is natural, succinct, and human-readable — rather than the C values of reflecting the underlying representation in spite of the human being.
The fact is that a BitSet's main operations are set and clear individual bits. It is as if the BitFlags are a namedtuple with Boolean elements whose underlying storage happens to be an integer.
In the case where you don't really care that the underlying storage is an integer, why use an integer in the first place? Why not use a namedtuple, or a set, or whatever else is appropriate? In the very rare case where you need to store a million of these things (and can't store them even more compactly with array or NumPy or similar), you can go get a third-party lib; the vast majority of the time, there's no advantage to using an integer.
The main reason is so that you can cast it to "int" and produce something that some API requires.
Except, of course, when the underlying representation is the whole point, because you're dealing with an API that's written in terms of integers.
right.
Therefore, the interface that makes the most sense is member access:
my_bit_flags.some_bit = True my_bit_flags.some_bit = False
I don't see the justification for writing these as
my_bit_flags |= TheBitFlagsClass.some_bit my_bit_flags &= ~TheBitFlagsClass.some_bit
The second line is particularly terrible because it exposes you to making mistakes like:
my_bit_flags &= TheBitFlagsClass.some_bit my_bit_flags |= ~TheBitFlagsClass.some_bit
— both of which are meaningless.
No they're not. Put some real names instead of toy names there:
readable = m.prot readable &= ProtFlags.Readable
Now it's true iff m.prot includes the Readable flag.
Of course usually you'd write this in a single line without mutation:
readable = m.prot & ProtFlags.Readable
We both know that the most readable version is just member access, like you would on any object: readable = m.prot.readable This usage of & to filter is unnecessarily complicated. The fact that the machine does so is no reason for the programmer to write it so.
But that just goes to show that the primary interface of bit flags is an immutable one; trying to force people to use mutating methods like set_bit and clear_bit is just getting in people's way. (And try to come up with a good name for the non-mutating operation that's obvious and reads like English and isn't approaching the ridiculous Apple level of verbosity you get in Cocoa methods like "bitSetWithBitClear:".)
I agree with you here. I think you should also have | so that you can build constants the way you're used to, although I'm not sure about & since I don't see when you would use it in preference to member access.
It also makes it hard to convert code between the alternate implementation of using a namedtuple. It should be easy to do that in my opinion.
Best,
Neil
On Thu, Mar 5, 2015 at 12:57 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 19:29, Neil Girdhar wrote:
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags.
It looks too complicated for such simple case. And it has an interface incompatible with plain int.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/ topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mar 6, 2015, at 1:42, Neil Girdhar <mistersheik@gmail.com> wrote:
On Fri, Mar 6, 2015 at 4:28 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Mar 5, 2015, at 20:26, Neil Girdhar <mistersheik@gmail.com> wrote:
Even if you constrain yourself to the BitFlags rather than the more general BitFields, I strongly disagree with the interface that people are proposing involving & and ~ operators. In general, good interface design reflects the way we think about objects — not their underlying representation.
But sometimes the object really is "an integer used as a set of bits in some C structure/protocol field/well-known API".
You can always get that integer by casting to integer.
For example, if we were designing os.open or mmap or whatever as a Pythonic interface, it wouldn't have a "flags" value that or's together multiple integers. We'd probably have separate keyword-only arguments for the less common flags, etc. But they weren't designed from scratch; they were designed to closely mirror the POSIX APIs. And that doesn't just mean simpler implementation, it means people who are familiar with those APIs know how to use them. It means the vast volumes of tutorials and sample code for opening file handles or mapping memory written for C applies to Python. And so on. So, the interface makes sense.
I disagree that there is any need to follow the style of the "vast volumes of tutorials and sample code in C" when designing Python libraries. The goal is for the Python code to be as natural as possible. Member access, and building constants using | are natural. Using &~ to clear a bit is not natural; It is a coincidence of implementation that distracts from what is happening.
For the vast majority of libraries, I agree. An XML parser or audio decoder has no need to follow cryptic C API standards. But libraries that are designed for close-to-the-metal access can be an exception--again, consider os.open, which automatically gives you access to every *nix plafform's platform-specific features. And wrapping C libraries that don't have much of a Python userbase can be another example. If enough people start using it, someone will write and document a higher-level Pythonic interface, but until that happens, having an interface which closely matches what people can find documentation, StackOverflow help, sample code, etc. for is a huge help. Consider PyGame. Much of it is still sparsely documented, but because it wraps the SDL APIs, you can almost always figure out what you need to do, which is part of the reason it's so popular while higher-level wrappers are not. (The other part of the reason is that it wraps almost all of the functionality of SDL, and nothing else can claim that, and again that's probably because it's a thin wrapper.) Or consider PyWin32: it has almost no documentation, and it's not at all Pythonic, but because you can look up a function on MSDN and directly use the C documentation, it's useful for all those areas of the Win32 API (and third-party COM libraries, etc.) that don't have higher-level wrappers. And there are plenty of protocols, file formats, etc. for which the documentation is written for C (or is just a C implementation, as with the predecessor to RTSP that I forget the name of) as well. In an ideal world, everything you wanted would have a high-level, Pythonic API--in fact, everything would be designed for Python in the first place. In the real world, you're better off with a C API than with no API at all.
So, one very good use for something like IntFlags is to allow people to keep using that C sample code (with trivial, easy-to-understand changes), but get better debugging, etc. when they do so--e.g., when you introspect an mmap object, it would be great if it could tell you that it was opened with PROT_READ | PROT_EXEC, instead of telling you "3", which you have to manually convert to bits and reverse-lookup in the docs or the module dict.
Yes, totally agree.
Not allowing people to use C-style operations if they use named bits means that someone who wants the advantages of named bits has to rewrite their familiar C-style code. Sure, maybe the result will be more readable (although that's arguable; the suggested alternatives are pretty verbose--especially since people keep suggesting mutating-only APIs...), but it means many people will stick with plain ints rather than rewrite, and those who do rewrite will end up with code that doesn't look like the familiar code that everyone knows how to read from C.
I totally agree with you that there should not only be mutating-only functions. I agree that | should be used for comining bit fields or flags. However, the people who are "familiar with C" (including me) are frankly dying :)
People have been saying that for a couple decades now, but there's still tons of functionality--not just system-level stuff, but APIs for high-level things like audio fingerprinting or animating sprites or streaming video or extending a Python interpreter--that only exists in C (or sometimes C++ or ObjC), or with very thin wrappers for higher-level languages. And that's still going to be true for a long time to come. More importantly, if C really were dead and irrelevant, there would be no need for this proposal; again, the only reason you ever care about packing flags into an int in the first place is for compatibility with C or C-style code. When you don't need that, just use a namedtuple or a set or keyword arguments or whatever in the first place.
Pandering to the past really gets you nowhere. Try to be a bit idealistic so that new Python code is natural, succinct, and human-readable — rather than the C values of reflecting the underlying representation in spite of the human being.
The fact is that a BitSet's main operations are set and clear individual bits. It is as if the BitFlags are a namedtuple with Boolean elements whose underlying storage happens to be an integer.
In the case where you don't really care that the underlying storage is an integer, why use an integer in the first place? Why not use a namedtuple, or a set, or whatever else is appropriate? In the very rare case where you need to store a million of these things (and can't store them even more compactly with array or NumPy or similar), you can go get a third-party lib; the vast majority of the time, there's no advantage to using an integer.
The main reason is so that you can cast it to "int" and produce something that some API requires.
Except, of course, when the underlying representation is the whole point, because you're dealing with an API that's written in terms of integers.
right.
Therefore, the interface that makes the most sense is member access:
my_bit_flags.some_bit = True my_bit_flags.some_bit = False
I don't see the justification for writing these as
my_bit_flags |= TheBitFlagsClass.some_bit my_bit_flags &= ~TheBitFlagsClass.some_bit
The second line is particularly terrible because it exposes you to making mistakes like:
my_bit_flags &= TheBitFlagsClass.some_bit my_bit_flags |= ~TheBitFlagsClass.some_bit
— both of which are meaningless.
No they're not. Put some real names instead of toy names there:
readable = m.prot readable &= ProtFlags.Readable
Now it's true iff m.prot includes the Readable flag.
Of course usually you'd write this in a single line without mutation:
readable = m.prot & ProtFlags.Readable
We both know that the most readable version is just member access, like you would on any object:
readable = m.prot.readable
This usage of & to filter is unnecessarily complicated. The fact that the machine does so is no reason for the programmer to write it so.
Right, so someone should write a higher-level library that wraps up mmap so you don't have to use it. But no one has done so yet, and if you want to use it without waiting another couple decades until someone gets around to it, you're using the C-style API.
But that just goes to show that the primary interface of bit flags is an immutable one; trying to force people to use mutating methods like set_bit and clear_bit is just getting in people's way. (And try to come up with a good name for the non-mutating operation that's obvious and reads like English and isn't approaching the ridiculous Apple level of verbosity you get in Cocoa methods like "bitSetWithBitClear:".)
I agree with you here. I think you should also have | so that you can build constants the way you're used to, although I'm not sure about & since I don't see when you would use it in preference to member access.
OK, if you have | and &, you automatically have |= and &=. There's no way to implement the former without automatically getting the latter. So if that's your suggestion, it's not possible in the first place, so you have to choose whether we get both or neither.
It also makes it hard to convert code between the alternate implementation of using a namedtuple. It should be easy to do that in my opinion.
Best,
Neil
On Thu, Mar 5, 2015 at 12:57 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 19:29, Neil Girdhar wrote:
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags.
It looks too complicated for such simple case. And it has an interface incompatible with plain int.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
It seems to me that we probably would agree on an interface even if we have a philosophical disagreement. I think it's possible to have clean, Pythonic interface that produces whatever integers you want. In short, my preferred interface is: __or__ (and __ior__) __setattr__ and __getattr__ __int__ and that's it. Is there really a use case for __and__, or __invert__? Given that you want to follow C so closely, I'm surprised that you don't prefer IntFields to IntFlags. I also gave a couple motivating examples for fields (here's a third: http://www.tagwith.com/question_332767_rgb-color-converting-into-565-format ). Best, Neil On Fri, Mar 6, 2015 at 11:41 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Mar 6, 2015, at 1:42, Neil Girdhar <mistersheik@gmail.com> wrote:
On Fri, Mar 6, 2015 at 4:28 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Mar 5, 2015, at 20:26, Neil Girdhar <mistersheik@gmail.com> wrote:
Even if you constrain yourself to the BitFlags rather than the more general BitFields, I strongly disagree with the interface that people are proposing involving & and ~ operators. In general, good interface design reflects the way we think about objects — not their underlying representation.
But sometimes the object really is "an integer used as a set of bits in some C structure/protocol field/well-known API".
You can always get that integer by casting to integer.
For example, if we were designing os.open or mmap or whatever as a Pythonic interface, it wouldn't have a "flags" value that or's together multiple integers. We'd probably have separate keyword-only arguments for the less common flags, etc. But they weren't designed from scratch; they were designed to closely mirror the POSIX APIs. And that doesn't just mean simpler implementation, it means people who are familiar with those APIs know how to use them. It means the vast volumes of tutorials and sample code for opening file handles or mapping memory written for C applies to Python. And so on. So, the interface makes sense.
I disagree that there is any need to follow the style of the "vast volumes of tutorials and sample code in C" when designing Python libraries. The goal is for the Python code to be as natural as possible. Member access, and building constants using | are natural. Using &~ to clear a bit is not natural; It is a coincidence of implementation that distracts from what is happening.
For the vast majority of libraries, I agree. An XML parser or audio decoder has no need to follow cryptic C API standards.
But libraries that are designed for close-to-the-metal access can be an exception--again, consider os.open, which automatically gives you access to every *nix plafform's platform-specific features.
It's just as "close to the metal" to write things with member access. It's not as if it's going to be much slower! It's just a question of how you express setting and clearing bits.
And wrapping C libraries that don't have much of a Python userbase can be another example. If enough people start using it, someone will write and document a higher-level Pythonic interface, but until that happens, having an interface which closely matches what people can find documentation, StackOverflow help, sample code, etc. for is a huge help.
Most people use StackOverflow and SO always adapts.
Consider PyGame. Much of it is still sparsely documented, but because it wraps the SDL APIs, you can almost always figure out what you need to do, which is part of the reason it's so popular while higher-level wrappers are not. (The other part of the reason is that it wraps almost all of the functionality of SDL, and nothing else can claim that, and again that's probably because it's a thin wrapper.) Or consider PyWin32: it has almost no documentation, and it's not at all Pythonic, but because you can look up a function on MSDN and directly use the C documentation, it's useful for all those areas of the Win32 API (and third-party COM libraries, etc.) that don't have higher-level wrappers.
And there are plenty of protocols, file formats, etc. for which the documentation is written for C (or is just a C implementation, as with the predecessor to RTSP that I forget the name of) as well.
In an ideal world, everything you wanted would have a high-level, Pythonic API--in fact, everything would be designed for Python in the first place. In the real world, you're better off with a C API than with no API at all.
I don't think the above API is so "high level". I think "x.b = False" is just better design than "x &= ~Class.b".
So, one very good use for something like IntFlags is to allow people to
keep using that C sample code (with trivial, easy-to-understand changes), but get better debugging, etc. when they do so--e.g., when you introspect an mmap object, it would be great if it could tell you that it was opened with PROT_READ | PROT_EXEC, instead of telling you "3", which you have to manually convert to bits and reverse-lookup in the docs or the module dict.
Yes, totally agree.
Not allowing people to use C-style operations if they use named bits means that someone who wants the advantages of named bits has to rewrite their familiar C-style code. Sure, maybe the result will be more readable (although that's arguable; the suggested alternatives are pretty verbose--especially since people keep suggesting mutating-only APIs...), but it means many people will stick with plain ints rather than rewrite, and those who do rewrite will end up with code that doesn't look like the familiar code that everyone knows how to read from C.
I totally agree with you that there should not only be mutating-only functions. I agree that | should be used for comining bit fields or flags. However, the people who are "familiar with C" (including me) are frankly dying :)
People have been saying that for a couple decades now, but there's still tons of functionality--not just system-level stuff, but APIs for high-level things like audio fingerprinting or animating sprites or streaming video or extending a Python interpreter--that only exists in C (or sometimes C++ or ObjC), or with very thin wrappers for higher-level languages. And that's still going to be true for a long time to come.
More importantly, if C really were dead and irrelevant, there would be no need for this proposal; again, the only reason you ever care about packing flags into an int in the first place is for compatibility with C or C-style code. When you don't need that, just use a namedtuple or a set or keyword arguments or whatever in the first place.
I'm not saying it's dead. I'm saying that pandering to an audience who knows C is a waste. I have a feeling that the real inertia has nothing to do with other people who might know C, and more to do with people like me and you who want to keep writing things the same way we've been writing things. Sometimes, we've been doing things the long way, and the next generation can write things the short way. It's not much more "high level". It's just simpler.
Pandering to the past really gets you nowhere. Try to be a bit idealistic so that new Python code is natural, succinct, and human-readable — rather than the C values of reflecting the underlying representation in spite of the human being.
The fact is that a BitSet's main operations are set and clear individual bits. It is as if the BitFlags are a namedtuple with Boolean elements whose underlying storage happens to be an integer.
In the case where you don't really care that the underlying storage is an integer, why use an integer in the first place? Why not use a namedtuple, or a set, or whatever else is appropriate? In the very rare case where you need to store a million of these things (and can't store them even more compactly with array or NumPy or similar), you can go get a third-party lib; the vast majority of the time, there's no advantage to using an integer.
The main reason is so that you can cast it to "int" and produce something that some API requires.
Except, of course, when the underlying representation is the whole point, because you're dealing with an API that's written in terms of integers.
right.
Therefore, the interface that makes the most sense is member access:
my_bit_flags.some_bit = True my_bit_flags.some_bit = False
I don't see the justification for writing these as
my_bit_flags |= TheBitFlagsClass.some_bit my_bit_flags &= ~TheBitFlagsClass.some_bit
The second line is particularly terrible because it exposes you to making mistakes like:
my_bit_flags &= TheBitFlagsClass.some_bit my_bit_flags |= ~TheBitFlagsClass.some_bit
— both of which are meaningless.
No they're not. Put some real names instead of toy names there:
readable = m.prot readable &= ProtFlags.Readable
Now it's true iff m.prot includes the Readable flag.
Of course usually you'd write this in a single line without mutation:
readable = m.prot & ProtFlags.Readable
We both know that the most readable version is just member access, like you would on any object:
readable = m.prot.readable
This usage of & to filter is unnecessarily complicated. The fact that the machine does so is no reason for the programmer to write it so.
Right, so someone should write a higher-level library that wraps up mmap so you don't have to use it. But no one has done so yet, and if you want to use it without waiting another couple decades until someone gets around to it, you're using the C-style API.
But that just goes to show that the primary interface of bit flags is an
immutable one; trying to force people to use mutating methods like set_bit and clear_bit is just getting in people's way. (And try to come up with a good name for the non-mutating operation that's obvious and reads like English and isn't approaching the ridiculous Apple level of verbosity you get in Cocoa methods like "bitSetWithBitClear:".)
I agree with you here. I think you should also have | so that you can build constants the way you're used to, although I'm not sure about & since I don't see when you would use it in preference to member access.
OK, if you have | and &, you automatically have |= and &=. There's no way to implement the former without automatically getting the latter. So if that's your suggestion, it's not possible in the first place, so you have to choose whether we get both or neither.
It also makes it hard to convert code between the alternate implementation
of using a namedtuple. It should be easy to do that in my opinion.
Best,
Neil
On Thu, Mar 5, 2015 at 12:57 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 19:29, Neil Girdhar wrote:
Have you looked at my IntFields generalization of IntFlags? It seems that many of your examples (permissions, e.g.) are better expressed with fields than with flags.
It looks too complicated for such simple case. And it has an interface incompatible with plain int.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/ topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Mar 6, 2015, at 04:28, Andrew Barnert wrote:
For example, if we were designing os.open or mmap or whatever as a Pythonic interface, it wouldn't have a "flags" value that or's together multiple integers. We'd probably have separate keyword-only arguments for the less common flags, etc. But they weren't designed from scratch; they were designed to closely mirror the POSIX APIs.
It makes me wonder what the os module would look like if Windows (or VMS, OS/2, classic Mac, or whatever other OSes have been supported by python in the present or past) *didn't* provide close mirrors of the POSIX APIs as part of their C runtime library. I mean, it's not like we have opendir and readdir. And while we do have fork and exec, there's a reason beyond convenience for spawn. Because those are the functions that _don't_ exist, or don't work right, on non-Unix platforms.
On Mar 6, 2015, at 5:51, random832@fastmail.us wrote:
On Fri, Mar 6, 2015, at 04:28, Andrew Barnert wrote:
For example, if we were designing os.open or mmap or whatever as a Pythonic interface, it wouldn't have a "flags" value that or's together multiple integers. We'd probably have separate keyword-only arguments for the less common flags, etc. But they weren't designed from scratch; they were designed to closely mirror the POSIX APIs.
It makes me wonder what the os module would look like if Windows (or VMS, OS/2, classic Mac, or whatever other OSes have been supported by python in the present or past) *didn't* provide close mirrors of the POSIX APIs as part of their C runtime library.
Classic Mac didn't provide anything remotely close to the POSIX APIs. IIRC, you called FSSpecMake to create an FSSpec structure from a volume ID, directory ID, and bare filename, then called FSSpecOpenDF with it, after which you'd call various other APIs to map blocks of data to memory handles. For that matter, Win16 and early Win9x didn't have anything quite like the POSIX APIs, but mapping CreateFile and HANDLE values to open and file descriptors wouldn't be quite as ridiculous as mapping FSSpecOpenDF and refnum values. At any rate, I think os.open has always been only available on Unix and NT. The whole point of it is to interface with libraries that want to use file descriptors or to access platform-specific features; if you want cross-platform files, you just call open. That's why the API is so close to the POSIX API; if it tried to wrap things up at a higher level, it wouldn't be able to provide access to flags that only Solaris offers, etc.
I mean, it's not like we have opendir and readdir. And while we do have fork and exec, there's a reason beyond convenience for spawn. Because those are the functions that _don't_ exist, or don't work right, on non-Unix platforms. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Mar 6, 2015, at 11:09, Andrew Barnert wrote:
Classic Mac didn't provide anything remotely close to the POSIX APIs. IIRC, you called FSSpecMake to create an FSSpec structure from a volume ID, directory ID, and bare filename, then called FSSpecOpenDF with it, after which you'd call various other APIs to map blocks of data to memory handles.
For that matter, Win16 and early Win9x didn't have anything quite like the POSIX APIs, but mapping CreateFile and HANDLE values to open and file descriptors wouldn't be quite as ridiculous as mapping FSSpecOpenDF and refnum values.
In both cases, open/read/write are in the libraries provided with the commonly available C compilers on the platform (MPW or Codewarrior in the Mac case, MSVC or Borland etc in the Windows/DOS case), and it doesn't really matter that they're not part of the platform proper. I don't think there's ever been a compiler for Windows or DOS that _didn't_ provide these functions. It depends on how you define "quite like" the POSIX APIs, I guess. Anyway, the source code of the os module for classic mac clearly shows that it is implemented in terms of pre-existing POSIX-like functions provided either by the supported build platforms or by something called "GUSI", rather than directly in terms of the native API. https://hg.python.org/cpython/file/364638d6434d/Mac/Modules/macmodule.c - you have to go further back to find a version of python that actually had dosmodule.c, but it looks basically the same.
At any rate, I think os.open has always been only available on Unix and NT.
Nope. List of functions on the os module on Mac in python 2.0: https://docs.python.org/release/2.0/mac/module-mac.html
The whole point of it is to interface with libraries that want to use file descriptors or to access platform-specific features; if you want cross-platform files, you just call open.
The os module provides the same interface on all platforms _so that_ the higher-level functions like builtin open can be implemented in terms of it. It's the implementation (and list of available functions) that's different between platforms, not the interface.
That's why the API is so close to the POSIX API; if it tried to wrap things up at a higher level, it wouldn't be able to provide access to flags that only Solaris offers, etc.
Your theory doesn't explain listdir and walk. The reason the API is so close to the POSIX API is because the work of emulating that API was already done on these platforms in order to support ports of Unix C programs, and because no-one saw any value in supporting platform-specific features not exposed through that layer.
On 05Mar2015 19:26, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 05.03.15 18:15, Luciano Ramalho wrote:
I don't like the name IntFlags: BitFlags makes more sense to me, since the key feature is supporting bitwise operators.
I chose this name because the concept IntFlags is very similar to the concept of Flags enums in C#. The Flags decorator in C# is as close to IntFlags as enums in C# close to IntEnum. The Int prefix is here because IntFlags is just an funny int (as IntEnum) and both IntFlags and IntEnum are purposed to replace int constants.
Calling it BitFlags has the additional advantage of making it very clear that it's not closely related to IntEnum.
But it is closely related to IntEnum. Both are int subclass and fully compatible with ints, both provides named constants, both have funny str and repr, both inherit common useful interface from Enum, both are purposed to replace integer constants.
I also prefer IntFlags, like IntEnum. I certainly have use cases where I do not care that there is an underlying numeric value, and am treating things like a set. But with fixed names (versus sets, which are open ended in their native form) and using an int underneath is both efficient an natural. But conversely, I have use cases where I do care. I am -1 on BitFlags. Cheers, Cameron Simpson <cs@zip.com.au> You can be psychotic and still be competent. - John L. Young, American Academy of Psychiatry and the Law on Ted Kaczynski, and probably most internet users
On 03/06/2015 01:41 PM, Serhiy Storchaka wrote:
bitset in C++ and BitSet in Java are fixed-size arrays of booleans. They support such operations as changing a range of bits, but are not compatible with ints and sets.
Java has an EnumSet, which is closer to what we're talking about. It supports set operations and uses a bit vector internally. -- Greg
On Mar 5, 2015, at 17:36, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 03/06/2015 01:41 PM, Serhiy Storchaka wrote:
bitset in C++ and BitSet in Java are fixed-size arrays of booleans. They support such operations as changing a range of bits, but are not compatible with ints and sets.
Java has an EnumSet, which is closer to what we're talking about. It supports set operations and uses a bit vector internally.
If you're looking for similar names in use, PyPI has (at least) modules named bitsets, bitset, bitmap, bitstring, bitarray, and flags, which are all related to what's being discussed here, but crucially without the enum-like naming of the bits. (I've used bitstring and bitarray for things like examining the bits of a TCP header; the others I just saw when searching for bitarray when I couldn't remember its name...)
On 03Mar2015 17:52, Serhiy Storchaka <storchaka@gmail.com> wrote:
Enum and IntEnum classes allow constants to have nice str() and repr() representations. [...] 3. It should have nice str() and repr().
print(stat.S_IROTH | stat.S_IWOTH) stat.S_IROTH|stat.S_IWOTH stat.S_IROTH | stat.S_IWOTH <StatFlags.S_IROTH|S_IWOTH: 6>
Regarding this one, and regarding some of the later discussion about "meaningless" values from some operations, when I wrote my _Clock_Flags I made the repr() pull off known flags and include the remainer if not zero. So your example above would come out: <StatFlags S_IROTH|S_IWOTH 0> and: <StatFlags S_IROTH|S_IWOTH 32> if there was an extra unknown "32" bit in the mix. On reflection, I'd like these to come out: <StatFlags S_IROTH|S_IWOTH> <StatFlags S_IROTH|S_IWOTH|32> (with space or "." or whatever). Cheers, Cameron Simpson <cs@zip.com.au> Isaac Asimov once remarked that friends had chided him for not patenting the electronic pocket calculator, since he wrote of similar devices back in the 1940's. His reply, "Have you ever noticed I only described what it looked like on the *outside*?" - ijl@mediaone.net
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
As long as we are dreaming :) class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096 a = Stat.RDONLY # creates a new instance of Stat, not a singleton b = Stat.RDONLY a is b # False a == b # True c = a a |= Stat.MANDLOCK c.MANDLOCK # 64 b.MANDLOCK # 0 c is a # True repr(a) # <Stat.MANDLOCK|RDONLY: 65> repr(b) # <Stat.RDONLY: 1> d = b | 32 # undefined value repr(d) # <Stat.32|RDONLY: 33> d.MANDLOCK = True repr(d) # <Stat.MANDLOCK|32|RDONLY: 97> repr(~d) # <Stat.RELATIME|NODIRATIME|NOATIME|512|APPEND|WRITE|SYNCHONOUS|NOEXEC|NODEV|NOSUID: 8094> I'm not at all sure I have that last one correct. -- ~Ethan~
On 07.03.15 10:07, Ethan Furman wrote:
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
IntFlags is purposed to replace existing integer constants (as IntEnum). globals().update(Stat.__members__)
On 03/07/2015 02:50 AM, Serhiy Storchaka wrote:
On 07.03.15 10:07, Ethan Furman wrote:
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
IntFlags is purposed to replace existing integer constants (as IntEnum).
globals().update(Stat.__members__)
And that can still work -- if the flag is accessed from the class (Stat.RDONLY) it will always be the value assigned (1); if it is accessed from a member, it will be the value assigned /if set/, otherwise 0. This also has the advantage of supporting both C style operations (x = Stat.WRITE | Stat.APPEND), or the more customary Python operations (x = Stat.WRITE; x.APPEND = True). -- ~Ethan~
On Mar 7, 2015, at 12:07 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why? Assuming the values are immutable (like int), the only difference is your is test, and I have no idea why your code would care about that. (If you're worried about the "a |=" below changing b--or, worse, changing the constant--there's no reason to worry. Just as "a = 1; a |= 2" doesn't affect any other variable holding the int 1, the same would be true here.)
b = Stat.RDONLY a is b # False a == b # True
c = a a |= Stat.MANDLOCK c.MANDLOCK # 64 b.MANDLOCK # 0 c is a # True
repr(a) # <Stat.MANDLOCK|RDONLY: 65> repr(b) # <Stat.RDONLY: 1>
d = b | 32 # undefined value repr(d) # <Stat.32|RDONLY: 33> d.MANDLOCK = True repr(d) # <Stat.MANDLOCK|32|RDONLY: 97>
repr(~d) # <Stat.RELATIME|NODIRATIME|NOATIME|512|APPEND|WRITE|SYNCHONOUS|NOEXEC|NODEV|NOSUID: 8094>
I'm not at all sure I have that last one correct.
That last one is the big question. In C, ~d is going to have the 8192, 16384, ... 2b bits set, not just the bits you defined and the gaps in between. Are you sure you want a different result in Python? (Especially if you're on a platform that does something with those extra bits, so you can get values back that actually have them set. Although I don't think that's an issue with stat, it is with lots of other flags from POSIX-land.) Look at all of the alternatives that have been suggested (round up to power of 256, guess what C would do on your platform with the most likely equivalent definitions, use signed instead of unsigned so the issue moves somewhere else, handle complementing with a special bool, maybe others); why is this one better? Why is it OK to set the undefined 512 bit but not the equally-undefined 8192 bit when someone does ~ on a value of 32?
On 03/07/2015 05:53 AM, Andrew Barnert wrote:
On Mar 7, 2015, at 12:07 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of predefined constants. It can be a combination of predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why?
Because by having mutable instances of Stat we can have more Python operations: instead of: x = Stat(some_Stat_value_from_somewhere) x = x | Stat.NOEXEC # to set the bit we can say: x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = True or, even more illustratively, instead of: x = Stat(some_Stat_value_from_somewhere) x &= ~Stat.NOEXEC # to clear the bit we can have: x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = False # much more readable
Assuming the values are immutable (like int), [...]
Drat. Instances of Stat would not be immutuable, but that means they can't be thin wrappers around `int`, doesn't it? Which also means more work around C call sites. Drat and double-drat.
repr(~d) # <Stat.RELATIME|NODIRATIME|NOATIME|512|APPEND|WRITE|SYNCHONOUS|NOEXEC|NODEV|NOSUID: 8094>
I'm not at all sure I have that last one correct.
That last one is the big question. In C, ~d is going to have the 8192, 16384, ... 2b bits set, not just the bits you defined and the gaps in between. Are you sure you want a different result in Python? (Especially if you're on a platform that does something with those extra bits, so you can get values back that actually have them set. Although I don't think that's an issue with stat, it is with lots of other flags from POSIX-land.)
A `bytes` (or `byte_size`) would need to be set to control how many bits got flipped. If not set, then only defined bits get flipped. -- ~Ethan~
On Sat, Mar 7, 2015 at 10:05 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
On Mar 7, 2015, at 12:07 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/03/2015 07:52 AM, Serhiy Storchaka wrote:
We need new type IntFlags. It is like IntEnum, but has differences:
1. The value of an instance should be not limited to the set of
On 03/07/2015 05:53 AM, Andrew Barnert wrote: predefined constants. It can be a combination of
predefined constants or even arbitrary integer.
2. The result of "|", "&" and "~" operators for IntFlags arguments should be an instance of the same IntFlags subclass.
3. It should have nice str() and repr().
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why?
Because by having mutable instances of Stat we can have more Python operations:
instead of:
x = Stat(some_Stat_value_from_somewhere) x = x | Stat.NOEXEC # to set the bit
we can say:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = True
or, even more illustratively, instead of:
x = Stat(some_Stat_value_from_somewhere) x &= ~Stat.NOEXEC # to clear the bit
we can have:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = False # much more readable
Yes, +1
Assuming the values are immutable (like int), [...]
Drat. Instances of Stat would not be immutuable, but that means they can't be thin wrappers around `int`, doesn't it? Which also means more work around C call sites. Drat and double-drat.
Can't your C call sites cast the object to int?
repr(~d) # <Stat.RELATIME|NODIRATIME|NOATIME|512|APPEND|WRITE|SYNCHONOUS|NOEXEC|NODEV|NOSUID: 8094>
I'm not at all sure I have that last one correct.
That last one is the big question. In C, ~d is going to have the 8192, 16384, ... 2b bits set, not just the bits you defined and the gaps in between. Are you sure you want a different result in Python? (Especially if you're on a platform that does something with those extra bits, so you can get values back that actually have them set. Although I don't think that's an issue with stat, it is with lots of other flags from POSIX-land.)
A `bytes` (or `byte_size`) would need to be set to control how many bits got flipped. If not set, then only defined bits get flipped.
Why do you need ~ at all? Do any API calls that you want to make want the inverted flags? Isn't the only point of inverting the bits in order to clear a field?
-- ~Ethan~
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On 03/07/2015 10:21 AM, Neil Girdhar wrote:
On Sat, Mar 7, 2015 at 10:05 AM, Ethan Furman wrote:
or, even more illustratively, instead of:
x = Stat(some_Stat_value_from_somewhere) x &= ~Stat.NOEXEC # to clear the bit
we can have:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = False # much more readable
Yes, +1
Drat. Instances of Stat would not be immutuable, but that means they can't be thin wrappers around `int`, doesn't it? Which also means more work around C call sites. Drat and double-drat.
Can't your C call sites cast the object to int?
They could, but then you no longer have a drop-in replacement, which is what IntEnum is. Plus it's a hassle. So IntFlag (or whatever it's called) would to be immutable, which means no neat tricks like `obj.bit_name = False`.
Why do you need ~ at all? Do any API calls that you want to make want the inverted flags? Isn't the only point of inverting the bits in order to clear a field?
Hopefully somebody else can address this point. -- ~Ethan~
On Sat, Mar 7, 2015 at 1:51 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/07/2015 10:21 AM, Neil Girdhar wrote:
On Sat, Mar 7, 2015 at 10:05 AM, Ethan Furman wrote:
or, even more illustratively, instead of:
x = Stat(some_Stat_value_from_somewhere) x &= ~Stat.NOEXEC # to clear the bit
we can have:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = False # much more readable
Yes, +1
Drat. Instances of Stat would not be immutuable, but that means they can't be thin wrappers around `int`, doesn't it? Which also means more work around C call sites. Drat and double-drat.
Can't your C call sites cast the object to int?
They could, but then you no longer have a drop-in replacement, which is what IntEnum is. Plus it's a hassle. So IntFlag (or whatever it's called) would to be immutable, which means no neat tricks like `obj.bit_name = False`.
A drop-in replacement for what? Is there a lot of Python code that manipulates flags that you expect people to convert to IntFlags from int? Isn't the point of IntFlags for writing new code?
Why do you need ~ at all? Do any API calls that you want to make want the inverted flags? Isn't the only point of inverting the bits in order to clear a field?
Hopefully somebody else can address this point.
-- ~Ethan~
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On 07.03.15 21:02, Neil Girdhar wrote:
A drop-in replacement for what? Is there a lot of Python code that manipulates flags that you expect people to convert to IntFlags from int? Isn't the point of IntFlags for writing new code?
No, the point of IntFlags to be drop-in replacement for integer constants used as flags. See my patch in issue23591.
For what it's worth, I really like your patch. My suggestion is to change a couple dozen lines so that IntFlags uses composition rather than inheritance, and provides setattr and getattr. As far as I can tell, this would still be a "drop-in replacement", although I'm still not sure in which contexts this has to work. Best, Neil On Saturday, March 7, 2015 at 2:40:29 PM UTC-5, Serhiy Storchaka wrote:
On 07.03.15 21:02, Neil Girdhar wrote:
A drop-in replacement for what? Is there a lot of Python code that manipulates flags that you expect people to convert to IntFlags from int? Isn't the point of IntFlags for writing new code?
No, the point of IntFlags to be drop-in replacement for integer constants used as flags. See my patch in issue23591.
_______________________________________________ Python-ideas mailing list Python...@python.org <javascript:> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Mar 7, 2015 at 1:51 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/07/2015 10:21 AM, Neil Girdhar wrote:
On Sat, Mar 7, 2015 at 10:05 AM, Ethan Furman wrote:
or, even more illustratively, instead of:
x = Stat(some_Stat_value_from_somewhere) x &= ~Stat.NOEXEC # to clear the bit
we can have:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = False # much more readable
Yes, +1
Drat. Instances of Stat would not be immutuable, but that means they can't be thin wrappers around `int`, doesn't it? Which also means more work around C call sites. Drat and double-drat.
Can't your C call sites cast the object to int?
They could, but then you no longer have a drop-in replacement, which is what IntEnum is. Plus it's a hassle. So IntFlag (or whatever it's called) would to be immutable, which means no neat tricks like `obj.bit_name = False`.
Looks like they already do cast to int: In [3]: class X: ...: def __int__(self): return 0 ...: In [4]: os.chmod('a', X()) I really don't see the problem.
Why do you need ~ at all? Do any API calls that you want to make want the inverted flags? Isn't the only point of inverting the bits in order to clear a field?
Hopefully somebody else can address this point.
-- ~Ethan~
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On 07.03.15 20:21, Neil Girdhar wrote:
Why do you need ~ at all? Do any API calls that you want to make want the inverted flags? Isn't the only point of inverting the bits in order to clear a field?
Because existing code uses it. mode = os.stat(path).st_mode os.chmod(path, mode & ~(stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH))
On 03/07/2015 04:05 PM, Ethan Furman wrote:
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why?
Because by having mutable instances of Stat we can have more Python operations:
instead of:
x = Stat(some_Stat_value_from_somewhere) x = x | Stat.NOEXEC # to set the bit
we can say:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = True
Please no. You're making a mutable type out of something that is conceptually (and in people's minds) an integer. Remember how long it can take to understand that a = 1 a = 2 does not change the integer "1" to now be "2". Georg
On 03/07/2015 12:28 PM, Georg Brandl wrote:
On 03/07/2015 04:05 PM, Ethan Furman wrote:
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why?
Because by having mutable instances of Stat we can have more Python operations:
instead of:
x = Stat(some_Stat_value_from_somewhere) x = x | Stat.NOEXEC # to set the bit
we can say:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = True
Please no. You're making a mutable type out of something that is conceptually (and in people's minds) an integer. Remember how long it can take to understand that
a = 1 a = 2
does not change the integer "1" to now be "2".
Good point. To do something like that the name would have be BitFlags or something no so solidly tied to "immutable". At any rate, for this to work would require `int(x)` around every call to a lower-level API, and that's a non-starter. -- ~Ethan~
On Sat, Mar 7, 2015 at 3:38 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/07/2015 12:28 PM, Georg Brandl wrote:
On 03/07/2015 04:05 PM, Ethan Furman wrote:
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why?
Because by having mutable instances of Stat we can have more Python operations:
instead of:
x = Stat(some_Stat_value_from_somewhere) x = x | Stat.NOEXEC # to set the bit
we can say:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = True
Please no. You're making a mutable type out of something that is conceptually (and in people's minds) an integer. Remember how long it can take to understand that
a = 1 a = 2
does not change the integer "1" to now be "2".
The fact that the flags are stored in an int — that is, the implementation has nothing at all to do with the conceptual nature of flags. Flags are properties. Your knowledge of the underlying implementation is misleading you. It would be just as easy and intuitive to implement those flags as keys and values in a dict. If flags were conceptually subtypes of int, then you should be able to do things like: flags ** 7 or flags // 91 Do you agree that this is totally meaningless? There is no "is a" relationship between Flags and int. There is a conversion between the conceptual mapping that is a Flags object to int for the sole purpose of calling into APIs.
Good point. To do something like that the name would have be BitFlags or something no so solidly tied to "immutable". At any rate, for this to work would require `int(x)` around every call to a lower-level API, and that's a non-starter.
Correct me if I'm wrong, but doesn't Boost.Python when generating Python methods that accept ints, automatically call __int__ on the arguments. Doesn't SWIG do the same? Can you give me some examples of methods that accept ints but don't call __int__? It seems to me to be a bug in those methods than in the caller. A method that wants an int should call __int__ on its argument — not expect that isinstance(X, int) etc.
-- ~Ethan~
On Mar 8, 2015, at 1:45 AM, Neil Girdhar <mistersheik@gmail.com> wrote:
On Sat, Mar 7, 2015 at 3:38 PM, Ethan Furman <ethan@stoneleaf.us> wrote: On 03/07/2015 12:28 PM, Georg Brandl wrote:
On 03/07/2015 04:05 PM, Ethan Furman wrote:
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why?
Because by having mutable instances of Stat we can have more Python operations:
instead of:
x = Stat(some_Stat_value_from_somewhere) x = x | Stat.NOEXEC # to set the bit
we can say:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = True
Please no. You're making a mutable type out of something that is conceptually (and in people's minds) an integer. Remember how long it can take to understand that
a = 1 a = 2
does not change the integer "1" to now be "2".
The fact that the flags are stored in an int — that is, the implementation has nothing at all to do with the conceptual nature of flags. Flags are properties. Your knowledge of the underlying implementation is misleading you. It would be just as easy and intuitive to implement those flags as keys and values in a dict.
Usually that's true. And when it's true, you use a dict (or a set or a namedtuple or whatever's appropriate), so you have no need for IntFlags in the first place. The only reason you'd ever want this class is when the fact that the flags are stored in an int is important. Sure, an IntFlags that isn't a subclass on int can define an __int__ method. But so can some class that doesn't use an int for storage in the first place. If you want to convert back and forth, that's perfectly fine, but why are you using an int for storage?
If flags were conceptually subtypes of int, then you should be able to do things like:
flags ** 7
or
flags // 91
Do you agree that this is totally meaningless? There is no "is a" relationship between Flags and int. There is a conversion between the conceptual mapping that is a Flags object to int for the sole purpose of calling into APIs.
Good point. To do something like that the name would have be BitFlags or something no so solidly tied to "immutable". At any rate, for this to work would require `int(x)` around every call to a lower-level API, and that's a non-starter.
Correct me if I'm wrong, but doesn't Boost.Python when generating Python methods that accept ints, automatically call __int__ on the arguments. Doesn't SWIG do the same? Can you give me some examples of methods that accept ints but don't call __int__? It seems to me to be a bug in those methods than in the caller. A method that wants an int should call __int__ on its argument — not expect that isinstance(X, int) etc.
-- ~Ethan~
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, Mar 9, 2015 at 8:29 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Mar 8, 2015, at 1:45 AM, Neil Girdhar <mistersheik@gmail.com> wrote:
On Sat, Mar 7, 2015 at 3:38 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
On 03/07/2015 12:28 PM, Georg Brandl wrote:
On 03/07/2015 04:05 PM, Ethan Furman wrote:
As long as we are dreaming :)
class Stat(IntFlag): RDONLY = 1 NOSUID = 2 NODEV = 4 NOEXEC = 8 SYNCHRONOUS = 16 MANDLOCK = 64 WRITE = 128 APPEND = 256 NOATIME = 1024 NODIRATIME = 2048 RELATIME = 4096
a = Stat.RDONLY # creates a new instance of Stat, not a singleton
Why?
Because by having mutable instances of Stat we can have more Python operations:
instead of:
x = Stat(some_Stat_value_from_somewhere) x = x | Stat.NOEXEC # to set the bit
we can say:
x = Stat(some_Stat_value_from_somewhere) x.NOEXEC = True
Please no. You're making a mutable type out of something that is conceptually (and in people's minds) an integer. Remember how long it can take to understand that
a = 1 a = 2
does not change the integer "1" to now be "2".
The fact that the flags are stored in an int — that is, the implementation has nothing at all to do with the conceptual nature of flags. Flags are properties. Your knowledge of the underlying implementation is misleading you. It would be just as easy and intuitive to implement those flags as keys and values in a dict.
Usually that's true. And when it's true, you use a dict (or a set or a namedtuple or whatever's appropriate), so you have no need for IntFlags in the first place. The only reason you'd ever want this class is when the fact that the flags are stored in an int is important.
Sure, an IntFlags that isn't a subclass on int can define an __int__ method. But so can some class that doesn't use an int for storage in the first place. If you want to convert back and forth, that's perfectly fine, but why are you using an int for storage?
I would word it this way: the only time you use IntFlags is when you need access to an integer representation. The implementation is free to do what it likes provided it meets the interface guarantees. You're not "using an int for storage" and interfaces should never make implementation promises. They should only make interface guarantees.
If flags were conceptually subtypes of int, then you should be able to do things like:
flags ** 7
or
flags // 91
Do you agree that this is totally meaningless? There is no "is a" relationship between Flags and int. There is a conversion between the conceptual mapping that is a Flags object to int for the sole purpose of calling into APIs.
In my opinion, this is the most important point when deciding on inheritance.
Good point. To do something like that the name would have be BitFlags or something no so solidly tied to "immutable". At any rate, for this to work would require `int(x)` around every call to a lower-level API, and that's a non-starter.
Correct me if I'm wrong, but doesn't Boost.Python when generating Python methods that accept ints, automatically call __int__ on the arguments. Doesn't SWIG do the same? Can you give me some examples of methods that accept ints but don't call __int__? It seems to me to be a bug in those methods than in the caller. A method that wants an int should call __int__ on its argument — not expect that isinstance(X, int) etc.
-- ~Ethan~
_______________________________________________
Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Sun, Mar 8, 2015, at 05:45, Neil Girdhar wrote:
If flags were conceptually subtypes of int, then you should be able to do things like:
flags ** 7
or
flags // 91
*cough* bool *cough*
Do you agree that this is totally meaningless? There is no "is a" relationship between Flags and int. There is a conversion between the conceptual mapping that is a Flags object to int for the sole purpose of calling into APIs.
People have been discussing bool since it was introduced. Many people have proposed that bool should not subclass int exactly for this reason (the Liskov substitution principle), but unfortunately bool evolved from int in Python 2.2.1 (I think?) when there was already a lot of code using int to implement Boolean variables with constants like False, True = 0, 1. In order not to break too much code, it was decided to allow bool to inherit from int. The inheritance of bool from int is not a good precedent when making inheritance decisions in the future. On Mon, Mar 9, 2015 at 9:49 AM, <random832@fastmail.us> wrote:
On Sun, Mar 8, 2015, at 05:45, Neil Girdhar wrote:
If flags were conceptually subtypes of int, then you should be able to do things like:
flags ** 7
or
flags // 91
*cough* bool *cough*
Do you agree that this is totally meaningless? There is no "is a" relationship between Flags and int. There is a conversion between the conceptual mapping that is a Flags object to int for the sole purpose of calling into APIs.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On Mon, Mar 9, 2015, at 10:00, Neil Girdhar wrote:
People have been discussing bool since it was introduced. Many people have proposed that bool should not subclass int exactly for this reason (the Liskov substitution principle), but unfortunately bool evolved from int in Python 2.2.1 (I think?) when there was already a lot of code using int to implement Boolean variables with constants like False, True = 0, 1. In order not to break too much code, it was decided to allow bool to inherit from int. The inheritance of bool from int is not a good precedent when making inheritance decisions in the future.
And we're already using int in all these places people are talking about dropping this new IntFlags class into. The situations seem exactly analogous to me.
The differences are that the flags are used in a tiny portion of Python code, and of that tiny amount of code, 99% of it probably uses nothing more than the minimum "flags interface" that the composition solution has. bool was not like that. Changing bool to be its own type would led to many more problems. On Mon, Mar 9, 2015 at 11:01 AM, <random832@fastmail.us> wrote:
On Mon, Mar 9, 2015, at 10:00, Neil Girdhar wrote:
People have been discussing bool since it was introduced. Many people have proposed that bool should not subclass int exactly for this reason (the Liskov substitution principle), but unfortunately bool evolved from int in Python 2.2.1 (I think?) when there was already a lot of code using int to implement Boolean variables with constants like False, True = 0, 1. In order not to break too much code, it was decided to allow bool to inherit from int. The inheritance of bool from int is not a good precedent when making inheritance decisions in the future.
And we're already using int in all these places people are talking about dropping this new IntFlags class into. The situations seem exactly analogous to me. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
--
--- You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/L5KfCEXFaII/unsubscribe. To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
On 10 March 2015 at 01:54, Neil Girdhar <mistersheik@gmail.com> wrote:
The differences are that the flags are used in a tiny portion of Python code, and of that tiny amount of code, 99% of it probably uses nothing more than the minimum "flags interface" that the composition solution has. bool was not like that. Changing bool to be its own type would led to many more problems.
The flag values in modules like "stat" and "socket" are not niche use cases where we can be cavalier with backwards compatibility concerns, as they get passed to operating system level APIs both inside and outside the standard library. Most application level code will never need to touch those modules, but system boundary code does it all the time. It's the exact same rationale as was used for adopting IntEnum in the socket and errno modules rather than the base Enum class. There's likely no need to get especially creative with the overall design here, as the 3.4 enum module should provide a good general architecture to follow, it's just the runtime behaviour that will be changing to be "ordered collection of combinatorial flags" rather than "ordered collection of named values". The main question in my mind is whether you might want named bitmask support as well, but that's probably not worth the extra complexity, since you don't need it for debugging purposes. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (21)
-
Alexander Heger -
Andrew Barnert -
Cameron Simpson -
Chris Angelico -
Chris Barker -
Chris Kaynor -
Ethan Furman -
Eugene Toder -
Florian Bruhin -
Georg Brandl -
Greg Ewing -
Joao S. O. Bueno -
Luciano Ramalho -
Neil Girdhar -
Nick Coghlan -
Paul Moore -
random832@fastmail.us -
Rob Cliffe -
Serhiy Storchaka -
Skip Montanaro -
Victor Stinner