Source code for noderunner.client

"""Client classes for noderunner"""
from noderunner.connection import Connection
from noderunner.protocol import Protocol
from noderunner.process import open_process
from noderunner.socket import get_sockets


[docs]class Client(object): """Primary class for interfacing with node The client class provides an easy to use interface for calling node functions, handling context and requirements. """ def __init__(self, secured=False): """Spawns a new node process and sets up communication""" self._secured = secured self._start() def _start(self): serv, cli, clifd = get_sockets() secret = "__NO_AUTH__" if self._secured: # pragma: nocover raise RuntimeError("Secure connections are not supported yet") self._proc = open_process(clifd, secret) self._con = Connection(serv) self._proto = Protocol(self._con, None if not self._secured else secret) self._proto.start()
[docs] def eval(self, code, context=None): """Evaluates the code and returns the result Evaluates the passed in string as JavaScript code. This code may optionally be run in a context. :param code: The code to be evaluated :type code: str :param context: Name of the context to run the code in. :type context: str :return: The result of evaluating the expression, as best represented in python. """ return self._proto.request_sync("eval", code=code, context=context)
[docs] def stop(self): """Stops the client and terminates the node process""" self._proto.stop() self._proc.terminate()
[docs] def context(self, name, reqs=[]): """Creates a named context Creates a named context with the passed in requirements pre-included, the context will have it's own global object making it hard to interfere with other things running in node. :param name: Name of the context to be created :type name: str :param reqs: The names of the requirements to be loaded into the context. :type reqs: list :return: a context object for the passed in name. :rtype: :class:`Context` """ name = self._proto.request_sync("mkcontext", name=name, requirements=reqs) return Context(self, name)
[docs] def get(self, path, context): """Gets the value of a javascript variable. Gets the value of a Javascript name/object path in the given context. The passed in path should be a list containing the path to the value that you want to read. For example a list with the elements 'console' and 'log' corresponds to "console.log". :param path: The path to the value to be retrived. :type path: list :return: The value of the object that the path points to. :rtype: :class:`JSError` or a JavaScript object. """ return self._proto.request_sync("get", path=path, context=context)
[docs] def set(self, path, val, context): """Sets the value of a javascript variable. Sets the value of a Javascript name/object path in the given context. The passed in path should be a list containing the path to teh value that you wish to set. For example a list with the elements 'console' and 'log' corresponds to "console.log". The new value has to be a convertable to JSON and thus javascript. Therefore nested dicts are fine but other objects will fail. :param path: The path to the value to be retrived. :type path: list :param val: The value to that the path should be set to :type val: int, str, list, tuple, dict, their subclasses and nested structures comprising of these. :param context: The context to run the command in. :type context: str :return: The changed value as it is represented in JavaScript. :rtype: A JavaScript object. """ return self._proto.request_sync("set", path=path, value=val, context=context)
[docs] def call(self, path, args, context): """Calls the function at path with the passed-in args Calls the function specified by the path using the passed in arguments. Functions are always called in the context of the next to last part of the path. This ensures that the calling ["console","log"] is called in the context of console. The arguments should be a list or tuple each containing an object that is covertable to JSON. :param path: The path to the function to be called. :type path: list :param args: A list arguments to call the function with. All the arguments have to convertable to JSON. :param context: The context containing the function :type context: str :return: The value returned by the function. :rtype: A JavaScript object. """ return self._proto.request_sync("call", path=path, args=args, context=context)
[docs]class Context(object): """A context in which certain commands such as eval can be run You almost never need to create a context this way, instead use the context function on your client object. """ def __init__(self, client, name): self._client = client self._name = name
[docs] def eval(self, code): """Evaluates the code in this context. Evaluates the passed in string and returns the result, the code will be evaluated in the context named in this instance. :param code: The code to be evaluated :type code: str :return: The result of evaluating the expression, as best represented in python. """ return self._client.eval(code, context=self._name)
[docs] def get(self, *path): """Gets the value of a javascript variable. Takes any number of arguments each representing one part of the path. For example calling this function with two arguments, "console" and "log" returns the value of console.log in javascript. :param \*path: The path to the value to be retrived. :type path: list :return: The value of the object that the path points to. :rtype: :class:`JSError` or a JavaScript object. """ return self._client.get(path, self._name)
[docs] def set(self, *args): """Sets the value of a javascript variable. Takes any number of arguments where all but the last one represents a part of the object path. The last argument is the value to set the value to. The function returns the value as it is represented in JavaScript. :param \*path: All but the last argument represent the path. The last argument is the value to set the path to. :return: The value that the object at the path was set to. :rtype: :class:`JSError` or A JavaScript object. """ args = list(args) val = args.pop() return self._client.set(args, val, self._name)
[docs] def call(self, *args): """Calls the function at path with the passed-in args Calls the function specified by the path using the passed in arguments. Functions are always called in the context of the next to last part of the path. This ensures that the calling ["console","log"] is called in the context of console. The arguments should be a list or tuple each containing an object that is covertable to JSON. The last argument to this function should be a collection containing items, all of which are convertable to JSON, that represent the arguments to call the function with. :param \*args: All but the last argument represent the path. The last argument should contain the arguments. :type \*args: list :return: The value returned by the function. :rtype: A JavaScript object. """ args = list(args) fn_args = args.pop() return self._client.call(args, fn_args, self._name)
@property def objects(self): """Returns a handle pointing the context global object. Retruns a handle that points the the global object or root object of this context. This is the recommended way to get a handle for interfacing with context. """ return Handle(self)
[docs]class Handle(object): """A handle is an object representing the path to some value. Handles are used for calling JavaScript code in a pythonic way. This is acheieved using the overridable magic methods provided in Python. Accessing an attribute that is not a member of the handle class returns a handle pointing to that object. For example accessing root.console.log, where root points to the global object in a context. Cuasing root to returns handle object pointing to console, and this object, in turn gives a reference to log. Calling a handle will call the object that it points to returning the value returned by the javascript function. """ __slots__ = ("_path", "_context") def __init__(self, context, path=[]): self._context = context self._path = path def __getattr__(self, name): return Handle(self._context, self._path + [name]) def __setattr__(self, name, value): if name in self.__slots__: super(Handle, self).__setattr__(name, value) else: self.__set(name, value) def __set(self, name, value): set_args = list(self._path) + [name, value] return self._context.set(*set_args)
[docs] def get(self): """Gets the value that the handle points to Performs a NodeRunner get operation returning the value that the handle points to. :returns: The JavaScript value that the handle points at :rtype: A JavaScript object """ return self._context.get(*self._path)
def __str__(self): return "Handle: " + ".".join(self._path) def __call__(self, *args): call_args = list(self._path) + [args] return self._context.call(*call_args) __getitem__ = __getattr__ __setitem__ = __set