Exemple #1
0
class Dummy(Model):
    _private = Int()
    computed = Int().tag(store=False)
    id = Int()
    enabled = Bool()
    string = Bool()
    list_of_int = List(int)
    list_of_str = List(str)
    list_of_any = List()
    list_of_tuple = List(Tuple())
    list_of_tuple_of_float = List(Tuple(float))
    tuple_of_any = Tuple()
    tuple_of_number = Tuple((float, int))
    tuple_of_int_or_model = Tuple((int, Model))
    tuple_of_forwarded = Tuple(ForwardTyped(lambda: NotYetDefined))
    set_of_any = Tuple()
    set_of_number = Set(float)
    set_of_model = Set(AbstractModel)
    dict_of_any = Dict()
    dict_of_str_any = Dict(str)
    dict_of_str_int = Dict(str, int)
    typed_int = Typed(int)
    typed_dict = Typed(dict)
    instance_of_model = Instance(AbstractModel)
    forwarded_instance = ForwardInstance(lambda: NotYetDefined)
    coerced_int = Coerced(int)
    prop = Property(lambda self: True)
    tagged_prop = Property(lambda self: 0).tag(store=True)
Exemple #2
0
class Comment(SQLModel):
    page = Instance(Page)
    author = Instance(User)
    status = Enum("pending", "approved")
    body = Str().tag(type=sa.UnicodeText())
    reply_to = ForwardInstance(lambda: Comment).tag(nullable=True)
    when = Instance(time)
Exemple #3
0
class ProxyAbstractWidgetItem(ProxyControl):
    #: Reference to the declaration
    declaration = ForwardInstance(lambda: AbstractWidgetItem)

    def set_row(self, row):
        pass

    def set_column(self, column):
        pass

    def set_text(self, text):
        pass

    def set_text_alignment(self, text_alignment):
        pass

    def set_icon(self, icon):
        pass

    def set_icon_size(self, size):
        pass

    def set_editable(self, editable):
        pass

    def set_checkable(self, checkable):
        pass
Exemple #4
0
class Page(NoSQLModel):
    title = Str()
    status = Enum("preview", "live")
    body = Str()
    author = Instance(User)
    images = List(Image)
    related = List(ForwardInstance(lambda: Page))
Exemple #5
0
class AbstractQtWidgetItem(AbstractQtWidgetItemGroup, ProxyAbstractWidgetItem):
    #:
    is_destroyed = Bool()

    #: Index within the view
    index = Instance(QModelIndex)

    #: Delegate widget to display when editing the cell
    #: if the widget is editable
    delegate = Instance(QtWidget)

    #: Reference to view
    view = ForwardInstance(_abstract_item_view)

    def create_widget(self):
        # View items have no widget!
        for child in self.children():
            if isinstance(child, (Pattern, QtWidget)):
                self.delegate = child

    def init_widget(self):
        pass

    def init_layout(self):
        super(AbstractQtWidgetItem, self).init_layout()
        self._update_index()

    def _update_index(self):
        """ Update where this item is within the model"""
        raise NotImplementedError

    def destroy(self):
        """ Set the flag so we know when this item is destroyed """
        self.is_destroyed = True
        super(AbstractQtWidgetItem, self).destroy()
Exemple #6
0
class ExecutableGraph(model.Graph):
    controller = ForwardInstance(_import_graph_calculator_controller)
    nxgraph = Property(lambda self: self._get_nxgraph(), cached=True)

    topologyChanged = Event()
    valuesChanged = Event()
    attributesChanged = Event()

    def _get_nxgraph(self):
        g = nx.MultiDiGraph()
        for node in self.nodes:
            g.add_node(node.id, id=node.id, name=node.name)
        for edge in self.edges:
            g.add_edge(edge.start_socket.node.id,
                       edge.end_socket.node.id,
                       id=edge.id,
                       source_socket=edge.start_socket.name,
                       target_socket=edge.end_socket.name)
        return g

    def _observe_topologyChanged(self, change):
        self.get_member('nxgraph').reset(self)
        self.execute_graph()

    def _observe_valuesChanged(self, change):
        self.execute_graph()

    def _observe_attributesChanged(self, change):
        self.execute_graph()

    def execute_graph(self):
        for node_id in nx.topological_sort(self.nxgraph):
            self.node_dict[node_id].update()
Exemple #7
0
class Bookmark(Atom):
    #: Name of it
    name = Unicode()

    #: Bible
    bible = ForwardInstance(lambda: Bible)

    #: Book
    book = Instance(Book)

    #: Chapter
    chapter = Instance(Chapter)

    #: Verse
    #verse = Instance(Verse)

    #: Save / load state
    state = Dict()

    def __init__(self, *args, **kwargs):
        super(Bookmark, self).__init__(*args, **kwargs)
        if kwargs.get('state') is None:
            #: If we're not loading from state
            self.state = {
                'bible': self.bible.version.key,
                'book': self.book.name,
                'chapter': self.chapter.number,
                #'verse': self.verse.number
            }

    def _default_name(self):
        return u"{} {}".format(self.book.name, self.chapter.number)

    def _default_bible(self):
        try:
            #: Prevent loading two bibles if it was bookmarked in a different bible
            bible = AppState.instance().bible
            if bible is not None:
                return bible
            return AppState.instance().get_bible(self.state['bible'])
        except KeyError:
            return None

    def _default_book(self):
        if not self.bible:
            return None
        try:
            return self.bible.get_book(self.state['book'])
        except KeyError:
            return None

    def _default_chapter(self):
        if not self.book:
            return None
        try:
            #: They're supposed to be in order
            return self.book.chapters[self.state['chapter'] - 1]
        except KeyError:
            return None
Exemple #8
0
class DeviceTransport(Model):

    #: The declaration that defined this transport
    declaration = Typed(extensions.DeviceTransport).tag(config=True)

    #: The transport specific config
    config = Instance(Model, ()).tag(config=True)

    #: The active protocol
    protocol = ForwardInstance(lambda: DeviceProtocol).tag(config=True)

    #: Connection state. Subclasses must implement and properly update this
    connected = Bool()

    #: Distinguish between transports that always spool (e.g. Printer, File I/O)
    #: or are dependent on the 'spooling' configuration option (e.g. Serial)
    always_spools = Bool()

    #: Most recent input/output. These can be observed to update the UI
    last_read = Bytes()
    last_write = Bytes()

    def __init__(self, *args, **kwargs):
        super(DeviceTransport, self).__init__(*args, **kwargs)
        if self.protocol:
            self.protocol.transport = self

    def _observe_protocol(self, change):
        """ Whenever the protocol changes update the transport reference

        """
        if (change['type'] == 'update'
                or change['type'] == 'create') and change['value']:
            self.protocol.transport = self

    def connect(self):
        """ Connect using whatever implementation necessary

        """
        raise NotImplementedError

    def write(self, data):
        """ Write using whatever implementation necessary

        """
        raise NotImplementedError

    def read(self, size=None):
        """ Read using whatever implementation necessary and
        invoke `protocol.data_received` with the output.

        """
        raise NotImplementedError

    def disconnect(self):
        """ Disconnect using whatever implementation necessary

        """
        raise NotImplementedError
Exemple #9
0
class Edge(GraphItem):
    id = Str()
    graph = ForwardInstance(import_graph_type)
    start_socket = ForwardInstance(import_socket_type)
    end_socket = ForwardInstance(import_socket_type)

    edge_type = Typed(EdgeType)

    def _default_edge_type(self):
        return EdgeType.EDGE_TYPE_BEZIER

    def _observe_start_socket(self, change):
        if change.get('oldvalue', None) is not None:
            s = change['oldvalue']
            if self in s.edges:
                s.edges.remove(self)
        if change['value'] is not None:
            change['value'].edges.append(self)
            if self.end_socket is not None and change[
                    'value'].data_type != self.end_socket.data_type:
                raise TypeError(
                    "Incompatible type for connection - %s->%s" %
                    (change['value'].data_type, self.end_socket.data_type))

    def _observe_end_socket(self, change):
        if change.get('oldvalue', None) is not None:
            s = change['oldvalue']
            if self in s.edges:
                s.edges.remove(self)
        if change['value'] is not None:
            change['value'].edges.append(self)
            if self.start_socket is not None and change[
                    'value'].data_type != self.start_socket.data_type:
                raise TypeError(
                    "Incompatible type for connection - %s->%s" %
                    (self.start_socket.data_type, change['value'].data_type))

    @property
    def data_type(self):
        return getattr(self.start_socket, "data_type",
                       getattr(self.end_socket, "data_type", ""))

    @property
    def is_open(self):
        return self.end_socket is None or self.start_socket is None
