การดำเนินการคำสั่งแบบไดนามิกการแยกวิเคราะห์และการจัดเก็บ
Dyncommands ช่วยให้คุณสามารถนำเข้าและเรียกใช้ฟังก์ชั่น Python แบบไดนามิก มีประโยชน์สำหรับการเพิ่มคำสั่งให้กับ IRC chatbots หรือแอปพลิเคชัน CLI โดยไม่ต้องรีสตาร์ท
เมื่อแยกวิเคราะห์สตริงมันจะแยกชื่อคำสั่งออกจากอาร์กิวเมนต์และดำเนินการฟังก์ชันที่เก็บไว้ด้วยอาร์กิวเมนต์เหล่านั้น ทุกครั้งที่มีการเรียกตัวแยกวิเคราะห์คุณสามารถส่งผ่าน kwargs ที่กำหนดเองของคุณเองว่าคำสั่งจะสามารถเข้าถึงได้
โมดูลคำสั่งทั้งหมดจะถูกรวบรวมผ่านการ จำกัด Python ก่อนที่จะได้รับอนุญาตให้ทำงาน คุณสามารถปิดการดำเนินการที่ถูก จำกัด ได้โดยการตั้งค่า CommandParser._unrestricted เป็น จริง แม้ว่านี่จะเป็นสิ่งที่ไม่น่าเชื่ออย่างมากเมื่อใช้รหัสที่ไม่น่าเชื่อถือ
from pathlib import Path
from dyncommands import CommandParser , CommandContext , CommandSource
output : str = ''
def callback ( text , * args ):
global output
output = text
path = Path ( 'path/to/directory' ) # Must be a directory with a `commands.json` file in it
parser = CommandParser ( path ) # Create the parser, which initializes using data located in the path directory
source = CommandSource ( callback ) # Create a source, which is used to talk back to the caller
input_ = 'command-that-returns-wow arg1 arg2' # this command would call zzz__command-that-returns-wow.py with arg1 and arg2
parser . parse ( CommandContext ( input_ , source )) # Parse the new context and run the command and callback (If no errors occur)
assert output == 'wow' ข้อมูลเมตาสำหรับคำสั่งจะถูกเก็บไว้ในคำ commands.json ไฟล์ json ของไดเรกทอรี CommandParser.commands_path นี่คือที่ข้อมูลทั้งหมดสำหรับตัวแยกวิเคราะห์ถูกโหลดและจัดเก็บ
commands.json ทั้งหมดไฟล์ JSON ได้รับการตรวจสอบด้วย JSON SCHEMA ผ่านแพ็คเกจ JSONSCHEMA PYTHON
| สำคัญ | พิมพ์ | คำอธิบาย | ค่าเริ่มต้น | ที่จำเป็น |
|---|---|---|---|---|
commandPrefix | สาย | สตริงจะต้องเริ่มต้นด้วยคำนำหน้านี้มิฉะนั้นจะถูกละเว้น สตริงที่ว่างเปล่ายอมรับทั้งหมด | N/A | ใช่ |
commands | อาร์เรย์ [ คำสั่ง ] | มีข้อมูลเมตาสำหรับโมดูลคำสั่งที่เก็บไว้ | N/A | ใช่ |
| สำคัญ | พิมพ์ | คำอธิบาย | ค่าเริ่มต้น | ที่จำเป็น |
|---|---|---|---|---|
name | สาย | ระบุคำสั่งเฉพาะกับ CommandParser | N/A | ใช่ |
usage | สาย | ข้อมูลการใช้งาน (วิธีการใช้ args) | - | เลขที่ |
description | สาย | คำอธิบายของคำสั่ง | - | เลขที่ |
permission | จำนวนเต็ม | ระดับการอนุญาตที่คำสั่งแหล่งข้อมูลต้องการเรียกใช้คำสั่ง | 0 | เลขที่ |
function | บูลีน โมฆะ | ไม่ว่าจะมีโมดูล Python ที่เกี่ยวข้องในการโหลด | โมฆะ | เลขที่ |
children | อาร์เรย์ [ คำสั่ง ] | คำสั่งย่อย; สิ่งเหล่านี้ได้รับการจัดการโดยฟังก์ชั่นของผู้ปกครอง (ไม่มีโมดูลที่เกี่ยวข้องสำหรับตัวเอง) | - | เลขที่ |
overridable | บูลีน | ไม่ว่าจะเป็น CommandParser สามารถแทนที่ข้อมูลใด ๆ ภายในวัตถุนี้ (ต้องเปิดใช้งานด้วยตนเอง) | จริง | เลขที่ |
disabled | บูลีน | หาก True ยังคงโหลดคำสั่ง แต่เพิ่ม disablederror เมื่อพยายามดำเนินการ | เท็จ | เลขที่ |
หมายเหตุ: โมดูลคำสั่งจะไม่ถูกโหลดเว้นแต่ว่าจะอยู่ในรายการใน commands.json ด้วยชุดคีย์ function เป็น TRUE
commands.json สารบัญ: {
"commandPrefix" : " ! " ,
"commands" : [
{
"name" : " test " ,
"usage" : " test [*args:any] " ,
"description" : " Test command. " ,
"permission" : 500 ,
"function" : true
},
{
"name" : " test2 " ,
"function" : false
}
]
} คำสั่งที่โหลดแบบไดนามิกจะแสดงโดยชื่อไฟล์พร้อมคำนำหน้าของ "zzz__" ภายในโมดูลคำสั่งมีฟังก์ชั่นที่กำหนดเป็น command ฟังก์ชั่นนี้จะถูกแมปกับแอตทริบิวต์ฟังก์ชั่นของ Command และเก็บไว้ในหน่วยความจำสำหรับการดำเนินการ ฟังก์ชั่นมีการเข้าถึง args ใด ๆ ที่แยกวิเคราะห์เช่นเดียวกับ Kwargs:
' Self ' ( Command ) ซึ่งเป็นที่ตั้งของข้อมูลเมตาสำหรับคำสั่งที่ถูกดำเนินการ
' Parser ' ( CommandParser ) ซึ่งเก็บรายการคำสั่งที่ลงทะเบียนและข้อมูลคำสั่ง
' บริบท ' ( CommandContext ) ซึ่งจัดหา CommandSource และข้อความต้นฉบับที่ส่งเพื่อการแยกวิเคราะห์
CommandParser.parse(context: CommandContext, **kwargs) เนื่องจากคำสั่งไม่สามารถนำเข้าโมดูลของตัวเองบางส่วนจึงรวมอยู่ใน globals ( math random และ string ) คุณลักษณะอื่น ๆ ที่รวมอยู่ในขอบเขตทั่วโลกคือ: getitem ( operator.getItem ) และ ImproperUsageError ( dyncommands.exceptions.improperusageError )
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 ) เมื่อใดก็ตามที่คุณสามารถเรียก CommandParser.reload() เพื่อโหลดโมดูลคำสั่งทั้งหมดและข้อมูลเมตาทั้งหมดจากที่เก็บข้อมูลดิสก์
../
│
├───[commands_path]/
│ ├─── commands.json
│ ├─── zzz__[command1].py
│ ├─── zzz__[command2].py
│ └─── zzz__[command3].py
│
ในการเพิ่มคำสั่งคุณสามารถป้อนข้อมูลด้วยตนเองลงในไฟล์ commands.json หรือใช้ CommandParser.add_command(text: str, link: bool = False, **kwargs) วิธีที่ง่ายที่สุดในการใช้วิธีนี้คือการอ่านโมดูลคำสั่งเป็นข้อความและส่งผ่านไปยังอาร์กิวเมนต์แรก นอกจากนี้คุณยังสามารถจัดเก็บโมดูลคำสั่งออนไลน์เพื่ออนุญาตให้มีการติดตั้งระยะไกลเนื่องจากการตั้งค่าพารามิเตอร์ ลิงค์ เป็น จริง จะอ่าน ข้อความ เป็นลิงค์และจะได้รับข้อมูลข้อความดิบจากลิงค์นั้น ตัวอย่าง: gist และ pastebin
หมายเหตุ: เมื่อเพิ่มคำสั่ง ต้อง เติมข้อมูลเมตาสำหรับ 'ชื่อ' สามารถทำได้ในรูปแบบของความคิดเห็น
การลบคำสั่งที่เพิ่มแล้วนั้นค่อนข้างง่าย เพียงแค่เรียก CommandParser.remove_command(name: str) ด้วยชื่อของคำสั่งที่คุณต้องการลบและมันจะลบทั้งข้อมูลเมตาและโมดูลคำสั่งจากดิสก์
หากคุณไม่ต้องการลบคำสั่งเมื่อลบทางเลือกที่ดีกว่าคือการปิดใช้งานด้วย CommandParser.set_disabled(name: str, value: bool)
# 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 ):
... parser = CommandParser ( './' )
with open ( 'some_metadata.json' ) as _file :
get_ = { 'name' : 'get' , 'usage' : 'get (username:string)' , 'permission' : 0 }
set_ = { 'name' : 'set' , 'usage' : 'set (username:string amount:integer)' , 'permission' : 500 }
children = [ get_ , set_ ]
parser . add_command ( _file . read (), name = 'my-command' , description = 'Command with child commands.' , children = children ) parser = CommandParser ( './' )
with open ( 'some_metadata.json' ) as _file :
metadata = json . load ( _file )
parser . add_command ( 'https://gist.github.com/random/892hdh2fh389x0wcmksio7m' , link = True , ** metadata ) Dyncommand CommandParser สนับสนุนการจัดการระดับการอนุญาตอย่างสม่ำเสมอดังนั้นคุณไม่จำเป็นต้องใช้ระบบที่คล้ายกันในทุกฟังก์ชันคำสั่ง
แต่ละคำสั่งมี permission ค่าข้อมูลเมตา (ยกเว้นค่าพิเศษ -1 ) คือระดับการอนุญาตขั้นต่ำที่ต้องการจาก CommandSource -1 หมายถึงข้อกำหนด "อนันต์" ซึ่งไม่มี CommandSource จะสามารถดำเนินการได้ในขณะที่ระบบการอนุญาตทำงานอยู่
หากต้องการปิดใช้งานระบบการอนุญาตให้ตั้งค่าแอตทริบิวต์ _ignore_permission ของ CommandParser เป็น TRUE หมายเหตุ: เนื่องจากแอตทริบิวต์นี้เริ่มต้นด้วย "_" การพยายามเปลี่ยนจากภายในฟังก์ชั่นของคำสั่งจะส่งผลให้การรวบรวมล้มเหลวและข้อยกเว้น