We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CryptoTicker migration is incomplete
I previously had:

```
widget.CryptoTicker(format="BTC: {avg}", **widget_defaults)
```

but it seems avg is not an allowed key any more per:

```
2021-09-01 14:42:17,968 ERROR libqtile generic_poll_text.py:poll():L90 got exception polling widget
Traceback (most recent call last):
  File "/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/generic_poll_text.py", line 88, in poll
    text = self.parse(body)
  File "/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/crypto_ticker.py", line 90, in parse
    return self.format.format(**variables)
```

/cc @Graeme22 
CryptoTicker migration is incomplete
I previously had:

```
widget.CryptoTicker(format="BTC: {avg}", **widget_defaults)
```

but it seems avg is not an allowed key any more per:

```
2021-09-01 14:42:17,968 ERROR libqtile generic_poll_text.py:poll():L90 got exception polling widget
Traceback (most recent call last):
  File "/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/generic_poll_text.py", line 88, in poll
    text = self.parse(body)
  File "/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/crypto_ticker.py", line 90, in parse
    return self.format.format(**variables)
```

/cc @Graeme22 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `libqtile/scripts/migrate.py`
Content:
```
1 # Copyright (c) 2021, Tycho Andersen. All rights reserved.
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
9 #
10 # The above copyright notice and this permission notice shall be included in
11 # all copies or substantial portions of the Software.
12 #
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 import filecmp
20 import os
21 import os.path
22 import shutil
23 import sys
24 from glob import glob
25 
26 BACKUP_SUFFIX = ".migrate.bak"
27 
28 try:
29     import bowler
30 except ImportError:
31     pass
32 
33 
34 def rename_hook(query, fro, to):
35     # could match on dotted_name< 'hook' '.' 'subscribe' '.' '{name}' >
36     # but the replacement gets more complicated...
37     selector = "'{name}'".format(name=fro)
38     q = query.select_pattern(selector)
39     q.current.kwargs["name"] = fro
40     return q.rename(to)
41 
42 
43 def client_name_updated(query):
44     """ Rename window_name_change -> client_name_updated"""
45     return rename_hook(query, "window_name_change", "client_name_updated")
46 
47 
48 def tile_master_windows_rename(query):
49     return (
50         query
51         .select_function("Tile")
52         .modify_argument("masterWindows", "master_length")
53     )
54 
55 
56 def threaded_poll_text_rename(query):
57     return (
58         query
59         .select_class("ThreadedPollText")
60         .rename("ThreadPoolText")
61     )
62 
63 
64 def pacman_to_checkupdates(query):
65     return (
66         query
67         .select_class("Pacman")
68         .rename("CheckUpdates")
69     )
70 
71 
72 def bitcoin_to_crypto(query):
73     return (
74         query
75         .select_class("BitcoinTicker")
76         .rename("CryptoTicker")
77     )
78 
79 
80 def hook_main_function(query):
81     def modify_main(node, capture, filename):
82         main = capture.get("function_def")
83         if main.prev_sibling:
84             for leaf in main.prev_sibling.leaves():
85                 if "startup" == leaf.value:
86                     return
87         args = capture.get("function_arguments")
88         if args:
89             args[0].remove()
90             main.prefix += "from libqtile import hook, qtile\n"
91             main.prefix += "@hook.subscribe.startup\n"
92 
93     return (
94         query
95         .select_function("main")
96         .is_def()
97         .modify(modify_main)
98     )
99 
100 
101 # Deprecated new_at_current key replaced by new_client_position.
102 # In the node, we want to change the key name
103 # and adapts its value depending of the previous value :
104 #   new_at_current=True => new_client_position=before_current
105 #   new_at_current<>True => new_client_position=after_current
106 def update_node_nac(node, capture, filename):
107     key = capture.get("k")
108     key.value = "new_client_position"
109     val = capture.get("v")
110     if val.value == "True":
111         val.value = "'before_current'"
112     else:
113         val.value = "'after_current'"
114 
115 
116 def new_at_current_to_new_client_position(query):
117     old_pattern = """
118         argument< k="new_at_current" "=" v=any >
119     """
120     return (
121         query
122         .select(old_pattern)
123         .modify(update_node_nac)
124     )
125 
126 
127 MIGRATIONS = [
128     client_name_updated,
129     tile_master_windows_rename,
130     threaded_poll_text_rename,
131     pacman_to_checkupdates,
132     bitcoin_to_crypto,
133     hook_main_function,
134     new_at_current_to_new_client_position,
135 ]
136 
137 
138 MODULE_RENAMES = [
139     ("libqtile.command_graph", "libqtile.command.graph"),
140     ("libqtile.command_client", "libqtile.command.client"),
141     ("libqtile.command_interface", "libqtile.command.interface"),
142     ("libqtile.command_object", "libqtile.command.base"),
143     ("libqtile.window", "libqtile.backend.x11.window"),
144 ]
145 
146 for (fro, to) in MODULE_RENAMES:
147     def f(query, fro=fro, to=to):
148         return (
149             query
150             .select_module(fro)
151             .rename(to)
152         )
153     MIGRATIONS.append(f)
154 
155 
156 def file_and_backup(config_dir):
157     for py in glob(os.path.join(config_dir, "*.py")):
158         backup = py + BACKUP_SUFFIX
159         yield py, backup
160 
161 
162 def do_migrate(args):
163     if "bowler" not in sys.modules:
164         print("bowler can't be found, not migrating config file")
165         print("install it and try again")
166         sys.exit(1)
167 
168     config_dir = os.path.dirname(args.config)
169     for py, backup in file_and_backup(config_dir):
170         shutil.copyfile(py, backup)
171 
172     for m in MIGRATIONS:
173         q = bowler.Query(config_dir)
174         m(q).execute(interactive=not args.yes, write=True)
175 
176     changed = False
177     for py, backup in file_and_backup(config_dir):
178         backup = py + BACKUP_SUFFIX
179         if not filecmp.cmp(py, backup, shallow=False):
180             changed = True
181             break
182 
183     if not changed:
184         print("Config unchanged.")
185         for _, backup in file_and_backup(config_dir):
186             os.remove(backup)
187 
188 
189 def add_subcommand(subparsers, parents):
190     parser = subparsers.add_parser(
191         "migrate",
192         parents=parents,
193         help="Migrate a configuration file to the current API"
194     )
195     parser.add_argument(
196         "-c",
197         "--config",
198         action="store",
199         default=os.path.expanduser(
200             os.path.join(os.getenv("XDG_CONFIG_HOME", "~/.config"), "qtile", "config.py")
201         ),
202         help="Use the specified configuration file (migrates every .py file in this directory)",
203     )
204     parser.add_argument(
205         "--yes",
206         action="store_true",
207         help="Automatically apply diffs with no confirmation",
208     )
209     parser.set_defaults(func=do_migrate)
210
```
--- 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/libqtile/scripts/migrate.py b/libqtile/scripts/migrate.py
--- a/libqtile/scripts/migrate.py
+++ b/libqtile/scripts/migrate.py
@@ -69,10 +69,32 @@
     )
 
 
