We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add helper functions to support loading json from files
Having integrated [RedisJSON](https://redisjson.io) it would be nice if we could extend the json().set() behaviour by some new functions

- [ ] json().set_file() - This would be set the json value stored at the key named _name_ to the contents of the file specified in _obj_, effectively the same behaviour as json().set()

- [ ] json().set_path() - This would be the same as above, except without a _name_ attribute, and pointing to a directory path. All files in that directory would be stored, under the assumption that the filename, minus it's extension would be the key name.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redis/commands/json/commands.py`
Content:
```
1 from deprecated import deprecated
2 
3 from redis.exceptions import DataError
4 
5 from .decoders import decode_dict_keys
6 from .path import Path
7 
8 
9 class JSONCommands:
10     """json commands."""
11 
12     def arrappend(self, name, path=Path.rootPath(), *args):
13         """Append the objects ``args`` to the array under the
14         ``path` in key ``name``.
15 
16         For more information: https://oss.redis.com/redisjson/commands/#jsonarrappend
17         """  # noqa
18         pieces = [name, str(path)]
19         for o in args:
20             pieces.append(self._encode(o))
21         return self.execute_command("JSON.ARRAPPEND", *pieces)
22 
23     def arrindex(self, name, path, scalar, start=0, stop=-1):
24         """
25         Return the index of ``scalar`` in the JSON array under ``path`` at key
26         ``name``.
27 
28         The search can be limited using the optional inclusive ``start``
29         and exclusive ``stop`` indices.
30 
31         For more information: https://oss.redis.com/redisjson/commands/#jsonarrindex
32         """  # noqa
33         return self.execute_command(
34             "JSON.ARRINDEX", name, str(path), self._encode(scalar), start, stop
35         )
36 
37     def arrinsert(self, name, path, index, *args):
38         """Insert the objects ``args`` to the array at index ``index``
39         under the ``path` in key ``name``.
40 
41         For more information: https://oss.redis.com/redisjson/commands/#jsonarrinsert
42         """  # noqa
43         pieces = [name, str(path), index]
44         for o in args:
45             pieces.append(self._encode(o))
46         return self.execute_command("JSON.ARRINSERT", *pieces)
47 
48     def arrlen(self, name, path=Path.rootPath()):
49         """Return the length of the array JSON value under ``path``
50         at key``name``.
51 
52         For more information: https://oss.redis.com/redisjson/commands/#jsonarrlen
53         """  # noqa
54         return self.execute_command("JSON.ARRLEN", name, str(path))
55 
56     def arrpop(self, name, path=Path.rootPath(), index=-1):
57         """Pop the element at ``index`` in the array JSON value under
58         ``path`` at key ``name``.
59 
60         For more information: https://oss.redis.com/redisjson/commands/#jsonarrpop
61         """  # noqa
62         return self.execute_command("JSON.ARRPOP", name, str(path), index)
63 
64     def arrtrim(self, name, path, start, stop):
65         """Trim the array JSON value under ``path`` at key ``name`` to the
66         inclusive range given by ``start`` and ``stop``.
67 
68         For more information: https://oss.redis.com/redisjson/commands/#jsonarrtrim
69         """  # noqa
70         return self.execute_command("JSON.ARRTRIM", name, str(path), start, stop)
71 
72     def type(self, name, path=Path.rootPath()):
73         """Get the type of the JSON value under ``path`` from key ``name``.
74 
75         For more information: https://oss.redis.com/redisjson/commands/#jsontype
76         """  # noqa
77         return self.execute_command("JSON.TYPE", name, str(path))
78 
79     def resp(self, name, path=Path.rootPath()):
80         """Return the JSON value under ``path`` at key ``name``.
81 
82         For more information: https://oss.redis.com/redisjson/commands/#jsonresp
83         """  # noqa
84         return self.execute_command("JSON.RESP", name, str(path))
85 
86     def objkeys(self, name, path=Path.rootPath()):
87         """Return the key names in the dictionary JSON value under ``path`` at
88         key ``name``.
89 
90         For more information: https://oss.redis.com/redisjson/commands/#jsonobjkeys
91         """  # noqa
92         return self.execute_command("JSON.OBJKEYS", name, str(path))
93 
94     def objlen(self, name, path=Path.rootPath()):
95         """Return the length of the dictionary JSON value under ``path`` at key
96         ``name``.
97 
98         For more information: https://oss.redis.com/redisjson/commands/#jsonobjlen
99         """  # noqa
100         return self.execute_command("JSON.OBJLEN", name, str(path))
101 
102     def numincrby(self, name, path, number):
103         """Increment the numeric (integer or floating point) JSON value under
104         ``path`` at key ``name`` by the provided ``number``.
105 
106         For more information: https://oss.redis.com/redisjson/commands/#jsonnumincrby
107         """  # noqa
108         return self.execute_command(
109             "JSON.NUMINCRBY", name, str(path), self._encode(number)
110         )
111 
112     @deprecated(version="4.0.0", reason="deprecated since redisjson 1.0.0")
113     def nummultby(self, name, path, number):
114         """Multiply the numeric (integer or floating point) JSON value under
115         ``path`` at key ``name`` with the provided ``number``.
116 
117         For more information: https://oss.redis.com/redisjson/commands/#jsonnummultby
118         """  # noqa
119         return self.execute_command(
120             "JSON.NUMMULTBY", name, str(path), self._encode(number)
121         )
122 
123     def clear(self, name, path=Path.rootPath()):
124         """
125         Empty arrays and objects (to have zero slots/keys without deleting the
126         array/object).
127 
128         Return the count of cleared paths (ignoring non-array and non-objects
129         paths).
130 
131         For more information: https://oss.redis.com/redisjson/commands/#jsonclear
132         """  # noqa
133         return self.execute_command("JSON.CLEAR", name, str(path))
134 
135     def delete(self, key, path=Path.rootPath()):
136         """Delete the JSON value stored at key ``key`` under ``path``.
137 
138         For more information: https://oss.redis.com/redisjson/commands/#jsondel
139         """
140         return self.execute_command("JSON.DEL", key, str(path))
141 
142     # forget is an alias for delete
143     forget = delete
144 
145     def get(self, name, *args, no_escape=False):
146         """
147         Get the object stored as a JSON value at key ``name``.
148 
149         ``args`` is zero or more paths, and defaults to root path
150         ```no_escape`` is a boolean flag to add no_escape option to get
151         non-ascii characters
152 
153         For more information: https://oss.redis.com/redisjson/commands/#jsonget
154         """  # noqa
155         pieces = [name]
156         if no_escape:
157             pieces.append("noescape")
158 
159         if len(args) == 0:
160             pieces.append(Path.rootPath())
161 
162         else:
163             for p in args:
164                 pieces.append(str(p))
165 
166         # Handle case where key doesn't exist. The JSONDecoder would raise a
167         # TypeError exception since it can't decode None
168         try:
169             return self.execute_command("JSON.GET", *pieces)
170         except TypeError:
171             return None
172 
173     def mget(self, keys, path):
174         """
175         Get the objects stored as a JSON values under ``path``. ``keys``
176         is a list of one or more keys.
177 
178         For more information: https://oss.redis.com/redisjson/commands/#jsonmget
179         """  # noqa
180         pieces = []
181         pieces += keys
182         pieces.append(str(path))
183         return self.execute_command("JSON.MGET", *pieces)
184 
185     def set(self, name, path, obj, nx=False, xx=False, decode_keys=False):
186         """
187         Set the JSON value at key ``name`` under the ``path`` to ``obj``.
188 
189         ``nx`` if set to True, set ``value`` only if it does not exist.
190         ``xx`` if set to True, set ``value`` only if it exists.
191         ``decode_keys`` If set to True, the keys of ``obj`` will be decoded
192         with utf-8.
193 
194         For the purpose of using this within a pipeline, this command is also
195         aliased to jsonset.
196 
197         For more information: https://oss.redis.com/redisjson/commands/#jsonset
198         """
199         if decode_keys:
200             obj = decode_dict_keys(obj)
201 
202         pieces = [name, str(path), self._encode(obj)]
203 
204         # Handle existential modifiers
205         if nx and xx:
206             raise Exception(
207                 "nx and xx are mutually exclusive: use one, the "
208                 "other or neither - but not both"
209             )
210         elif nx:
211             pieces.append("NX")
212         elif xx:
213             pieces.append("XX")
214         return self.execute_command("JSON.SET", *pieces)
215 
216     def strlen(self, name, path=None):
217         """Return the length of the string JSON value under ``path`` at key
218         ``name``.
219 
220         For more information: https://oss.redis.com/redisjson/commands/#jsonstrlen
221         """  # noqa
222         pieces = [name]
223         if path is not None:
224             pieces.append(str(path))
225         return self.execute_command("JSON.STRLEN", *pieces)
226 
227     def toggle(self, name, path=Path.rootPath()):
228         """Toggle boolean value under ``path`` at key ``name``.
229         returning the new value.
230 
231         For more information: https://oss.redis.com/redisjson/commands/#jsontoggle
232         """  # noqa
233         return self.execute_command("JSON.TOGGLE", name, str(path))
234 
235     def strappend(self, name, value, path=Path.rootPath()):
236         """Append to the string JSON value. If two options are specified after
237         the key name, the path is determined to be the first. If a single
238         option is passed, then the rootpath (i.e Path.rootPath()) is used.
239 
240         For more information: https://oss.redis.com/redisjson/commands/#jsonstrappend
241         """  # noqa
242         pieces = [name, str(path), self._encode(value)]
243         return self.execute_command("JSON.STRAPPEND", *pieces)
244 
245     def debug(self, subcommand, key=None, path=Path.rootPath()):
246         """Return the memory usage in bytes of a value under ``path`` from
247         key ``name``.
248 
249         For more information: https://oss.redis.com/redisjson/commands/#jsondebg
250         """  # noqa
251         valid_subcommands = ["MEMORY", "HELP"]
252         if subcommand not in valid_subcommands:
253             raise DataError("The only valid subcommands are ", str(valid_subcommands))
254         pieces = [subcommand]
255         if subcommand == "MEMORY":
256             if key is None:
257                 raise DataError("No key specified")
258             pieces.append(key)
259             pieces.append(str(path))
260         return self.execute_command("JSON.DEBUG", *pieces)
261 
262     @deprecated(
263         version="4.0.0", reason="redisjson-py supported this, call get directly."
264     )
265     def jsonget(self, *args, **kwargs):
266         return self.get(*args, **kwargs)
267 
268     @deprecated(
269         version="4.0.0", reason="redisjson-py supported this, call get directly."
270     )
271     def jsonmget(self, *args, **kwargs):
272         return self.mget(*args, **kwargs)
273 
274     @deprecated(
275         version="4.0.0", reason="redisjson-py supported this, call get directly."
276     )
277     def jsonset(self, *args, **kwargs):
278         return self.set(*args, **kwargs)
279
```
--- 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/redis/commands/json/commands.py b/redis/commands/json/commands.py
--- a/redis/commands/json/commands.py
+++ b/redis/commands/json/commands.py
@@ -1,3 +1,6 @@
+import os
+from json import JSONDecodeError, loads
+
 from deprecated import deprecated
 
 from redis.exceptions import DataError
@@ -213,6 +216,54 @@
             pieces.append("XX")
         return self.execute_command("JSON.SET", *pieces)
 
+    def set_file(self, name, path, file_name, nx=False, xx=False, decode_keys=False):
+        """
+        Set the JSON value at key ``name`` under the ``path`` to the content
+        of the json file ``file_name``.
+
+        ``nx`` if set to True, set ``value`` only if it does not exist.
+        ``xx`` if set to True, set ``value`` only if it exists.
+        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded
+        with utf-8.
+
+        """
+
+        with open(file_name, "r") as fp:
+            file_content = loads(fp.read())
+
+        return self.set(name, path, file_content, nx=nx, xx=xx, decode_keys=decode_keys)
+
+    def set_path(self, json_path, root_folder, nx=False, xx=False, decode_keys=False):
+        """
+        Iterate over ``root_folder`` and set each JSON file to a value
+        under ``json_path`` with the file name as the key.
+
+        ``nx`` if set to True, set ``value`` only if it does not exist.
+        ``xx`` if set to True, set ``value`` only if it exists.
+        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded
+        with utf-8.
+
+        """
+        set_files_result = {}
+        for root, dirs, files in os.walk(root_folder):
+            for file in files:
+                file_path = os.path.join(root, file)
+                try:
+                    file_name = file_path.rsplit(".")[0]
+                    self.set_file(
+                        file_name,
+                        json_path,
+                        file_path,
+                        nx=nx,
+                        xx=xx,
+                        decode_keys=decode_keys,
+                    )
+                    set_files_result[file_path] = True
+                except JSONDecodeError:
+                    set_files_result[file_path] = False
+
+        return set_files_result
+
     def strlen(self, name, path=None):
         """Return the length of the string JSON value under ``path`` at key
         ``name``.

{"golden_diff": "diff --git a/redis/commands/json/commands.py b/redis/commands/json/commands.py\n--- a/redis/commands/json/commands.py\n+++ b/redis/commands/json/commands.py\n@@ -1,3 +1,6 @@\n+import os\n+from json import JSONDecodeError, loads\n+\n from deprecated import deprecated\n \n from redis.exceptions import DataError\n@@ -213,6 +216,54 @@\n             pieces.append(\"XX\")\n         return self.execute_command(\"JSON.SET\", *pieces)\n \n+    def set_file(self, name, path, file_name, nx=False, xx=False, decode_keys=False):\n+        \"\"\"\n+        Set the JSON value at key ``name`` under the ``path`` to the content\n+        of the json file ``file_name``.\n+\n+        ``nx`` if set to True, set ``value`` only if it does not exist.\n+        ``xx`` if set to True, set ``value`` only if it exists.\n+        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded\n+        with utf-8.\n+\n+        \"\"\"\n+\n+        with open(file_name, \"r\") as fp:\n+            file_content = loads(fp.read())\n+\n+        return self.set(name, path, file_content, nx=nx, xx=xx, decode_keys=decode_keys)\n+\n+    def set_path(self, json_path, root_folder, nx=False, xx=False, decode_keys=False):\n+        \"\"\"\n+        Iterate over ``root_folder`` and set each JSON file to a value\n+        under ``json_path`` with the file name as the key.\n+\n+        ``nx`` if set to True, set ``value`` only if it does not exist.\n+        ``xx`` if set to True, set ``value`` only if it exists.\n+        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded\n+        with utf-8.\n+\n+        \"\"\"\n+        set_files_result = {}\n+        for root, dirs, files in os.walk(root_folder):\n+            for file in files:\n+                file_path = os.path.join(root, file)\n+                try:\n+                    file_name = file_path.rsplit(\".\")[0]\n+                    self.set_file(\n+                        file_name,\n+                        json_path,\n+                        file_path,\n+                        nx=nx,\n+                        xx=xx,\n+                        decode_keys=decode_keys,\n+                    )\n+                    set_files_result[file_path] = True\n+                except JSONDecodeError:\n+                    set_files_result[file_path] = False\n+\n+        return set_files_result\n+\n     def strlen(self, name, path=None):\n         \"\"\"Return the length of the string JSON value under ``path`` at key\n         ``name``.\n", "issue": "Add helper functions to support loading json from files\nHaving integrated [RedisJSON](https://redisjson.io) it would be nice if we could extend the json().set() behaviour by some new functions\r\n\r\n- [ ] json().set_file() - This would be set the json value stored at the key named _name_ to the contents of the file specified in _obj_, effectively the same behaviour as json().set()\r\n\r\n- [ ] json().set_path() - This would be the same as above, except without a _name_ attribute, and pointing to a directory path. All files in that directory would be stored, under the assumption that the filename, minus it's extension would be the key name.\n", "before_files": [{"content": "from deprecated import deprecated\n\nfrom redis.exceptions import DataError\n\nfrom .decoders import decode_dict_keys\nfrom .path import Path\n\n\nclass JSONCommands:\n    \"\"\"json commands.\"\"\"\n\n    def arrappend(self, name, path=Path.rootPath(), *args):\n        \"\"\"Append the objects ``args`` to the array under the\n        ``path` in key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrappend\n        \"\"\"  # noqa\n        pieces = [name, str(path)]\n        for o in args:\n            pieces.append(self._encode(o))\n        return self.execute_command(\"JSON.ARRAPPEND\", *pieces)\n\n    def arrindex(self, name, path, scalar, start=0, stop=-1):\n        \"\"\"\n        Return the index of ``scalar`` in the JSON array under ``path`` at key\n        ``name``.\n\n        The search can be limited using the optional inclusive ``start``\n        and exclusive ``stop`` indices.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrindex\n        \"\"\"  # noqa\n        return self.execute_command(\n            \"JSON.ARRINDEX\", name, str(path), self._encode(scalar), start, stop\n        )\n\n    def arrinsert(self, name, path, index, *args):\n        \"\"\"Insert the objects ``args`` to the array at index ``index``\n        under the ``path` in key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrinsert\n        \"\"\"  # noqa\n        pieces = [name, str(path), index]\n        for o in args:\n            pieces.append(self._encode(o))\n        return self.execute_command(\"JSON.ARRINSERT\", *pieces)\n\n    def arrlen(self, name, path=Path.rootPath()):\n        \"\"\"Return the length of the array JSON value under ``path``\n        at key``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrlen\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.ARRLEN\", name, str(path))\n\n    def arrpop(self, name, path=Path.rootPath(), index=-1):\n        \"\"\"Pop the element at ``index`` in the array JSON value under\n        ``path`` at key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrpop\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.ARRPOP\", name, str(path), index)\n\n    def arrtrim(self, name, path, start, stop):\n        \"\"\"Trim the array JSON value under ``path`` at key ``name`` to the\n        inclusive range given by ``start`` and ``stop``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrtrim\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.ARRTRIM\", name, str(path), start, stop)\n\n    def type(self, name, path=Path.rootPath()):\n        \"\"\"Get the type of the JSON value under ``path`` from key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsontype\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.TYPE\", name, str(path))\n\n    def resp(self, name, path=Path.rootPath()):\n        \"\"\"Return the JSON value under ``path`` at key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonresp\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.RESP\", name, str(path))\n\n    def objkeys(self, name, path=Path.rootPath()):\n        \"\"\"Return the key names in the dictionary JSON value under ``path`` at\n        key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonobjkeys\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.OBJKEYS\", name, str(path))\n\n    def objlen(self, name, path=Path.rootPath()):\n        \"\"\"Return the length of the dictionary JSON value under ``path`` at key\n        ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonobjlen\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.OBJLEN\", name, str(path))\n\n    def numincrby(self, name, path, number):\n        \"\"\"Increment the numeric (integer or floating point) JSON value under\n        ``path`` at key ``name`` by the provided ``number``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonnumincrby\n        \"\"\"  # noqa\n        return self.execute_command(\n            \"JSON.NUMINCRBY\", name, str(path), self._encode(number)\n        )\n\n    @deprecated(version=\"4.0.0\", reason=\"deprecated since redisjson 1.0.0\")\n    def nummultby(self, name, path, number):\n        \"\"\"Multiply the numeric (integer or floating point) JSON value under\n        ``path`` at key ``name`` with the provided ``number``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonnummultby\n        \"\"\"  # noqa\n        return self.execute_command(\n            \"JSON.NUMMULTBY\", name, str(path), self._encode(number)\n        )\n\n    def clear(self, name, path=Path.rootPath()):\n        \"\"\"\n        Empty arrays and objects (to have zero slots/keys without deleting the\n        array/object).\n\n        Return the count of cleared paths (ignoring non-array and non-objects\n        paths).\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonclear\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.CLEAR\", name, str(path))\n\n    def delete(self, key, path=Path.rootPath()):\n        \"\"\"Delete the JSON value stored at key ``key`` under ``path``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsondel\n        \"\"\"\n        return self.execute_command(\"JSON.DEL\", key, str(path))\n\n    # forget is an alias for delete\n    forget = delete\n\n    def get(self, name, *args, no_escape=False):\n        \"\"\"\n        Get the object stored as a JSON value at key ``name``.\n\n        ``args`` is zero or more paths, and defaults to root path\n        ```no_escape`` is a boolean flag to add no_escape option to get\n        non-ascii characters\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonget\n        \"\"\"  # noqa\n        pieces = [name]\n        if no_escape:\n            pieces.append(\"noescape\")\n\n        if len(args) == 0:\n            pieces.append(Path.rootPath())\n\n        else:\n            for p in args:\n                pieces.append(str(p))\n\n        # Handle case where key doesn't exist. The JSONDecoder would raise a\n        # TypeError exception since it can't decode None\n        try:\n            return self.execute_command(\"JSON.GET\", *pieces)\n        except TypeError:\n            return None\n\n    def mget(self, keys, path):\n        \"\"\"\n        Get the objects stored as a JSON values under ``path``. ``keys``\n        is a list of one or more keys.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonmget\n        \"\"\"  # noqa\n        pieces = []\n        pieces += keys\n        pieces.append(str(path))\n        return self.execute_command(\"JSON.MGET\", *pieces)\n\n    def set(self, name, path, obj, nx=False, xx=False, decode_keys=False):\n        \"\"\"\n        Set the JSON value at key ``name`` under the ``path`` to ``obj``.\n\n        ``nx`` if set to True, set ``value`` only if it does not exist.\n        ``xx`` if set to True, set ``value`` only if it exists.\n        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded\n        with utf-8.\n\n        For the purpose of using this within a pipeline, this command is also\n        aliased to jsonset.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonset\n        \"\"\"\n        if decode_keys:\n            obj = decode_dict_keys(obj)\n\n        pieces = [name, str(path), self._encode(obj)]\n\n        # Handle existential modifiers\n        if nx and xx:\n            raise Exception(\n                \"nx and xx are mutually exclusive: use one, the \"\n                \"other or neither - but not both\"\n            )\n        elif nx:\n            pieces.append(\"NX\")\n        elif xx:\n            pieces.append(\"XX\")\n        return self.execute_command(\"JSON.SET\", *pieces)\n\n    def strlen(self, name, path=None):\n        \"\"\"Return the length of the string JSON value under ``path`` at key\n        ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonstrlen\n        \"\"\"  # noqa\n        pieces = [name]\n        if path is not None:\n            pieces.append(str(path))\n        return self.execute_command(\"JSON.STRLEN\", *pieces)\n\n    def toggle(self, name, path=Path.rootPath()):\n        \"\"\"Toggle boolean value under ``path`` at key ``name``.\n        returning the new value.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsontoggle\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.TOGGLE\", name, str(path))\n\n    def strappend(self, name, value, path=Path.rootPath()):\n        \"\"\"Append to the string JSON value. If two options are specified after\n        the key name, the path is determined to be the first. If a single\n        option is passed, then the rootpath (i.e Path.rootPath()) is used.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonstrappend\n        \"\"\"  # noqa\n        pieces = [name, str(path), self._encode(value)]\n        return self.execute_command(\"JSON.STRAPPEND\", *pieces)\n\n    def debug(self, subcommand, key=None, path=Path.rootPath()):\n        \"\"\"Return the memory usage in bytes of a value under ``path`` from\n        key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsondebg\n        \"\"\"  # noqa\n        valid_subcommands = [\"MEMORY\", \"HELP\"]\n        if subcommand not in valid_subcommands:\n            raise DataError(\"The only valid subcommands are \", str(valid_subcommands))\n        pieces = [subcommand]\n        if subcommand == \"MEMORY\":\n            if key is None:\n                raise DataError(\"No key specified\")\n            pieces.append(key)\n            pieces.append(str(path))\n        return self.execute_command(\"JSON.DEBUG\", *pieces)\n\n    @deprecated(\n        version=\"4.0.0\", reason=\"redisjson-py supported this, call get directly.\"\n    )\n    def jsonget(self, *args, **kwargs):\n        return self.get(*args, **kwargs)\n\n    @deprecated(\n        version=\"4.0.0\", reason=\"redisjson-py supported this, call get directly.\"\n    )\n    def jsonmget(self, *args, **kwargs):\n        return self.mget(*args, **kwargs)\n\n    @deprecated(\n        version=\"4.0.0\", reason=\"redisjson-py supported this, call get directly.\"\n    )\n    def jsonset(self, *args, **kwargs):\n        return self.set(*args, **kwargs)\n", "path": "redis/commands/json/commands.py"}], "after_files": [{"content": "import os\nfrom json import JSONDecodeError, loads\n\nfrom deprecated import deprecated\n\nfrom redis.exceptions import DataError\n\nfrom .decoders import decode_dict_keys\nfrom .path import Path\n\n\nclass JSONCommands:\n    \"\"\"json commands.\"\"\"\n\n    def arrappend(self, name, path=Path.rootPath(), *args):\n        \"\"\"Append the objects ``args`` to the array under the\n        ``path` in key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrappend\n        \"\"\"  # noqa\n        pieces = [name, str(path)]\n        for o in args:\n            pieces.append(self._encode(o))\n        return self.execute_command(\"JSON.ARRAPPEND\", *pieces)\n\n    def arrindex(self, name, path, scalar, start=0, stop=-1):\n        \"\"\"\n        Return the index of ``scalar`` in the JSON array under ``path`` at key\n        ``name``.\n\n        The search can be limited using the optional inclusive ``start``\n        and exclusive ``stop`` indices.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrindex\n        \"\"\"  # noqa\n        return self.execute_command(\n            \"JSON.ARRINDEX\", name, str(path), self._encode(scalar), start, stop\n        )\n\n    def arrinsert(self, name, path, index, *args):\n        \"\"\"Insert the objects ``args`` to the array at index ``index``\n        under the ``path` in key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrinsert\n        \"\"\"  # noqa\n        pieces = [name, str(path), index]\n        for o in args:\n            pieces.append(self._encode(o))\n        return self.execute_command(\"JSON.ARRINSERT\", *pieces)\n\n    def arrlen(self, name, path=Path.rootPath()):\n        \"\"\"Return the length of the array JSON value under ``path``\n        at key``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrlen\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.ARRLEN\", name, str(path))\n\n    def arrpop(self, name, path=Path.rootPath(), index=-1):\n        \"\"\"Pop the element at ``index`` in the array JSON value under\n        ``path`` at key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrpop\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.ARRPOP\", name, str(path), index)\n\n    def arrtrim(self, name, path, start, stop):\n        \"\"\"Trim the array JSON value under ``path`` at key ``name`` to the\n        inclusive range given by ``start`` and ``stop``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonarrtrim\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.ARRTRIM\", name, str(path), start, stop)\n\n    def type(self, name, path=Path.rootPath()):\n        \"\"\"Get the type of the JSON value under ``path`` from key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsontype\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.TYPE\", name, str(path))\n\n    def resp(self, name, path=Path.rootPath()):\n        \"\"\"Return the JSON value under ``path`` at key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonresp\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.RESP\", name, str(path))\n\n    def objkeys(self, name, path=Path.rootPath()):\n        \"\"\"Return the key names in the dictionary JSON value under ``path`` at\n        key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonobjkeys\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.OBJKEYS\", name, str(path))\n\n    def objlen(self, name, path=Path.rootPath()):\n        \"\"\"Return the length of the dictionary JSON value under ``path`` at key\n        ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonobjlen\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.OBJLEN\", name, str(path))\n\n    def numincrby(self, name, path, number):\n        \"\"\"Increment the numeric (integer or floating point) JSON value under\n        ``path`` at key ``name`` by the provided ``number``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonnumincrby\n        \"\"\"  # noqa\n        return self.execute_command(\n            \"JSON.NUMINCRBY\", name, str(path), self._encode(number)\n        )\n\n    @deprecated(version=\"4.0.0\", reason=\"deprecated since redisjson 1.0.0\")\n    def nummultby(self, name, path, number):\n        \"\"\"Multiply the numeric (integer or floating point) JSON value under\n        ``path`` at key ``name`` with the provided ``number``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonnummultby\n        \"\"\"  # noqa\n        return self.execute_command(\n            \"JSON.NUMMULTBY\", name, str(path), self._encode(number)\n        )\n\n    def clear(self, name, path=Path.rootPath()):\n        \"\"\"\n        Empty arrays and objects (to have zero slots/keys without deleting the\n        array/object).\n\n        Return the count of cleared paths (ignoring non-array and non-objects\n        paths).\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonclear\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.CLEAR\", name, str(path))\n\n    def delete(self, key, path=Path.rootPath()):\n        \"\"\"Delete the JSON value stored at key ``key`` under ``path``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsondel\n        \"\"\"\n        return self.execute_command(\"JSON.DEL\", key, str(path))\n\n    # forget is an alias for delete\n    forget = delete\n\n    def get(self, name, *args, no_escape=False):\n        \"\"\"\n        Get the object stored as a JSON value at key ``name``.\n\n        ``args`` is zero or more paths, and defaults to root path\n        ```no_escape`` is a boolean flag to add no_escape option to get\n        non-ascii characters\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonget\n        \"\"\"  # noqa\n        pieces = [name]\n        if no_escape:\n            pieces.append(\"noescape\")\n\n        if len(args) == 0:\n            pieces.append(Path.rootPath())\n\n        else:\n            for p in args:\n                pieces.append(str(p))\n\n        # Handle case where key doesn't exist. The JSONDecoder would raise a\n        # TypeError exception since it can't decode None\n        try:\n            return self.execute_command(\"JSON.GET\", *pieces)\n        except TypeError:\n            return None\n\n    def mget(self, keys, path):\n        \"\"\"\n        Get the objects stored as a JSON values under ``path``. ``keys``\n        is a list of one or more keys.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonmget\n        \"\"\"  # noqa\n        pieces = []\n        pieces += keys\n        pieces.append(str(path))\n        return self.execute_command(\"JSON.MGET\", *pieces)\n\n    def set(self, name, path, obj, nx=False, xx=False, decode_keys=False):\n        \"\"\"\n        Set the JSON value at key ``name`` under the ``path`` to ``obj``.\n\n        ``nx`` if set to True, set ``value`` only if it does not exist.\n        ``xx`` if set to True, set ``value`` only if it exists.\n        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded\n        with utf-8.\n\n        For the purpose of using this within a pipeline, this command is also\n        aliased to jsonset.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonset\n        \"\"\"\n        if decode_keys:\n            obj = decode_dict_keys(obj)\n\n        pieces = [name, str(path), self._encode(obj)]\n\n        # Handle existential modifiers\n        if nx and xx:\n            raise Exception(\n                \"nx and xx are mutually exclusive: use one, the \"\n                \"other or neither - but not both\"\n            )\n        elif nx:\n            pieces.append(\"NX\")\n        elif xx:\n            pieces.append(\"XX\")\n        return self.execute_command(\"JSON.SET\", *pieces)\n\n    def set_file(self, name, path, file_name, nx=False, xx=False, decode_keys=False):\n        \"\"\"\n        Set the JSON value at key ``name`` under the ``path`` to the content\n        of the json file ``file_name``.\n\n        ``nx`` if set to True, set ``value`` only if it does not exist.\n        ``xx`` if set to True, set ``value`` only if it exists.\n        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded\n        with utf-8.\n\n        \"\"\"\n\n        with open(file_name, \"r\") as fp:\n            file_content = loads(fp.read())\n\n        return self.set(name, path, file_content, nx=nx, xx=xx, decode_keys=decode_keys)\n\n    def set_path(self, json_path, root_folder, nx=False, xx=False, decode_keys=False):\n        \"\"\"\n        Iterate over ``root_folder`` and set each JSON file to a value\n        under ``json_path`` with the file name as the key.\n\n        ``nx`` if set to True, set ``value`` only if it does not exist.\n        ``xx`` if set to True, set ``value`` only if it exists.\n        ``decode_keys`` If set to True, the keys of ``obj`` will be decoded\n        with utf-8.\n\n        \"\"\"\n        set_files_result = {}\n        for root, dirs, files in os.walk(root_folder):\n            for file in files:\n                file_path = os.path.join(root, file)\n                try:\n                    file_name = file_path.rsplit(\".\")[0]\n                    self.set_file(\n                        file_name,\n                        json_path,\n                        file_path,\n                        nx=nx,\n                        xx=xx,\n                        decode_keys=decode_keys,\n                    )\n                    set_files_result[file_path] = True\n                except JSONDecodeError:\n                    set_files_result[file_path] = False\n\n        return set_files_result\n\n    def strlen(self, name, path=None):\n        \"\"\"Return the length of the string JSON value under ``path`` at key\n        ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonstrlen\n        \"\"\"  # noqa\n        pieces = [name]\n        if path is not None:\n            pieces.append(str(path))\n        return self.execute_command(\"JSON.STRLEN\", *pieces)\n\n    def toggle(self, name, path=Path.rootPath()):\n        \"\"\"Toggle boolean value under ``path`` at key ``name``.\n        returning the new value.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsontoggle\n        \"\"\"  # noqa\n        return self.execute_command(\"JSON.TOGGLE\", name, str(path))\n\n    def strappend(self, name, value, path=Path.rootPath()):\n        \"\"\"Append to the string JSON value. If two options are specified after\n        the key name, the path is determined to be the first. If a single\n        option is passed, then the rootpath (i.e Path.rootPath()) is used.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsonstrappend\n        \"\"\"  # noqa\n        pieces = [name, str(path), self._encode(value)]\n        return self.execute_command(\"JSON.STRAPPEND\", *pieces)\n\n    def debug(self, subcommand, key=None, path=Path.rootPath()):\n        \"\"\"Return the memory usage in bytes of a value under ``path`` from\n        key ``name``.\n\n        For more information: https://oss.redis.com/redisjson/commands/#jsondebg\n        \"\"\"  # noqa\n        valid_subcommands = [\"MEMORY\", \"HELP\"]\n        if subcommand not in valid_subcommands:\n            raise DataError(\"The only valid subcommands are \", str(valid_subcommands))\n        pieces = [subcommand]\n        if subcommand == \"MEMORY\":\n            if key is None:\n                raise DataError(\"No key specified\")\n            pieces.append(key)\n            pieces.append(str(path))\n        return self.execute_command(\"JSON.DEBUG\", *pieces)\n\n    @deprecated(\n        version=\"4.0.0\", reason=\"redisjson-py supported this, call get directly.\"\n    )\n    def jsonget(self, *args, **kwargs):\n        return self.get(*args, **kwargs)\n\n    @deprecated(\n        version=\"4.0.0\", reason=\"redisjson-py supported this, call get directly.\"\n    )\n    def jsonmget(self, *args, **kwargs):\n        return self.mget(*args, **kwargs)\n\n    @deprecated(\n        version=\"4.0.0\", reason=\"redisjson-py supported this, call get directly.\"\n    )\n    def jsonset(self, *args, **kwargs):\n        return self.set(*args, **kwargs)\n", "path": "redis/commands/json/commands.py"}]}