Skip to main content

Dynamic command execution, parsing, and storage.

Project description

dyncommands

Dynamic command execution, parsing, and storage.


Tests Codecov MIT License

PyPI Python CPython


About:

Dyncommands is a python package that allows you to dynamically execute loaded python modules (commands) through parsing strings.

When parsing a string, it separates the command name from arguments, and executes the stored function with those arguments. Each time the parser is called, you can pass in your own custom kwargs that the command will have access to.

All command modules are compiled through RestrictedPython before being allowed to run.

How to use:

Command metadata:

Metadata for commands are stored in the commands.json file inside the commands_path of the parser. This is where all the data for the parser is loaded or stored.

There are two top-level keys:

  • commandPrefix: str
    • String being parsed must start with this string, otherwise it is ignored. Empty string accepts all.
  • commands: array[object]
    • Contains command objects

Available metadata keys for objects inside the commands array are:

  • name: str (Required)
    • Uniquely identifies the command to the CommandParser.
  • usage: str
  • description: str
  • permission: int
    • The permission level the CommandSource requires to run the command.
      • Ignored if ignore_permissions is True in the CommandParser instance.
  • function: bool
    • Whether there is an associated python module to load.
  • children: array[object]
    • Sub-commands; these are handled by the parent's function. (No associated modules for themselves)
  • overridable: bool
    • Whether the CommandParser can override any data inside this object (must be manually enabled)
  • disabled: bool
    • If true still load command, but raise a DisabledError when attempting to execute.

NOTE: Commands modules are not loaded unless they are listed in commands.json with the "function" key set to true.

Example commands.json contents:

{
  "commandPrefix": "!",
  "commands": [
    {
      "name": "test",
      "usage": "test [*args:any]",
      "description": "Test command.",
      "permission": 500,
      "function": true
    },
    {
      "name": "test2",
      "function": false
    }
  ]
}

Command modules:

Dynamically-loaded commands are denoted by filename with a prefix of "zzz__". Inside a command module, there is a function defined as "command". This function will be mapped to a Command's function attribute and stored in memory for execution. The function has access to any args that were parsed, as well as kwargs:

  1. 'self' (Command), which houses the metadata for the command that's being executed.

  2. 'parser' (CommandParser), which stores the list of registered commands and command data.

  3. 'context' (CommandContext), which supplies the CommandSource and the original text sent for parsing.

  • Any custom kwargs passed to CommandParser.parse.

Since commands cannot import heir own modules, some are included in globals (math, random, and string).

Example command module:

def command(*args, **kwargs):
    self, context = kwargs.pop('self'), kwargs.pop('context')
    source = context.source
    if len(args) == 2:
        amount, sides = abs(int(getitem(args, 0))), abs(int(getitem(args, 1)))
        if amount > 0 and sides > 0:
            dice_rolls = [f"{(str(i + 1) + ':') if amount > 1 else ''} {str(random.randint(1, sides))}/{sides}" for i in range(amount)]
            source.send_feedback(f"/me \U0001f3b2 {source.display_name} rolled {'a die' if amount == 1 else str(amount) + ' dice'} with {sides} side{'' if sides == 1 else 's'}: {', '.join(dice_rolls)} \U0001f3b2")
        else:
            raise ImproperUsageError(self, context)
    else:
        raise ImproperUsageError(self, context)

At any time, you can call CommandParser.reload() to reload all command modules and metadata from disk storage.

Example file structure:

../
│
├───[commands_path]/
│       ├─── commands.json
│       ├─── zzz__[command1].py
│       ├─── zzz__[command2].py
│       └─── zzz__[command3].py
│

Adding/Removing Commands:

To add commands, you can either manually enter data into a commands.json file, or use the CommandParser.add_command(text: str, link: bool = False, **kwargs) method. The easiest way to use this method is to read the command module as text and pass that to the first argument. You can also store command modules online to allow for remote installation, as setting the link parameter to True will read text as a link, and will get the raw text data from that link. Ex: gist and pastebin.

NOTE: When adding a command, metadata for 'name' must to be filled. This can be done in the form of comments.

Example of metadata as comments:

# Name: points
# Usage: points [get (username:string)| set (username:string amount:integer)]
# Description: Get your current points
# Permission: 0
# Children: [{'name': 'get', 'usage': 'get (username:string)', 'permission':0}, {'name': 'set', 'usage': 'set (username:string amount:integer)', 'permission':500}]
def command(*args, **kwargs):
    ...

Removing an already added command is relatively easy. Just call CommandParser.remove_command(name: str) with the name of the command that you want to remove, and it will delete both the metadata and the command module from the disk.

If you don't want to delete the command when removing, a better alternative is to disable it with CommandParser.set_disabled(name: str, value: bool).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dyncommands-1.0.0.tar.gz (16.0 kB view details)

Uploaded Source

Built Distribution

dyncommands-1.0.0-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

File details

Details for the file dyncommands-1.0.0.tar.gz.

File metadata

  • Download URL: dyncommands-1.0.0.tar.gz
  • Upload date:
  • Size: 16.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.0 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.10.0

File hashes

Hashes for dyncommands-1.0.0.tar.gz
Algorithm Hash digest
SHA256 46cc2fbf1e8c719411fcb7da978fdd70cbcc0596adf522e59b2b0244533b6cc7
MD5 5c97e6e110c1c02efb87d553924b881c
BLAKE2b-256 8240d0ef022e7424c48a8548f186193d59376c8fac86db5fb607757982bd0c0b

See more details on using hashes here.

Provenance

File details

Details for the file dyncommands-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: dyncommands-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 15.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.0 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.10.0

File hashes

Hashes for dyncommands-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 85a87763e883ce356ff645a23aa1b6dd0350b7d4e53475d077e8e7f001e78415
MD5 4f373543f3c9cec7992ec106c9a4af28
BLAKE2b-256 ceef6030339f36c3e60f1ded7c59ba5cc606e7bd93536ce19db29419e0d3c9d7

See more details on using hashes here.

Provenance

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page