Source code for v8cffi.context
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import six
from _v8 import ffi, lib
from . import exceptions
__all__ = ['Context']
_DEFAULT_SCRIPT_NAME = '<anonymous>'
def _read_file(path):
with open(path, 'rb') as fh:
return fh.read()
def _is_utf_8(txt):
"""
Check a string is utf-8 encoded
:param bytes txt: utf-8 string
:return: Whether the string\
is utf-8 encoded or not
:rtype: bool
"""
assert isinstance(txt, six.binary_type)
try:
_ = six.text_type(txt, 'utf-8')
except (TypeError, UnicodeEncodeError):
return False
else:
return True
@six.python_2_unicode_compatible
class _String(object):
"""
A wrapper for C(ffi) strings
Usage::
with String() as s:
lib.my_cffi_foo(s.string_ptr, s.len_ptr)
result = str(s)
:ivar string_ptr: String pointer,\
default: :py:data:`cffi.NULL`
:type string_ptr: :py:class:`ffi.CData<char **>`
:ivar len_ptr: String length, default: 0
:type len_ptr: :py:class:`ffi.CData<size_t *>`
"""
def __init__(self):
self.string_ptr = None
self.len_ptr = None
def __enter__(self):
"""
Instantiate string_ptr and len_ptr
"""
assert self.string_ptr is None
assert self.len_ptr is None
self.string_ptr = ffi.new('char **')
self.string_ptr[0] = ffi.NULL
self.len_ptr = ffi.new('size_t *', 0)
return self
def __exit__(self, *_, **__):
"""
Clean up string_ptr and len_ptr
"""
assert self.string_ptr is not None
assert self.len_ptr is not None
lib.v8cffi_free(self.string_ptr[0])
self.string_ptr = None
self.len_ptr = None
def __str__(self):
"""
:return: Representation of the string
:rtype: str
"""
return six.text_type(self.to_bytes(), 'utf-8')
def to_bytes(self):
"""
:return: Representation of the string, utf-8 encoded
:rtype: bytes
"""
return ffi.buffer(self.string_ptr[0], self.len_ptr[0])[:]
[docs]class Context(object):
"""
An execution environment that allows\
separate, unrelated, JS applications\
to run in a single instance of V8.\
It may be thought as a browser tab.
Running scripts within the same Context\
is thread-safe.
There may be many Contexts per VM
:param vm: Initialized VM
:type vm: :py:class:`.VM`
"""
def __init__(self, vm):
self._vm = vm
self._c_context = None
def __enter__(self):
"""
See :py:func:`set_up` method for docs
"""
assert self._c_context is None
assert self._vm.is_alive()
self._c_context = ffi.new('v8cffi_context_t **')
self._c_context[0] = ffi.NULL
code = lib.v8cffi_context_new(self._c_context, self._vm.get_c_vm()[0])
if code != lib.E_V8_OK:
raise exceptions.get_exception(code)
return self
def __exit__(self, *_, **__):
"""
See :py:func:`tear_down` method for docs
"""
assert self._c_context is not None
assert self._vm.is_alive()
lib.v8cffi_context_free(self._c_context[0])
self._c_context = None
[docs] def set_up(self):
"""
Initialize the context.\
Remember to call :py:func:`tear_down`\
before exiting the application.\
It's recommended to use a ``with``\
statement instead of this method\
to ensure clean up
:raises V8MemoryError: if there\
is no memory for allocating it,\
the process should die afterwards anyway,\
there is little point in catching this
"""
return self.__enter__()
[docs] def tear_down(self):
"""
Destructs the context
"""
return self.__exit__()
[docs] def load_libs(self, scripts_paths):
"""
Load script files into the context.\
This can be thought as the HTML script tag.\
The files content must be utf-8 encoded.
This is a shortcut for reading the files\
and pass the content to :py:func:`run_script`
:param list scripts_paths: Script file paths.
:raises OSError: If there was an error\
manipulating the files. This should not\
normally be caught
:raises V8Error: if there was\
an error running the JS script
"""
for path in scripts_paths:
self.run_script(_read_file(path), identifier=path)
[docs] def run_script(self, script, identifier=_DEFAULT_SCRIPT_NAME):
"""
Run a JS script within the context.\
All code is ran synchronously,\
there is no event loop. It's thread-safe
:param script: utf-8 encoded or unicode string
:type script: bytes or str
:param identifier: utf-8 encoded or unicode string.\
This is used as the name of the script\
(ie: in stack-traces)
:type identifier: bytes or str
:return: Result of running the JS script
:rtype: str
:raises V8Error: if there was\
an error running the JS script
"""
assert isinstance(script, six.text_type) or _is_utf_8(script)
assert isinstance(identifier, six.text_type) or _is_utf_8(identifier)
if isinstance(script, six.text_type):
script = script.encode('utf-8')
if isinstance(identifier, six.text_type):
identifier = identifier.encode('utf-8')
with _String() as output:
with _String() as error:
code = lib.v8cffi_run_script(
self._c_context[0],
script,
len(script),
identifier,
len(identifier),
output.string_ptr,
output.len_ptr,
error.string_ptr,
error.len_ptr)
if code != lib.E_V8_OK:
raise exceptions.get_exception(code)(six.text_type(error))
return six.text_type(output)