Forum Archive

JavaScriptCore adding custom functions

Cethric

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 this

def 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

JonB

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 using context.evaluateScript_ ?

Cethric

the context

JSContext = ObjCClass("JSContext")
context = JSContext.new()

I am trying to access j.log_ from javascript
Yes I am using context.evaluateScript

JonB

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?

omz

@JonB Haven't tried this particular code, but new() is generally the same as alloc().init() in Objective-C, though it's not used very much.

Cethric

@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

Cethric

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>
JonB

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

Cethric

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?

omz

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. :(

Cethric

That is a pity the intention is to us the JSContext to run a Bullet physics port 'Ammo' as python did not have a pure python implimentation. As I said earlier the function is not required but it would of helped
Thank you for the help @omz and @JonB anyway

JonB

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.

Cethric

@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

Cethric

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

JonB

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.

Cethric

That is what I thought as well however

git remote
origin https://github.com/Cethric/OpenGLES-Pythonista.git
JonB

what is the push command you used? it should be simply
git push or git push origin. If you type anything after push which is not a name of a repo, it interprets it as a url.

Cethric
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

JonB

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.

JonB
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.

Cethric

Unfortunately this is still an error

[OpenGLES]$ git fetch origin    
imported refs/remotes/origin/master 03b88f1b46485cab6e7a9f4e51bd442b34c4d3a1
[OpenGLES]$ git merge origin/master    
['origin/master']
______________________________
head is already up to date
[OpenGLES]$ git push    
Attempting to push to: https://github.com/Cethric/OpenGLES-Pythonista.git, branch: refs/heads/master
Enter username: Cethric
Enter password: password
stash: <class 'urllib2.URLError'>: <urlopen error [Errno 32] Broken pipe>
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 1022, in send_pack
    data=req_data.getvalue())
  File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/stash/lib/dulwich/client.py", line 978, in _smart_request
    resp = self._http_request(url, headers, data)
  File "/private/var/mobile/Containers/Shared/AppGroup/877D6E00-D22C-4407-9A69-C27FA4B9356A/Documents/site-packages/stash/lib/dulwich/client.py", line 943, in _http_request
    resp = self.opener.open(req)
  File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/urllib2.py", line 405, in open
    response = self._open(req, data)
  File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/urllib2.py", line 423, in _open
    '_open', req)
  File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/urllib2.py", line 383, in _call_chain
    result = func(*args)
  File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/urllib2.py", line 1223, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/var/mobile/Containers/Bundle/Application/FD006978-84C6-4483-A56E-1A3F28092E50/Pythonista.app/Frameworks/PythonistaKit.framework/pylib/urllib2.py", line 1185, in do_open
    raise URLError(err)
URLError: <urlopen error [Errno 32] Broken pipe>
[OpenGLES]$ 
JonB

hmm. can you show me the output of git branch, git branch -r, and git log -l 25 -fmerge:{merge}\ncommit:{commit}\n------
What you can also try: create a new clone, in a different folder, copy in your changes from the other folder, commit and push.

Cethric
[OpenGLES]$ git branch    
* master 94c5a82 [ origin/master 03b88f1] 
[OpenGLES]$ git branch -r    
origin/master 03b88f1 
[OpenGLES]$ git log -l 25 -fmerge:{merge}\ncommit:{commit}\n-----
merge:
commit:94c5a825317121c1e7e808826812104d0b1b4484
-----
merge:
commit:03b88f1b46485cab6e7a9f4e51bd442b34c4d3a1
-----
merge:
commit:ec742c54bb24114c69e8c4d70b84b611c6615e43
-----
merge:
commit:f4c938ef075431a6a28cd9b23481839d454d11bd
-----
merge:
commit:fd6c368b46ed13b2c3f0b462b336c8ee2708131b
-----
merge:
commit:6898e37344b6f94886319ad8d46f88dc28bf653b
-----
merge:
commit:8e540065e27d06ac36ed22cfd4140a0101f3f032
-----
merge:
commit:7cf032ed95253b800423a92ac012d77fe131c152
-----
[OpenGLES]$ 

That was a new folder, how ever I will try again

Cethric

Unfortunately in my attempts to get git to work again I have lost a lot of my work, so while the newest data is there there are still some components with I will need to fix.

JonB

try

git branch recovery 94c5a82
git checkout recovery

if you just committed over top of your old work, it is still there

Cethric

Unfortunately the loss occurred before I made any push to Github so my files are not there, I will be checking my iTunes backups later today to see if the files are there. Otherwise I will not be able to do any more work on this for at least another 2 weeks due to exams.

JonB

how did the loss occur? if you committed it locally, it may still be hidden in the repo, unless you deletd the entire folder. if you simply did a pull, or copied files over, or deleted all, you may be in luck. it might not show up in the log... let me refresh my memory on how to search for headless objects.

Cethric

The loss occured when transfering the files from the old directry to the new one, how ever I did not check to make sure that the files had actually been move before deleting the old files (I have also emptied the .Trashes folder so I can not recover from there either)

JonB

@Cethric Did you delete the old folder (including the hidden Git folder)? If the .git folder is there, you should be able to:

from dulwich import porcelain
import os
os.chdir(theoldfolder)
o=porcelain.Repo('.').object_store
commit=o['94c5a825317121c1e7e808826812104d0b1b4484']
tree=o[commit.tree]
print tree._entries

You can plug the sha strings in the tree for the file you are looking for into the object store o, which returns a Blob object. You can then use as_raw_string() and write the result to a file.

Cethric

Unfortunatly i have deleted the directory including the .git folder. The issue is there was no commit with the contents of the files I lost

Cethric

I have been able to replace the files however nothing renders now

Cethric

Due to my post somehow being marked as spam it is now here

JonB

which euclid library are you using? what is the pip name, or github repo?

Cethric

This is the repo listed in the header part of the file. http://code.google.com/p/pyeuclid

Cethric

Anyone have an idea why it isn't working?

JonB

I took a look, although didnt get too far.
My one observation is that it seems like you are swinging for the fences.... you have shaders, view/perspective manipulation, physics, models loading from xml, render loops, etc.... have you tried taking a step back and drawing a single stationary triangle with the default shader?
It seems like there are many things that can go wrong for folks, like floats vs ints, working in normalized vs screen coords, getting the coordinate systems wrong (for instance ios screen coords are upper left, but gl is bottom left). I have to say what you have started here looks pretty cool.

Cethric

Thank you for the response and I will look into it. I believe this is the result of me pushing straight into it with the belief of it was working before so why shouldn't it now.

JonB

maybe take a look at GLKBaseEffect as a way to rule out the custom shaders...

omz

@Cethric Sorry about the overzealous spam filter. I've whitelisted your account, so this shouldn't happen anymore.

Cethric

@omz thank you. I think it was marked a spam due to the proxy server that AU TAFE's use not thinking I was logged in to the network. Thus it was replacing the content of my post. Either way thank you

Cethric

@JonB I have pulled most of the example back so it just renders a Triangle which does work, however the view flickers. Any ideas? (I have updated the github repo, if that helps.)

Cethric

Nevermind, it is fixed now, I don't really know what I did but it is working now.