Exemple #10
0
class QtPlotItem3D(QtPlotItem2D):
    """Use forward instance to not cause import issues if
    not installed."""

    widget = ForwardInstance(gl_view_widget)

    def create_widget(self):
        from pyqtgraph.opengl import GLViewWidget

        if isinstance(self.parent(), AbstractQtPlotItem):
            self.widget = self.parent_widget()
        else:
            self.widget = GLViewWidget(parent=self.parent_widget())
            self.widget.opts["distance"] = 40
            self.widget.raise_()

    def init_signals(self):
        pass

    def _create_grid(self):
        from pyqtgraph.opengl import GLGridItem

        gx = GLGridItem()
        gx.rotate(90, 0, 1, 0)
        gx.translate(-10, 0, 0)
        self.widget.addItem(gx)
        gy = GLGridItem()
        gy.rotate(90, 1, 0, 0)
        gy.translate(0, -10, 0)
        self.widget.addItem(gy)
        gz = GLGridItem()
        gz.translate(0, 0, -10)
        self.widget.addItem(gz)

    def set_z(self, z):
        self._refresh_plot()

    def _refresh_plot(self):
        import numpy as np

        # import pyqtgraph as pg
        from pyqtgraph import opengl as gl

        self._create_grid()
        pts = np.vstack(
            [self.declaration.x, self.declaration.y,
             self.declaration.z]).transpose()
        plt = gl.GLLinePlotItem(
            pos=pts
        )  # , color=pg.glColor((i,n*1.3)), width=(i+1)/10., antialias=True)
        self.widget.addItem(plt)

    def set_grid(self, grid):
        pass
Exemple #11
0
class HttpRequest(Atom):
    """ The request object created for fetch calls. 
    It's based on the design of Tornado's HttpRequest.
    
    """
    #: Request url
    url = Unicode()

    #: Request method
    method = Unicode('get')

    #: Request headers
    headers = Dict()

    #: Retry count
    retries = Int()

    #: Request parameter data
    data = Dict()

    #: Content type
    content_type = Unicode("application/x-www-urlencoded")

    #: Raw request body
    body = Unicode()

    #: Response created
    response = ForwardInstance(lambda: HttpResponse)

    #: Called when complete
    callback = Callable()

    #: Streaming callback
    streaming_callback = Callable()

    #: Start time
    start_time = Float()

    def __init__(self, *args, **kwargs):
        """ Build the request as configured.
        
        """
        super(HttpRequest, self).__init__(*args, **kwargs)
        self.start_time = time.time()
        self.response = HttpResponse(request=self)
        self.init_request()

    def init_request(self):
        """ Initialize the request using whatever native means necessary 
        
        """
        raise NotImplementedError
Exemple #12
0
class Fit1D(FitObject):
    'class for fitting 1d datasets'

    parent = ForwardInstance(lambda: ap.data.XYDataObject)
    plot = ForwardInstance(lambda: ap.plot.Plot1D)

    fitted = Bool(
        default=False
    )  # boolean which indicates if current model and data are fitted

    _model = Value()
    result = Value()

    _fit = Typed(Fit)

    def __init__(self, parent, *args, **kwargs):
        self.parent = parent
        super(Fit1D, self).__init__(*args, **kwargs)

        self.result = None

    def add_model(self, model):
        if self._model: del self._model

        if isinstance(model, str):
            self._model = get_model(model, self.parent.x, self.parent.y)
        else:
            self._model = model

        self.fitted = False

    @observe('parent.x', 'parent.x_updated', 'parent.y', 'parent.y_updated')
    def _data_updated(self, change):
        self.fitted = False

    def execute(self, *options, **kwoptions):
        self._fit = Fit(self._model, self.parent.x, self.parent.y)
        self.result = self._fit.execute(*options, **kwoptions)
Exemple #13
0
class VTKCanvas(Control):
    """ A control which can be used to embded vtk renderers.

    """
    #: The vtk renderer to display in the window. This should be used
    #: if only a single renderer is required for the scene.
    renderer = d_(ForwardInstance(vtkRenderer))

    #: The list of vtk renderers to display in the window. This should
    #: be used if multiple renderers are required for the scene.
    renderers = d_(List(ForwardInstance(vtkRenderer)))

    #: A VTKCanvas expands freely in height and width by default.
    hug_width = set_default('ignore')
    hug_height = set_default('ignore')

    #: A reference to the ProxyVTKCanvas object.
    proxy = Typed(ProxyVTKCanvas)

    def render(self):
        """ Request a render of the underlying scene.

        """
        if self.proxy_is_active:
            self.proxy.render()

    #--------------------------------------------------------------------------
    # Observers
    #--------------------------------------------------------------------------
    @observe('renderer', 'renderers')
    def _update_proxy(self, change):
        """ An observer which sends state change to the proxy.

        """
        # The superclass handler implementation is sufficient.
        super(VTKCanvas, self)._update_proxy(change)
Exemple #14
0
class Page(SQLModel):
    title = Str().tag(length=60)
    status = Enum("preview", "live")
    body = Str().tag(type=sa.UnicodeText())
    author = Instance(User)
    if DATABASE_URL.startswith("postgres"):
        images = List(Instance(Image))
        related = List(ForwardInstance(lambda: Page)).tag(nullable=True)
        tags = List(str)

    visits = BigInt()
    date = Instance(date)
    last_updated = Instance(datetime)
    rating = Instance(Decimal)
    ranking = Float().tag(name="order")

    # A bit verbose but provides a custom column specification
    data = Instance(object).tag(column=sa.Column("data", sa.LargeBinary()))
Exemple #15
0
class UIView(UIResponder):
    """ From:
        https://developer.apple.com/documentation/uikit/uiview?language=objc
    """
    #__signature__ = set_default((dict(initWithFrame='CGRect'),))

    yoga = ForwardInstance(yoga_class)

    def _default_yoga(self):
        return yoga_class()(self, 'yoga')

    #: Properties
    backgroundColor = ObjcProperty('UIColor')
    hidden = ObjcProperty('bool')
    alpha = ObjcProperty('float')
    opaque = ObjcProperty('bool')
    tintColor = ObjcProperty('UIColor')
    tintAdjustmentMode = ObjcProperty('UIViewTintAdjustmentMode')
    clipsToBounds = ObjcProperty('bool')
    clearsContextBeforeDrawing = ObjcProperty('bool')
    maskView = ObjcProperty('UIView')
    userInteractionEnabled = ObjcProperty('bool')
    multipleTouchEnabled = ObjcProperty('bool')
    exclusiveTouch = ObjcProperty('bool')

    frame = ObjcProperty('CGRect')
    bounds = ObjcProperty('CGRect')
    center = ObjcProperty('CGPoint')
    transform = ObjcProperty('CGAffineTransform')

    layoutMargins = ObjcProperty('UIEdgeInserts')
    preservesSuperviewLayoutMargins = ObjcProperty('bool')

    #: Methods
    addSubview = ObjcMethod('UIView')
    bringSubviewToFront = ObjcMethod('UIView')
    sendSubviewToBack = ObjcMethod('UIView')
    removeFromSuperview = ObjcMethod()
    insertSubview = ObjcMethod(
        'UIView',
        dict(atIndex='NSInteger', aboveSubview='UIView',
             belowSubview='UIView'))
    exchangeSubviewAtIndex = ObjcMethod('NSInteger',
                                        dict(withSubviewAtIndex='NSInteger'))
Exemple #16
0
class AtomAxes(Atom):
    """axis for matplot, has reference to which plots, takes care of updateing etc"""

    mp_parent = ForwardInstance(
        lambda: MatPlot)  #todo figure out if its needed
    axes = Instance(Axes)
    data_objects = Dict(default={})

    def __init__(self, mp_parent, axes):
        self.mp_parent = mp_parent
        self.axes = axes
        super(AtomAxes, self).__init__()

    def __add__(self, other):
        #add a dataobject to these axes
        self.add_dataobject(other)

    def __sub__(self, other):
        # remove a dataobject from these axes
        self.remove_dataobject(other)

    def add_dataobject(self, data_obj):
        uid = id(data_obj)
        if uid in self.data_objects:
            raise KeyError(
                'Data object already added')  #todo print the label and s***e

        self.data_objects[uid] = data_obj
        data_obj.plot.line = self.axes.plot(data_obj.plot.x,
                                            data_obj.plot.y)[0]

        data_obj.plot.observe('updated', self.redraw_plot)

    def remove_dataobject(self, uid):
        assert type(uid) == int
        data_obj = self.data_objects.pop(uid)
        data_obj.plot.unobserve('updated')

    def redraw_plot(self, change):
        print(change)
        print('redrawing')
        self.axes.relim()
        self.axes.autoscale_view()
