Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
JavaScriptCore adding custom functions
-
In ObjC to add a new function to the JSContext it is as simple as
context[@"functionname"] = (function)
however this does not work in Pythonista as the ObjClass does not support__getitem__
Futher research shows that I should be able to do thisdef log_(_self, _cmd, _msg): print "LOG:", ObjCInstance(_msg) JSExport_Class = create_objc_class( 'JSExport_Class', methods=[log_], protocols=['JSExport', 'NSObject'], debug=False) j = JSExport_Class.alloc().init() context.setObject_forKeyedSubscript_(j, "j")
how ever when i access the function j it returns the JSExport_Class and I cannot access the function
What am I doing wrong?
Thanks in advanced -
what is
context
in this.. err.. context? It should be an ObjCInstance, not an ObjCClass.
Are you able to run j.log_("test") from within python?
Are you usingcontext.evaluateScript_
? -
the context
JSContext = ObjCClass("JSContext") context = JSContext.new()
I am trying to access
j.log_
from javascript
Yes I am usingcontext.evaluateScript
-
Does .new() actually work? I had convinced myself before that .alloc().init() was the way to go, but I don't remember specifics.
Have you tried to access j.log_ from python/objc, just to make sure your object created properly?
-
@JonB Haven't tried this particular code, but
new()
is generally the same asalloc().init()
in Objective-C, though it's not used very much. -
@JonB I did use
j.log_
from python and it worked as expected. I was even able to send the ObjC object to the JSContext and then pull it out and use it again however I could not use it in javascript -
Don't know if it will help but here is the full code
# coding: utf-8 import objc_util from objc_util import * import ctypes ObjCClass("NSBundle").bundleWithPath_("/System/Library/Frameworks/JavaScriptCore.framework").load() def exceptHandler(*args): print args JSContext = ObjCClass("JSContext") context = JSContext.new() def error_func(_self, _context, _error): print "JAVASCRIPT ERROR" print ObjCInstance(_error) eb = ObjCBlock(error_func, None, [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]) context.setExceptionHandler_(eb) context.evaluateScript_("function print(args){return args;}") def func_(_self, _cmd, _msg,): print ObjCInstance(_msg) JSExport_TEST = create_objc_class("JSExport_TEST", methods=[func_], protocols=["JSExport"]) cmd = JSExport_TEST.alloc().init() cmd.func_("a") context.setObject_forKeyedSubscript_("abc", "test_var") context.setObject_forKeyedSubscript_(cmd, "test_var2") print context.evaluateScript_("test_var") print context.evaluateScript_("test_var2")
Which outputs this
a abc <JSExport_TEST_4: 0x1740154d0>
-
from what i am reading, it seems you need to define a custom protocol? Not sure if we can do that in objc util yet
-
Yeah ok, the function is not necessary yet it would just really help, hopefully a method will be implemented. Any suggestions on how I would go about creating it myself?
-
I've experimented a little bit with this; unfortunately, it seems that it's impossible to use
JSExport
with dynamically generated protocols.The first comment under this Gist seems to confirm this. Basically, the JavaScriptCore implementation seems to rely on type encoding information that is generated by the compiler, and doesn't use the encoding of the protocol when it's generated at runtime via
objc_allocateProtocol
,protocol_addMethodDescription
etc., so you're pretty much out of luck with this one. :( -
-
i dont claim to understand what you are trying to do... but if you just want to call a python metnod from javascript, you can do it within a webview delegate. It takes the long way around javascript->webview-> python-> objc.... you might be able to get the context from the webview objc object to make sending objects the other way easier.
-
@JonB I would update the repo to make it more clear what I am attempting to do but both GitView and Stash fail to push my repositary
-
A OSError is produced from an attempt by duleich to create a new process
stash: <type 'exceptions.OSError'>: [Errno 1] Operation not permitted Traceback (most recent call last): File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/stash/stash.py", line 1234, in exec_py_file execfile(file_path, namespace, namespace) File "../stash/bin/git.py", line 723, in <module> ns.func(args) File "../stash/bin/git.py", line 565, in git_push porcelain.push(repo.repo.path, result.url, branch_name, opener=opener) File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/dulwich/porcelain.py", line 533, in push File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/dulwich/client.py", line 450, in send_pack File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/dulwich/client.py", line 648, in _connect File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/subprocess.py", line 711, in __init__ errread, errwrite) File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/subprocess.py", line 1205, in _execute_child self.pid = os.fork() OSError: [Errno 1] Operation not permitted ``` I am aware that iOS apps are not allowed to spawn new processes
-
you likely forgot the https -- in that case it thinks you are rferring to a folder. i probably should just go ahead and patch this with a more useful error, since just about everyone makes that mistake at least once.
-
That is what I thought as well however
git remote origin https://github.com/Cethric/OpenGLES-Pythonista.git
-
what is the push command you used? it should be simply
git push
orgit push origin
. If you type anything after push which is not a name of a repo, it interprets it as a url. -
StaSh v0.4.3 [~/Documents]$ cd site-packages [site-packages]$ cd OpenGLES [OpenGLES]$ git remote origin https://github.com/Cethric/OpenGLES-Pythonista.git [OpenGLES]$ git push master Attempting to push to: master, branch: refs/heads/master Enter username: Cethric Enter password: password stash: <type 'exceptions.OSError'>: [Errno 1] Operation not permitted [OpenGLES]$ stashconf py_traceback 1 [OpenGLES]$ git push master Attempting to push to: master, branch: refs/heads/master Enter username: Cethric Enter password: password stash: <type 'exceptions.OSError'>: [Errno 1] Operation not permitted Traceback (most recent call last): File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/stash/stash.py", line 1234, in exec_py_file execfile(file_path, namespace, namespace) File "../stash/bin/git.py", line 723, in <module> ns.func(args) File "../stash/bin/git.py", line 565, in git_push porcelain.push(repo.repo.path, result.url, branch_name, opener=opener) File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/stash/lib/dulwich/porcelain.py", line 533, in push r.object_store.generate_pack_contents, progress=errstream.write) File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/stash/lib/dulwich/client.py", line 450, in send_pack proto, unused_can_read = self._connect('receive-pack', path) File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/stash/lib/dulwich/client.py", line 648, in _connect stderr=self._stderr)) File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/subprocess.py", line 711, in __init__ errread, errwrite) File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/subprocess.py", line 1205, in _execute_child self.pid = os.fork() OSError: [Errno 1] Operation not permitted [OpenGLES]$
I also tryed
git push origin
however that failed with a broken pipe -
git push master
tries to push to a url named master!broken pipe is usually caused by the current branch not being a direct descendant of the remote repo. you need to do a fetch/merge first, then commit, and push again.
-
git fetch origin git merge origin/master git commit git push
should work. although i just had dulwich fail when i tried to fetch from ywangd/stash.git...
basically, if you ever make changes on github, including just editing Readme.md, your local repo is no longer a descendant of the remote. if you commit locally, github cant deal with that situation, it wants only fast forward changes. so you must merge locally first.