how to use `NSScriptCommandDescription initWithSuiteName:commandName:dictionary:` - cocoa

I want to know how to use
NSScriptCommandDescription initWithSuiteName:commandName:dictionary:, esp. how the dictionary is supposed to look like.
It would be nice to see a simple example for a command with a single parameter of type NSString and a return value of type NSString.
I think the documentation about how the dictionary should look like can be found here, esp in "Table B-8, Command dictionary".
However, I am trying with this example and it doesn't work (returns nil):
cmdDesc = NSScriptCommandDescription.alloc().initWithSuiteName_commandName_dictionary_(
"Standard Suite",
"execPython",
{
"CommandClass": "NSScriptCommand", # default behavior
"AppleEventCode": "expy", # 4-char code
"Type": "text", # return-type
"ResultAppleEventCode": "NSString", # return-type
"Arguments": [{
"Type": "NSString",
"AppleEventCode": "data"
}]
}
)
(Note: I really want to know exactly that here; I don't want to know how to register scripting definitions or do other stuff with this question.)

I know this is a VERY old questions but I have stumble upon this and it seems that the this statckoverflow question is the most common result when doing a google search on how to use the NSScriptCommandDescription with arguments.
The code from the accepted answer is correct EXCEPT on how the arguments value is created. The arguments value must be a dictionary that maps argument names with argument descriptions. Using the accepted answer code as an example, in order to pass arguments, our dictionary should look as follows:
cmdDesc = NSScriptCommandDescription.alloc().initWithSuiteName_commandName_dictionary_(
"Chromium Suite",
"exec Python",
{
"Name": "exec Python",
"CommandClass": "NSScriptCommand", # default behavior
"AppleEventCode": "ExPy", # 4-char code
"AppleEventClassCode": "CrSu",
"Type": "NSString", # return-type
"ResultAppleEventCode": "ctxt", # return-type
"Arguments": {
"firstArg": {
"Type": "NSString",
"AppleEventCode": "comm"
}
}
}
)
The above dictionary works on ObjectC (I don't know if the python code does work).

The only thing I can see there that disagrees with the documentation is your value for "Type"; while the documentation doesn't show an example for "Type" in the context of a command, its value in the documentation's examples for other contexts is "NSString", not "text". So, try the Cocoa class name instead of the AppleScript class name.

The "Arguments" section still doesn't really work correct for me (I got strange crashs) but I can also just omit that and still get an argument passed at runtime.
This is the code:
import objc
NSObject = objc.lookUpClass("NSObject")
sharedScriptSuiteReg = objc.lookUpClass("NSScriptSuiteRegistry").sharedScriptSuiteRegistry()
NSScriptCommandDescription = objc.lookUpClass("NSScriptCommandDescription")
sharedAppleEventMgr = objc.lookUpClass("NSAppleEventManager").sharedAppleEventManager()
NSAppleEventDescriptor = objc.lookUpClass("NSAppleEventDescriptor")
from PyObjCTools.TestSupport import fourcc
def register_scripting():
cmdDesc = NSScriptCommandDescription.alloc().initWithSuiteName_commandName_dictionary_(
"Chromium Suite",
"exec Python",
{
"Name": "exec Python",
"CommandClass": "NSScriptCommand", # default behavior
"AppleEventCode": "ExPy", # 4-char code
"AppleEventClassCode": "CrSu",
"Type": "NSString", # return-type
"ResultAppleEventCode": "ctxt", # return-type
"Arguments": {
#"----": {
# "Type": "NSString",
# "AppleEventCode": "comm"
#}
}
}
)
assert cmdDesc is not None
sharedScriptSuiteReg.registerCommandDescription_(cmdDesc)
sharedAppleEventMgr.setEventHandler_andSelector_forEventClass_andEventID_(
appScriptHandler, appScriptHandler.handleExecPy,
fourcc("CrSu"), fourcc("ExPy"))
def handleExecPy(self, ev, replyEv):
print "execPython called,",
cmd = ev.descriptorForKeyword_(fourcc("comm")).stringValue()
print "cmd:", repr(cmd)
res = eval(cmd)
res = unicode(res)
replyEv.setDescriptor_forKeyword_(NSAppleEventDescriptor.descriptorWithString_(res), fourcc("----"))
return True
try:
class AppScriptHandler(NSObject):
def handleExecPy(self, ev, replyEv):
try: return handleExecPy(self, ev, replyEv)
except: traceback.print_exc()
return
except:
AppScriptHandler = objc.lookUpClass("AppScriptHandler")
appScriptHandler = AppScriptHandler.alloc().init()
This works together with this simple client demo:
#!/usr/bin/python
import aem
fullpath = aem.findapp.byname("Google Chrome")
app = aem.Application(fullpath)
def execPy(cmd):
return app.event("CrSuExPy", {"comm": cmd}).send()
print execPy("app.bundle()")
def simple_shell():
import sys
try: import readline
except: pass # ignore
while True:
try:
s = raw_input("> ")
except:
print "breaked debug shell:", sys.exc_info()[0].__name__
break
s = s.strip()
if s: print execPy(s)
simple_shell()
The full code can be seen here and in action here.

Related

VSCode keybinding that inputs large amount of code (Python: Visual Studio Code)

I'm currently trying to add a keybinding in Vscode that inputs multiple lines of code. Below is my current attempt at doing that – but it's not working due to some syntaxical errors. Would anyone know how I'm supposed to format this?
{
"key": "ctrl+y",
"command" : "type",
"args": {
"text": "
leave1 = False
while(leave1 == False):
try:
driver.find_element(By.XPATH, '')
except Exception:
time.sleep(0.5)
else:
leave1 = True
"
}
}

Validate Python TypedDict at runtime

I'm working in a Python 3.8+ Django/Rest-Framework environment enforcing types in new code but built on a lot of untyped legacy code and data. We are using TypedDicts extensively for ensuring that data we are generating passes to our TypeScript front-end with the proper data type.
MyPy/PyCharm/etc. does a great job of checking that our new code spits out data that conforms, but we want to test that the output of our many RestSerializers/ModelSerializers fits the TypeDict. If I have a serializer and typed dict like:
class PersonSerializer(ModelSerializer):
class Meta:
model = Person
fields = ['first', 'last']
class PersonData(TypedDict):
first: str
last: str
email: str
and then run code like:
person_dict: PersonData = PersonSerializer(Person.objects.first()).data
Static type checkers don't be able to figure out that person_dict is missing the required email key, because (by design of PEP-589) it is just a normal dict. But I can write something like:
annotations = PersonData.__annotations__
for k in annotations:
assert k in person_dict # or something more complex.
assert isinstance(person_dict[k], annotations[k])
and it will find that email is missing from the data of the serializer. This is well and good in this case, where I don't have any changes introduced by from __future__ import annotations (not sure if this would break it), and all my type annotations are bare types. But if PersonData were defined like:
class PersonData(TypedDict):
email: Optional[str]
affiliations: Union[List[str], Dict[int, str]]
then isinstance is not good enough to check if the data passes (since "Subscripted generics cannot be used with class and instance checks").
What I'm wondering is if there already exists a callable function/method (in mypy or another checker) that would allow me to validate a TypedDict (or even a single variable, since I can iterate a dict myself) against an annotation and see if it validates?
I'm not concerned about speed, etc., since the point of this is to check all our data/methods/functions once and then remove the checks later once we're happy that our current data validates.
The simplest solution I found works using pydantic.
from typing import cast, TypedDict
import pydantic
class SomeDict(TypedDict):
val: int
name: str
# this could be a valid/invalid declaration
obj: SomeDict = {
'val': 12,
'name': 'John',
}
# validate with pydantic
try:
obj = cast(SomeDict, pydantic.create_model_from_typeddict(SomeDict)(**obj).dict())
except pydantic.ValidationError as exc:
print(f"ERROR: Invalid schema: {exc}")
EDIT: When type checking this, it currently returns an error, but works as expected. See here: https://github.com/samuelcolvin/pydantic/issues/3008
You may want to have a look at https://pypi.org/project/strongtyping/. This may help.
In the docs you can find this example:
from typing import List, TypedDict
from strongtyping.strong_typing import match_class_typing
#match_class_typing
class SalesSummary(TypedDict):
sales: int
country: str
product_codes: List[str]
# works like expected
SalesSummary({"sales": 10, "country": "Foo", "product_codes": ["1", "2", "3"]})
# will raise a TypeMisMatch
SalesSummary({"sales": "Foo", "country": 10, "product_codes": [1, 2, 3]})
A little bit of a hack, but you can check two types using mypy command line -c options. Just wrap it in a python function:
import subprocess
def is_assignable(type_to, type_from) -> bool:
"""
Returns true if `type_from` can be assigned to `type_to`,
e. g. type_to := type_from
Example:
>>> is_assignable(bool, str)
False
>>> from typing import *
>>> is_assignable(Union[List[str], Dict[int, str]], List[str])
True
"""
code = "\n".join((
f"import typing",
f"type_to: {type_to}",
f"type_from: {type_from}",
f"type_to = type_from",
))
return subprocess.call(("mypy", "-c", code)) == 0
You could do something like this:
def validate(typ: Any, instance: Any) -> bool:
for property_name, property_type in typ.__annotations__.items():
value = instance.get(property_name, None)
if value is None:
# Check for missing keys
print(f"Missing key: {property_name}")
return False
elif property_type not in (int, float, bool, str):
# check if property_type is object (e.g. not a primitive)
result = validate(property_type, value)
if result is False:
return False
elif not isinstance(value, property_type):
# Check for type equality
print(f"Wrong type: {property_name}. Expected {property_type}, got {type(value)}")
return False
return True
And then test some object, e.g. one that was passed to your REST endpoint:
class MySubModel(TypedDict):
subfield: bool
class MyModel(TypedDict):
first: str
last: str
email: str
sub: MySubModel
m = {
'email': 'JohnDoeAtDoeishDotCom',
'first': 'John'
}
assert validate(MyModel, m) is False
This one prints the first error and returns bool, you could change that to exceptions, possibly with all the missing keys. You could also extend it to fail on additional keys than defined by the model.
I like your solution!. In order to avoid iteration fixes for some user, I added some code to your solution :D
def validate_custom_typed_dict(instance: Any, custom_typed_dict:TypedDict) -> bool|Exception:
key_errors = []
type_errors = []
for property_name, type_ in my_typed_dict.__annotations__.items():
value = instance.get(property_name, None)
if value is None:
# Check for missing keys
key_errors.append(f"\t- Missing property: '{property_name}' \n")
elif type_ not in (int, float, bool, str):
# check if type is object (e.g. not a primitive)
result = validate_custom_typed_dict(type_, value)
if result is False:
type_errors.append(f"\t- '{property_name}' expected {type_}, got {type(value)}\n")
elif not isinstance(value, type_):
# Check for type equality
type_errors.append(f"\t- '{property_name}' expected {type_}, got {type(value)}\n")
if len(key_errors) > 0 or len(type_errors) > 0:
error_message = f'\n{"".join(key_errors)}{"".join(type_errors)}'
raise Exception(error_message)
return True
some console output:
Exception:
- Missing property: 'Combined_cycle'
- Missing property: 'Solar_PV'
- Missing property: 'Hydro'
- 'timestamp' expected <class 'str'>, got <class 'int'>
- 'Diesel_engines' expected <class 'float'>, got <class 'int'>

Groovy. Get parameters values where part of values are parametrized

Hi I would like to do something like this...
DEV_HOST = "some hostname"
DEV_INST = "2"
DEV_TEST_HOST = "some other hostname"
DEV_TEST_INST = "3"
get_values(DEV_TEST_HOST)
def get_values(environment) {
println environment
println ${environment}_INST
}
Desired output should be:
some other hostname
3
Is it even possible?
Thanks!
You can not pass the value to a function and then introspect what the
variable was named when calling that function (or let's say, if it's
possible, then it's just so many levels of overkill).
Assuming you have some sort of map (e.g. the environment), you pass in
the key you are interested instead. Then do all the transformations on
the key and then ask the environment for the key. E.g.
def env = [
DEV_TEST_HOST: "some other hostname" ,
DEV_TEST_INST: "3",
]
get_values(env, "DEV_TEST_HOST")
def get_values(env, key) {
println env[key]
println env[key.replace('HOST', 'INST')]
}
In case you want to reorganize you map in more clear way, you can use this:
env = [
DEV: [
HOST: "some hostname",
INST: "2"
],
DEV_TEST: [
HOST: "some other hostname",
INST: "3"
]
]
def get_values(v) {
println env[v].HOST
println env[v]."HOST"
println env[v].INST
println env[v]."INST"
}
get_values("DEV")
get_values("DEV_TEST")

What is the Proper Method of Assigning JSON within a Ruby Variable?

The following JSON is a transaction what will be sent to the Ripple Network to query accounts that hold cryptographic assets at a Gateway (somewhat like a bank, more like a trust account between its clients). This script is to be used in conjunction with PHP to fetch a Gateway's issued balances and ignored it's hot-wallet or day-to-day operations wallet. My question is what is the proper way to:
a. Assign JSON within a Ruby variable?
b. What is the best way to escape double quotes and deal with newlines where brackets and square brackets occur within the JSON syntax?
The JSON follows:
ripple_path="/home/rippled/build/rippled"
conf = "--conf /etc/rippled/rippled.cfg"
puts "About to set the JSON lines "
gatewayStart = "\"method\": \"gateway_balances\","
paramsLine = "\"params\": [ {"
accountLine = "\"account\": \"rGgS5Hw3PhSp3VNT43PDTXze9YfdthHUH\","
hotwalletLine = "\"hotwallet\": \"rKYNhsT3aLymkGH7WL7ZUHkm6RE27iuM4C\","
liLine = "\"ledger_index\": \"validated\","
strictLine = "\"strict\": "
trueLine = true
endLine = " } ] }"
balancesLine = "#{gatewayStart} #{paramsLine} #{accountLine} #>{hotwalletLine} #{liLine} #{strictLine} #{trueLine} #{endLine}"
lineString = "#{balancesLine.to_s}"
linetoJSON = "#{lineString}"
puts "linetoJSON: #{linetoJSON} "
cmd2=`#{ripple_path} #{conf} json gateway_balances #{linetoJSON}`
cmder="#{ripple_path} #{conf} json gateway_balances #{linetoJSON}"
puts "Done."
The output is:
root#xagate:WorkingDirectory# ruby gatewaybal.rb
About to set the JSON lines
linetoJSON: "method": "gateway_balances", "params": [ { "account":
"rGgS5Hw3PhSp3VNT43PDTXze9YfdthHUH", "hotwallet": "rKYNhsT3aLymkGH7WL7ZUHkm6RE27iuM4C", "ledger_index": "validated", "strict":rue } ] }
Loading: "/etc/rippled/rippled.cfg"
rippled [options] <command> <params>
General Options:
-h [ --help ] Display this message.
.....
Done.
It is noteworthy that this command also returns a badSyntax error when executed manually via the command line. Please see here for the mirror of this issue raised on the ripple forums.
jsonLine = "'{ \"account\": \"rGgS5Hw3PhSp3VNT43PDTXze9YfdthHUH\", \"hotwallet\": \"rKYNhsT3aLymkGH7WL7ZUHkm6RE27iuM4C\", \"ledger_index\": \"validated\", \"strict\": true }'"
Is the proper way to assign this JSON within a single variable; this solution was provided by JoelKatz. The completed code is now available on GitHub.

Automatically add spaces around "=>"

On SublimeText 3 I try to automatically add spaces before and after "=>"
I try to add this in User Keybinding:
{ "keys": ["equals,>"], "command": "insert_snippet", "args": { "name": "Packages/User/spacer.sublime-snippet" } }
And this is my snippet:
<snippet>
<content><![CDATA[ => ]]></content>
</snippet>
But it's not working.
Console says Unknown key equals,>
equals is redundant. So correct settings:
{ "keys": [">"], "command": "insert_snippet", "args": { "name": "Packages/User/spacer.sublime-snippet" } }
Next time please look for errors in the console first.
UPDATE
I want it matchs on "=>".
["=",">"] should be used in this case
{ "keys": ["=",">"], "command": "insert_snippet", "args": { "name": "Packages/User/spacer.sublime-snippet" } }
When I read the question I realized that I frequently insert spaces on both sides of something, so I knocked up the Sublime Text plugin below for my own use and, as an afterthought, decided to post it here.
This plugin adds a single space both before and after any selected text, e.g. "Sel" --> " Sel ". Multiple selections are, of course, handled. Single cursors are ignored otherwise you'd just be adding two spaces. It is compatible with both Sublime Text v.2 and v.3.
Save the following code in a file called AddSpacesAroundSelection.py and place the file somewhere in your Sublime Text Packages folder. e.g. ~/.config/sublime-text-3/Packages/User/
# File: AddSpacesAroundSelection.py
# Command: add_spaces_around_selection
# Keys: { "keys": ["ctrl+space"], "command": "add_spaces_around_selection" }
import sublime, sublime_plugin
class AddSpacesAroundSelectionCommand(sublime_plugin.TextCommand):
"""
The AddSpacesAroundSelectionCommand class is a Sublime Text plugin which
adds a single space on both sides of each selection. e.g. "Sel" -> " Sel "
"""
def run(self, edit):
""" run() is called when the command is run. """
space_char = " "
# Loop through all the selections.
for sel in self.view.sel():
# If something is actually selected (i.e. not just a cursor) then
# insert a space on both sides of the selected text.
if sel.size() > 0:
# Insert the space at the end of the selection before the
# beginning of it or the insertion position will be wrong.
self.view.insert(edit, sel.end(), space_char)
self.view.insert(edit, sel.begin(), space_char)
# End of def run()
# End of class AddSpacesAroundSelectionCommand()
Add a key binding to your user .sublime-keymap file. On my system the ctrl+space key bindings were not in use and they seemed appropriate to use.
{ "keys": ["ctrl+space"], "command": "add_spaces_around_selection" },
Hope this helps.

Resources