Exemple #17
0
class Pipe(Operation):
    #: Reference to the implementation control
    proxy = Typed(ProxyPipe)

    #: Spline to make the pipe along
    spline = d_(Instance(Shape))

    #: Profile to make the pipe from
    profile = d_(ForwardInstance(WireFactory))

    #: Fill mode
    fill_mode = d_(
        Enum(None, 'corrected_frenet', 'fixed', 'frenet', 'constant_normal',
             'darboux', 'guide_ac', 'guide_plan', 'guide_ac_contact',
             'guide_plan_contact', 'discrete_trihedron')).tag(view=True,
                                                              group='Pipe')

    @observe('spline', 'profile', 'fill_mode')
    def _update_proxy(self, change):
        super(Pipe, self)._update_proxy(change)
Exemple #18
0
class DeviceTransport(Model):

    #: The declaration that defined this transport
    declaration = Typed(extensions.DeviceTransport).tag(config=True)

    #: The transport specific config
    config = Instance(Model, ()).tag(config=True)

    #: The active protocol
    protocol = ForwardInstance(lambda: DeviceProtocol).tag(config=True)

    #: Connection state. Subclasses must implement and properly update this
    connected = Bool()

    #: Distinguish between transports that always spool (e.g. Printer, File I/O)
    #: or are dependent on the 'spooling' configuration option (e.g. Serial)
    always_spools = Bool()

    def connect(self):
        """ Connect using whatever implementation necessary
        
        """
        raise NotImplementedError

    def write(self, data):
        """ Write using whatever implementation necessary
        
        """
        raise NotImplementedError

    def read(self, size=None):
        """ Read using whatever implementation necessary.
        
        """
        raise NotImplementedError

    def disconnect(self):
        """ Disconnect using whatever implementation necessary
        
        """
        raise NotImplementedError
Exemple #19
0
class Block(Declarative):
    """ An object which dynamically insert's its children into another block's parent object.
    
    The 'Block' object is used to cleanly and easily insert it's children
    into the children of another object. The 'Object' instance assigned to the
    'object' property of the 'Block' will be parented with the parent of
    the 'Include'. Creating an 'Include' with no parent is a programming
    error.
    
    """

    #: The Block to which this blocks children should be inserted into
    block = d_(ForwardInstance(lambda: Block))

    def initialize(self):
        """ A reimplemented initializer.

        This method will add the include objects to the parent of the
        include and ensure that they are initialized.

        """
        super(Block, self).initialize()
        if self.block:
            self.block.parent.insert_children(self.block, self.children)

    def _observe_block(self, change):
        """ A change handler for the 'objects' list of the Include.

        If the object is initialized objects which are removed will be
        unparented and objects which are added will be reparented. Old
        objects will be destroyed if the 'destroy_old' flag is True.

        """
        if self.is_initialized:
            if change['type'] == 'update':
                old_block = change['oldvalue']
                old_block.parent.remove_children(old_block, self.children)
                new_block = change['value']
                new_block.parent.insert_children(new_block, self.children)
Exemple #20
0
class Pipe(Operation):
    """ An operation that extrudes a profile along a spline, wire, or path.
    
    Attributes
    ----------
    
    spline: Edge or Wire
        The spline to extrude along.
    profile: Wire
        The profile to extrude.
    fill_mode: String, optional
        The fill mode to use.
    
        
    Examples
    --------
    
    See examples/pipes.enaml
        
    """
    #: Reference to the implementation control
    proxy = Typed(ProxyPipe)

    #: Spline to make the pipe along
    spline = d_(ForwardInstance(WireFactory))

    #: Profile to make the pipe from
    profile = d_(Instance(Shape))

    #: Fill mode
    fill_mode = d_(
        Enum(None, 'corrected_frenet', 'fixed', 'frenet', 'constant_normal',
             'darboux', 'guide_ac', 'guide_plan', 'guide_ac_contact',
             'guide_plan_contact', 'discrete_trihedron')).tag(view=True,
                                                              group='Pipe')

    @observe('spline', 'profile', 'fill_mode')
    def _update_proxy(self, change):
        super(Pipe, self)._update_proxy(change)
Exemple #21
0
class Dataset(Atom):
    """Represent a node in the data store."""

    #: Custom metadata attached to the node.
    metadata = Dict(str)

    def __getitem__(self, key: str) -> Union[DataArray, "Dataset"]:
        return self._data[key]

    def __contains__(self, key: str) -> bool:
        return key in self._data

    def __iter__(self) -> Iterator[str]:
        return iter(self._data)

    def values(self) -> Iterator[Union[DataArray, "Dataset"]]:
        """Iterable on the values stored."""
        return iter(self._data.values())

    def items(self) -> Iterator[Tuple[str, Union[DataArray, "Dataset"]]]:
        """Iterable on the keys and values stored."""
        return iter(self._data.items())

    _data = Dict(str, ForwardInstance(lambda: (DataArray, Dataset)))
