2015-02-21 6 views
3

Я пытаюсь передать PyN8 данные Python (списки, dicts, строки ..., произвольно вложенные).Преобразование объектов Python в JavaScript для PyV8

class Global(object): 
    def __init__(self, data): 
     self.data = data 
ctx = PyV8.JSContext(Global([{'a':1}])) 
ctx.enter() 
res = ctx.eval('data.length') 
js_len = PyV8.convert(res) 
print js_len 

Код выше не печатает None, по-видимому, потому что объект данных не преобразуется в JSArray и, таким образом data.length вычисляется в undefined. Есть ли надежный способ сделать необходимое преобразование в PyV8, кроме использования JSON?

+0

Я никогда не использовал глобальный параметр таким образом в pyv8. Я просто пишу это, как 'CTX = PyV8.JSContext() ctx.enter() ctx.locals.data = [{ 'а': 1}] печати ctx.eval ('' data.length) ' Не могли бы вы попробовать это как мир привет, прежде чем делать что-то более сложное? –

+0

Спасибо, но это не лучше. 'ctx.locals.x = [{'a': 1}]; ctx.eval ('x.length'); ctx.eval ('x [0] .a') 'печатает None и 1. –

ответ

4

По-видимому, PyV8 неправильно преобразовывает списки python в массивы Javascript, что приводит my_list.length к возврату undefined, который преобразуется в None.

ctx = PyV8.JSContext() 
ctx.enter() 
ctx.locals.a = [{'a':1}] 
print ctx.locals.a 
#> [{'a': 1}] 
print ctx.eval("a.length") 
#> None 
print ctx.eval("a[0].a") 
#> 1 
ctx.locals.blub = {'a':1} 
print ctx.eval("blub.a") 
#> 1 
print ctx.eval("Object.keys(blub)") 
#> a 
ctx.locals.blub = {'a':[1,2,3]} 
print ctx.eval("Object.keys(blub)") 
#> a 
print ctx.eval("blub.a") 
#> [1, 2, 3] 
ctx.locals.blub2 = [{'a':[1,2,3]}] 
print ctx.eval("blub2") 
#> [{'a': [1, 2, 3]}] 
print ctx.eval("blub2.length") 
#> None 
print ctx.eval("Array.isArray(blub2)") 
#> False 
print ctx.eval("typeof(blub2)") 
#> object 
print ctx.eval("blub2[0].a") 
#> [1, 2, 3] 
print ctx.eval("typeof(blub.a)") 
#> object 
print ctx.eval("Array.isArray(blub.a)") 
#> False 

Ответ заключается в использовании PyV8.JSArray(my_list). Я написал следующие вспомогательные функции для моего проекта, которые имеют дело с различными небольшими проблемами и упрощают преобразование между объектами python и js. Они нацелены на at a specific version of PyV8 however (which is the only version I can recommend, see discussion in the linked issues), поэтому ваши результаты могут отличаться, если вы используете их как есть. Пример использования:

ctx.locals.blub3 = get_js_obj({'a':[1,2,3]}) 
ctx.locals.blub4 = get_js_obj([1,2,3]) 
ctx.eval("blub3.a.length") 
#> 3 
ctx.eval("blub4.length") 
#> 3 

И вот функции.

def access_with_js(ctx, route): 
    if len(route) == 0: 
     raise Exception("route must have at least one element") 
    accessor_string = route[0] 
    for elem in route[1:]: 
     if type(elem) in [str, unicode]: 
      accessor_string += "['" + elem + "']" 
     elif type(elem) == int: 
      accessor_string += "[" + str(elem) + "]" 
     else: 
      raise Exception("invalid element in route, must be text or number") 
    return ctx.eval(accessor_string) 

def get_py_obj(ctx, obj, route=[]): 
    def dict_is_empty(dict): 
     for key in dict: 
      return False 
     return True 

    def access(obj, key): 
     if key in obj: 
      return obj[key] 
     return None 

    cloned = None 
    if isinstance(obj, list) or isinstance(obj, PyV8.JSArray): 
     cloned = [] 
     temp = str(access_with_js(ctx, route)) #working around a problem with PyV8 r429 
     num_elements = len(obj) 
     for index in range(num_elements): 
      elem = obj[index] 
      cloned.append(get_py_obj(ctx, elem, route + [index])) 
    elif isinstance(obj, dict) or isinstance(obj, PyV8.JSObject): 
     cloned = {} 
     for key in obj.keys(): 
      cloned_val = None 
      if type(key) == int: 
       #workaround for a problem with PyV8 where it won't let me access 
       #objects with integer accessors 
       val = None 
       try: 
        val = access(obj, str(key)) 
       except KeyError: 
        pass 
       if val == None: 
        val = access(obj, key) 
       cloned_val = get_py_obj(ctx, val, route + [key]) 
      else: 
       cloned_val = get_py_obj(ctx, access(obj, key), route + [key]) 
      cloned[key] = cloned_val 
    elif type(obj) == str: 
     cloned = obj.decode('utf-8') 
    else: 
     cloned = obj 
    return cloned 

def get_js_obj(ctx,obj): 
    #workaround for a problem with PyV8 where it will implicitely convert python lists to js objects 
    #-> we need to explicitely do the conversion. see also the wrapper classes for JSContext above. 
    if isinstance(obj, list): 
     js_list = [] 
     for entry in obj: 
      js_list.append(get_js_obj(ctx,entry)) 
     return PyV8.JSArray(js_list) 
    elif isinstance(obj, dict): 
     js_obj = ctx.eval("new Object();") # PyV8.JSObject cannot be instantiated from Python 
     for key in obj.keys(): 

      try: 
       js_obj[key] = get_js_obj(ctx,obj[key]) 
      except Exception, e: 
       # unicode keys raise a Boost.Python.ArgumentError 
       # which can't be caught directly: 
       # https://mail.python.org/pipermail/cplusplus-sig/2010-April/015470.html 
       if (not str(e).startswith("Python argument types in")): 
        raise 
       import unicodedata 
       js_obj[unicodedata.normalize('NFKD', key).encode('ascii','ignore')] = get_js_obj(ctx,obj[key]) 
     return js_obj 
    else: 
     return obj 
+0

Привет @Jthorpe. Благодарим за редактирование. Я думаю, что аргументация get_js_obj с использованием питона python в оригинале заключалась в том, что они правильно преобразуются в объект JS при назначении результирующего объекта локальным контекстам. Поэтому я не видел необходимости проводить там ручную конвертацию. Я также немного удивляюсь вашей модифицированной версии - работает ли она после выполнения таких заданий, как 'js_obj [key] = some_obj' на стороне python? –

+0

Я не использовал это ни для чего другого, кроме обычных базовых типов (bool, string, int, float, tuples, lists и dicts), но он отлично работает для меня. Проблема была в том, что я хотел создать [тонкую обертку] (https://github.com/jdthorpe/ajvpy) библиотеку [AJV] (https://github.com/epoberezkin/ajv) и ранее использовать 'ctx.locals.x = get_js_obj ({'hi': 'world'})' в python приведет к тому, что объект JS будет таким, что 'x.hasOwnProperty ('items')' был 'true' (ссылаясь на python' dict.items'). – Jthorpe

+0

Я вижу, интересно - я не знал, что PyV8 также назначит эти свойства. Хорошо, если вы протестировали его, и он работает, я верю, что могу позволить редактировать стенд. –