+def reset_format(node, capture, filename):
+    args = capture.get("class_arguments")
+    if args:
+        if args[0].type == 260:  # argument list
+            n_children = len(args[0].children)
+            for i in range(n_children):
+                # we only want to remove the format argument
+                if 'format' in str(args[0].children[i]):
+                    # remove the argument and the trailing or preceeding comma
+                    if i == n_children - 1:  # last argument
+                        args[0].children[i - 1].remove()
+                        args[0].children[i - 1].remove()
+                    else:
+                        args[0].children[i].remove()
+                        args[0].children[i].remove()
+
+                    break
+        else:  # there's only one argument
+            args[0].remove()
+
+
 def bitcoin_to_crypto(query):
     return (
         query
         .select_class("BitcoinTicker")
+        .modify(reset_format)
         .rename("CryptoTicker")
     )

{"golden_diff": "diff --git a/libqtile/scripts/migrate.py b/libqtile/scripts/migrate.py\n--- a/libqtile/scripts/migrate.py\n+++ b/libqtile/scripts/migrate.py\n@@ -69,10 +69,32 @@\n     )\n \n \n+def reset_format(node, capture, filename):\n+    args = capture.get(\"class_arguments\")\n+    if args:\n+        if args[0].type == 260:  # argument list\n+            n_children = len(args[0].children)\n+            for i in range(n_children):\n+                # we only want to remove the format argument\n+                if 'format' in str(args[0].children[i]):\n+                    # remove the argument and the trailing or preceeding comma\n+                    if i == n_children - 1:  # last argument\n+                        args[0].children[i - 1].remove()\n+                        args[0].children[i - 1].remove()\n+                    else:\n+                        args[0].children[i].remove()\n+                        args[0].children[i].remove()\n+\n+                    break\n+        else:  # there's only one argument\n+            args[0].remove()\n+\n+\n def bitcoin_to_crypto(query):\n     return (\n         query\n         .select_class(\"BitcoinTicker\")\n+        .modify(reset_format)\n         .rename(\"CryptoTicker\")\n     )\n", "issue": "CryptoTicker migration is incomplete\nI previously had:\r\n\r\n```\r\nwidget.CryptoTicker(format=\"BTC: {avg}\", **widget_defaults)\r\n```\r\n\r\nbut it seems avg is not an allowed key any more per:\r\n\r\n```\r\n2021-09-01 14:42:17,968 ERROR libqtile generic_poll_text.py:poll():L90 got exception polling widget\r\nTraceback (most recent call last):\r\n  File \"/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/generic_poll_text.py\", line 88, in poll\r\n    text = self.parse(body)\r\n  File \"/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/crypto_ticker.py\", line 90, in parse\r\n    return self.format.format(**variables)\r\n```\r\n\r\n/cc @Graeme22 \nCryptoTicker migration is incomplete\nI previously had:\r\n\r\n```\r\nwidget.CryptoTicker(format=\"BTC: {avg}\", **widget_defaults)\r\n```\r\n\r\nbut it seems avg is not an allowed key any more per:\r\n\r\n```\r\n2021-09-01 14:42:17,968 ERROR libqtile generic_poll_text.py:poll():L90 got exception polling widget\r\nTraceback (most recent call last):\r\n  File \"/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/generic_poll_text.py\", line 88, in poll\r\n    text = self.parse(body)\r\n  File \"/home/tycho/.local/lib/python3.9/site-packages/libqtile/widget/crypto_ticker.py\", line 90, in parse\r\n    return self.format.format(**variables)\r\n```\r\n\r\n/cc @Graeme22 \n", "before_files": [{"content": "# Copyright (c) 2021, Tycho Andersen. All rights reserved.\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nimport filecmp\nimport os\nimport os.path\nimport shutil\nimport sys\nfrom glob import glob\n\nBACKUP_SUFFIX = \".migrate.bak\"\n\ntry:\n    import bowler\nexcept ImportError:\n    pass\n\n\ndef rename_hook(query, fro, to):\n    # could match on dotted_name< 'hook' '.' 'subscribe' '.' '{name}' >\n    # but the replacement gets more complicated...\n    selector = \"'{name}'\".format(name=fro)\n    q = query.select_pattern(selector)\n    q.current.kwargs[\"name\"] = fro\n    return q.rename(to)\n\n\ndef client_name_updated(query):\n    \"\"\" Rename window_name_change -> client_name_updated\"\"\"\n    return rename_hook(query, \"window_name_change\", \"client_name_updated\")\n\n\ndef tile_master_windows_rename(query):\n    return (\n        query\n        .select_function(\"Tile\")\n        .modify_argument(\"masterWindows\", \"master_length\")\n    )\n\n\ndef threaded_poll_text_rename(query):\n    return (\n        query\n        .select_class(\"ThreadedPollText\")\n        .rename(\"ThreadPoolText\")\n    )\n\n\ndef pacman_to_checkupdates(query):\n    return (\n        query\n        .select_class(\"Pacman\")\n        .rename(\"CheckUpdates\")\n    )\n\n\ndef bitcoin_to_crypto(query):\n    return (\n        query\n        .select_class(\"BitcoinTicker\")\n        .rename(\"CryptoTicker\")\n    )\n\n\ndef hook_main_function(query):\n    def modify_main(node, capture, filename):\n        main = capture.get(\"function_def\")\n        if main.prev_sibling:\n            for leaf in main.prev_sibling.leaves():\n                if \"startup\" == leaf.value:\n                    return\n        args = capture.get(\"function_arguments\")\n        if args:\n            args[0].remove()\n            main.prefix += \"from libqtile import hook, qtile\\n\"\n            main.prefix += \"@hook.subscribe.startup\\n\"\n\n    return (\n        query\n        .select_function(\"main\")\n        .is_def()\n        .modify(modify_main)\n    )\n\n\n# Deprecated new_at_current key replaced by new_client_position.\n# In the node, we want to change the key name\n# and adapts its value depending of the previous value :\n#   new_at_current=True => new_client_position=before_current\n#   new_at_current<>True => new_client_position=after_current\ndef update_node_nac(node, capture, filename):\n    key = capture.get(\"k\")\n    key.value = \"new_client_position\"\n    val = capture.get(\"v\")\n    if val.value == \"True\":\n        val.value = \"'before_current'\"\n    else:\n        val.value = \"'after_current'\"\n\n\ndef new_at_current_to_new_client_position(query):\n    old_pattern = \"\"\"\n        argument< k=\"new_at_current\" \"=\" v=any >\n    \"\"\"\n    return (\n        query\n        .select(old_pattern)\n        .modify(update_node_nac)\n    )\n\n\nMIGRATIONS = [\n    client_name_updated,\n    tile_master_windows_rename,\n    threaded_poll_text_rename,\n    pacman_to_checkupdates,\n    bitcoin_to_crypto,\n    hook_main_function,\n    new_at_current_to_new_client_position,\n]\n\n\nMODULE_RENAMES = [\n    (\"libqtile.command_graph\", \"libqtile.command.graph\"),\n    (\"libqtile.command_client\", \"libqtile.command.client\"),\n    (\"libqtile.command_interface\", \"libqtile.command.interface\"),\n    (\"libqtile.command_object\", \"libqtile.command.base\"),\n    (\"libqtile.window\", \"libqtile.backend.x11.window\"),\n]\n\nfor (fro, to) in MODULE_RENAMES:\n    def f(query, fro=fro, to=to):\n        return (\n            query\n            .select_module(fro)\n            .rename(to)\n        )\n    MIGRATIONS.append(f)\n\n\ndef file_and_backup(config_dir):\n    for py in glob(os.path.join(config_dir, \"*.py\")):\n        backup = py + BACKUP_SUFFIX\n        yield py, backup\n\n\ndef do_migrate(args):\n    if \"bowler\" not in sys.modules:\n        print(\"bowler can't be found, not migrating config file\")\n        print(\"install it and try again\")\n        sys.exit(1)\n\n    config_dir = os.path.dirname(args.config)\n    for py, backup in file_and_backup(config_dir):\n        shutil.copyfile(py, backup)\n\n    for m in MIGRATIONS:\n        q = bowler.Query(config_dir)\n        m(q).execute(interactive=not args.yes, write=True)\n\n    changed = False\n    for py, backup in file_and_backup(config_dir):\n        backup = py + BACKUP_SUFFIX\n        if not filecmp.cmp(py, backup, shallow=False):\n            changed = True\n            break\n\n    if not changed:\n        print(\"Config unchanged.\")\n        for _, backup in file_and_backup(config_dir):\n            os.remove(backup)\n\n\ndef add_subcommand(subparsers, parents):\n    parser = subparsers.add_parser(\n        \"migrate\",\n        parents=parents,\n        help=\"Migrate a configuration file to the current API\"\n    )\n    parser.add_argument(\n        \"-c\",\n        \"--config\",\n        action=\"store\",\n        default=os.path.expanduser(\n            os.path.join(os.getenv(\"XDG_CONFIG_HOME\", \"~/.config\"), \"qtile\", \"config.py\")\n        ),\n        help=\"Use the specified configuration file (migrates every .py file in this directory)\",\n    )\n    parser.add_argument(\n        \"--yes\",\n        action=\"store_true\",\n        help=\"Automatically apply diffs with no confirmation\",\n    )\n    parser.set_defaults(func=do_migrate)\n", "path": "libqtile/scripts/migrate.py"}], "after_files": [{"content": "# Copyright (c) 2021, Tycho Andersen. All rights reserved.\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nimport filecmp\nimport os\nimport os.path\nimport shutil\nimport sys\nfrom glob import glob\n\nBACKUP_SUFFIX = \".migrate.bak\"\n\ntry:\n    import bowler\nexcept ImportError:\n    pass\n\n\ndef rename_hook(query, fro, to):\n    # could match on dotted_name< 'hook' '.' 'subscribe' '.' '{name}' >\n    # but the replacement gets more complicated...\n    selector = \"'{name}'\".format(name=fro)\n    q = query.select_pattern(selector)\n    q.current.kwargs[\"name\"] = fro\n    return q.rename(to)\n\n\ndef client_name_updated(query):\n    \"\"\" Rename window_name_change -> client_name_updated\"\"\"\n    return rename_hook(query, \"window_name_change\", \"client_name_updated\")\n\n\ndef tile_master_windows_rename(query):\n    return (\n        query\n        .select_function(\"Tile\")\n        .modify_argument(\"masterWindows\", \"master_length\")\n    )\n\n\ndef threaded_poll_text_rename(query):\n    return (\n        query\n        .select_class(\"ThreadedPollText\")\n        .rename(\"ThreadPoolText\")\n    )\n\n\ndef pacman_to_checkupdates(query):\n    return (\n        query\n        .select_class(\"Pacman\")\n        .rename(\"CheckUpdates\")\n    )\n\n\ndef reset_format(node, capture, filename):\n    args = capture.get(\"class_arguments\")\n    if args:\n        if args[0].type == 260:  # argument list\n            n_children = len(args[0].children)\n            for i in range(n_children):\n                # we only want to remove the format argument\n                if 'format' in str(args[0].children[i]):\n                    # remove the argument and the trailing or preceeding comma\n                    if i == n_children - 1:  # last argument\n                        args[0].children[i - 1].remove()\n                        args[0].children[i - 1].remove()\n                    else:\n                        args[0].children[i].remove()\n                        args[0].children[i].remove()\n\n                    break\n        else:  # there's only one argument\n            args[0].remove()\n\n\ndef bitcoin_to_crypto(query):\n    return (\n        query\n        .select_class(\"BitcoinTicker\")\n        .modify(reset_format)\n        .rename(\"CryptoTicker\")\n    )\n\n\ndef hook_main_function(query):\n    def modify_main(node, capture, filename):\n        main = capture.get(\"function_def\")\n        if main.prev_sibling:\n            for leaf in main.prev_sibling.leaves():\n                if \"startup\" == leaf.value:\n                    return\n        args = capture.get(\"function_arguments\")\n        if args:\n            args[0].remove()\n            main.prefix += \"from libqtile import hook, qtile\\n\"\n            main.prefix += \"@hook.subscribe.startup\\n\"\n\n    return (\n        query\n        .select_function(\"main\")\n        .is_def()\n        .modify(modify_main)\n    )\n\n\n# Deprecated new_at_current key replaced by new_client_position.\n# In the node, we want to change the key name\n# and adapts its value depending of the previous value :\n#   new_at_current=True => new_client_position=before_current\n#   new_at_current<>True => new_client_position=after_current\ndef update_node_nac(node, capture, filename):\n    key = capture.get(\"k\")\n    key.value = \"new_client_position\"\n    val = capture.get(\"v\")\n    if val.value == \"True\":\n        val.value = \"'before_current'\"\n    else:\n        val.value = \"'after_current'\"\n\n\ndef new_at_current_to_new_client_position(query):\n    old_pattern = \"\"\"\n        argument< k=\"new_at_current\" \"=\" v=any >\n    \"\"\"\n    return (\n        query\n        .select(old_pattern)\n        .modify(update_node_nac)\n    )\n\n\nMIGRATIONS = [\n    client_name_updated,\n    tile_master_windows_rename,\n    threaded_poll_text_rename,\n    pacman_to_checkupdates,\n    bitcoin_to_crypto,\n    hook_main_function,\n    new_at_current_to_new_client_position,\n]\n\n\nMODULE_RENAMES = [\n    (\"libqtile.command_graph\", \"libqtile.command.graph\"),\n    (\"libqtile.command_client\", \"libqtile.command.client\"),\n    (\"libqtile.command_interface\", \"libqtile.command.interface\"),\n    (\"libqtile.command_object\", \"libqtile.command.base\"),\n    (\"libqtile.window\", \"libqtile.backend.x11.window\"),\n]\n\nfor (fro, to) in MODULE_RENAMES:\n    def f(query, fro=fro, to=to):\n        return (\n            query\n            .select_module(fro)\n            .rename(to)\n        )\n    MIGRATIONS.append(f)\n\n\ndef file_and_backup(config_dir):\n    for py in glob(os.path.join(config_dir, \"*.py\")):\n        backup = py + BACKUP_SUFFIX\n        yield py, backup\n\n\ndef do_migrate(args):\n    if \"bowler\" not in sys.modules:\n        print(\"bowler can't be found, not migrating config file\")\n        print(\"install it and try again\")\n        sys.exit(1)\n\n    config_dir = os.path.dirname(args.config)\n    for py, backup in file_and_backup(config_dir):\n        shutil.copyfile(py, backup)\n\n    for m in MIGRATIONS:\n        q = bowler.Query(config_dir)\n        m(q).execute(interactive=not args.yes, write=True)\n\n    changed = False\n    for py, backup in file_and_backup(config_dir):\n        backup = py + BACKUP_SUFFIX\n        if not filecmp.cmp(py, backup, shallow=False):\n            changed = True\n            break\n\n    if not changed:\n        print(\"Config unchanged.\")\n        for _, backup in file_and_backup(config_dir):\n            os.remove(backup)\n\n\ndef add_subcommand(subparsers, parents):\n    parser = subparsers.add_parser(\n        \"migrate\",\n        parents=parents,\n        help=\"Migrate a configuration file to the current API\"\n    )\n    parser.add_argument(\n        \"-c\",\n        \"--config\",\n        action=\"store\",\n        default=os.path.expanduser(\n            os.path.join(os.getenv(\"XDG_CONFIG_HOME\", \"~/.config\"), \"qtile\", \"config.py\")\n        ),\n        help=\"Use the specified configuration file (migrates every .py file in this directory)\",\n    )\n    parser.add_argument(\n        \"--yes\",\n        action=\"store_true\",\n        help=\"Automatically apply diffs with no confirmation\",\n    )\n    parser.set_defaults(func=do_migrate)\n", "path": "libqtile/scripts/migrate.py"}]}