Exemple #22
0
class Client(Model):
    """ Handles logging into protonmail
    
    """
    #: API access
    api = ForwardInstance(lambda: API)

    #: Use blocking API
    blocking = Bool()

    #: Debug api calls
    debug = Bool(True)

    #: Is logged in
    is_logged_in = Bool()

    def _default_api(self):
        return API(client=self)

    # =========================================================================
    # Parameters for api/auth/info
    # =========================================================================
    AppVersion = Str('Web_3.14.21')
    ApiVersion = Str('3')

    #: Username
    Username = Str()

    #: Web
    ClientID = Str("Web")

    #: Client secret from WebClient/env/configDefault.js
    ClientSecret = Str("4957cc9a2e0a2a49d02475c9d013478d")

    #: Auth info from login
    AuthInfo = Instance(responses.AuthInfoResponse)

    # =========================================================================
    # Parameters for api/auth/
    # =========================================================================
    #: Computed client ephemeral, set in _default_ClientProof
    ClientEphemeral = Bytes()

    #: Default key size
    KeySize = Int(2048)

    #: Get the hashed password
    HashedPassword = Bytes()

    def _observe_HashedPassword(self, change):
        # Recalculate the proof when the HashedPassword changes
        self.ClientProof = self._default_ClientProof()

    def _default_ClientProof(self):
        """ Computes the ClientProof from the AuthInfo """
        info = self.AuthInfo
        proofs = auth.generate_proofs(self.KeySize,
                                      b64d(auth.read_armored(info.Modulus)),
                                      self.HashedPassword,
                                      b64d(info.ServerEphemeral))

        self.ClientEphemeral = proofs['client_ephemeral']
        self.ExpectedServerProof = proofs['server_proof']

        return proofs['client_proof']

    #: Client proof
    ClientProof = Bytes()

    #: Expected server proof
    ExpectedServerProof = Bytes()

    #: Auth response from login
    Auth = Instance(responses.AuthResponse)

    #: Code for api/auth
    TwoFactorCode = Instance(TwoFactorCode)

    EventID = Str()

    def _default_EventID(self):
        return self.Auth and self.Auth.EventID

    # =========================================================================
    # Parameters for api/auth/cookies/
    # =========================================================================

    #: Used for api/auth/cookies/
    ResponseType = Str("token")

    GrantType = Str("refresh_token")

    RedirectURI = Str("https://protonmail.com")

    def _default_State(self):
        return auth.generate_random_string(24)

    #: Random string
    State = Str()

    #: Result from the cookies request
    AuthCookies = Instance(responses.AuthCookiesResponse)

    #: Cookies set
    Cookies = Instance((CookieJar, SimpleCookie))

    #: TODO: How to make this secure?
    SessionStorage = Dict()

    #: The hashed mailbox password
    MailboxPassword = Str()

    # =========================================================================
    # Results from api/users/
    # =========================================================================
    #: User info
    User = Instance(User)

    # =========================================================================
    # Results from api/addresses/
    # =========================================================================
    Addresses = List(Address)

    # =========================================================================
    # Results for api/settings/
    # =========================================================================
    #: Settings
    Settings = Instance(UserSettings)

    # =========================================================================
    # Results for api/settings/
    # =========================================================================
    #: Settings
    mail = Instance(UserSettings)

    #: Remote public keys
    PublicKeys = Dict()

    #: Encrypted private key
    PrivateKey = Instance(PGPKey)

    def _default_PrivateKey(self):
        if not self.Auth:
            return
        key, _ = PGPKey.from_blob(self.Auth.EncPrivateKey)
        return key

    def _observe_Auth(self, change):
        if self.Auth:
            self.PrivateKey = self._default_PrivateKey()
            self.is_logged_in = True
        else:
            del self.PrivateKey
            self.is_logged_in = False

    def get_public_key(self, email, timeout=None):
        """ Get the public keys for the given email
        
        Parameters
        ----------
        emails: String
            Email to retrieve
        timeout: Int or Float
            Time to wait when blocking
            
        Returns
        --------
        result: Dict
        """
        if not self.blocking:
            return self._get_public_key(email)
        return run_sync(self._get_public_key, email, timeout=timeout)

    @coroutine
    def _get_public_key(self, email):
        email = utils.str(email)
        r = yield self.api.keys('?Email={}'.format(email),
                                blocking=False,
                                response=responses.KeysResponse)
        self.PublicKeys[email] = [
            PGPKey.from_blob(k.PublicKey)[0] for k in r.Keys
        ]
        return_value(r)

    def get_addresses(self, timeout=None):
        """ Get addresses this User has
        
        Parameters
        ----------
        timeout: Int or Float
            Time to wait when blocking
            
        Returns
        --------
        result: User
        """
        if not self.blocking:
            return self._get_addresses()
        return run_sync(self._get_addresses, timeout=timeout)

    @coroutine
    def _get_addresses(self):
        r = yield self.api.addresses(blocking=False,
                                     response=responses.AddressesResponse)
        if r.Code == 1000:
            self.Addresses = r.Addresses
        return_value(r)

    def get_user_info(self, timeout=None):
        """ Get the info aboute this User
        
        Parameters
        ----------
        timeout: Int or Float
            Time to wait when blocking
            
        Returns
        --------
        result: User
        """
        if not self.blocking:
            return self._get_user_info()
        return run_sync(self._get_user_info, timeout=timeout)

    @coroutine
    def _get_user_info(self):
        r = yield self.api.users(blocking=False,
                                 response=responses.UsersResponse)
        if r.Code == 1000:
            self.User = r.User
        return_value(r)

    def read_message(self, message, timeout=None):
        """ Read and decrypt a Message if necessary
       
        Parameters
        ----------
        message: protonmail.message.Message or Dict
   
        Returns
        -------
        result: String
            Decrypted message
        """
        if not self.blocking:
            return self._read_message(message)
        return run_sync(self._read_message, message, timeout=timeout)

    @coroutine
    def _read_message(self, message):
        if not isinstance(message, Message):
            raise TypeError("expected a protonmail.models.Message instance")

        # If the message hasn't been read yet, do that now
        if not message.Body:
            resp = yield self.api.messages(message.ID,
                                           blocking=False,
                                           response=responses.MessageResponse)
            if resp.Code != 1000:
                raise ValueError("Unexpected response: {}".format(
                    resp.to_json()))
            message = resp.Message

        # Read and decrypt if needed
        msg = PGPMessage.from_blob(message.Body)
        if msg.is_signed:
            email = message.SenderAddress
            if email not in self.PublicKeys:
                yield self._get_public_key(email)
            pk = self.PublicKeys.get(email)
            if not pk:
                raise SecurityError("Failed to verify signed message!")
            pk[0].verify(msg)  # TODO: Support mutiple keys

        # Decrypt
        with self.PrivateKey.unlock(self.MailboxPassword) as key:
            message.decrypt(key)
        return_value(message)

    def create_draft(self, address=None, message=None, timeout=None):
        """ Create a message as a draft. This will populate an ID for 
        the message.
 
        Parameters
        ----------
        address: protonmail.models.Address or None
            Address to send with, if None, the default will be used
        message: protonmail.models.Message or None
            Message to create a draft for, if None, one will be created
        timeout: Int or Float
            Timeout to wait when blocking
         
        Returns
        -------
        result: protonmail.responses.MessageResponse
        """
        if not self.blocking:
            return self._create_draft(address, message)
        return run_sync(self._create_draft, address, message, timeout=timeout)

    @coroutine
    def _create_draft(self, address=None, message=None):
        if message is None:
            # Create a new message
            user = self.User
            if not user:
                r = yield self._get_user_info()
                user = self.User

            if not address:
                addresses = self.Addresses
                if not addresses:
                    r = yield self._get_addresses()
                    addresses = self.Addresses
                if not addresses:
                    raise ValueError("No email addresses available")
                address = addresses[0]

            name = (address.DisplayName or address.Name or user.DisplayName
                    or user.Name)
            message = Message(AddressID=address.ID,
                              IsRead=1,
                              MIMEType='text/html',
                              Sender=EmailAddress(Name=name,
                                                  Address=address.Email))

        # Make sure it's encrypted
        message.encrypt(self.PrivateKey.pubkey)

        r = yield self.api.messages(method='POST',
                                    blocking=False,
                                    response=responses.MessageResponse,
                                    json={
                                        'AttachmentKeyPackets': [],
                                        'id':
                                        None,
                                        'Message':
                                        message.to_json(
                                            'AddressID',
                                            'Sender',
                                            'IsRead',
                                            'CCList',
                                            'BCCList',
                                            'MIMEType',
                                            'Subject',
                                            'Body',
                                            'ToList',
                                        )
                                    })
        if r.Message:
            r.Message.Client = self
        return_value(r)

    def save_draft(self, message, timeout=None):
        """ Encrypt (if necessary) and save the message as a draft.

        Parameters
        ----------
        message: protonmail.models.Message
        
        Returns
        -------
        result: protonmail.responses.MessageResponse
        """
        if not self.blocking:
            return self._save_draft(message)
        return run_sync(self._save_draft, message, timeout=timeout)

    @coroutine
    def _save_draft(self, message):
        if not isinstance(message, Message):
            raise TypeError("expected a protonmail.models.Message instance")
        if not message.ID:
            raise ValueError("Cannot save a draft without an ID. "
                             "Use create_draft first.")

        # Encrypt for this client only
        message.encrypt(self.PrivateKey.pubkey)

        # Should never happen
        if not message.is_encrypted():
            raise SecurityError("Failed to encrypted draft")

        r = yield self.api.messages(message.ID,
                                    method='PUT',
                                    blocking=False,
                                    response=responses.MessageResponse,
                                    json={
                                        'AttachmentKeyPackets': {},
                                        'id':
                                        message.ID,
                                        'Message':
                                        message.to_json(
                                            'AddressID',
                                            'Sender',
                                            'IsRead',
                                            'CCList',
                                            'BCCList',
                                            'MIMEType',
                                            'Subject',
                                            'Body',
                                            'ToList',
                                        )
                                    })
        if r.Message:
            r.Message.Client = self
        return_value(r)

    def send_message(self, message, timeout=None):
        """ Encrypt and send the message.
        
        Parameters
        ----------
        message: protonmail.models.Message
        
        Returns
        -------
        result: protonmail.responses.MessageResponse
        """
        if not self.blocking:
            return self._send_message(message)
        return run_sync(self._send_message, message, timeout=timeout)

    @coroutine
    def _send_message(self, message):
        if not isinstance(message, Message):
            raise TypeError("expected a protonmail.models.Message instance")
        if not message.ToList:
            raise ValueError("message missing email to addresses")

        # Read draft from server if needed
        if message.ID and not message.Body:
            r = yield self.api.messages(message.ID,
                                        blocking=False,
                                        response=responses.MessageResponse)
            message = r.Message

        # Decrypt
        if message.Body and not message.DecryptedBody:
            yield self._read_message(message)

        # Get any missing keys
        keys = self.PublicKeys
        emails = list(
            set([
                to.Address
                for to in (message.ToList + message.CCList + message.BCCList)
            ]))
        for e in emails:
            if e not in keys:
                yield self._get_public_key(e)
        keys = self.PublicKeys

        # Extract the session key
        #cipher = SymmetricKeyAlgorithm.AES256
        #session_key = auth.generate_session_key(cipher)
        with self.PrivateKey.unlock(self.MailboxPassword) as uk:
            cipher, session_key = auth.decrypt_session_key(message.Body,
                                                           key=uk)

        pkg = {
            'Addresses': {},
            'Body': "",
            'MIMEType': message.MIMEType or "text/html",
            'Type': 0,
        }

        # If we need to send the key in clear
        cleartext = False
        for to in message.ToList:
            pk = keys.get(to.Address)
            if pk is None:
                raise SecurityError("Failed to get public key for: "
                                    "{}".format(to.Address))

            if pk:
                # Inside user
                # I guess the server does this? Encrypt body for email's pubkey
                #pkg['Body'] = pk.encrypt(pkg['Body'], cipher=cipher,
                #                         sessionkey=session_key)

                # Encrypt the session key for this user
                # TODO: Support multiple keys
                sk = auth.encrypt_session_key(session_key,
                                              key=pk[0],
                                              cipher=cipher)

                pkg['Addresses'][to.Address] = {
                    'AttachmentKeyPackets': {},
                    'BodyKeyPacket': utils.str(b64e(sk)),
                    'Signature': 0,
                    'Type': Message.SEND_PM
                }
                pkg['Type'] |= Message.SEND_PM
            elif False and message.IsEncrypted:  # Disabled for now
                # Enc outside user
                token = message.generate_reply_token(cipher)
                enc_token = PGPMessage.new(b64d(token)).encrypt(
                    message.Password).message.__bytes__()

                pkg['Addresses'][to.Address] = {
                    'Auth': 0,
                    'PasswordHint': message.PasswordHint,
                    'Type': Message.SEND_EO,
                    'Token': token,
                    'EncToken': utils.str(b64e(enc_token)),
                    'AttachmentKeyPackets': {},
                    'BodyKeyPacket': utils.str(b64e(session_key)),
                    'Signature': int(pkg['Body'].is_signed),
                }
            else:
                cleartext = True

                # Outside user
                pkg['Addresses'][to.Address] = {
                    'Signature': 0,
                    'Type': Message.SEND_CLEAR
                }
                pkg['Type'] |= Message.SEND_CLEAR

        if cleartext and message.ExpirationTime and not message.Password:
            raise SecurityError("Expiring emails to non-ProtonMail recipients" \
                                "require a message password to be set")

        # Sending to a non PM user screws all security
        if cleartext:
            pkg['BodyKey'] = {
                'Algorithm': cipher.name.lower(),
                'Key': utils.str(b64e(session_key))
            }
            pkg['AttachmentKeys'] = {}  # TODO

        # Get the message
        msg = PGPMessage.new(message.DecryptedBody)

        # Sign it
        with self.PrivateKey.unlock(self.MailboxPassword) as uk:
            msg |= uk.sign(msg)

        # Encrypt it using the session key and encode it
        msg = self.PrivateKey.pubkey.encrypt(msg,
                                             cipher=cipher,
                                             sessionkey=session_key)
        # Now encode it
        pkg['Body'] = utils.str(b64e(msg.message.__bytes__()))

        r = yield self.api.messages(message.ID,
                                    method='POST',
                                    blocking=False,
                                    response=responses.MessageSendResponse,
                                    json={
                                        'ExpirationTime': 0,
                                        'id': message.ID,
                                        'Packages': [pkg]
                                    })
        return_value(r)

    def check_events(self, timeout=None):
        """ Check for updates"""
        if not self.blocking:
            return self._check_events()
        return run_sync(self._check_events, timeout=timeout)

    @coroutine
    def _check_events(self):
        eid = id or self.EventID
        data = yield self.api.events(eid, blocking=False)
        self.EventID = data['EventID']
        return_value(data)

    def send_simple(self, **kwargs):
        """ Simple API for sending email """
        if not self.blocking:
            return self._send_simple(**kwargs)
        return run_sync(self._send_simple, **kwargs)

    @coroutine
    def _send_simple(self, to, subject="", body="", cc=None, bcc=None):
        if not to:
            raise ValueError("Please enter one or more recipient email "
                             "addresses")
        r = yield self._create_draft()
        if r.Code != 1000:
            raise ValueError("Failed to create draft: {}".format(r.to_json()))
        m = r.Message
        m.Subject = subject
        m.DecryptedBody = body
        if not isinstance(to, (tuple, list)):
            to = [to]
        m.ToList = [EmailAddress(Address=addr) for addr in to]
        if cc is not None:
            m.CCList = [EmailAddress(Address=addr) for addr in cc]
        if bcc is not None:
            m.BCCList = [EmailAddress(Address=addr) for addr in bcc]
        r = yield self._save_draft(m)
        if r.Code != 1000:
            raise ValueError("Failed to save draft: {}".format(r.to_json()))
        r = yield self._send_message(m)
        if r.Code != 1000:
            raise ValueError("Failed to send message: {}".format(r.to_json()))
        return_value(r)
