You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(Apologies in advance if I'm not using the right terminology, and for the wall of text.)
I'd like to be able to add aliases for some of my constants. As an example, I'm writing a TFTP library, and the TFTP spec uses some terminology that might be unfamiliar to users, so I'd like to be able to alias the older terms to newer, more familiar terms, while still giving access to the old terms as well. There seem to be a couple ways to do this, but neither has the full behavior I want.
What I want to see is for each constant, regardless of whether it's an alias or not, to report itself in __repr__() (i.e. self.name) as the name it was declared with, but also for constants with the same value to report themselves as equal. I don't especially care about sorting order in this case, but it would make sense to me to order them according to their values rather than their declared order.
The first way to declare aliases is as follows:
classTransferMode(constantly.Values):
"""Representation of the different transfer modes. Also includes some convenient aliases. The various transfer modes are defined in RFC 1350. """NETASCII=constantly.ValueConstant("netascii")
OCTET=constantly.ValueConstant("octet")
MAIL=constantly.ValueConstant("mail")
ASCII=constantly.ValueConstant("netascii")
TEXT=constantly.ValueConstant("netascii")
BINARY=constantly.ValueConstant("octet")
This gives me the first part (each constant reports itself as the name it was declared with), but it doesn't give me the second part (that is, TransferMode.NETASCII == TransferMode.ASCII is False when I'd like it to be True).
The second way to do aliases is as follows:
classTransferMode(constantly.Values):
"""Representation of the different transfer modes. Also includes some convenient aliases. The various transfer modes are defined in RFC 1350. """NETASCII=constantly.ValueConstant("netascii")
OCTET=constantly.ValueConstant("octet")
MAIL=constantly.ValueConstant("mail")
ASCII=NETASCIITEXT=NETASCIIBINARY=OCTET
When I do this, I get TransferMode.NETASCII == TransferMode.ASCII like I want, but now NETASCII and ASCII report themselves as TEXT, when I would expect them to report themselves as NETASCII and ASCII, respectively. I don't see the same thing happen to OCTET after declaring BINARY = OCTET, but BINARY does report itself as OCTET. The sorting order seems to be kept the same and I can still look up all of the constants by name. Placing the declaration of BINARY a line before the declaration of TEXT doesn't change the sorting behavior, so it's not quite like the constant is being treated as though it were redefined where TEXT was defined.
TransferMode.iterconstants() reports all six declared constants, but with the wrong names for half of them.
Removing the declaration of TEXT reverts NETASCII to reporting itself as NETASCII, but now ASCII reports itself as NETASCII, too. It seems to indicate that the ValueConstant instance reports itself as whichever of its aliases is last in the English dictionary.
Flags similarly doesn't support aliasing, but it has an additional problem. Consider the following (highly simplified) example:
class Test(constantly.Flags):
A = constantly.FlagConstant(1)
B = constantly.FlagConstant(2)
C = constantly.FlagConstant(1)
This is definitely not what should happen. Even ignoring the weird naming, Test.A ^ Test.C should be <Test={}>, not <Test={A,C}>! Those flags are no longer set! Using the second declaration style has correct bitwise results here, but it still has the same problems as ValueConstants.
It's also bad when one of the constants is a combination of other flags.
class Test(constantly.Flags):
A = constantly.FlagConstant(1)
B = constantly.FlagConstant(2)
C = constantly.FlagConstant(3)
~Test.B behaves properly now, and Test.A | Test.C and Test.A ^ Test.C behave like the last example. We can't even get there with the second declaration style, though, as declaring C = A | B complains AttributeError: 'FlagConstant' object has no attribute 'names' because A and B haven't been properly set up yet.
Regarding an actual solution to this, I think the easiest way to implement this for ValueConstants would be to redefine the comparison operators to compare self.value rather than self._index. That would sufficiently allow aliases using the first declaration method, though it would change the comparison ordering (IMO to something that makes better sense, but it is a noticeable interface change). It also still leaves the aliases listed in iterconstants(), which isn't ideal.
Because FlagConstants also have a value attribute, that implementation would begin to apply to them, too, in the most basic cases, but it still has the problem with bitwise operations. It also doesn't work very well if you're creating an alias representing multiple flags being set. Making aliases work properly for FlagConstant may require some overhaul of its internals.
NamedConstants don't have an associated value, so you'd have to add one to properly mimic the equality functionality. That could probably be as simple as a new alias_of() function (or class?) that copies the original constant's _index to the new alias. Equality checks would then compare _index first. This would also group all aliases of the same constant together when iterating over the class, and also give a good way to filter out aliases, which can apply to the other Constant types as well.
How feasible is this to add? The current behavior leaves a bit to be desired.
The text was updated successfully, but these errors were encountered:
(Apologies in advance if I'm not using the right terminology, and for the wall of text.)
I'd like to be able to add aliases for some of my constants. As an example, I'm writing a TFTP library, and the TFTP spec uses some terminology that might be unfamiliar to users, so I'd like to be able to alias the older terms to newer, more familiar terms, while still giving access to the old terms as well. There seem to be a couple ways to do this, but neither has the full behavior I want.
What I want to see is for each constant, regardless of whether it's an alias or not, to report itself in
__repr__()
(i.e.self.name
) as the name it was declared with, but also for constants with the same value to report themselves as equal. I don't especially care about sorting order in this case, but it would make sense to me to order them according to their values rather than their declared order.The first way to declare aliases is as follows:
This gives me the first part (each constant reports itself as the name it was declared with), but it doesn't give me the second part (that is,
TransferMode.NETASCII == TransferMode.ASCII
is False when I'd like it to be True).The second way to do aliases is as follows:
When I do this, I get
TransferMode.NETASCII == TransferMode.ASCII
like I want, but nowNETASCII
andASCII
report themselves asTEXT
, when I would expect them to report themselves asNETASCII
andASCII
, respectively. I don't see the same thing happen toOCTET
after declaringBINARY = OCTET
, butBINARY
does report itself asOCTET
. The sorting order seems to be kept the same and I can still look up all of the constants by name. Placing the declaration ofBINARY
a line before the declaration ofTEXT
doesn't change the sorting behavior, so it's not quite like the constant is being treated as though it were redefined whereTEXT
was defined.TransferMode.iterconstants()
reports all six declared constants, but with the wrong names for half of them.Removing the declaration of
TEXT
revertsNETASCII
to reporting itself asNETASCII
, but nowASCII
reports itself asNETASCII
, too. It seems to indicate that theValueConstant
instance reports itself as whichever of its aliases is last in the English dictionary.Flags
similarly doesn't support aliasing, but it has an additional problem. Consider the following (highly simplified) example:This is definitely not what should happen. Even ignoring the weird naming,
Test.A ^ Test.C
should be<Test={}>
, not<Test={A,C}>
! Those flags are no longer set! Using the second declaration style has correct bitwise results here, but it still has the same problems asValueConstant
s.It's also bad when one of the constants is a combination of other flags.
~Test.B
behaves properly now, andTest.A | Test.C
andTest.A ^ Test.C
behave like the last example. We can't even get there with the second declaration style, though, as declaringC = A | B
complainsAttributeError: 'FlagConstant' object has no attribute 'names'
becauseA
andB
haven't been properly set up yet.Regarding an actual solution to this, I think the easiest way to implement this for
ValueConstant
s would be to redefine the comparison operators to compareself.value
rather thanself._index
. That would sufficiently allow aliases using the first declaration method, though it would change the comparison ordering (IMO to something that makes better sense, but it is a noticeable interface change). It also still leaves the aliases listed initerconstants()
, which isn't ideal.Because
FlagConstant
s also have avalue
attribute, that implementation would begin to apply to them, too, in the most basic cases, but it still has the problem with bitwise operations. It also doesn't work very well if you're creating an alias representing multiple flags being set. Making aliases work properly forFlagConstant
may require some overhaul of its internals.NamedConstant
s don't have an associated value, so you'd have to add one to properly mimic the equality functionality. That could probably be as simple as a newalias_of()
function (or class?) that copies the original constant's_index
to the new alias. Equality checks would then compare_index
first. This would also group all aliases of the same constant together when iterating over the class, and also give a good way to filter out aliases, which can apply to the otherConstant
types as well.How feasible is this to add? The current behavior leaves a bit to be desired.
The text was updated successfully, but these errors were encountered: