A global style setup and usage bug via FormatCode, FormatFile[dev branch only]
See original GitHub issueShort Description:
If you would like to set up default style globally, you’re getting back DEFAULT_STYLE_FACTORY
that was defined as Pep8 (this is the reason why Pep8 style works well) for all possible pre-defined styles(google, chromium and etc…)
Long Description:
I hack on one project that uses yapf
and caught one interesting bug in last development version.
This function sets up a configuration of global style SetGlobalStyle().
It is being used by FormatCode
( FormatFile
uses it implicitly via FormatCode
call )
I found some tests that are using a global style’s setting from the previous test or setting up own style:
- https://github.com/google/yapf/blob/master/yapftests/yapf_test.py#L229
- https://github.com/google/yapf/blob/master/yapftests/yapf_test.py#L238
- https://github.com/google/yapf/blob/master/yapftests/yapf_test.py#L1130
In Format Code we have next string: Check yapf_api.py - https://github.com/google/yapf/blob/master/yapf/yapflib/yapf_api.py#L120
style.SetGlobalStyle(style.CreateStyleFromConfig(style_config))
The style_config
parameter defined like a string
or None
: https://github.com/google/yapf/blob/master/yapf/yapflib/yapf_api.py#L23 .
It has three options:
- pre-defined style by name: ‘pep8’, ‘google’ and etc
- path to a file with style
- None, use a DEFAULT_STYLE_FACTORY
And how it was ( https://github.com/google/yapf/blob/7e088293e4ea05bfd479258ae7748cd57e84a218/yapf/yapflib/style.py#L316 ):
if style_config is None:
return DEFAULT_STYLE_FACTORY()
Ok, but in last changes we got this :
Check: style.py - https://github.com/google/yapf/blob/master/yapf/yapflib/style.py#L320
styles = (CreatePEP8Style(), CreateGoogleStyle(),
CreateFacebookStyle(), CreateChromiumStyle())
def_style = False
if style_config is None:
for style in styles:
if _style == style:
def_style = True
break
if not def_style:
return _style
return DEFAULT_STYLE_FACTORY()
style_factory = _STYLE_NAME_TO_FACTORY.get(style_config.lower())
Check DEFAULT_STYLE_FACTORY: https://github.com/google/yapf/blob/master/yapf/yapflib/style.py#L419
# The default style - used if yapf is not invoked without specifically
# requesting a formatting style.
DEFAULT_STYLE = 'pep8'
DEFAULT_STYLE_FACTORY = CreatePEP8Style
(test for this part has number 3 This code, has next steps:
- firstly, does check style_config is
None
, Ok, we gotNone
(remember we set up style globally, it willNone
always, ) - Is the globally defined style in the list of default styles?
- Nope, we’ve defined our own style and returned a _style that references to our style. (def_style = False)
in first look all is good but it does not:
# what if I do something like this
style.SetGlobalStyle(style.CreateChromiumStyle())
yapf_api.FormatCode(<input_unformatted_code_here>, style_config=None)
# or like in the given test
yapf_api.FormatCode(<input_unformatted_code_here>, style_config= style.SetGlobalStyle(style.CreateChromiumStyle()))
both calls are equivalent to each other, but we get a DEFAULT_STYLE_FACTORY back!!!
(but first is much more readable and clear, because
setter
always returnsNone
and It sets the global state, and we do not confuse a caller about the callee’s arguments contract).
We get this because when we check _style
(a global style) it assigns to def_style a True value and breaking out from the loop. The second condition would be ignored and DEFAULT_STYLE_FACTORY is being returned back as a result of a callee.
# caller calls the method:
#yapf_api.FormatCode(<input_unformatted_code_here>,
# style_config= style.SetGlobalStyle(style.CreateChromiumStyle()))
styles = (CreatePEP8Style(), CreateGoogleStyle(),
CreateFacebookStyle(), CreateChromiumStyle())
def_style = False
if style_config is None: # style_config = None, but _style = CreateChromiumStyle()
for style in styles:
if _style == style: # ok we got this as the last element, it is really default style
def_style = True
break # get out from the loop
if not def_style: # def_style = True, but `not def_style` is a False
return _style # skip this step, our condition gets false
return DEFAULT_STYLE_FACTORY() # yeah, method returns DEFAULT_STYLE_FACTORY that's equal al CreatePEP8Style by default
style_factory = _STYLE_NAME_TO_FACTORY.get(style_config.lower())
In General, this code raises some questions like:
- Have tests been relevant for the current piece of code?
- Why was KISS principle ignored? ( excerpt was copy-pasted from ipython console)
In [3]: styles = (style.CreatePEP8Style(), style.CreateGoogleStyle(),
style.CreateFacebookStyle(), style.CreateChromiumStyle())
In [4]: style._style in styles
Out[4]: True
In [5]: if style._style in styles: print('true')
true
In [6]: style.SetGlobalStyle(style.CreateChromiumStyle())
In [7]: style._style == style.CreateChromiumStyle()
Out[7]: True
In [8]: style._style in styles
Out[8]: True
In [9]: if style._style in styles: print('true')
true
In [10]:
I rewrote this part in my fork and changed the behavior and callee’s contract.
if style_config equals None
it returns a global style( _style). On start _style equals a DEFAULT_STYLE_FACTORY. After that, we must apply next convention that appears as a caller-callee contract: either use a style_config or use a global style(via SetGlobalStyle) and style_config keep None
.
This approach has a positive and negative effects:
- I implemented a global
getter
because we have a globalsetter
by the module level. - A global
getter
is breaking an encapsulation of a global style, but it has broken already - you can change the global _style externally that is a global state change. And Yeah I’ve been reading eliben’s todo and I absolutely agree with that.
Issue Analytics
- State:
- Created 7 years ago
- Comments:11 (6 by maintainers)
From what I can see, it looks like we should be returning
_style
here instead ofDEFAULT_STYLE_FACTORY()
. Does this seem correct?@gwelymernans I commented your commit.