This practice leads to errors in cases like:
class C:
_1: int # Already mentioned in your docs.
x: int
_x: int # SyntaxError: duplicate argument 'x' in function definition.
Can you explain to me what is the use case for stripping the underscores. The altered name only appears, as far as I can tell, as a parameter name in the generated init method.
EDIT: attrs.evolve is affected, also:
@attrs.define
class C:
_x: int
__y: int
@attrs.define
class D:
__x__: int
evolve(C(), ...) needs keywords
-
-
C__y. The class def replaces __y with _C__y before it gets to attrs.define. evolve removes the leading underscore from the attribute name.
- BUG:
evolve(D(), ... is impossible with any keywords. The compiler doesn't mangle dunder names, so the attribute is D.__x__. The generated __init__ has parameter x__ because define() stripped both underscores from the parameter name. evolve() only strips the first underscore, and so tries to call `D.init(x_=something).
- To fix this bug,
evolve() should remove all leading underscores. Replace _funcs.py line 366
init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
with
init_name = lstrip(attr_name, "_")
If not, then I consider this a bug and the behavior should be removed. Or deprecated, at least.
If you are going to deprecate it, then I would add a keyword to define() and field(), called (for instance) keep_unders with a default of False. A True value will generate __init__ or __attrs_init__ using the actual attribute name without stripping. Any call to the class constructor will use the unstripped name. After the deprecation period, keep_unders will be ignored and treated as True. This parameter can be deprecated and removed in a later version.
This practice leads to errors in cases like:
Can you explain to me what is the use case for stripping the underscores. The altered name only appears, as far as I can tell, as a parameter name in the generated init method.
EDIT:
attrs.evolveis affected, also:evolve(C(), ...)needs keywordsx.C__y. The class def replaces__ywith_C__ybefore it gets toattrs.define.evolveremoves the leading underscore from the attribute name.evolve(D(), ...is impossible with any keywords. The compiler doesn't mangle dunder names, so the attribute isD.__x__. The generated__init__has parameterx__becausedefine()stripped both underscores from the parameter name.evolve()only strips the first underscore, and so tries to call `D.init(x_=something).evolve()should remove all leading underscores. Replace _funcs.py line 366init_name = attr_name if attr_name[0] != "_" else attr_name[1:]with
init_name = lstrip(attr_name, "_")If not, then I consider this a bug and the behavior should be removed. Or deprecated, at least.
If you are going to deprecate it, then I would add a keyword to
define()andfield(), called (for instance)keep_underswith a default of False. A True value will generate__init__or__attrs_init__using the actual attribute name without stripping. Any call to the class constructor will use the unstripped name. After the deprecation period,keep_underswill be ignored and treated as True. This parameter can be deprecated and removed in a later version.