Exemple #23
0
class DevServerSession(Atom):
    """ Connect to a dev server running on the LAN
        or if host is 0.0.0.0 server a page to let
        code be pasted in. Note this should NEVER be used
        in a released app!
    """

    #: Singleton Instance of this class
    _instance = None

    #: Reference to the current Application
    app = ForwardInstance(get_app)

    #: Host to connect to (in client mode) or
    #: if set to "server" it will enable "server" mode
    host = Unicode()

    #: Port to serve on (in server mode) or port to connect to (in client mode)
    port = Int(8888)

    #: URL to connect to (in client mode)
    url = Unicode('ws://192.168.21.119:8888/dev')

    #: Websocket connection state
    connected = Bool()

    #: Message buffer
    buf = Unicode()

    #: Dev session mode
    mode = Enum('client', 'server', 'remote')

    #: Hotswap support class
    hotswap = Instance(Hotswapper)

    #: Delegate dev server
    servers = List(Subclass(DevServer),
                   default=[
                       TornadoDevServer,
                       TwistedDevServer,
                   ])
    server = Instance(DevServer)

    #: Delegate dev client
    clients = List(Subclass(DevClient),
                   default=[
                       TornadoDevClient,
                       TwistedDevClient,
                   ])
    client = Instance(DevClient)

    # -------------------------------------------------------------------------
    # Initialization
    # -------------------------------------------------------------------------
    @classmethod
    def initialize(cls, *args, **kwargs):
        """ Create an instance of this class. """
        try:
            return DevServerSession(*args, **kwargs)
        except ImportError:
            pass

    @classmethod
    def instance(cls):
        """ Get the singleton instance  """
        return cls._instance

    def __init__(self, *args, **kwargs):
        """ Overridden constructor that forces only one instance to ever exist. 
        
        """
        if self.instance() is not None:
            raise RuntimeError("A DevServerClient instance already exists!")
        super(DevServerSession, self).__init__(*args, **kwargs)
        DevServerSession._instance = self

    def start(self):
        """ Start the dev session. Attempt to use tornado first, then try 
        twisted
        
        """
        print("Starting debug client cwd: {}".format(os.getcwd()))
        print("Sys path: {}".format(sys.path))

        #: Initialize the hotswapper
        self.hotswap = Hotswapper(debug=False)

        if self.mode == 'server':
            self.server.start(self)
        else:
            self.client.start(self)

    # -------------------------------------------------------------------------
    # Defaults
    # -------------------------------------------------------------------------
    def _default_mode(self):
        """ If host is set to server then serve it from the app! """
        host = self.host
        if host == 'server':
            return 'server'
        elif host == 'remote':
            return 'remote'
        return 'client'

    def _default_url(self):
        """ Websocket URL to connect to and listen for reload requests """
        host = 'localhost' if self.mode == 'remote' else self.host
        return 'ws://{}:{}/dev'.format(host, self.port)

    def _default_app(self):
        """ Application instance """
        return get_app().instance()

    def _default_server(self):
        for Server in self.servers:
            if Server.available():
                return Server()
        raise NotImplementedError(
            "No dev servers are available! "
            "Include tornado or twisted in your requirements!")

    def _default_client(self):
        for Client in self.clients:
            if Client.available():
                return Client()
        raise NotImplementedError(
            "No dev clients are available! "
            "Include tornado or twisted in your requirements!")

    def _observe_connected(self, change):
        """ Log connection state changes """
        print("Dev session {}".format(
            "connected" if self.connected else "disconnected"))

    # -------------------------------------------------------------------------
    # Dev Session API
    # -------------------------------------------------------------------------
    def write_message(self, data, binary=False):
        """ Write a message to the active client
        
        """
        self.client.write_message(data, binary=binary)

    def handle_message(self, data):
        """ When we get a message """
        msg = json.loads(data)
        print("Dev server message: {}".format(msg))
        handler_name = 'do_{}'.format(msg['type'])
        if hasattr(self, handler_name):
            handler = getattr(self, handler_name)
            result = handler(msg)
            return {'ok': True, 'result': result}
        else:
            err = "Warning: Unhandled message: {}".format(msg)
            print(err)
            return {'ok': False, 'message': err}

    # -------------------------------------------------------------------------
    # Message handling API
    # -------------------------------------------------------------------------
    def do_reload(self, msg):
        """ Called when the dev server wants to reload the view. """
        #: TODO: This should use the autorelaoder
        app = self.app

        #: Show loading screen
        try:
            self.app.widget.showLoading("Reloading... Please wait.", now=True)
            #self.app.widget.restartPython(now=True)
            #sys.exit(0)
        except:
            #: TODO: Implement for iOS...
            pass
        self.save_changed_files(msg)

        if app.load_view is None:
            print("Warning: Reloading the view is not implemented. "
                  "Please set `app.load_view` to support this.")
            return
        if app.view is not None:
            try:
                app.view.destroy()
            except:
                pass

        def wrapped(f):
            def safe_reload(*args, **kwargs):
                try:
                    return f(*args, **kwargs)
                except:
                    #: Display the error
                    app.send_event(Command.ERROR, traceback.format_exc())

            return safe_reload

        app.deferred_call(wrapped(app.load_view), app)

    def do_hotswap(self, msg):
        """ Attempt to hotswap the code """
        #: Show hotswap tooltip
        try:
            self.app.widget.showTooltip("Hot swapping...", now=True)
        except:
            pass
        self.save_changed_files(msg)

        hotswap = self.hotswap
        app = self.app
        try:
            print("Attempting hotswap....")
            with hotswap.active():
                hotswap.update(app.view)
        except:
            #: Display the error
            app.send_event(Command.ERROR, traceback.format_exc())

    # -------------------------------------------------------------------------
    # Utility methods
    # -------------------------------------------------------------------------
    def save_changed_files(self, msg):
        #: On iOS we can't write in the app bundle
        if os.environ.get('TMP'):
            tmp_dir = os.environ['TMP']
            if not os.path.exists(tmp_dir):
                os.makedirs(tmp_dir)
            if tmp_dir not in sys.path:
                sys.path.insert(0, tmp_dir)

                import site
                reload(site)

        with cd(sys.path[0]):
            #: Clear cache
            if os.path.exists('__enamlcache__'):
                shutil.rmtree('__enamlcache__')
            for fn in msg['files']:
                print("Updating {}".format(fn))
                folder = os.path.dirname(fn)
                if folder and not os.path.exists(folder):
                    os.makedirs(folder)
                with open(fn, 'wb') as f:
                    f.write(msg['files'][fn].encode('utf-8'))
