https://stackoverflow.com/questions/65780086/how-to-program-hotstrings-in-python-like-in-autohotkey/65783573#65783573
Usage Examples:
calling custom-defined functions:
Code: Select all
msgbox py("uuid_2_shortid", "ef0cbdd0-b71d-446e-8498-745a95606fc9") ; ⟹ "0L0M7x23bkSEmHRalWBvyQ"
msgbox py("shortid_2_uuid", "0L0M7x23bkSEmHRalWBvyQ") ; ⟹ "ef0cbdd0-b71d-446e-8498-745a95606fc9"
msgbox ticks := py("time_now_in_ticks") ; ⟹ 638363906753198976
msgbox py("ticks_to_readable_time", ticks) ; ⟹ "2023-11-24 02:45:24.107276"
Code: Select all
msgbox py("eval", "[x**2 for x in range(10)]")[3] ; ⟹ 9
msgbox py("eval", "(lambda radius: 3.14159 * radius ** 2)(5)") ; ⟹ 78.935749999
Code: Select all
msgbox py("exec", statements := "
(
import math
sin_pi = math.sin(math.pi)
log_e = math.log(math.e)
sqrt_of_16 = math.sqrt(16)
factorial_of_5 = math.factorial(5)
)", 1)["factorial_of_5"] ; ⟹ 120
msgbox py("exec", statements := "
(
import random
result = {
'random_int': random.randint(1, 100),
'random_float': random.random(),
'random_choice': random.choice(['apple', 'banana', 'cherry', 'date'])
}
)", 1)["result"]["random_choice"] ; ⟹ (e.g.) "banana"
Code: Select all
msgbox py("exec", statements := "
(
def get_public_ip():
try:
from urllib.request import urlopen
with urlopen('https://api.ipify.org') as response:
return response.read().decode('utf-8')
except Exception as e:
return str(e)
public_ip = get_public_ip()
)", 1)["public_ip"] ; ⟹ (e.g.) "69.200.103.49"
1. make sure you have python runtime installed (the example code used below uses some syntax only available for python 3.10+)
2. install pywin32 by downloading the correct version from the https://github.com/mhammond/pywin32/releases
3. save the python code further below at the bottom of this post(adapted from @MrDoge's example at https://stackoverflow.com/questions/65780086/how-to-program-hotstrings-in-python-like-in-autohotkey#65783573) as (say) "AHKCOMServer.py"
4. Register the COM server:
Code: Select all
python "AHKCOMServer.py" --register
5. include this function in your AHK code (replace "JSON.parse" with the name of your json deserialization function)
Code: Select all
py(action, param?, jsonParseResult:=0){
static comServer := ComObject("Python.AHKCOMServer")
result := comServer.COMServerHandler(action, param?)
if jsonParseResult
result := JSON.parse(result)
return result
}
Further dicussions/tutorials:
viewtopic.php?t=29394
https://stackoverflow.com/questions/58599829/call-python-function-with-arguments-and-get-returned-value-in-autohotkey/67428298#67428298
AHKCOMServer.py:
Code: Select all
import uuid
import base64
import os
import datetime
import json
import types
class BasicServer:
_public_methods_ = ["COMServerHandler"]
@staticmethod
def COMServerHandler(action, param=None):
"""Handles different operations based on the action."""
match action:
case "uuid_2_shortid":
return BasicServer.dotnet_uuid2shortid(param)
case "shortid_2_uuid":
return BasicServer.dotnet_shortid2uuid(param)
case "ticks_to_readable_time":
return BasicServer.ticks_to_readable_time(param)
case "time_now_in_ticks":
return BasicServer.time_now_in_ticks()
case "eval":
return BasicServer.eval(param)
case "exec":
return BasicServer.exec(param)
case _:
return 0
@staticmethod
def dotnet_uuid2shortid(longID):
"""Converts a GUID to a shorter ID representation."""
b64 = (
base64.urlsafe_b64encode(uuid.UUID(longID).bytes_le)
.rstrip(b"=")
.decode("utf-8")
)
shortID = b64.replace("+", "-").replace("/", "_")
return shortID[:22]
@staticmethod
def dotnet_shortid2uuid(shortID):
"""Converts a shorter ID back to a GUID."""
b64 = shortID.replace("-", "+").replace("_", "/") + "=="
longID = str(uuid.UUID(bytes_le=base64.urlsafe_b64decode(b64)))
return longID
@staticmethod
def ticks_to_readable_time(ticks):
"""Converts .NET ticks to a human-readable datetime."""
return str(
datetime.datetime(1, 1, 1)
+ datetime.timedelta(microseconds=int(ticks) // 10)
)
@staticmethod
def time_now_in_ticks():
"""Returns the current time in .NET ticks."""
dotnet_epoch = datetime.datetime(1, 1, 1)
current_utc_time = datetime.datetime.utcnow()
time_span = current_utc_time - dotnet_epoch
ticks = time_span.total_seconds() * 10000000
return int(ticks)
@staticmethod
def eval(code_str):
try:
# Evaluate the string as Python code and return the result.
return eval(code_str)
except Exception as e:
# Catch and return any errors that occur during evaluation.
return str(e)
@staticmethod
def exec(statements):
try:
# Create a dictionary to capture local variables after exec
local_variables = {}
global_variables = {}
exec(statements, global_variables, local_variables)
serializable_locals = {k: v for k, v in local_variables.items() if not isinstance(v, (types.ModuleType, types.FunctionType))}
return json.dumps(serializable_locals)
except Exception as e:
# Catch and return any errors that occur during exec
return str(e)
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Error: need to supply arg (" "--register" " or " "--unregister" ")")
sys.exit(1)
else:
import win32com.server.register
import win32com.server.exception
myClsid = "{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}"
myProgID = "Python.AHKCOMServer"
if sys.argv[1] == "--register":
import pythoncom
import os.path
realPath = os.path.realpath(__file__)
nameNoExt = os.path.splitext(os.path.basename(realPath))[0]
"""
https://github.com/mhammond/pywin32/blob/main/com/win32com/server/register.py
Registers a Python object as a COM Server. This enters almost all necessary
information in the system registry, allowing COM to use the object.
clsid -- The (unique) CLSID of the server.
pythonInstString -- A string holding the instance name that will be created
whenever COM requests a new object.
desc -- The description of the COM object.
progID -- The user name of this object (eg, Word.Document)
verProgId -- The user name of this version's implementation (eg Word.6.Document)
defIcon -- The default icon for the object.
threadingModel -- The threading model this object supports.
policy -- The policy to use when creating this object.
catids -- A list of category ID's this object belongs in.
other -- A dictionary of extra items to be registered.
addPyComCat -- A flag indicating if the object should be added to the list
of Python servers installed on the machine. If None (the default)
then it will be registered when running from python source, but
not registered if running in a frozen environment.
dispatcher -- The dispatcher to use when creating this object.
clsctx -- One of the CLSCTX_* constants.
addnPath -- An additional path the COM framework will add to sys.path
before attempting to create the object.
"""
win32com.server.register.RegisterServer(
clsid=myClsid,
pythonInstString=nameNoExt + ".BasicServer",
progID=myProgID,
desc="Basic Server for AHK COM interop",
clsctx=pythoncom.CLSCTX_LOCAL_SERVER,
addnPath=os.path.dirname(realPath),
)
print("Registered COM server.")
elif sys.argv[1] == "--unregister":
print("Starting to unregister...")
win32com.server.register.UnregisterServer(myClsid, myProgID)
print("Unregistered COM server.")
else:
print("Error: arg not recognized")