question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

A global style setup and usage bug via FormatCode, FormatFile[dev branch only]

See original GitHub issue

Short 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:

  1. https://github.com/google/yapf/blob/master/yapftests/yapf_test.py#L229
  2. https://github.com/google/yapf/blob/master/yapftests/yapf_test.py#L238
  3. 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:

  1. pre-defined style by name: ‘pep8’, ‘google’ and etc
  2. path to a file with style
  3. 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:

  1. firstly, does check style_config is None, Ok, we got None(remember we set up style globally, it will None always, )
  2. Is the globally defined style in the list of default styles?
  3. 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 returns None 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:

  1. I implemented a global getter because we have a global setter by the module level.
  2. 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:closed
  • Created 7 years ago
  • Comments:11 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
bwendlingcommented, Sep 17, 2016

From what I can see, it looks like we should be returning _style here instead of DEFAULT_STYLE_FACTORY(). Does this seem correct?

diff --git a/yapf/yapflib/style.py b/yapf/yapflib/style.py
index cebbc27..3ee6395 100644
--- a/yapf/yapflib/style.py
+++ b/yapf/yapflib/style.py
@@ -327,7 +327,7 @@ def CreateStyleFromConfig(style_config):
         break
     if not def_style:
         return _style
-    return DEFAULT_STYLE_FACTORY()
+    return _style
   style_factory = _STYLE_NAME_TO_FACTORY.get(style_config.lower())
   if style_factory is not None:
     return style_factory()
0reactions
rojastercommented, Oct 3, 2016

@gwelymernans I commented your commit.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Automatically format and lint code with pre-commit - Interrupt
How to use pre-commit for automated linting, formatting, and styling firmware code by using Python, clang-format, clang-tidy, and prettier.
Read more >
User and Workspace Settings - Visual Studio Code
To open the Settings editor, use the following VS Code menu command: On Windows/Linux - File > Preferences > Settings. On macOS -...
Read more >
Troubleshooting CI/CD - GitLab Docs
The pipeline shows a yaml invalid badge and does not start running if any syntax or formatting problems are found. Edit .gitlab-ci.yml with...
Read more >
How to Add Commit Hooks to Git with Husky to Automate ...
There are a lot of tools to automate our code tasks. We can check for syntax issues with ESLint and format our code...
Read more >
Advanced Git Log | Atlassian Git Tutorial
Advanced Git log. Formatting Log Output Filtering the Commit History Summary. The purpose of any version control system is to record changes to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found