diff --git a/.github/labeler.yml b/.github/labeler.yml index 3eff6854081..d1fc32abfea 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -231,6 +231,7 @@ - redbot/core/_global_checks.py - redbot/core/_settings_caches.py - redbot/core/_sharedlibdeprecation.py + - redbot/core/_json.py - redbot/core/utils/_internal_utils.py # Tests - redbot/pytest/__init__.py diff --git a/redbot/__main__.py b/redbot/__main__.py index d32e7fd3be3..10e212bf13f 100644 --- a/redbot/__main__.py +++ b/redbot/__main__.py @@ -6,7 +6,6 @@ import asyncio import functools import getpass -import json import logging import os import pip @@ -27,7 +26,7 @@ from redbot.core.bot import Red, ExitCodes, _NoOwnerSet from redbot.core._cli import interactive_config, confirm, parse_cli_flags from redbot.setup import get_data_dir, get_name, save_config -from redbot.core import data_manager, _drivers +from redbot.core import data_manager, _drivers, _json from redbot.core._debuginfo import DebugInfo from redbot.core._sharedlibdeprecation import SharedLibImportWarner @@ -43,7 +42,7 @@ def _get_instance_names(): with data_manager.config_file.open(encoding="utf-8") as fs: - data = json.load(fs) + data = _json.load(fs) return sorted(data.keys()) diff --git a/redbot/cogs/audio/apis/api_utils.py b/redbot/cogs/audio/apis/api_utils.py index 8c5c44ae5e7..0480d0821b2 100644 --- a/redbot/cogs/audio/apis/api_utils.py +++ b/redbot/cogs/audio/apis/api_utils.py @@ -1,5 +1,4 @@ import datetime -import json from collections import namedtuple from dataclasses import dataclass, field from pathlib import Path @@ -9,6 +8,7 @@ import lavalink from red_commons.logging import getLogger +from redbot.core import _json from redbot.core.bot import Red from redbot.core.i18n import Translator from redbot.core.utils.chat_formatting import humanize_list @@ -50,7 +50,7 @@ def __post_init__(self): self.updated_on: datetime.datetime = datetime.datetime.fromtimestamp(self.last_updated) if isinstance(self.query, str): - self.query = json.loads(self.query) + self.query = _json.loads(self.query) @dataclass @@ -61,7 +61,7 @@ class LavalinkCacheFetchForGlobalResult: def __post_init__(self): if isinstance(self.data, str): self.data_string = str(self.data) - self.data = json.loads(self.data) + self.data = _json.loads(self.data) @dataclass @@ -75,7 +75,7 @@ class PlaylistFetchResult: def __post_init__(self): if isinstance(self.tracks, str): - self.tracks = json.loads(self.tracks) + self.tracks = _json.loads(self.tracks) @dataclass @@ -87,7 +87,7 @@ class QueueFetchResult: def __post_init__(self): if isinstance(self.track, str): - self.track = json.loads(self.track) + self.track = _json.loads(self.track) if self.track: self.track_object = lavalink.Track(self.track) diff --git a/redbot/cogs/audio/apis/global_db.py b/redbot/cogs/audio/apis/global_db.py index 732fec4209e..577f9832a17 100644 --- a/redbot/cogs/audio/apis/global_db.py +++ b/redbot/cogs/audio/apis/global_db.py @@ -1,6 +1,5 @@ import asyncio import contextlib -import json from copy import copy from pathlib import Path @@ -10,7 +9,7 @@ from lavalink.rest_api import LoadResult from red_commons.logging import getLogger -from redbot.core import Config +from redbot.core import Config, _json from redbot.core.bot import Red from redbot.core.commands import Cog from redbot.core.i18n import Translator @@ -74,7 +73,7 @@ async def get_call(self, query: Optional[Query] = None) -> dict: headers={"Authorization": self.api_key, "X-Token": self._handshake_token}, params={"query": query}, ) as r: - search_response = await r.json(loads=json.loads) + search_response = await r.json(loads=_json.loads) log.trace( "GET || Ping %s || Status code %s || %s", r.headers.get("x-process-time"), @@ -105,7 +104,7 @@ async def get_spotify(self, title: str, author: Optional[str]) -> dict: headers={"Authorization": self.api_key, "X-Token": self._handshake_token}, params=params, ) as r: - search_response = await r.json(loads=json.loads) + search_response = await r.json(loads=_json.loads) log.trace( "GET/spotify || Ping %s || Status code %s || %s - %s", r.headers.get("x-process-time"), @@ -175,13 +174,13 @@ async def get_perms(self): if (not is_enabled) or self.api_key is None: return global_api_user with contextlib.suppress(Exception): - async with aiohttp.ClientSession(json_serialize=json.dumps) as session: + async with aiohttp.ClientSession(json_serialize=_json.dumps) as session: async with session.get( f"{_API_URL}api/v2/users/me", headers={"Authorization": self.api_key, "X-Token": self._handshake_token}, ) as resp: if resp.status == 200: - search_response = await resp.json(loads=json.loads) + search_response = await resp.json(loads=_json.loads) global_api_user["fetched"] = True global_api_user["can_read"] = search_response.get("can_read", False) global_api_user["can_post"] = search_response.get("can_post", False) diff --git a/redbot/cogs/audio/apis/interface.py b/redbot/cogs/audio/apis/interface.py index 1fa55ab0f93..8e3bc6faf1b 100644 --- a/redbot/cogs/audio/apis/interface.py +++ b/redbot/cogs/audio/apis/interface.py @@ -1,7 +1,6 @@ import asyncio import contextlib import datetime -import json import random import time @@ -15,7 +14,7 @@ from red_commons.logging import getLogger from lavalink.rest_api import LoadResult, LoadType -from redbot.core import Config, commands +from redbot.core import Config, commands, _json from redbot.core.bot import Red from redbot.core.commands import Cog, Context from redbot.core.i18n import Translator @@ -896,7 +895,7 @@ async def fetch_track( ): try: time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp()) - data = json.dumps(results._raw) + data = _json.dumps(results._raw) if all(k in data for k in ["loadType", "playlistInfo", "isSeekable", "isStream"]): task = ( "insert", diff --git a/redbot/cogs/audio/apis/persist_queue_wrapper.py b/redbot/cogs/audio/apis/persist_queue_wrapper.py index acd89a55c90..cee72dada3e 100644 --- a/redbot/cogs/audio/apis/persist_queue_wrapper.py +++ b/redbot/cogs/audio/apis/persist_queue_wrapper.py @@ -1,5 +1,4 @@ import concurrent -import json import time from pathlib import Path @@ -9,7 +8,7 @@ import lavalink from red_commons.logging import getLogger -from redbot.core import Config +from redbot.core import Config, _json from redbot.core.bot import Red from redbot.core.commands import Cog from redbot.core.i18n import Translator @@ -129,7 +128,7 @@ async def enqueued(self, guild_id: int, room_id: int, track: lavalink.Track): "room_id": int(room_id), "played": False, "time": enqueue_time, - "track": json.dumps(track), + "track": _json.dumps(track), "track_id": track_identifier, }, ) diff --git a/redbot/cogs/audio/apis/playlist_wrapper.py b/redbot/cogs/audio/apis/playlist_wrapper.py index 090dbaac3d1..7bebdb087ec 100644 --- a/redbot/cogs/audio/apis/playlist_wrapper.py +++ b/redbot/cogs/audio/apis/playlist_wrapper.py @@ -1,5 +1,4 @@ import concurrent -import json from pathlib import Path from types import SimpleNamespace @@ -7,7 +6,7 @@ from red_commons.logging import getLogger -from redbot.core import Config +from redbot.core import Config, _json from redbot.core.bot import Red from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter @@ -258,7 +257,7 @@ async def upsert( "scope_id": int(scope_id), "author_id": int(author_id), "playlist_url": playlist_url, - "tracks": json.dumps(tracks), + "tracks": _json.dumps(tracks), }, ) diff --git a/redbot/cogs/audio/apis/spotify.py b/redbot/cogs/audio/apis/spotify.py index 05b8d74a468..fb021b75ae3 100644 --- a/redbot/cogs/audio/apis/spotify.py +++ b/redbot/cogs/audio/apis/spotify.py @@ -1,6 +1,5 @@ import base64 import contextlib -import json import time from pathlib import Path @@ -9,7 +8,7 @@ import aiohttp from red_commons.logging import getLogger -from redbot.core import Config +from redbot.core import Config, _json from redbot.core.bot import Red from redbot.core.commands import Cog, Context from redbot.core.i18n import Translator @@ -100,7 +99,7 @@ async def get( if params is None: params = {} async with self.session.request("GET", url, params=params, headers=headers) as r: - data = await r.json(loads=json.loads) + data = await r.json(loads=_json.loads) if r.status != 200: log.verbose("Issue making GET request to %r: [%s] %r", url, r.status, data) return data @@ -154,7 +153,7 @@ async def post( ) -> MutableMapping: """Make a POST call to spotify.""" async with self.session.post(url, data=payload, headers=headers) as r: - data = await r.json(loads=json.loads) + data = await r.json(loads=_json.loads) if r.status != 200: log.verbose("Issue making POST request to %r: [%s] %r", url, r.status, data) return data diff --git a/redbot/cogs/audio/apis/youtube.py b/redbot/cogs/audio/apis/youtube.py index 93bb3943845..54984b276a2 100644 --- a/redbot/cogs/audio/apis/youtube.py +++ b/redbot/cogs/audio/apis/youtube.py @@ -1,4 +1,3 @@ -import json from pathlib import Path from typing import TYPE_CHECKING, Mapping, Optional, Union @@ -6,7 +5,7 @@ import aiohttp from red_commons.logging import getLogger -from redbot.core import Config +from redbot.core import Config, _json from redbot.core.bot import Red from redbot.core.commands import Cog from redbot.core.i18n import Translator @@ -80,7 +79,7 @@ async def get_call(self, query: str) -> Optional[str]: ) return None else: - search_response = await r.json(loads=json.loads) + search_response = await r.json(loads=_json.loads) for search_result in search_response.get("items", []): if search_result["id"]["kind"] == "youtube#video": return f"https://www.youtube.com/watch?v={search_result['id']['videoId']}" diff --git a/redbot/cogs/audio/core/__init__.py b/redbot/cogs/audio/core/__init__.py index d78d4c56baa..d401dd40a07 100644 --- a/redbot/cogs/audio/core/__init__.py +++ b/redbot/cogs/audio/core/__init__.py @@ -1,6 +1,5 @@ import asyncio import datetime -import json from collections import Counter, defaultdict from pathlib import Path @@ -9,7 +8,7 @@ import aiohttp import discord -from redbot.core import Config +from redbot.core import Config, _json from redbot.core.bot import Red from redbot.core.commands import Cog from redbot.core.data_manager import cog_data_path @@ -78,7 +77,7 @@ def __init__(self, bot: Red): add_reactions=True, ) - self.session = aiohttp.ClientSession(json_serialize=json.dumps) + self.session = aiohttp.ClientSession(json_serialize=_json.dumps) self.cog_ready_event = asyncio.Event() self._ws_resume = defaultdict(asyncio.Event) self._ws_op_codes = defaultdict(asyncio.LifoQueue) diff --git a/redbot/cogs/audio/core/commands/playlists.py b/redbot/cogs/audio/core/commands/playlists.py index b654828f8e3..804c3626322 100644 --- a/redbot/cogs/audio/core/commands/playlists.py +++ b/redbot/cogs/audio/core/commands/playlists.py @@ -1,5 +1,4 @@ import asyncio -import json import math import os import tarfile @@ -14,7 +13,7 @@ import lavalink from red_commons.logging import getLogger -from redbot.core import commands +from redbot.core import commands, _json from redbot.core.commands import UserInputOptional from redbot.core.data_manager import cog_data_path from redbot.core.i18n import Translator @@ -742,7 +741,7 @@ async def command_playlist_download( playlist_data["link"] = playlist.url file_name = playlist.id playlist_data.update({"schema": schema, "version": version}) - playlist_data = json.dumps(playlist_data).encode("utf-8") + playlist_data = _json.dumps(playlist_data).encode("utf-8") to_write = BytesIO() to_write.write(playlist_data) to_write.seek(0) @@ -1832,7 +1831,7 @@ async def command_playlist_upload( try: async with self.session.request("GET", file_url) as r: uploaded_playlist = await r.json( - content_type="text/plain", encoding="utf-8", loads=json.loads + content_type="text/plain", encoding="utf-8", loads=_json.loads ) except UnicodeDecodeError: return await self.send_embed_msg(ctx, title=_("Not a valid playlist file.")) diff --git a/redbot/cogs/audio/core/utilities/miscellaneous.py b/redbot/cogs/audio/core/utilities/miscellaneous.py index 4e3edce3011..486d8a415ff 100644 --- a/redbot/cogs/audio/core/utilities/miscellaneous.py +++ b/redbot/cogs/audio/core/utilities/miscellaneous.py @@ -2,7 +2,6 @@ import contextlib import datetime import functools -import json import re import struct from pathlib import Path @@ -12,7 +11,7 @@ import lavalink from red_commons.logging import getLogger -from redbot.core import bank, commands +from redbot.core import bank, commands, _json from redbot.core.commands import Context from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter, can_user_send_messages_in @@ -310,7 +309,7 @@ async def data_schema_migration(self, from_version: int, to_version: int) -> Non uri = t.get("info", {}).get("uri") if uri: t = {"loadType": "V2_COMPAT", "tracks": [t], "query": uri} - data = json.dumps(t) + data = _json.dumps(t) if all( k in data for k in ["loadType", "playlistInfo", "isSeekable", "isStream"] diff --git a/redbot/cogs/audio/core/utilities/playlists.py b/redbot/cogs/audio/core/utilities/playlists.py index 57f00cc3e0e..21845bcced7 100644 --- a/redbot/cogs/audio/core/utilities/playlists.py +++ b/redbot/cogs/audio/core/utilities/playlists.py @@ -1,7 +1,6 @@ import asyncio import contextlib import datetime -import json import math import random import time @@ -15,7 +14,7 @@ from lavalink import NodeNotFound from red_commons.logging import getLogger -from redbot.core import commands +from redbot.core import commands, _json from redbot.core.i18n import Translator from redbot.core.utils import AsyncIter from redbot.core.utils.chat_formatting import box @@ -381,7 +380,7 @@ async def _load_v3_playlist( uri = t.get("info", {}).get("uri") if uri: t = {"loadType": "V2_COMPAT", "tracks": [t], "query": uri} - data = json.dumps(t) + data = _json.dumps(t) if all(k in data for k in ["loadType", "playlistInfo", "isSeekable", "isStream"]): database_entries.append( { @@ -684,7 +683,7 @@ def humanize_scope( return str(ctx) if ctx else _("the User") if the else _("User") async def _get_bundled_playlist_tracks(self): - async with aiohttp.ClientSession(json_serialize=json.dumps) as session: + async with aiohttp.ClientSession(json_serialize=_json.dumps) as session: async with session.get( CURATED_DATA + f"?timestamp={int(time.time())}", headers={"content-type": "application/json"}, @@ -692,7 +691,7 @@ async def _get_bundled_playlist_tracks(self): if response.status != 200: return 0, [] try: - data = json.loads(await response.read()) + data = _json.loads(await response.read()) except Exception as exc: log.error( "Curated playlist couldn't be parsed, report this error.", exc_info=exc diff --git a/redbot/cogs/audio/manager.py b/redbot/cogs/audio/manager.py index 7e7d0375a6d..e3229ddf50a 100644 --- a/redbot/cogs/audio/manager.py +++ b/redbot/cogs/audio/manager.py @@ -2,7 +2,6 @@ import asyncio.subprocess # disables for # https://github.com/PyCQA/pylint/issues/1469 import contextlib import itertools -import json import pathlib import platform import re @@ -19,7 +18,7 @@ from discord.backoff import ExponentialBackoff from red_commons.logging import getLogger -from redbot.core import data_manager, Config +from redbot.core import data_manager, Config, _json from redbot.core.i18n import Translator from .errors import ( @@ -555,7 +554,7 @@ async def _partial_shutdown(self) -> None: async def _download_jar(self) -> None: log.info("Downloading Lavalink.jar...") - async with aiohttp.ClientSession(json_serialize=json.dumps) as session: + async with aiohttp.ClientSession(json_serialize=_json.dumps) as session: async with session.get(self.LAVALINK_DOWNLOAD_URL) as response: if response.status == 404: # A 404 means our LAVALINK_DOWNLOAD_URL is invalid, so likely the jar version diff --git a/redbot/cogs/downloader/json_mixins.py b/redbot/cogs/downloader/json_mixins.py index 441d016472b..e3eb94f31a2 100644 --- a/redbot/cogs/downloader/json_mixins.py +++ b/redbot/cogs/downloader/json_mixins.py @@ -1,7 +1,8 @@ -import json from pathlib import Path from typing import Any, Dict, Tuple +from redbot.core import _json + from .info_schemas import REPO_SCHEMA, update_mixin from .log import log @@ -26,8 +27,8 @@ def _read_info_file(self) -> None: if self._info_file.exists(): try: with self._info_file.open(encoding="utf-8") as f: - info = json.load(f) - except json.JSONDecodeError as e: + info = _json.load(f) + except _json.JSONDecodeError as e: log.error( "Invalid JSON information file at path: %s\nError: %s", self._info_file, str(e) ) diff --git a/redbot/cogs/streams/streamtypes.py b/redbot/cogs/streams/streamtypes.py index 5c7290eb4be..ac9e92dfe6e 100644 --- a/redbot/cogs/streams/streamtypes.py +++ b/redbot/cogs/streams/streamtypes.py @@ -1,6 +1,5 @@ import asyncio import contextlib -import json import logging import time from dateutil.parser import parse as parse_time @@ -21,6 +20,7 @@ StreamNotFound, YoutubeQuotaExceeded, ) +from redbot.core import _json from redbot.core.i18n import Translator from redbot.core.utils.chat_formatting import humanize_number, humanize_timedelta @@ -472,7 +472,7 @@ async def is_online(self): async with session.get(url) as r: data = await r.text(encoding="utf-8") if r.status == 200: - data = json.loads(data) + data = _json.loads(data) # Reset the retry count since we successfully got information about this # channel's streams self.retry_count = 0 diff --git a/redbot/core/_drivers/json.py b/redbot/core/_drivers/json.py index 4dd60a71c4b..7bf66b7bb4f 100644 --- a/redbot/core/_drivers/json.py +++ b/redbot/core/_drivers/json.py @@ -1,5 +1,4 @@ import asyncio -import json import logging import os import pickle @@ -9,7 +8,7 @@ from typing import Any, AsyncIterator, Dict, Optional, Tuple from uuid import uuid4 -from .. import data_manager, errors +from .. import data_manager, errors, _json from .base import BaseDriver, IdentifierData, ConfigCategory __all__ = ["JsonDriver"] @@ -113,11 +112,11 @@ def _load_data(self): try: with self.data_path.open("r", encoding="utf-8") as fs: - self.data = json.load(fs) + self.data = _json.load(fs) except FileNotFoundError: self.data = {} with self.data_path.open("w", encoding="utf-8") as fs: - json.dump(self.data, fs) + _json.dump(self.data, fs) def migrate_identifier(self, raw_identifier: int): if self.unique_cog_identifier in self.data: @@ -143,7 +142,7 @@ async def set(self, identifier_data: IdentifierData, value=None): full_identifiers = identifier_data.to_tuple()[1:] # This is both our deepcopy() and our way of making sure this value is actually JSON # serializable. - value_copy = json.loads(json.dumps(value)) + value_copy = _json.loads(_json.dumps(value)) async with self._lock: for i in full_identifiers[:-1]: @@ -182,8 +181,8 @@ async def aiter_cogs(cls) -> AsyncIterator[Tuple[str, str]]: continue with fpath.open() as f: try: - data = json.load(f) - except json.JSONDecodeError: + data = _json.load(f) + except _json.JSONDecodeError: continue if not isinstance(data, dict): continue @@ -245,7 +244,7 @@ def _save_json(path: Path, data: Dict[str, Any]) -> None: tmp_file = "{}-{}.tmp".format(filename, uuid4().fields[0]) tmp_path = path.parent / tmp_file with tmp_path.open(encoding="utf-8", mode="w") as fs: - json.dump(data, fs) + _json.dump(data, fs) fs.flush() # This does get closed on context exit, ... os.fsync(fs.fileno()) # but that needs to happen prior to this line diff --git a/redbot/core/_drivers/postgres/postgres.py b/redbot/core/_drivers/postgres/postgres.py index 5e77c0819fa..752d2d174dd 100644 --- a/redbot/core/_drivers/postgres/postgres.py +++ b/redbot/core/_drivers/postgres/postgres.py @@ -1,5 +1,4 @@ import getpass -import json import sys from pathlib import Path from typing import Optional, Any, AsyncIterator, Tuple, Union, Callable, List @@ -10,7 +9,7 @@ except ModuleNotFoundError: asyncpg = None -from ... import data_manager, errors +from ... import data_manager, errors, _json from ..base import BaseDriver, IdentifierData, ConfigCategory from ..log import log @@ -146,14 +145,14 @@ async def get(self, identifier_data: IdentifierData): # The result is None both when postgres yields no results, or when it yields a NULL row # A 'null' JSON value would be returned as encoded JSON, i.e. the string 'null' raise KeyError - return json.loads(result) + return _json.loads(result) async def set(self, identifier_data: IdentifierData, value=None): try: await self._execute( "SELECT red_config.set($1, $2::jsonb)", encode_identifier_data(identifier_data), - json.dumps(value), + _json.dumps(value), ) except asyncpg.ErrorInAssignmentError: raise errors.CannotSetSubfield diff --git a/redbot/core/_json.py b/redbot/core/_json.py new file mode 100644 index 00000000000..8c6d37adca0 --- /dev/null +++ b/redbot/core/_json.py @@ -0,0 +1,66 @@ +import json +from typing import Any, Callable, Optional, TextIO + +try: + import orjson +except (ModuleNotFoundError, ImportError): + HAS_ORJSON = False +else: + HAS_ORJSON = True + + +__all__ = ( + "JSONEncodeError", + "JSONDecodeError", + "dumps", + "dump", + "loads", + "load" +) + + +if HAS_ORJSON: + JSONEncodeError = orjson.JSONEncodeError # type: ignore + JSONDecodeError = orjson.JSONDecodeError # type: ignore +else: + JSONEncodeError = ValueError, TypeError + JSONDecodeError = json.JSONDecodeError + + +def dumps( + obj: Any, + *, + default: Optional[Callable[[Any], Any]] = None, + orjson_option: Optional[int] = None, + **json_kwargs: Any, +) -> str: + if HAS_ORJSON: + return orjson.dumps( # type: ignore + obj, + default=default, + option=orjson_option, + ).decode("utf-8") + else: + return json.dumps(obj, default=default, **json_kwargs) + + +def dump( + obj: Any, + fp: TextIO, + *, + default: Optional[Callable[[Any], Any]] = None, + orjson_option: Optional[int] = None, + **kwargs: Any, +) -> None: + fp.write(dumps(obj, default=default, orjson_option=orjson_option, **kwargs)) + + +def loads(obj: Any, **json_kwargs: Any) -> Any: + if HAS_ORJSON: + return orjson.loads(obj) # type: ignore + else: + return json.loads(obj, **json_kwargs) + + +def load(fp: TextIO, **kwargs: Any) -> Any: + return loads(fp.read(), **kwargs) diff --git a/redbot/core/config.py b/redbot/core/config.py index dc6bbed7464..03757c4afd1 100644 --- a/redbot/core/config.py +++ b/redbot/core/config.py @@ -1,6 +1,5 @@ import asyncio import collections.abc -import json import logging import pickle import weakref @@ -19,7 +18,7 @@ ) import discord - +from . import _json from ._drivers import BaseDriver, ConfigCategory, IdentifierData, get_driver __all__ = ( @@ -816,7 +815,7 @@ def _register_default(self, key: str, **kwargs: Any): self._defaults[key] = {} # this serves as a 'deep copy' and verification that the default is serializable to JSON - data = json.loads(json.dumps(kwargs)) + data = _json.loads(_json.dumps(kwargs)) for k, v in data.items(): to_add = self._get_defaults_dict(k, v) diff --git a/redbot/core/data_manager.py b/redbot/core/data_manager.py index c221301b151..e1aeaf6bb2c 100644 --- a/redbot/core/data_manager.py +++ b/redbot/core/data_manager.py @@ -1,5 +1,4 @@ import inspect -import json import logging import os import sys @@ -10,7 +9,7 @@ import platformdirs -from . import commands +from . import commands, _json from ._cli import ExitCodes __all__ = ( @@ -66,7 +65,7 @@ def load_existing_config(): return {} with config_file.open(encoding="utf-8") as fs: - return json.load(fs) + return _json.load(fs) def create_temp_config(): @@ -89,7 +88,7 @@ def create_temp_config(): config[name] = default_dirs with config_file.open("w", encoding="utf-8") as fs: - json.dump(config, fs, indent=4) + _json.dump(config, fs, indent=4) def load_basic_configuration(instance_name_: str): @@ -112,7 +111,7 @@ def load_basic_configuration(instance_name_: str): try: with config_file.open(encoding="utf-8") as fs: - config = json.load(fs) + config = _json.load(fs) except FileNotFoundError: print( "You need to configure the bot instance using `redbot-setup`" diff --git a/redbot/core/utils/__init__.py b/redbot/core/utils/__init__.py index dc3687f6d82..c9e1855d77e 100644 --- a/redbot/core/utils/__init__.py +++ b/redbot/core/utils/__init__.py @@ -1,6 +1,5 @@ from __future__ import annotations import asyncio -import json import logging from asyncio import as_completed, Semaphore from asyncio.futures import isfuture @@ -31,7 +30,7 @@ from discord.ext import commands as dpy_commands from discord.utils import maybe_coroutine -from redbot.core import commands +from redbot.core import commands, _json if TYPE_CHECKING: GuildMessageable = Union[ @@ -580,7 +579,7 @@ async def setup(bot): log.critical("'%s' does not exist.", str(info_json)) except KeyError: log.critical("'%s' is missing an entry for 'end_user_data_statement'", str(info_json)) - except json.JSONDecodeError as exc: + except _json.JSONDecodeError as exc: log.critical("'%s' is not a valid JSON file.", str(info_json), exc_info=exc) except UnicodeError as exc: log.critical("'%s' has a bad encoding.", str(info_json), exc_info=exc) @@ -636,8 +635,8 @@ async def setup(bot): When ``info.json`` does not exist. KeyError When ``info.json`` does not have the ``end_user_data_statement`` key. - json.JSONDecodeError - When ``info.json`` can't be decoded with ``json.load()`` + JSONDecodeError + When ``info.json`` can't be decoded with ``.load()`` UnicodeError When ``info.json`` can't be decoded due to bad encoding. Exception @@ -647,7 +646,7 @@ async def setup(bot): file = Path(file).parent.absolute() info_json = file / "info.json" with info_json.open(encoding="utf-8") as fp: - return json.load(fp)["end_user_data_statement"] + return _json.load(fp)["end_user_data_statement"] @overload diff --git a/redbot/core/utils/_internal_utils.py b/redbot/core/utils/_internal_utils.py index 08813620384..6fa1bdc906f 100644 --- a/redbot/core/utils/_internal_utils.py +++ b/redbot/core/utils/_internal_utils.py @@ -3,7 +3,6 @@ import asyncio import collections.abc import contextlib -import json import logging import os import re @@ -38,7 +37,7 @@ from red_commons.logging import VERBOSE, TRACE from redbot import VersionInfo -from redbot.core import data_manager +from redbot.core import data_manager, _json from redbot.core.utils.chat_formatting import box if TYPE_CHECKING: @@ -245,10 +244,10 @@ async def create_backup(dest: Path = Path.home()) -> Optional[Path]: repo_output.append({"url": repo.url, "name": repo.name, "branch": repo.branch}) repos_file = data_path / "cogs" / "RepoManager" / "repos.json" with repos_file.open("w") as fs: - json.dump(repo_output, fs, indent=4) + _json.dump(repo_output, fs, indent=4) instance_file = data_path / "instance.json" with instance_file.open("w") as fs: - json.dump({data_manager.instance_name(): data_manager.basic_config}, fs, indent=4) + _json.dump({data_manager.instance_name(): data_manager.basic_config}, fs, indent=4) for f in data_path.glob("**/*"): if not any(ex in str(f) for ex in exclusions) and f.is_file(): to_backup.append(f) diff --git a/redbot/setup.py b/redbot/setup.py index 270fa7315a1..5284b82a2e8 100644 --- a/redbot/setup.py +++ b/redbot/setup.py @@ -4,7 +4,6 @@ _early_init() import asyncio -import json import logging import sys import re @@ -20,7 +19,7 @@ create_backup as red_create_backup, cli_level_to_log_level, ) -from redbot.core import config, data_manager, _drivers +from redbot.core import config, data_manager, _drivers, _json from redbot.core._cli import ExitCodes from redbot.core.data_manager import appdir, config_dir, config_file from redbot.core._drivers import BackendType, IdentifierData @@ -48,7 +47,7 @@ def save_config(name, data, remove=False): _config[name] = data with config_file.open("w", encoding="utf-8") as fs: - json.dump(_config, fs, indent=4) + _json.dump(_config, fs, indent=4) def get_data_dir(*, instance_name: str, data_path: Optional[Path], interactive: bool) -> str: