diff --git a/docs/src/API Reference/ext/index.md b/docs/src/API Reference/ext/index.md index 5ddb94028..1283405cf 100644 --- a/docs/src/API Reference/ext/index.md +++ b/docs/src/API Reference/ext/index.md @@ -5,6 +5,9 @@ These files contain useful features that help you develop a bot - [Debug Extension](debug_ext) - An extension preloaded with a load of debugging utilities to help you find and fix bugs +- [Jurigged](jurigged) + - An extension to enable live code patching for faster development + - [Paginators](paginators) - An automatic message paginator to help you get a lot of information across diff --git a/docs/src/API Reference/ext/jurigged.md b/docs/src/API Reference/ext/jurigged.md new file mode 100644 index 000000000..ace945bf2 --- /dev/null +++ b/docs/src/API Reference/ext/jurigged.md @@ -0,0 +1 @@ +::: naff.ext.jurigged diff --git a/docs/src/Guides/22 Live Patching.md b/docs/src/Guides/22 Live Patching.md new file mode 100644 index 000000000..b2ff67071 --- /dev/null +++ b/docs/src/Guides/22 Live Patching.md @@ -0,0 +1,19 @@ +# Live Patching + +NAFF has a few built-in extensions that add some features, primarily for debugging. One of these extensions that you can enable separately is to add [`jurigged`](https://github.com/breuleux/jurigged) for live patching of code. + +## How to enable + +```py +bot.load_extension("naff.ext.jurigged") +``` + +That's it! The extension will handle all of the leg work, and all you'll notice is that you have more messages in your logs (depending on the log level). + +## What is jurigged? + +`jurigged` is a library written to allow code hot reloading in Python. It allows you to edit code and have it automagically be updated in your program the next time it is run. The code under the hood is extremely complicated, but the interface to use it is relatively simple. + +## How is this useful? + +NAFF takes advantage of jurigged to reload any and all commands that were edited whenever a change is made, allowing you to have more uptime with while still adding/improving features of your bot. diff --git a/docs/src/Guides/22 Voice.md b/docs/src/Guides/23 Voice.md similarity index 100% rename from docs/src/Guides/22 Voice.md rename to docs/src/Guides/23 Voice.md diff --git a/docs/src/Guides/23 Localisation.md b/docs/src/Guides/24 Localisation.md similarity index 100% rename from docs/src/Guides/23 Localisation.md rename to docs/src/Guides/24 Localisation.md diff --git a/docs/src/Guides/24 Error Tracking.md b/docs/src/Guides/25 Error Tracking.md similarity index 100% rename from docs/src/Guides/24 Error Tracking.md rename to docs/src/Guides/25 Error Tracking.md diff --git a/naff/ext/jurigged.py b/naff/ext/jurigged.py new file mode 100644 index 000000000..98913b6ab --- /dev/null +++ b/naff/ext/jurigged.py @@ -0,0 +1,209 @@ +import inspect +from pathlib import Path +from types import ModuleType +from typing import Callable, Dict + +from naff import Extension, SlashCommand, listen +from naff.client.errors import ExtensionLoadException, ExtensionNotFound +from naff.client.utils.misc_utils import find +from naff.client.const import get_logger + +try: + from jurigged import watch, CodeFile + from jurigged.live import WatchOperation + from jurigged.codetools import ( + AddOperation, + DeleteOperation, + UpdateOperation, + LineDefinition, + ) +except ModuleNotFoundError: + get_logger().error( + "jurigged not installed, cannot enable jurigged integration. Install with `pip install naff[jurigged]`" + ) + raise + + +__all__ = ("Jurigged", "setup") + + +def get_all_commands(module: ModuleType) -> Dict[str, Callable]: + """ + Get all SlashCommands from a specified module. + + Args: + module: Module to extract commands from + """ + commands = {} + + def is_extension(e) -> bool: + """Check that an object is an extension.""" + return inspect.isclass(e) and issubclass(e, Extension) and e is not Extension + + def is_slashcommand(e) -> bool: + """Check that an object is a slash command.""" + return isinstance(e, SlashCommand) + + for _name, item in inspect.getmembers(module, is_extension): + inspect_result = inspect.getmembers(item, is_slashcommand) + exts = [] + for _, val in inspect_result: + exts.append(val) + commands[f"{module.__name__}"] = exts + + return {k: v for k, v in commands.items() if v is not None} + + +class Jurigged(Extension): + @listen(event_name="on_startup") + async def jurigged_startup(self) -> None: + """Jurigged starting utility.""" + self.command_cache = {} + self.bot.logger.warning("Setting sync_ext to True by default for syncing changes") + self.bot.sync_ext = True + + self.bot.logger.info("Loading jurigged") + path = Path().resolve() + self.watcher = watch(f"{path}/[!.]*.py", logger=self.jurigged_log) + self.watcher.prerun.register(self.jurigged_prerun) + self.watcher.postrun.register(self.jurigged_postrun) + + def jurigged_log(self, event: WatchOperation | AddOperation | DeleteOperation | UpdateOperation) -> None: + """ + Log a jurigged event + + Args: + event: jurigged event + """ + if isinstance(event, WatchOperation): + self.bot.logger.debug(f"Watch {event.filename}") + elif isinstance(event, (Exception, SyntaxError)): + self.bot.logger.exception("Jurigged encountered an error", exc_info=True) + else: + event_str = "{action} {dotpath}:{lineno}{extra}" + action = None + lineno = event.defn.stashed.lineno + dotpath = event.defn.dotpath() + extra = "" + + if isinstance(event.defn, LineDefinition): + dotpath = event.defn.parent.dotpath() + extra = f" | {event.defn.text}" + + if isinstance(event, AddOperation): + action = "Add" + if isinstance(event.defn, LineDefinition): + action = "Run" + elif isinstance(event, UpdateOperation): + action = "Update" + elif isinstance(event, DeleteOperation): + action = "Delete" + if not action: + self.bot.logger.debug(event) + else: + self.bot.logger.debug(event_str.format(action=action, dotpath=dotpath, lineno=lineno, extra=extra)) + + def jurigged_prerun(self, _path: str, cf: CodeFile) -> None: + """ + Jurigged prerun event. + + Args: + path: Path to file + cf: File information + """ + if self.bot.get_ext(cf.module_name): + self.bot.logger.debug(f"Caching {cf.module_name}") + self.command_cache = get_all_commands(cf.module) + + def jurigged_postrun(self, _path: str, cf: CodeFile) -> None: + """ + Jurigged postrun event. + + Args: + path: Path to file + cf: File information + """ + if self.bot.get_ext(cf.module_name): + self.bot.logger.debug(f"Checking {cf.module_name}") + commands = get_all_commands(cf.module) + + self.bot.logger.debug("Checking for changes") + for module, cmds in commands.items(): + # Check if a module was removed + if module not in commands: + self.bot.logger.debug(f"Module {module} removed") + self.bot.unload_extension(module) + + # Check if a module is new + elif module not in self.command_cache: + self.bot.logger.debug(f"Module {module} added") + try: + self.bot.load_extension(module) + except ExtensionLoadException: + self.bot.logger.warning(f"Failed to load new module {module}") + + # Check if a module has more/less commands + elif len(self.command_cache[module]) != len(cmds): + self.bot.logger.debug("Number of commands changed, reloading") + try: + self.bot.reload_extension(module) + except ExtensionNotFound: + try: + self.bot.load_extension(module) + except ExtensionLoadException: + self.bot.logger.warning(f"Failed to update module {module}") + except ExtensionLoadException: + self.bot.logger.warning(f"Failed to update module {module}") + + # Check each command for differences + else: + for cmd in cmds: + old_cmd = find( + lambda x, cmd=cmd: x.resolved_name == cmd.resolved_name, + self.command_cache[module], + ) + + # Extract useful info + old_args = old_cmd.options + old_arg_names = [] + new_arg_names = [] + if old_args: + old_arg_names = [x.name.default for x in old_args] + new_args = cmd.options + if new_args: + new_arg_names = [x.name.default for x in new_args] + + # No changes + if not old_args and not new_args: + continue + + # Check if number of args has changed + if len(old_arg_names) != len(new_arg_names): + self.bot.logger.debug("Number of arguments changed, reloading") + try: + self.bot.reload_extension(module) + except Exception: + self.bot.logger.exception(f"Failed to update module {module}", exc_info=True) + + # Check if arg names have changed + elif len(set(old_arg_names) - set(new_arg_names)) > 0: + self.bot.logger.debug("Argument names changed, reloading") + try: + self.bot.reload_extension(module) + except Exception: + self.bot.logger.exception(f"Failed to update module {module}", exc_info=True) + + # Check if arg types have changed + elif any(new_args[idx].type != x.type for idx, x in enumerate(old_args)): + self.bot.logger.debug("Argument types changed, reloading") + try: + self.bot.reload_extension(module) + except Exception: + self.bot.logger.exception(f"Failed to update module {module}", exc_info=True) + else: + self.bot.logger.debug("No changes detected") + self.command_cache.clear() + + +def setup(bot) -> None: + Jurigged(bot) diff --git a/poetry.lock b/poetry.lock index 7f93f408b..8515339e9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,7 +16,7 @@ multidict = ">=4.5,<7.0" yarl = ">=1.0,<2.0" [package.extras] -speedups = ["cchardet", "brotli", "aiodns"] +speedups = ["Brotli", "aiodns", "cchardet"] [[package]] name = "aiosignal" @@ -29,6 +29,14 @@ python-versions = ">=3.6" [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "ansicon" +version = "1.89.0" +description = "Python wrapper for loading Jason Hood's ANSICON" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "async-timeout" version = "4.0.2" @@ -46,10 +54,10 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -tests_no_zope = ["cloudpickle", "pytest-mypy-plugins", "mypy", "six", "pytest (>=4.3.0)", "pympler", "hypothesis", "coverage[toml] (>=5.0.2)"] -tests = ["cloudpickle", "zope.interface", "pytest-mypy-plugins", "mypy", "six", "pytest (>=4.3.0)", "pympler", "hypothesis", "coverage[toml] (>=5.0.2)"] -docs = ["sphinx-notfound-page", "zope.interface", "sphinx", "furo"] -dev = ["cloudpickle", "pre-commit", "sphinx-notfound-page", "sphinx", "furo", "zope.interface", "pytest-mypy-plugins", "mypy", "six", "pytest (>=4.3.0)", "pympler", "hypothesis", "coverage[toml] (>=5.0.2)"] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] +tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] [[package]] name = "black" @@ -67,10 +75,23 @@ platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} [package.extras] -uvloop = ["uvloop (>=0.15.2)"] -jupyter = ["tokenize-rt (>=3.2.0)", "ipython (>=7.8.0)"] -d = ["aiohttp (>=3.7.4)"] colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "blessed" +version = "1.19.1" +description = "Easy, practical library for making terminal apps, by providing an elegant, well-documented interface to Colors, Keyboard input, and screen Positioning capabilities." +category = "main" +optional = false +python-versions = ">=2.7" + +[package.dependencies] +jinxed = {version = ">=1.1.0", markers = "platform_system == \"Windows\""} +six = ">=1.9.0" +wcwidth = ">=0.1.4" [[package]] name = "cfgv" @@ -102,6 +123,14 @@ python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "codefind" +version = "0.1.3" +description = "Find code objects and their referents" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + [[package]] name = "colorama" version = "0.4.5" @@ -138,8 +167,8 @@ optional = false python-versions = ">=3.7" [package.extras] -testing = ["pytest-timeout (>=2.1)", "pytest-cov (>=3)", "pytest (>=7.1.2)", "coverage (>=6.4.2)", "covdefaults (>=2.2)"] -docs = ["sphinx-autodoc-typehints (>=1.19.1)", "sphinx (>=5.1.1)", "furo (>=2022.6.21)"] +docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] +testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] [[package]] name = "frozenlist" @@ -168,6 +197,34 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "jinxed" +version = "1.2.0" +description = "Jinxed Terminal Library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ansicon = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "jurigged" +version = "0.5.3" +description = "Live update of Python functions" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +blessed = ">=1.17.12,<2.0.0" +codefind = ">=0.1.3,<0.2.0" +ovld = ">=0.3.1,<0.4.0" +watchdog = ">=1.0.2" + +[package.extras] +develoop = ["giving (>=0.3.6,<0.4.0)", "hrepr (>=0.4.0,<0.5.0)", "rich (>=10.13.0,<11.0.0)"] + [[package]] name = "multidict" version = "6.0.2" @@ -190,9 +247,9 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=3.10" [package.extras] -reports = ["lxml"] -python2 = ["typed-ast (>=1.4.0,<2)"] dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] [[package]] name = "mypy-extensions" @@ -210,6 +267,9 @@ category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +[package.dependencies] +setuptools = "*" + [[package]] name = "orjson" version = "3.7.12" @@ -218,6 +278,14 @@ category = "main" optional = true python-versions = ">=3.7" +[[package]] +name = "ovld" +version = "0.3.2" +description = "Overloading Python functions" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" + [[package]] name = "pathspec" version = "0.9.0" @@ -235,8 +303,8 @@ optional = false python-versions = ">=3.7" [package.extras] -test = ["pytest (>=6)", "pytest-mock (>=3.6)", "pytest-cov (>=2.7)", "appdirs (==1.4.4)"] -docs = ["sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)", "proselint (>=0.10.2)", "furo (>=2021.7.5b38)"] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [[package]] name = "pre-commit" @@ -262,6 +330,27 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "setuptools" +version = "65.3.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "toml" version = "0.10.2" @@ -300,8 +389,27 @@ filelock = ">=3.4.1,<4" platformdirs = ">=2.4,<3" [package.extras] -testing = ["pytest-timeout (>=2.1)", "pytest-randomly (>=3.10.3)", "pytest-mock (>=3.6.1)", "pytest-freezegun (>=0.4.2)", "pytest-env (>=0.6.2)", "pytest (>=7.0.1)", "packaging (>=21.3)", "flaky (>=3.7)", "coverage-enable-subprocess (>=1)", "coverage (>=6.2)"] -docs = ["towncrier (>=21.9)", "sphinx-rtd-theme (>=1)", "sphinx-argparse (>=0.3.1)", "sphinx (>=5.1.1)", "proselint (>=0.13)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "watchdog" +version = "2.1.9" +description = "Filesystem events monitoring" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" [[package]] name = "yarl" @@ -321,7 +429,7 @@ orjson = ["orjson"] [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "05642a4dd000c0ddab4ec77d8c5c941f305a5c0c6fd23f7887a2e19bfeb61af6" +content-hash = "3014d49f86b05fbc78d76d11b8e42cd5f779b7e2ffe6ded9638681fcc7d4fb1b" [metadata.files] aiohttp = [ @@ -402,6 +510,10 @@ aiosignal = [ {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"}, {file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"}, ] +ansicon = [ + {file = "ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec"}, + {file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"}, +] async-timeout = [ {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, @@ -435,6 +547,10 @@ black = [ {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, ] +blessed = [ + {file = "blessed-1.19.1-py2.py3-none-any.whl", hash = "sha256:63b8554ae2e0e7f43749b6715c734cc8f3883010a809bf16790102563e6cf25b"}, + {file = "blessed-1.19.1.tar.gz", hash = "sha256:9a0d099695bf621d4680dd6c73f6ad547f6a3442fbdbe80c4b1daa1edbc492fc"}, +] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, @@ -447,6 +563,10 @@ click = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] +codefind = [ + {file = "codefind-0.1.3-py3-none-any.whl", hash = "sha256:3ffe85b74595b5c9f82391a11171ce7d68f1f555485720ab922f3b86f9bf30ec"}, + {file = "codefind-0.1.3.tar.gz", hash = "sha256:5667050361bf601a253031b2437d16b7d82cb0fa0e756d93e548c7b35ce6f910"}, +] colorama = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, @@ -532,6 +652,14 @@ idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] +jinxed = [ + {file = "jinxed-1.2.0-py2.py3-none-any.whl", hash = "sha256:cfc2b2e4e3b4326954d546ba6d6b9a7a796ddcb0aef8d03161d005177eb0d48b"}, + {file = "jinxed-1.2.0.tar.gz", hash = "sha256:032acda92d5c57cd216033cbbd53de731e6ed50deb63eb4781336ca55f72cda5"}, +] +jurigged = [ + {file = "jurigged-0.5.3-py3-none-any.whl", hash = "sha256:355a9bddf42cae541e862796fb125827fc35573a982c6f35d3dc5621e59c91e3"}, + {file = "jurigged-0.5.3.tar.gz", hash = "sha256:47cf4e9f10455a39602caa447888c06adda962699c65f19d8c37509817341b5e"}, +] multidict = [ {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, @@ -670,6 +798,10 @@ orjson = [ {file = "orjson-3.7.12-cp39-none-win_amd64.whl", hash = "sha256:c1e4297b5dee3e14e068cc35505b3e1a626dd3fb8d357842902616564d2f713f"}, {file = "orjson-3.7.12.tar.gz", hash = "sha256:05f20fa1a368207d16ecdf16072c3be58f85c4954cd2ed6c9704463963b9791a"}, ] +ovld = [ + {file = "ovld-0.3.2-py3-none-any.whl", hash = "sha256:3a5f08f66573198b490fc69dcf93a2ad9b4d90fd1fef885cf7a8dbe565f17837"}, + {file = "ovld-0.3.2.tar.gz", hash = "sha256:f8918636c240a2935175406801944d4314823710b3afbd5a8db3e79cd9391c42"}, +] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, @@ -690,6 +822,13 @@ pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, @@ -717,6 +856,14 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] +setuptools = [ + {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"}, + {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -733,6 +880,37 @@ virtualenv = [ {file = "virtualenv-20.16.3-py2.py3-none-any.whl", hash = "sha256:4193b7bc8a6cd23e4eb251ac64f29b4398ab2c233531e66e40b19a6b7b0d30c1"}, {file = "virtualenv-20.16.3.tar.gz", hash = "sha256:d86ea0bb50e06252d79e6c241507cb904fcd66090c3271381372d6221a3970f9"}, ] +watchdog = [ + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"}, + {file = "watchdog-2.1.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591"}, + {file = "watchdog-2.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd"}, + {file = "watchdog-2.1.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3"}, + {file = "watchdog-2.1.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d"}, + {file = "watchdog-2.1.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_armv7l.whl", hash = "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_i686.whl", hash = "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64.whl", hash = "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_s390x.whl", hash = "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6"}, + {file = "watchdog-2.1.9-py3-none-win32.whl", hash = "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1"}, + {file = "watchdog-2.1.9-py3-none-win_amd64.whl", hash = "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c"}, + {file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"}, + {file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] yarl = [ {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, diff --git a/pyproject.toml b/pyproject.toml index c2aadc59c..4fbdcef29 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,10 +19,17 @@ emoji = "^2.1.0" version = "^3.6.8" optional = true +[tool.poetry.dependencies.jurigged] +version = "^0.5.3" +optional = true + [tool.poetry.extras] orjson = [ "orjson", ] +jurigged = [ + "jurigged", +] [tool.poetry.dev-dependencies] black = "^22.3.0" diff --git a/requirements.txt b/requirements.txt index 71a90b73c..fedbc3642 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp attrs discord-typings>=0.5.1 -emoji +emoji tomli diff --git a/setup.py b/setup.py index 84915242a..50bd22636 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,7 @@ "voice": ["PyNaCl>=1.5.0,<1.6"], "speedup": ["aiodns", "orjson", "Brotli"], "sentry": ["sentry-sdk"], + "jurigged": ["jurigged"], } extras_require["all"] = list(itertools.chain.from_iterable(extras_require.values())) extras_require["docs"] = extras_require["all"] + [