We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
dict type options in config files
One can also put a dict value into a config file (please add to http://www.pantsbuild.org/options.html):

    [scope]
    config: {
        "domain": "example.com",
      }

This is not well handled though:
```shell
./pants options --options-scope=scope --options-name=config --options-output-format=json
{
  "scope.config": {
    "source": "CONFIG", 
    "value": "{\"domain\": \"example.com\",}"
  }
}
```
expected:
```shell
./pants options --options-scope=scope --options-name=config --options-output-format=json
{
  "scope.config": {
    "source": "CONFIG", 
    "value": {
      "domain": "example.com"
    }
  }
}
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/python/pants/core_tasks/explain_options_task.py`
Content:
```
1 # coding=utf-8
2 # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
3 # Licensed under the Apache License, Version 2.0 (see LICENSE).
4 
5 from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
6                         unicode_literals, with_statement)
7 
8 import json
9 
10 from colors import black, blue, cyan, green, magenta, red, white
11 from packaging.version import Version
12 
13 from pants.option.ranked_value import RankedValue
14 from pants.task.console_task import ConsoleTask
15 from pants.version import PANTS_SEMVER
16 
17 
18 class ExplainOptionsTask(ConsoleTask):
19   """Display meta-information about options.
20 
21   This "meta-information" includes what values options have, and what values they *used* to have
22   before they were overridden by a higher-rank value (eg, a HARDCODED value overridden by a CONFIG
23   value and then a cli FLAG value).
24   """
25 
26   @classmethod
27   def register_options(cls, register):
28     super(ExplainOptionsTask, cls).register_options(register)
29     register('--scope', help='Only show options in this scope.')
30     register('--name', help='Only show options with this name.')
31     register('--rank', choices=RankedValue.get_names(),
32              help='Only show options with at least this importance.')
33     register('--show-history', type=bool,
34              help='Show the previous values options had before being overridden.')
35     register('--only-overridden', type=bool,
36              help='Only show values that overrode defaults.')
37     register('--skip-inherited', type=bool, default=True,
38              help='Do not show inherited options, unless their values differ from their parents.')
39     register('--output-format', choices=['text', 'json'], default='text',
40              help='Specify the format options will be printed.')
41 
42   def _scope_filter(self, scope):
43     pattern = self.get_options().scope
44     return not pattern or scope.startswith(pattern)
45 
46   def _option_filter(self, option):
47     pattern = self.get_options().name
48     if not pattern:
49       return True
50     pattern = pattern.replace('-', '_')
51     return option == pattern
52 
53   def _rank_filter(self, rank):
54     pattern = self.get_options().rank
55     if not pattern:
56       return True
57     return rank >= RankedValue.get_rank_value(pattern)
58 
59   def _rank_color(self, rank):
60     if not self.get_options().colors:
61       return lambda x: x
62     if rank == RankedValue.NONE: return white
63     if rank == RankedValue.HARDCODED: return white
64     if rank == RankedValue.ENVIRONMENT: return red
65     if rank == RankedValue.CONFIG: return blue
66     if rank == RankedValue.FLAG: return magenta
67     return black
68 
69   def _format_scope(self, scope, option, no_color=False):
70     if no_color:
71       return '{scope}{option}'.format(
72         scope='{}.'.format(scope) if scope else '',
73         option=option,
74       )
75     scope_color = cyan if self.get_options().colors else lambda x: x
76     option_color = blue if self.get_options().colors else lambda x: x
77     return '{scope}{option}'.format(
78       scope=scope_color('{}.'.format(scope) if scope else ''),
79       option=option_color(option),
80     )
81 
82   def _format_record(self, record):
83     value_color = green if self.get_options().colors else lambda x: x
84     rank_color = self._rank_color(record.rank)
85     simple_value = str(record.value)
86     formatted_value = value_color(simple_value)
87     simple_rank = RankedValue.get_rank_name(record.rank)
88     formatted_rank = '(from {rank}{details})'.format(
89       rank=simple_rank,
90       details=rank_color(' {}'.format(record.details)) if record.details else '',
91     )
92     if self.is_json():
93       return simple_value.replace('\n', ''), simple_rank
94     elif self.is_text():
95       return '{value} {rank}'.format(
96         value=formatted_value,
97         rank=formatted_rank,
98       )
99 
100   def _show_history(self, history):
101     for record in reversed(list(history)[:-1]):
102       if record.rank > RankedValue.NONE:
103         yield '  overrode {}'.format(self._format_record(record))
104 
105   def _force_option_parsing(self):
106     scopes = filter(self._scope_filter, list(self.context.options.known_scope_to_info.keys()))
107     for scope in scopes:
108       self.context.options.for_scope(scope)
109 
110   def _get_parent_scope_option(self, scope, name):
111     if not scope:
112       return None, None
113     parent_scope = ''
114     if '.' in scope:
115       parent_scope, _ = scope.rsplit('.', 1)
116     options = self.context.options.for_scope(parent_scope)
117     try:
118       return parent_scope, options[name]
119     except AttributeError:
120       return None, None
121 
122   def is_json(self):
123     return self.get_options().output_format == 'json'
124 
125   def is_text(self):
126     return self.get_options().output_format == 'text'
127 
128   def console_output(self, targets):
129     self._force_option_parsing()
130     if self.is_json():
131       output_map = {}
132     for scope, options in sorted(self.context.options.tracker.option_history_by_scope.items()):
133       if not self._scope_filter(scope):
134         continue
135       for option, history in sorted(options.items()):
136         if not self._option_filter(option):
137           continue
138         if not self._rank_filter(history.latest.rank):
139           continue
140         if self.get_options().only_overridden and not history.was_overridden:
141           continue
142         # Skip the option if it has already passed the deprecation period.
143         if history.latest.deprecation_version and PANTS_SEMVER >= Version(
144           history.latest.deprecation_version):
145           continue
146         if self.get_options().skip_inherited:
147           parent_scope, parent_value = self._get_parent_scope_option(scope, option)
148           if parent_scope is not None and parent_value == history.latest.value:
149             continue
150         if self.is_json():
151           opt_vals = self._format_record(history.latest)
152           scope_key = self._format_scope(scope, option, True)
153           inner_map = dict(value=opt_vals[0], source=opt_vals[1])
154           output_map[scope_key] = inner_map
155         elif self.is_text():
156           yield '{} = {}'.format(self._format_scope(scope, option),
157                                  self._format_record(history.latest))
158         if self.get_options().show_history:
159           history_list = []
160           for line in self._show_history(history):
161             if self.is_text():
162               yield line
163             elif self.is_json():
164               history_list.append(line.strip())
165           if self.is_json():
166             inner_map["history"] = history_list
167     if self.is_json():
168       yield json.dumps(output_map, indent=2, sort_keys=True)
169
```

Path: `src/python/pants/backend/jvm/tasks/scalafmt.py`
Content:
```
1 # coding=utf-8
2 # Copyright 2016 Pants project contributors (see CONTRIBUTORS.md).
3 # Licensed under the Apache License, Version 2.0 (see LICENSE).
4 
5 from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
6                         unicode_literals, with_statement)
7 
8 from abc import abstractproperty
9 
10 from pants.backend.jvm.tasks.nailgun_task import NailgunTask
11 from pants.base.exceptions import TaskError
12 from pants.java.jar.jar_dependency import JarDependency
13 from pants.option.custom_types import file_option
14 from pants.util.memo import memoized_property
15 from pants.util.meta import AbstractClass
16 
17 
18 class ScalaFmt(NailgunTask, AbstractClass):
19   """Abstract class to run ScalaFmt commands.
20 
21   Classes that inherit from this should override get_command_args and
22   process_results to run different scalafmt commands
23 
24   :API: public
25   """
26   _SCALAFMT_MAIN = 'org.scalafmt.cli.Cli'
27   _SCALA_SOURCE_EXTENSION = '.scala'
28 
29   @classmethod
30   def register_options(cls, register):
31     super(ScalaFmt, cls).register_options(register)
32     register('--skip', type=bool, fingerprint=False, help='Skip Scalafmt Check')
33     register('--configuration', advanced=True, type=file_option, fingerprint=False,
34               help='Path to scalafmt config file, if not specified default scalafmt config used')
35     register('--target-types',
36              default={'scala_library', 'junit_tests', 'java_tests'},
37              advanced=True,
38              type=set,
39              help='The target types to apply formatting to.')
40     cls.register_jvm_tool(register,
41                           'scalafmt',
42                           classpath=[
43                           JarDependency(org='com.geirsson',
44                                         name='scalafmt-cli_2.11',
45                                         rev='0.2.11')
46                           ])
47 
48   @memoized_property
49   def _formatted_target_types(self):
50     aliases = self.get_options().target_types
51     registered_aliases = self.context.build_file_parser.registered_aliases()
52     return tuple({target_type
53                   for alias in aliases
54                   for target_type in registered_aliases.target_types_by_alias[alias]})
55 
56   def execute(self):
57     """Runs Scalafmt on all found Scala Source Files."""
58     if self.get_options().skip:
59       return
60 
61     targets = self.get_non_synthetic_scala_targets(self.context.targets())
62     sources = self.calculate_sources(targets)
63 
64     if sources:
65       files = ",".join(sources)
66 
67       result = self.runjava(classpath=self.tool_classpath('scalafmt'),
68                    main=self._SCALAFMT_MAIN,
69                    args=self.get_command_args(files),
70                    workunit_name='scalafmt')
71 
72       self.process_results(result)
73 
74   @abstractproperty
75   def get_command_args(self, files):
76     """Returns the arguments used to run Scalafmt command.
77 
78     The return value should be an array of strings.  For
79     example, to run the Scalafmt help command:
80     ['--help']
81     """
82 
83   @abstractproperty
84   def process_results(self, result):
85     """This method processes the results of the scalafmt command.
86 
87     No return value is expected.  If an error occurs running
88     Scalafmt raising a TaskError is recommended.
89     """
90 
91   def get_non_synthetic_scala_targets(self, targets):
92     return filter(
93       lambda target: isinstance(target, self._formatted_target_types)
94                      and target.has_sources(self._SCALA_SOURCE_EXTENSION)
95                      and (not target.is_synthetic),
96       targets)
97 
98   def calculate_sources(self, targets):
99     sources = set()
100     for target in targets:
101       sources.update(source for source in target.sources_relative_to_buildroot()
102                       if source.endswith(self._SCALA_SOURCE_EXTENSION))
103     return sources
104 
105 
106 class ScalaFmtCheckFormat(ScalaFmt):
107   """This Task checks that all scala files in the target are formatted
108   correctly.
109 
110   If the files are not formatted correctly an error is raised
111   including the command to run to format the files correctly
112 
113   :API: public
114   """
115   deprecated_options_scope = 'compile.scalafmt'
116   deprecated_options_scope_removal_version = '1.5.0.dev0'
117 
118   def get_command_args(self, files):
119     # If no config file is specified use default scalafmt config.
120     config_file = self.get_options().configuration
121     args = ['--test', '--files', files]
122     if config_file!= None:
123       args.extend(['--config', config_file])
124 
125     return args
126 
127   def process_results(self, result):
128     if result != 0:
129       raise TaskError('Scalafmt failed with exit code {}; to fix run: '
130                       '`./pants fmt <targets>`'.format(result), exit_code=result)
131 
132 
133 class ScalaFmtFormat(ScalaFmt):
134   """This Task reads all scala files in the target and emits
135   the source in a standard style as specified by the configuration
136   file.
137 
138   This task mutates the underlying flies.
139 
140   :API: public
141   """
142 
143   def get_command_args(self, files):
144     # If no config file is specified use default scalafmt config.
145     config_file = self.get_options().configuration
146     args = ['-i', '--files', files]
147     if config_file!= None:
148       args.extend(['--config', config_file])
149 
150     return args
151 
152   def process_results(self, result):
153     # Processes the results of running the scalafmt command.
154     if result != 0:
155       raise TaskError('Scalafmt failed to format files', exit_code=result)
156
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/python/pants/backend/jvm/tasks/scalafmt.py b/src/python/pants/backend/jvm/tasks/scalafmt.py
--- a/src/python/pants/backend/jvm/tasks/scalafmt.py
+++ b/src/python/pants/backend/jvm/tasks/scalafmt.py
@@ -33,9 +33,9 @@
     register('--configuration', advanced=True, type=file_option, fingerprint=False,
               help='Path to scalafmt config file, if not specified default scalafmt config used')
     register('--target-types',
-             default={'scala_library', 'junit_tests', 'java_tests'},
+             default=['scala_library', 'junit_tests', 'java_tests'],
              advanced=True,
-             type=set,
+             type=list,
              help='The target types to apply formatting to.')
     cls.register_jvm_tool(register,
                           'scalafmt',
@@ -47,7 +47,7 @@
 
   @memoized_property
   def _formatted_target_types(self):
-    aliases = self.get_options().target_types
+    aliases = set(self.get_options().target_types)
     registered_aliases = self.context.build_file_parser.registered_aliases()
     return tuple({target_type
                   for alias in aliases
diff --git a/src/python/pants/core_tasks/explain_options_task.py b/src/python/pants/core_tasks/explain_options_task.py
--- a/src/python/pants/core_tasks/explain_options_task.py
+++ b/src/python/pants/core_tasks/explain_options_task.py
@@ -80,18 +80,18 @@
     )
 
   def _format_record(self, record):
-    value_color = green if self.get_options().colors else lambda x: x
-    rank_color = self._rank_color(record.rank)
-    simple_value = str(record.value)
-    formatted_value = value_color(simple_value)
     simple_rank = RankedValue.get_rank_name(record.rank)
-    formatted_rank = '(from {rank}{details})'.format(
-      rank=simple_rank,
-      details=rank_color(' {}'.format(record.details)) if record.details else '',
-    )
     if self.is_json():
-      return simple_value.replace('\n', ''), simple_rank
+      return record.value, simple_rank
     elif self.is_text():
+      simple_value = str(record.value)
+      value_color = green if self.get_options().colors else lambda x: x
+      formatted_value = value_color(simple_value)
+      rank_color = self._rank_color(record.rank)
+      formatted_rank = '(from {rank}{details})'.format(
+        rank=simple_rank,
+        details=rank_color(' {}'.format(record.details)) if record.details else '',
+      )
       return '{value} {rank}'.format(
         value=formatted_value,
         rank=formatted_rank,
@@ -148,9 +148,16 @@
           if parent_scope is not None and parent_value == history.latest.value:
             continue
         if self.is_json():
-          opt_vals = self._format_record(history.latest)
+          value, rank_name = self._format_record(history.latest)
           scope_key = self._format_scope(scope, option, True)
-          inner_map = dict(value=opt_vals[0], source=opt_vals[1])
+          # We rely on the fact that option values are restricted to a set of types compatible with
+          # json. In particular, we expect dict, list, str, bool, int and float, and so do no
+          # processing here.
+          # TODO(John Sirois): The option parsing system currently lets options of unexpected types
+          # slide by, which can lead to un-overridable values and which would also blow up below in
+          # json encoding, fix options to restrict the allowed `type`s:
+          #   https://github.com/pantsbuild/pants/issues/4695
+          inner_map = dict(value=value, source=rank_name)
           output_map[scope_key] = inner_map
         elif self.is_text():
           yield '{} = {}'.format(self._format_scope(scope, option),

{"golden_diff": "diff --git a/src/python/pants/backend/jvm/tasks/scalafmt.py b/src/python/pants/backend/jvm/tasks/scalafmt.py\n--- a/src/python/pants/backend/jvm/tasks/scalafmt.py\n+++ b/src/python/pants/backend/jvm/tasks/scalafmt.py\n@@ -33,9 +33,9 @@\n     register('--configuration', advanced=True, type=file_option, fingerprint=False,\n               help='Path to scalafmt config file, if not specified default scalafmt config used')\n     register('--target-types',\n-             default={'scala_library', 'junit_tests', 'java_tests'},\n+             default=['scala_library', 'junit_tests', 'java_tests'],\n              advanced=True,\n-             type=set,\n+             type=list,\n              help='The target types to apply formatting to.')\n     cls.register_jvm_tool(register,\n                           'scalafmt',\n@@ -47,7 +47,7 @@\n \n   @memoized_property\n   def _formatted_target_types(self):\n-    aliases = self.get_options().target_types\n+    aliases = set(self.get_options().target_types)\n     registered_aliases = self.context.build_file_parser.registered_aliases()\n     return tuple({target_type\n                   for alias in aliases\ndiff --git a/src/python/pants/core_tasks/explain_options_task.py b/src/python/pants/core_tasks/explain_options_task.py\n--- a/src/python/pants/core_tasks/explain_options_task.py\n+++ b/src/python/pants/core_tasks/explain_options_task.py\n@@ -80,18 +80,18 @@\n     )\n \n   def _format_record(self, record):\n-    value_color = green if self.get_options().colors else lambda x: x\n-    rank_color = self._rank_color(record.rank)\n-    simple_value = str(record.value)\n-    formatted_value = value_color(simple_value)\n     simple_rank = RankedValue.get_rank_name(record.rank)\n-    formatted_rank = '(from {rank}{details})'.format(\n-      rank=simple_rank,\n-      details=rank_color(' {}'.format(record.details)) if record.details else '',\n-    )\n     if self.is_json():\n-      return simple_value.replace('\\n', ''), simple_rank\n+      return record.value, simple_rank\n     elif self.is_text():\n+      simple_value = str(record.value)\n+      value_color = green if self.get_options().colors else lambda x: x\n+      formatted_value = value_color(simple_value)\n+      rank_color = self._rank_color(record.rank)\n+      formatted_rank = '(from {rank}{details})'.format(\n+        rank=simple_rank,\n+        details=rank_color(' {}'.format(record.details)) if record.details else '',\n+      )\n       return '{value} {rank}'.format(\n         value=formatted_value,\n         rank=formatted_rank,\n@@ -148,9 +148,16 @@\n           if parent_scope is not None and parent_value == history.latest.value:\n             continue\n         if self.is_json():\n-          opt_vals = self._format_record(history.latest)\n+          value, rank_name = self._format_record(history.latest)\n           scope_key = self._format_scope(scope, option, True)\n-          inner_map = dict(value=opt_vals[0], source=opt_vals[1])\n+          # We rely on the fact that option values are restricted to a set of types compatible with\n+          # json. In particular, we expect dict, list, str, bool, int and float, and so do no\n+          # processing here.\n+          # TODO(John Sirois): The option parsing system currently lets options of unexpected types\n+          # slide by, which can lead to un-overridable values and which would also blow up below in\n+          # json encoding, fix options to restrict the allowed `type`s:\n+          #   https://github.com/pantsbuild/pants/issues/4695\n+          inner_map = dict(value=value, source=rank_name)\n           output_map[scope_key] = inner_map\n         elif self.is_text():\n           yield '{} = {}'.format(self._format_scope(scope, option),\n", "issue": "dict type options in config files\nOne can also put a dict value into a config file (please add to http://www.pantsbuild.org/options.html):\r\n\r\n    [scope]\r\n    config: {\r\n        \"domain\": \"example.com\",\r\n      }\r\n\r\nThis is not well handled though:\r\n```shell\r\n./pants options --options-scope=scope --options-name=config --options-output-format=json\r\n{\r\n  \"scope.config\": {\r\n    \"source\": \"CONFIG\", \r\n    \"value\": \"{\\\"domain\\\": \\\"example.com\\\",}\"\r\n  }\r\n}\r\n```\r\nexpected:\r\n```shell\r\n./pants options --options-scope=scope --options-name=config --options-output-format=json\r\n{\r\n  \"scope.config\": {\r\n    \"source\": \"CONFIG\", \r\n    \"value\": {\r\n      \"domain\": \"example.com\"\r\n    }\r\n  }\r\n}\r\n```\r\n\n", "before_files": [{"content": "# coding=utf-8\n# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport json\n\nfrom colors import black, blue, cyan, green, magenta, red, white\nfrom packaging.version import Version\n\nfrom pants.option.ranked_value import RankedValue\nfrom pants.task.console_task import ConsoleTask\nfrom pants.version import PANTS_SEMVER\n\n\nclass ExplainOptionsTask(ConsoleTask):\n  \"\"\"Display meta-information about options.\n\n  This \"meta-information\" includes what values options have, and what values they *used* to have\n  before they were overridden by a higher-rank value (eg, a HARDCODED value overridden by a CONFIG\n  value and then a cli FLAG value).\n  \"\"\"\n\n  @classmethod\n  def register_options(cls, register):\n    super(ExplainOptionsTask, cls).register_options(register)\n    register('--scope', help='Only show options in this scope.')\n    register('--name', help='Only show options with this name.')\n    register('--rank', choices=RankedValue.get_names(),\n             help='Only show options with at least this importance.')\n    register('--show-history', type=bool,\n             help='Show the previous values options had before being overridden.')\n    register('--only-overridden', type=bool,\n             help='Only show values that overrode defaults.')\n    register('--skip-inherited', type=bool, default=True,\n             help='Do not show inherited options, unless their values differ from their parents.')\n    register('--output-format', choices=['text', 'json'], default='text',\n             help='Specify the format options will be printed.')\n\n  def _scope_filter(self, scope):\n    pattern = self.get_options().scope\n    return not pattern or scope.startswith(pattern)\n\n  def _option_filter(self, option):\n    pattern = self.get_options().name\n    if not pattern:\n      return True\n    pattern = pattern.replace('-', '_')\n    return option == pattern\n\n  def _rank_filter(self, rank):\n    pattern = self.get_options().rank\n    if not pattern:\n      return True\n    return rank >= RankedValue.get_rank_value(pattern)\n\n  def _rank_color(self, rank):\n    if not self.get_options().colors:\n      return lambda x: x\n    if rank == RankedValue.NONE: return white\n    if rank == RankedValue.HARDCODED: return white\n    if rank == RankedValue.ENVIRONMENT: return red\n    if rank == RankedValue.CONFIG: return blue\n    if rank == RankedValue.FLAG: return magenta\n    return black\n\n  def _format_scope(self, scope, option, no_color=False):\n    if no_color:\n      return '{scope}{option}'.format(\n        scope='{}.'.format(scope) if scope else '',\n        option=option,\n      )\n    scope_color = cyan if self.get_options().colors else lambda x: x\n    option_color = blue if self.get_options().colors else lambda x: x\n    return '{scope}{option}'.format(\n      scope=scope_color('{}.'.format(scope) if scope else ''),\n      option=option_color(option),\n    )\n\n  def _format_record(self, record):\n    value_color = green if self.get_options().colors else lambda x: x\n    rank_color = self._rank_color(record.rank)\n    simple_value = str(record.value)\n    formatted_value = value_color(simple_value)\n    simple_rank = RankedValue.get_rank_name(record.rank)\n    formatted_rank = '(from {rank}{details})'.format(\n      rank=simple_rank,\n      details=rank_color(' {}'.format(record.details)) if record.details else '',\n    )\n    if self.is_json():\n      return simple_value.replace('\\n', ''), simple_rank\n    elif self.is_text():\n      return '{value} {rank}'.format(\n        value=formatted_value,\n        rank=formatted_rank,\n      )\n\n  def _show_history(self, history):\n    for record in reversed(list(history)[:-1]):\n      if record.rank > RankedValue.NONE:\n        yield '  overrode {}'.format(self._format_record(record))\n\n  def _force_option_parsing(self):\n    scopes = filter(self._scope_filter, list(self.context.options.known_scope_to_info.keys()))\n    for scope in scopes:\n      self.context.options.for_scope(scope)\n\n  def _get_parent_scope_option(self, scope, name):\n    if not scope:\n      return None, None\n    parent_scope = ''\n    if '.' in scope:\n      parent_scope, _ = scope.rsplit('.', 1)\n    options = self.context.options.for_scope(parent_scope)\n    try:\n      return parent_scope, options[name]\n    except AttributeError:\n      return None, None\n\n  def is_json(self):\n    return self.get_options().output_format == 'json'\n\n  def is_text(self):\n    return self.get_options().output_format == 'text'\n\n  def console_output(self, targets):\n    self._force_option_parsing()\n    if self.is_json():\n      output_map = {}\n    for scope, options in sorted(self.context.options.tracker.option_history_by_scope.items()):\n      if not self._scope_filter(scope):\n        continue\n      for option, history in sorted(options.items()):\n        if not self._option_filter(option):\n          continue\n        if not self._rank_filter(history.latest.rank):\n          continue\n        if self.get_options().only_overridden and not history.was_overridden:\n          continue\n        # Skip the option if it has already passed the deprecation period.\n        if history.latest.deprecation_version and PANTS_SEMVER >= Version(\n          history.latest.deprecation_version):\n          continue\n        if self.get_options().skip_inherited:\n          parent_scope, parent_value = self._get_parent_scope_option(scope, option)\n          if parent_scope is not None and parent_value == history.latest.value:\n            continue\n        if self.is_json():\n          opt_vals = self._format_record(history.latest)\n          scope_key = self._format_scope(scope, option, True)\n          inner_map = dict(value=opt_vals[0], source=opt_vals[1])\n          output_map[scope_key] = inner_map\n        elif self.is_text():\n          yield '{} = {}'.format(self._format_scope(scope, option),\n                                 self._format_record(history.latest))\n        if self.get_options().show_history:\n          history_list = []\n          for line in self._show_history(history):\n            if self.is_text():\n              yield line\n            elif self.is_json():\n              history_list.append(line.strip())\n          if self.is_json():\n            inner_map[\"history\"] = history_list\n    if self.is_json():\n      yield json.dumps(output_map, indent=2, sort_keys=True)\n", "path": "src/python/pants/core_tasks/explain_options_task.py"}, {"content": "# coding=utf-8\n# Copyright 2016 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nfrom abc import abstractproperty\n\nfrom pants.backend.jvm.tasks.nailgun_task import NailgunTask\nfrom pants.base.exceptions import TaskError\nfrom pants.java.jar.jar_dependency import JarDependency\nfrom pants.option.custom_types import file_option\nfrom pants.util.memo import memoized_property\nfrom pants.util.meta import AbstractClass\n\n\nclass ScalaFmt(NailgunTask, AbstractClass):\n  \"\"\"Abstract class to run ScalaFmt commands.\n\n  Classes that inherit from this should override get_command_args and\n  process_results to run different scalafmt commands\n\n  :API: public\n  \"\"\"\n  _SCALAFMT_MAIN = 'org.scalafmt.cli.Cli'\n  _SCALA_SOURCE_EXTENSION = '.scala'\n\n  @classmethod\n  def register_options(cls, register):\n    super(ScalaFmt, cls).register_options(register)\n    register('--skip', type=bool, fingerprint=False, help='Skip Scalafmt Check')\n    register('--configuration', advanced=True, type=file_option, fingerprint=False,\n              help='Path to scalafmt config file, if not specified default scalafmt config used')\n    register('--target-types',\n             default={'scala_library', 'junit_tests', 'java_tests'},\n             advanced=True,\n             type=set,\n             help='The target types to apply formatting to.')\n    cls.register_jvm_tool(register,\n                          'scalafmt',\n                          classpath=[\n                          JarDependency(org='com.geirsson',\n                                        name='scalafmt-cli_2.11',\n                                        rev='0.2.11')\n                          ])\n\n  @memoized_property\n  def _formatted_target_types(self):\n    aliases = self.get_options().target_types\n    registered_aliases = self.context.build_file_parser.registered_aliases()\n    return tuple({target_type\n                  for alias in aliases\n                  for target_type in registered_aliases.target_types_by_alias[alias]})\n\n  def execute(self):\n    \"\"\"Runs Scalafmt on all found Scala Source Files.\"\"\"\n    if self.get_options().skip:\n      return\n\n    targets = self.get_non_synthetic_scala_targets(self.context.targets())\n    sources = self.calculate_sources(targets)\n\n    if sources:\n      files = \",\".join(sources)\n\n      result = self.runjava(classpath=self.tool_classpath('scalafmt'),\n                   main=self._SCALAFMT_MAIN,\n                   args=self.get_command_args(files),\n                   workunit_name='scalafmt')\n\n      self.process_results(result)\n\n  @abstractproperty\n  def get_command_args(self, files):\n    \"\"\"Returns the arguments used to run Scalafmt command.\n\n    The return value should be an array of strings.  For\n    example, to run the Scalafmt help command:\n    ['--help']\n    \"\"\"\n\n  @abstractproperty\n  def process_results(self, result):\n    \"\"\"This method processes the results of the scalafmt command.\n\n    No return value is expected.  If an error occurs running\n    Scalafmt raising a TaskError is recommended.\n    \"\"\"\n\n  def get_non_synthetic_scala_targets(self, targets):\n    return filter(\n      lambda target: isinstance(target, self._formatted_target_types)\n                     and target.has_sources(self._SCALA_SOURCE_EXTENSION)\n                     and (not target.is_synthetic),\n      targets)\n\n  def calculate_sources(self, targets):\n    sources = set()\n    for target in targets:\n      sources.update(source for source in target.sources_relative_to_buildroot()\n                      if source.endswith(self._SCALA_SOURCE_EXTENSION))\n    return sources\n\n\nclass ScalaFmtCheckFormat(ScalaFmt):\n  \"\"\"This Task checks that all scala files in the target are formatted\n  correctly.\n\n  If the files are not formatted correctly an error is raised\n  including the command to run to format the files correctly\n\n  :API: public\n  \"\"\"\n  deprecated_options_scope = 'compile.scalafmt'\n  deprecated_options_scope_removal_version = '1.5.0.dev0'\n\n  def get_command_args(self, files):\n    # If no config file is specified use default scalafmt config.\n    config_file = self.get_options().configuration\n    args = ['--test', '--files', files]\n    if config_file!= None:\n      args.extend(['--config', config_file])\n\n    return args\n\n  def process_results(self, result):\n    if result != 0:\n      raise TaskError('Scalafmt failed with exit code {}; to fix run: '\n                      '`./pants fmt <targets>`'.format(result), exit_code=result)\n\n\nclass ScalaFmtFormat(ScalaFmt):\n  \"\"\"This Task reads all scala files in the target and emits\n  the source in a standard style as specified by the configuration\n  file.\n\n  This task mutates the underlying flies.\n\n  :API: public\n  \"\"\"\n\n  def get_command_args(self, files):\n    # If no config file is specified use default scalafmt config.\n    config_file = self.get_options().configuration\n    args = ['-i', '--files', files]\n    if config_file!= None:\n      args.extend(['--config', config_file])\n\n    return args\n\n  def process_results(self, result):\n    # Processes the results of running the scalafmt command.\n    if result != 0:\n      raise TaskError('Scalafmt failed to format files', exit_code=result)\n", "path": "src/python/pants/backend/jvm/tasks/scalafmt.py"}], "after_files": [{"content": "# coding=utf-8\n# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport json\n\nfrom colors import black, blue, cyan, green, magenta, red, white\nfrom packaging.version import Version\n\nfrom pants.option.ranked_value import RankedValue\nfrom pants.task.console_task import ConsoleTask\nfrom pants.version import PANTS_SEMVER\n\n\nclass ExplainOptionsTask(ConsoleTask):\n  \"\"\"Display meta-information about options.\n\n  This \"meta-information\" includes what values options have, and what values they *used* to have\n  before they were overridden by a higher-rank value (eg, a HARDCODED value overridden by a CONFIG\n  value and then a cli FLAG value).\n  \"\"\"\n\n  @classmethod\n  def register_options(cls, register):\n    super(ExplainOptionsTask, cls).register_options(register)\n    register('--scope', help='Only show options in this scope.')\n    register('--name', help='Only show options with this name.')\n    register('--rank', choices=RankedValue.get_names(),\n             help='Only show options with at least this importance.')\n    register('--show-history', type=bool,\n             help='Show the previous values options had before being overridden.')\n    register('--only-overridden', type=bool,\n             help='Only show values that overrode defaults.')\n    register('--skip-inherited', type=bool, default=True,\n             help='Do not show inherited options, unless their values differ from their parents.')\n    register('--output-format', choices=['text', 'json'], default='text',\n             help='Specify the format options will be printed.')\n\n  def _scope_filter(self, scope):\n    pattern = self.get_options().scope\n    return not pattern or scope.startswith(pattern)\n\n  def _option_filter(self, option):\n    pattern = self.get_options().name\n    if not pattern:\n      return True\n    pattern = pattern.replace('-', '_')\n    return option == pattern\n\n  def _rank_filter(self, rank):\n    pattern = self.get_options().rank\n    if not pattern:\n      return True\n    return rank >= RankedValue.get_rank_value(pattern)\n\n  def _rank_color(self, rank):\n    if not self.get_options().colors:\n      return lambda x: x\n    if rank == RankedValue.NONE: return white\n    if rank == RankedValue.HARDCODED: return white\n    if rank == RankedValue.ENVIRONMENT: return red\n    if rank == RankedValue.CONFIG: return blue\n    if rank == RankedValue.FLAG: return magenta\n    return black\n\n  def _format_scope(self, scope, option, no_color=False):\n    if no_color:\n      return '{scope}{option}'.format(\n        scope='{}.'.format(scope) if scope else '',\n        option=option,\n      )\n    scope_color = cyan if self.get_options().colors else lambda x: x\n    option_color = blue if self.get_options().colors else lambda x: x\n    return '{scope}{option}'.format(\n      scope=scope_color('{}.'.format(scope) if scope else ''),\n      option=option_color(option),\n    )\n\n  def _format_record(self, record):\n    simple_rank = RankedValue.get_rank_name(record.rank)\n    if self.is_json():\n      return record.value, simple_rank\n    elif self.is_text():\n      simple_value = str(record.value)\n      value_color = green if self.get_options().colors else lambda x: x\n      formatted_value = value_color(simple_value)\n      rank_color = self._rank_color(record.rank)\n      formatted_rank = '(from {rank}{details})'.format(\n        rank=simple_rank,\n        details=rank_color(' {}'.format(record.details)) if record.details else '',\n      )\n      return '{value} {rank}'.format(\n        value=formatted_value,\n        rank=formatted_rank,\n      )\n\n  def _show_history(self, history):\n    for record in reversed(list(history)[:-1]):\n      if record.rank > RankedValue.NONE:\n        yield '  overrode {}'.format(self._format_record(record))\n\n  def _force_option_parsing(self):\n    scopes = filter(self._scope_filter, list(self.context.options.known_scope_to_info.keys()))\n    for scope in scopes:\n      self.context.options.for_scope(scope)\n\n  def _get_parent_scope_option(self, scope, name):\n    if not scope:\n      return None, None\n    parent_scope = ''\n    if '.' in scope:\n      parent_scope, _ = scope.rsplit('.', 1)\n    options = self.context.options.for_scope(parent_scope)\n    try:\n      return parent_scope, options[name]\n    except AttributeError:\n      return None, None\n\n  def is_json(self):\n    return self.get_options().output_format == 'json'\n\n  def is_text(self):\n    return self.get_options().output_format == 'text'\n\n  def console_output(self, targets):\n    self._force_option_parsing()\n    if self.is_json():\n      output_map = {}\n    for scope, options in sorted(self.context.options.tracker.option_history_by_scope.items()):\n      if not self._scope_filter(scope):\n        continue\n      for option, history in sorted(options.items()):\n        if not self._option_filter(option):\n          continue\n        if not self._rank_filter(history.latest.rank):\n          continue\n        if self.get_options().only_overridden and not history.was_overridden:\n          continue\n        # Skip the option if it has already passed the deprecation period.\n        if history.latest.deprecation_version and PANTS_SEMVER >= Version(\n          history.latest.deprecation_version):\n          continue\n        if self.get_options().skip_inherited:\n          parent_scope, parent_value = self._get_parent_scope_option(scope, option)\n          if parent_scope is not None and parent_value == history.latest.value:\n            continue\n        if self.is_json():\n          value, rank_name = self._format_record(history.latest)\n          scope_key = self._format_scope(scope, option, True)\n          # We rely on the fact that option values are restricted to a set of types compatible with\n          # json. In particular, we expect dict, list, str, bool, int and float, and so do no\n          # processing here.\n          # TODO(John Sirois): The option parsing system currently lets options of unexpected types\n          # slide by, which can lead to un-overridable values and which would also blow up below in\n          # json encoding, fix options to restrict the allowed `type`s:\n          #   https://github.com/pantsbuild/pants/issues/4695\n          inner_map = dict(value=value, source=rank_name)\n          output_map[scope_key] = inner_map\n        elif self.is_text():\n          yield '{} = {}'.format(self._format_scope(scope, option),\n                                 self._format_record(history.latest))\n        if self.get_options().show_history:\n          history_list = []\n          for line in self._show_history(history):\n            if self.is_text():\n              yield line\n            elif self.is_json():\n              history_list.append(line.strip())\n          if self.is_json():\n            inner_map[\"history\"] = history_list\n    if self.is_json():\n      yield json.dumps(output_map, indent=2, sort_keys=True)\n", "path": "src/python/pants/core_tasks/explain_options_task.py"}, {"content": "# coding=utf-8\n# Copyright 2016 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nfrom abc import abstractproperty\n\nfrom pants.backend.jvm.tasks.nailgun_task import NailgunTask\nfrom pants.base.exceptions import TaskError\nfrom pants.java.jar.jar_dependency import JarDependency\nfrom pants.option.custom_types import file_option\nfrom pants.util.memo import memoized_property\nfrom pants.util.meta import AbstractClass\n\n\nclass ScalaFmt(NailgunTask, AbstractClass):\n  \"\"\"Abstract class to run ScalaFmt commands.\n\n  Classes that inherit from this should override get_command_args and\n  process_results to run different scalafmt commands\n\n  :API: public\n  \"\"\"\n  _SCALAFMT_MAIN = 'org.scalafmt.cli.Cli'\n  _SCALA_SOURCE_EXTENSION = '.scala'\n\n  @classmethod\n  def register_options(cls, register):\n    super(ScalaFmt, cls).register_options(register)\n    register('--skip', type=bool, fingerprint=False, help='Skip Scalafmt Check')\n    register('--configuration', advanced=True, type=file_option, fingerprint=False,\n              help='Path to scalafmt config file, if not specified default scalafmt config used')\n    register('--target-types',\n             default=['scala_library', 'junit_tests', 'java_tests'],\n             advanced=True,\n             type=list,\n             help='The target types to apply formatting to.')\n    cls.register_jvm_tool(register,\n                          'scalafmt',\n                          classpath=[\n                          JarDependency(org='com.geirsson',\n                                        name='scalafmt-cli_2.11',\n                                        rev='0.2.11')\n                          ])\n\n  @memoized_property\n  def _formatted_target_types(self):\n    aliases = set(self.get_options().target_types)\n    registered_aliases = self.context.build_file_parser.registered_aliases()\n    return tuple({target_type\n                  for alias in aliases\n                  for target_type in registered_aliases.target_types_by_alias[alias]})\n\n  def execute(self):\n    \"\"\"Runs Scalafmt on all found Scala Source Files.\"\"\"\n    if self.get_options().skip:\n      return\n\n    targets = self.get_non_synthetic_scala_targets(self.context.targets())\n    sources = self.calculate_sources(targets)\n\n    if sources:\n      files = \",\".join(sources)\n\n      result = self.runjava(classpath=self.tool_classpath('scalafmt'),\n                   main=self._SCALAFMT_MAIN,\n                   args=self.get_command_args(files),\n                   workunit_name='scalafmt')\n\n      self.process_results(result)\n\n  @abstractproperty\n  def get_command_args(self, files):\n    \"\"\"Returns the arguments used to run Scalafmt command.\n\n    The return value should be an array of strings.  For\n    example, to run the Scalafmt help command:\n    ['--help']\n    \"\"\"\n\n  @abstractproperty\n  def process_results(self, result):\n    \"\"\"This method processes the results of the scalafmt command.\n\n    No return value is expected.  If an error occurs running\n    Scalafmt raising a TaskError is recommended.\n    \"\"\"\n\n  def get_non_synthetic_scala_targets(self, targets):\n    return filter(\n      lambda target: isinstance(target, self._formatted_target_types)\n                     and target.has_sources(self._SCALA_SOURCE_EXTENSION)\n                     and (not target.is_synthetic),\n      targets)\n\n  def calculate_sources(self, targets):\n    sources = set()\n    for target in targets:\n      sources.update(source for source in target.sources_relative_to_buildroot()\n                      if source.endswith(self._SCALA_SOURCE_EXTENSION))\n    return sources\n\n\nclass ScalaFmtCheckFormat(ScalaFmt):\n  \"\"\"This Task checks that all scala files in the target are formatted\n  correctly.\n\n  If the files are not formatted correctly an error is raised\n  including the command to run to format the files correctly\n\n  :API: public\n  \"\"\"\n  deprecated_options_scope = 'compile.scalafmt'\n  deprecated_options_scope_removal_version = '1.5.0.dev0'\n\n  def get_command_args(self, files):\n    # If no config file is specified use default scalafmt config.\n    config_file = self.get_options().configuration\n    args = ['--test', '--files', files]\n    if config_file!= None:\n      args.extend(['--config', config_file])\n\n    return args\n\n  def process_results(self, result):\n    if result != 0:\n      raise TaskError('Scalafmt failed with exit code {}; to fix run: '\n                      '`./pants fmt <targets>`'.format(result), exit_code=result)\n\n\nclass ScalaFmtFormat(ScalaFmt):\n  \"\"\"This Task reads all scala files in the target and emits\n  the source in a standard style as specified by the configuration\n  file.\n\n  This task mutates the underlying flies.\n\n  :API: public\n  \"\"\"\n\n  def get_command_args(self, files):\n    # If no config file is specified use default scalafmt config.\n    config_file = self.get_options().configuration\n    args = ['-i', '--files', files]\n    if config_file!= None:\n      args.extend(['--config', config_file])\n\n    return args\n\n  def process_results(self, result):\n    # Processes the results of running the scalafmt command.\n    if result != 0:\n      raise TaskError('Scalafmt failed to format files', exit_code=result)\n", "path": "src/python/pants/backend/jvm/tasks/scalafmt.py"}]}