Exemple #24
0
    }], [{
        1: 2
    }], [{
        2: ''
    }]),
    (Dict(int, int), [{
        1: 2
    }], [{
        1: 2
    }], [{
        '': 2
    }, {
        2: ''
    }]),
    (Instance((int, float)), [1, 2.0, None], [1, 2.0, None], ['']),
    (ForwardInstance(lambda: (int, float)), [1, 2.0], [1, 2.0], ['']),
    (Typed(float), [1.0, None], [1.0, None], [1]),
    (ForwardTyped(lambda: float), [1.0], [1.0], [1]),
    (Subclass(CAtom), [Atom], [Atom], [int, 1]),
    (Subclass((CAtom, float)), [Atom], [Atom], [int, 1]),
    (ForwardSubclass(lambda: CAtom), [Atom], [Atom], [int]),
] + ([(Range(sys.maxsize, sys.maxsize + 2), [sys.maxsize, sys.maxsize + 2],
       [sys.maxsize, sys.maxsize + 2],
       [sys.maxsize - 1, sys.maxsize + 3])] if sys.version_info >
     (3, ) else []))
def test_validation_modes(member, set_values, values, raising_values):
    """Test the validation modes.

    """
    class MemberTest(Atom):
class Sensor(JavaBridgeObject):
    """ A wrapper for an Android sensor. This should be retrieved using
    the `Sensor.get(sensor_type)` method.
    
    Examples
    --------
    
    # In a function with an @inlineCallbacks or @coroutine decorator
    def on_data(data):
        # Handle data here...
        # data is a dict with keys
        # {'acc': <accuracy>, 'data': [v1, v2, ...], 'sensor': id, 'time': t}
    
    def on_ready(sensor):
        if sensor is not None:
            sensor.start(callback=on_data)
            # ...
            sensor.stop()
    
    """
    __nativeclass__ = set_default('android.hardware.Sensor')

    #: Reference to the sensor manager
    manager = ForwardInstance(lambda: SensorManager)

    #: Sensor type
    type = Int()

    TYPE_ACCELEROMETER = 1
    TYPE_MAGNETIC_FIELD = 2
    TYPE_ORIENTATION = 3  # Depreciated
    TYPE_GYROSCOPE = 4
    TYPE_LIGHT = 5
    TYPE_PRESSURE = 6
    TYPE_TEMPERATURE = 7  # Depreciated Use ambient temp instead
    TYPE_PROXIMITY = 8
    TYPE_GRAVITY = 9
    TYPE_LINEAR_ACCELERATION = 10
    TYPE_ROTATION_VECTOR = 11
    TYPE_RELATIVE_HUMIDITY = 12
    TYPE_AMBIENT_TEMPERATURE = 13
    TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14
    TYPE_GAME_ROTATION_VECTOR = 15
    TYPE_GYROSCOPE_UNCALIBRATED = 16
    TYPE_SIGNIFICANT_MOTION = 17
    TYPE_STEP_DETECTOR = 18
    TYPE_STEP_COUNTER = 19
    TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20
    TYPE_HEART_RATE = 21
    TYPE_POSE_6DOF = 28  # Requires API 24+
    TYPE_STATIONARY_DETECT = 29  # Requires API 24+
    TYPE_MOTION_DETECT = 30  # Requires API 24+
    TYPE_HEART_BEAT = 31  # Requires API 24+
    TYPE_LOW_LATENCY_OFFBODY_DETECT = 34  # Requires API 26+

    SENSOR_DELAY_NORMAL = 3
    SENSOR_DELAY_UI = 2
    SENSOR_DELAY_GAME = 1
    SENSOR_DELAY_FASTEST = 0

    getFifoMaxEventCount = JavaMethod(returns='int')
    getFifoReservedEventCount = JavaMethod(returns='int')
    getMaximumRange = JavaMethod(returns='float')
    getMaxDelay = JavaMethod(returns='int')
    getMinDelay = JavaMethod(returns='int')
    getName = JavaMethod(returns='java.lang.String')
    getPower = JavaMethod(returns='float')
    getReportingMode = JavaMethod(returns='int')
    getResolution = JavaMethod(returns='float')
    getVendor = JavaMethod(returns='java.lang.String')
    getVersion = JavaMethod(returns='int')
    getType = JavaMethod(returns='int')
    getStringType = JavaMethod(returns='java.lang.String')
    isWakeUpSensor = JavaMethod(returns='boolean')

    # -------------------------------------------------------------------------
    # SensorEventListener API
    # -------------------------------------------------------------------------
    onSensorChanged = JavaCallback('android.hardware.SensorEvent')
    onAccuracyChanged = JavaCallback('android.hardware.Sensor', 'int')

    @classmethod
    def get(cls, sensor_type):
        """ Shortcut that acquires the default Sensor of a given type. 
        
        Parameters
        ----------
            sensor_type: int
                Type of sensor to get.
        
        Returns
        -------
            result: Future 
                A future that resolves to an instance of the Sensor or None
                if the sensor is not present or access is not allowed.
        
        """
        app = AndroidApplication.instance()
        f = app.create_future()

        def on_sensor(sid, mgr):
            if sid is None:
                f.set_result(None)
            else:
                f.set_result(Sensor(__id__=sid, manager=mgr, type=sensor_type))

        SensorManager.get().then(lambda sm: sm.getDefaultSensor(sensor_type).
                                 then(lambda sid, sm=sm: on_sensor(sid, sm)))

        return f

    def start(self, callback, rate=SENSOR_DELAY_NORMAL):
        """ Start listening to sensor events. Sensor event data depends
        on the type of sensor that was given to 
        
        Parameters
        ----------
            callback: Callable
                A callback that takes one argument that will be passed
                the sensor data. Sensor data is a dict with data based on
                the type of sensor.
            rate: Integer
                How fast to update. One of the Sensor.SENSOR_DELAY values   
        
        Returns
        -------
            result: Future
                A future that resolves to whether the register call
                completed.
        
        """
        if not self.manager:
            raise RuntimeError(
                "Cannot start a sensor without a SensorManager!")
        self.onSensorChanged.connect(callback)
        return self.manager.registerListener(self.getId(), self, rate)

    def stop(self):
        """ Stop listening to sensor events. This should be done in
        on resume.
        """
        self.manager.unregisterListener(self.getId(), self)
Exemple #26
0
class BridgeObject(Atom):
    """ A proxy to a class in java. This sends the commands over
    the bridge for execution.  The object is stored in a map
    with the given id and is valid until this object is deleted.
    
    Parameters
    ----------
    __id__: Int
        If an __id__ keyward argument is passed during creation,
        this will assume the object was already created and
        only a reference to the object with the given id is needed.

    """
    __slots__ = ('__weakref__', )

    #: Native Class name
    __nativeclass__ = Unicode()

    #: Constructor signature
    __signature__ = Tuple()

    #: Suppressed methods / fields
    __suppressed__ = Dict()

    #: Callbacks
    __callbacks__ = Dict()

    #: Bridge object ID
    __id__ = Int(0, factory=generate_id)

    #: ID of this class
    __bridge_id__ = Int()

    #: Prefix to add to all names used during method and property calls
    #: used for nested objects
    __prefix__ = Unicode()

    #: Bridge
    __app__ = ForwardInstance(get_app_class)

    def _default___app__(self):
        return get_app_class().instance()

    def _default___bridge_id__(self):
        cls = self.__class__
        if cls not in CLASS_CACHE:
            CLASS_CACHE[cls] = generate_property_id()
        return CLASS_CACHE[cls]

    def getId(self):
        return self.__id__

    def __init__(self, *args, **kwargs):
        """ Sends the event to create this View in Java """
        super(BridgeObject, self).__init__(**kwargs)

        #: Send the event over the bridge to construct the view
        __id__ = kwargs.get('__id__', None)
        CACHE[self.__id__] = self
        if __id__ is None:
            self.__app__.send_event(
                Command.CREATE,  #: method
                self.__id__,  #: id to assign in bridge cache
                self.__bridge_id__,
                self.__nativeclass__,
                [
                    msgpack_encoder(sig, arg)
                    for sig, arg in zip(self.__signature__, args)
                ],
            )

    def __del__(self):
        """ Destroy this object and send a command to destroy the actual object
        reference the bridge implementation holds (allowing it to be released).
        """
        self.__app__.send_event(
            Command.DELETE,  #: method
            self.__id__,  #: id to assign in java
        )
        _cleanup_id(self)
Exemple #27
0
class Block(Declarative):
    """ An object which dynamically insert's its children into another block's parent object.
    
    The 'Block' object is used to cleanly and easily insert it's children
    into the children of another object. The 'Object' instance assigned to the
    'block' property of the 'Block' will be parented with the parent of
    the 'Block'. Creating a 'Block' with no parent is a programming
    error.
    
    """

    #: The Block to which this blocks children should be inserted into
    block = d_(ForwardInstance(lambda: Block))

    #: If replace, replace all parent's children (except the block of course)
    mode = d_(Enum('replace', 'append'))

    def initialize(self):
        """ A reimplemented initializer.

        This method will add the include objects to the parent of the
        include and ensure that they are initialized.

        """
        super(Block, self).initialize()

        if self.block:  #: This block is setting the content of another block
            #: Remove the existing blocks children
            if self.mode == 'replace':
                #: Clear the blocks children
                for c in self.block.children:
                    c.destroy()
            #: Add this blocks children to the other block
            self.block.insert_children(None, self.children)

        else:  #: This block is inserting it's children into it's parent
            self.parent.insert_children(self, self.children)

    def _observe_block(self, change):
        """ A change handler for the 'objects' list of the Include.

        If the object is initialized objects which are removed will be
        unparented and objects which are added will be reparented. Old
        objects will be destroyed if the 'destroy_old' flag is True.

        """
        if self.is_initialized:
            if change['type'] == 'update':
                raise NotImplementedError
                old_block = change['oldvalue']
                old_block.parent.remove_children(old_block, self.children)
                new_block = change['value']
                new_block.parent.insert_children(new_block, self.children)

    def _observe__children(self, change):
        if not self.is_initialized:
            return
        if change['type'] == 'update':
            if self.block:
                if self.mode == 'replace':
                    self.block.children = change['value']
                else:
                    for c in change['oldvalue']:
                        self.block.children.remove(c)
                        c.destroy()
                    before = self.block.children[
                        -1] if self.block.children else None
                    self.block.insert_children(before, change['value'])
            else:
                for c in change['oldvalue']:
                    if c not in change['value']:
                        c.destroy()
                self.parent.insert_children(self, change['value'])
Exemple #28
0
class BridgeObject(Atom):
    """A proxy to a class in java. This sends the commands over
    the bridge for execution.  The object is stored in a map
    with the given id and is valid until this object is deleted.

    Parameters
    ----------
    __id__: Int, Future, or None
        If an __id__ keyword argument is passed during creation, then

            If the __id__ is an int, this will assume the object was already
            created and only a reference to the object with the given id is
            needed.

            If the __id__ is a Future (as specified by the app event loop),
            then the __id__ of he future will be used. When the future
            completes this object will then be put into the cache. This allows
            passing results directly instead of using the `.then()` method.

    """

    __slots__ = ("__weakref__", "__constructor_ids__")

    #: Native Class name
    __nativeclass__: ClassVar[str] = ""

    #: Constructor signature
    __signature__: ClassVar[list[Union[dict, str, "BridgeObject", type]]] = []

    #: Constructor cache id
    __constructor_ids__: ClassVar[list[int]]

    #: Suppressed methods / fields
    __suppressed__ = Dict()

    #: Callbacks
    __callbacks__ = Dict()

    #: Bridge object ID
    __id__ = Int(0, factory=generate_id)

    #: Prefix to add to all names used during method and property calls
    #: used for nested objects
    __prefix__ = Str()

    #: Bridge
    __app__ = ForwardInstance(get_app_class)

    @classmethod
    def __init_subclass__(cls, *args, **kwargs):
        #: Convert signature to string
        cls.__signature__ = [convert_arg(arg) for arg in cls.__signature__]
        # Create a method id for each signature length as some may be
        # optional. TODO: Rethink this...
        n = max(1, len(cls.__signature__))
        cls.__constructor_ids__ = [method_id() for i in range(n)]
        REGISTRY[cls.__nativeclass__] = cls

    def _default___app__(self):
        return get_app_class().instance()

    def getId(self):
        return self.__id__

    def __init__(self, *args, **kwargs):
        """Sends the event to create this object in Java."""

        #: Send the event over the bridge to construct the view
        __id__ = kwargs.pop("__id__", None)
        cache = True
        if __id__ is not None:
            if isinstance(__id__, int):
                kwargs["__id__"] = __id__
            elif isinstance(__id__, Future):
                #: If a future is given don't store this object in the cache
                #: until after the future completes
                f = __id__

                def set_result(f):
                    CACHE[f.__id__] = self

                f.add_done_callback(set_result)

                #: The future is used to return the result
                kwargs["__id__"] = f.__id__

                #: Save it into the cache when the result from the future
                #: arrives over the bridge.
                cache = False
            else:
                raise TypeError("Invalid __id__ reference, expected an int or"
                                f"a future/deferred object, got: {__id__}")

        #: Construct the object
        super().__init__(**kwargs)

        if cache:
            CACHE[self.__id__] = self

        if __id__ is None:
            if args:
                constructor_id = self.__constructor_ids__[len(args) - 1]
            else:
                constructor_id = self.__constructor_ids__[0]
            self.__app__.send_event(
                Command.CREATE,  #: method
                self.__id__,  #: id to assign in bridge cache
                constructor_id,
                self.__nativeclass__,
                [
                    msgpack_encoder(sig, arg)
                    for sig, arg in zip(self.__signature__, args)
                ],
            )

    def __del__(self):
        """Destroy this object and send a command to destroy the actual object
        reference the bridge implementation holds (allowing it to be released).
        """
        ref = self.__id__
        self.__app__.send_event(Command.DELETE, ref)
        try:
            del CACHE[ref]
        except KeyError:
            pass
Exemple #29
0
    typed.set_index(5)
    cl2 = typed.clone()
    assert cl2.index == typed.index
    validators = ([typed.item]
                  if hasattr(typed, 'item') else typed.validate_mode[1])
    c_validators = ([cl2.item]
                    if hasattr(typed, 'item') else cl2.validate_mode[1])
    for v, cv in zip(validators, c_validators):
        assert cv is not v
        assert isinstance(cv, type(v))


# XXX should the kwargs be copied rather than simply re-assigned
@pytest.mark.parametrize(
    "member, cloned_attributes",
    [(ForwardSubclass(lambda: object), ['resolve']),
     (ForwardTyped(lambda: object,
                   (1, ), {'a': 1}), ['resolve', 'args', 'kwargs']),
     (ForwardInstance(lambda: object,
                      (1, ), {'a': 1}), ['resolve', 'args', 'kwargs'])])
def test_cloning_forward(member, cloned_attributes):
    """Test that subclasses of Member are properly cloned.

    """
    member.set_index(5)
    clone = member.clone()
    assert clone.index == member.index
    for attr in cloned_attributes:
        assert getattr(clone, attr) is getattr(member, attr)
Exemple #30
0
class ViewerProcess(ProcessLineReceiver):
    #: Window id obtained after starting the process
    window_id = Int()

    #: Process handle
    process = Instance(object)

    #: Reference to the plugin
    plugin = ForwardInstance(lambda: ViewerPlugin)

    #: Document
    document = ForwardInstance(document_type)

    #: Rendering error
    errors = Str()

    #: Process terminated intentionally
    terminated = Bool(False)

    #: Count restarts so we can detect issues with startup s
    restarts = Int()

    #: Max number it will attempt to restart
    max_retries = Int(20)

    #: ID count
    _id = Int()

    #: Holds responses temporarily
    _responses = Dict()

    #: Seconds to ping
    _ping_rate = Int(40)

    #: Capture stderr separately
    err_to_out = set_default(False)

    def redraw(self):
        if self.document:
            # Trigger a reload
            self.document.version += 1
        else:
            self.set_version(self._id)

    @observe('document', 'document.version')
    def _update_document(self, change):
        doc = self.document
        if doc is None:
            self.set_filename('-')
        else:
            self.set_filename(doc.name)
            self.set_version(doc.version)

    def send_message(self, method, *args, **kwargs):
        # Defer until it's ready
        if not self.transport or not self.window_id:
            #log.debug('renderer | message not ready deferring')
            timed_call(1000, self.send_message, method, *args, **kwargs)
            return
        _id = kwargs.pop('_id')
        _silent = kwargs.pop('_silent', False)

        request = {
            'jsonrpc': '2.0',
            'method': method,
            'params': args or kwargs
        }
        if _id is not None:
            request['id'] = _id
        if not _silent:
            log.debug(f'renderer | sent | {request}')
        encoded_msg = jsonpickle.dumps(request).encode() + b'\r\n'
        deferred_call(self.transport.write, encoded_msg)

    async def start(self):
        atexit.register(self.terminate)
        cmd = [sys.executable]
        if not sys.executable.endswith('declaracad'):
            cmd.extend(['-m', 'declaracad'])
        cmd.extend(['view', '-', '-f'])
        loop = asyncio.get_event_loop()
        self.process = await loop.subprocess_exec(lambda: self, *cmd)
        return self.process

    def restart(self):
        self.window_id = 0
        self.restarts += 1

        # TODO: 100 is probably excessive
        if self.restarts > self.max_retries:
            plugin = self.plugin
            plugin.workbench.message_critical(
                "Viewer failed to start",
                "Could not get the viewer to start after several attempts.")

            raise RuntimeError(
                "renderer | Failed to successfully start renderer aborting!")

        log.debug(f"Attempting to restart viewer {self.process}")
        deferred_call(self.start)

    def connection_made(self, transport):
        super().connection_made(transport)
        self.schedule_ping()
        self.terminated = False

    def data_received(self, data):
        line = data.decode()
        try:
            response = jsonpickle.loads(line)
            # log.debug(f"viewer | resp | {response}")
        except Exception as e:
            log.debug(f"viewer | out | {line.rstrip()}")
            response = {}

        doc = self.document

        if not isinstance(response, dict):
            log.debug(f"viewer | out | {response.rstrip()}")
            return

        #: Special case for startup
        response_id = response.get('id')
        if response_id == 'window_id':
            self.window_id = response['result']
            self.restarts = 0  # Clear the restart count
            return
        elif response_id == 'keep_alive':
            return
        elif response_id == 'invoke_command':
            command_id = response.get('command_id')
            parameters = response.get('parameters', {})
            log.debug(f"viewer | out | {command_id}({parameters})")
            self.plugin.workbench.invoke_command(command_id, parameters)
        elif response_id == 'render_error':
            if doc:
                doc.errors.extend(response['error']['message'].split("\n"))
            return
        elif response_id == 'render_success':
            if doc:
                doc.errors = []
            return
        elif response_id == 'capture_output':
            # Script output capture it
            if doc:
                doc.output = response['result'].split("\n")
            return
        elif response_id == 'shape_selection':
            #: TODO: Do something with this?
            if doc:
                doc.output.append(str(response['result']))
            return
        elif response_id is not None:
            # Lookup the deferred object that should be stored for this id
            # when it is called and invoke the callback or errback based on the
            # result
            d = self._responses.get(response_id)
            if d is not None:
                del self._responses[response_id]
                try:
                    error = response.get('error')
                    if error is not None:
                        if doc:
                            doc.errors.extend(
                                error.get('message', '').split("\n"))
                        d.add_done_callback(error)
                    else:
                        d.add_done_callback(response.get('result'))
                    return
                except Exception as e:
                    log.warning("RPC response not properly handled!")
                    log.exception(e)

            else:
                log.warning("Got unexpected reply")
            # else we got a response from something else, possibly an error?
        if 'error' in response and doc:
            doc.errors.extend(response['error'].get('message', '').split("\n"))
            doc.output.append(line)
        elif 'message' in response and doc:
            doc.output.extend(response['message'].split("\n"))
        elif doc:
            # Append to output
            doc.output.extend(line.split("\n"))

    def err_received(self, data):
        """ Catch and log error output attempting to decode it

        """
        for line in data.split(b"\n"):
            if not line:
                continue
            if line.startswith(b"QWidget::") or line.startswith(b"QPainter::"):
                continue
            try:
                line = line.decode()
                log.debug(f"render | err | {line}")
                if self.document:
                    self.document.errors.append(line)
            except Exception as e:
                log.debug(f"render | err | {line}")

    def process_ended(self, reason):
        log.warning(f"renderer | process ended: {reason}")
        if not self.terminated:
            # Clear the filename on crash so it works when reset
            #self.document = None
            self.restart()
        log.warning("renderer | stdout closed")

    def terminate(self):
        super(ViewerProcess, self).terminate()
        terminated = True

    def schedule_ping(self):
        """ Ping perioidcally so the process stays awake """
        if self.terminated:
            return
        # Ping the viewer to tell it to keep alive
        self.send_message("ping", _id="keep_alive", _silent=True)
        timed_call(self._ping_rate * 1000, self.schedule_ping)

    def __getattr__(self, name):
        """ Proxy all calls not defined here to the remote viewer.

        This makes doing `setattr(renderer, attr, value)` get passed to the
        remote viewer.

        """
        if name.startswith('set_'):

            def remote_viewer_call(*args, **kwargs):
                d = asyncio.Future()
                self._id += 1
                kwargs['_id'] = self._id
                self._responses[self._id] = d
                self.send_message(name, *args, **kwargs)
                return d

            return remote_viewer_call
        raise AttributeError("No attribute %s" % name)