Ejemplo n.º 1
0
class XBM(Atom):
    """ A simple class representing an XMB image.

    """
    #: The width of the xbm image.
    width = Int()

    #: The height of the xbm image.
    height = Int()

    #: The bytestring of image data.
    data = Bytes()

    def __init__(self, width, height, data):
        """ Initialize an XBM image.

        Parameters
        ----------
        width : int
            The width of the bitmap.

        height : int
            The height of the bitmap.

        data : list
            A list of 1s and 0s which represent the bitmap data.
            The length must be equal to width * height.

        """
        assert len(data) == (width * height)
        bytes_list = []
        for row in range(height):
            val = 0
            offset = row * width
            for col in range(width):
                d = col % 8
                if col > 0 and d == 0:
                    bytes_list.append(val)
                    val = 0
                v = data[offset + col]
                val |= v << (7 - d)
            bytes_list.append(val)
        self.width = width
        self.height = height
        self.data = bytes(bytes_list)

    def toBitmap(self):
        size = QSize(self.width, self.height)
        return QBitmap.fromData(size, self.data, QImage.Format_Mono)
Ejemplo n.º 2
0
class Connection(Atom, LineReceiver):
    #: Name
    name = Str()

    #: Connection state
    connected = Int()

    #: Log output
    output = ContainerList()

    #: Split on newlines
    delimiter = Bytes(b'\n')

    #: Connection errors
    errors = Str()

    #: The factory that created this connection
    factory = Instance(ConnectionFactory)

    def connect(self):
        return self.factory.connect(self)

    def connectionMade(self):
        self.connected = 1

    def connectionLost(self, reason):
        self.connected = 0
        self.errors = str(reason)

    def lineReceived(self, line):
        self.output.append(line)

    def write(self, message):
        if self.transport is None:
            return IOError("Not connected")
        if not isinstance(message, bytes):
            message = message.encode()
        return self.transport.write(message)

    def disconnect(self):
        """ """
        if self.transport:
            self.transport.loseConnection()
Ejemplo n.º 3
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)
Ejemplo n.º 4
0
                      ], 3 * [1], ['a'] + [] if sys.version_info >=
  (3, ) else [1.0e35]),
 (Long(strict=True), [long(1)], [long(1)], [1.0, 1] if sys.version_info <
  (3, ) else [0.1]),
 (Long(strict=False), [1, 1.0, int(1)], 3 * [1], ['a']),
 (Range(0, 2), [0, 2], [0, 2], [-1, 3, '']),
 (Range(2, 0), [0, 2], [0, 2], [-1, 3]),
 (Range(0), [0, 3], [0, 3], [-1]),
 (Range(high=2), [-1, 2], [-1, 2], [3]),
 (Float(), [1, int(1), 1.1], [1.0, 1.0, 1.1], ['']),
 (Float(strict=True), [1.1], [1.1], [1]),
 (FloatRange(0.0, 0.5), [0.0, 0.5], [0.0, 0.5], [-0.1, 0.6]),
 (FloatRange(0.5, 0.0), [0.0, 0.5], [0.0, 0.5], [-0.1, 0.6]),
 (FloatRange(0.0), [0.0, 0.6], [0.0, 0.6], [-0.1, '']),
 (FloatRange(high=0.5), [-0.3, 0.5], [-0.3, 0.5], [0.6]),
 (Bytes(), [b'a', u'a'], [b'a'] * 2, [1]),
 (Bytes(strict=True), [b'a'], [b'a'], [u'a']),
 (Str(), [b'a', u'a'], ['a'] * 2, [1]),
 (Str(strict=True), [b'a'] if sys.version_info <
  (3, ) else [u'a'], ['a'], [u'a'] if sys.version_info < (3, ) else [b'a']),
 (Unicode(), [b'a', u'a'], [u'a'] * 2, [1]),
 (Unicode(strict=True), [u'a'], [u'a'], [b'a']),
 (Enum(1, 2, 'a'), [1, 2, 'a'], [1, 2, 'a'], [3]),
 (Callable(), [int, None], [int, None], [1]),
 (Coerced(set), [{1}, [1], (1, )], [{1}] * 3, [1]),
 (Coerced(int, coercer=lambda x: int(str(x), 2)), ['101'], [5], []),
 (Coerced(
     (int, float), coercer=lambda x: int(str(x), 2)), ['101'], [5], []),
 (Coerced(int, coercer=lambda x: []), [], [], ['']),
 (Tuple(), [(1, )], [(1, )], [[1]]),
 (Tuple(Int()), [(1, )], [(1, )], [(1.0, )]),
Ejemplo n.º 5
0
class Device(Model, asyncio.Protocol):
    #: Name
    name = Str().tag(config=True)

    #: UUID
    uuid = Str().tag(config=True)

    #: Default
    default = Bool().tag(config=True)

    #: Device state
    connected = Bool()
    busy = Bool()
    last_read = Bytes()
    last_write = Bytes()
    errors = Str()

    #: Config
    config = Instance(DeviceConfig, ()).tag(config=True)

    #: The connection
    connection = Instance(Connection).tag(config=True)

    def _default_uuid(self):
        return str(uuid.uuid4().hex)

    def __hash__(self):
        return int(self.uuid, 16)

    def __eq__(self, other):
        if not isinstance(other, Device):
            return False
        return self.uuid == other.uuid

    # -------------------------------------------------------------------------
    # Protocol API
    # -------------------------------------------------------------------------
    def connection_made(self, transport):
        self.connected = True
        self.connection.transport = transport

    def connection_lost(self, exc):
        self.connected = False
        self.errors = f'{exc}'

    def data_received(self, data):
        self.last_read = data

    def pause_writing(self):
        #print(self.connection.transport.get_write_buffer_size())
        pass

    def resume_writing(self):
        #print(self.connection.transport.get_write_buffer_size())
        pass

    async def wait_until(self,
                         fn,
                         timeout=30,
                         message="Timeout hit",
                         rate=0.1):
        """ Wait for the fn to return true or until the timeout hits

        Parameters
        ----------
        fn: Callable
            A function that returns a boolean when ready
        timeout: Float or None
            Time in seconds to wait before giving an error or None
            to block forever
        message: Str
            Message to set on the error if a timeout occurs
        rate: Float
            Time in seconds to wait before checking the fn again

        """
        start = time.time()
        while not fn():
            await asyncio.sleep(rate)
            if timeout is not None and (time.time() - start) > timeout:
                raise TimeoutError(message)

    # -------------------------------------------------------------------------
    # Device API
    # -------------------------------------------------------------------------
    async def connect(self):
        """ Make the connection and wait until connection_made is called.

        """
        if self.connected:
            return
        await self.connection.connect(self)

        # Wait for it to connect
        await self.wait_until(lambda: self.connected,
                              30,
                              message="Connection timeout")

    async def write(self, data):
        """ Write the data and wait until the write buffer is empty.

        Parameters
        ----------
        data: Bytes or Str
            Data to write to the device

        """
        if not self.connected:
            return IOError("Not connected")
        if not isinstance(data, bytes):
            data = data.encode()
        self.last_write = data

        # Write just puts it into the write buffer
        # So wait until the buffer is empty (all written)
        # or the connection drops
        self.connection.transport.write(data)
        get_buffer_size = self.connection.transport.get_write_buffer_size
        await self.wait_until(
            lambda: not self.connected or get_buffer_size() == 0, timeout=None)
        if not self.connected:
            raise IOError("Connection dropped")

    async def disconnect(self):
        """ Drop the connection. This will call connection_lost when it is
        actually closed.

        """
        if not self.connected:
            return
        await self.connection.disconnect()

    def convert(self, point):
        """ Convert a point based on this device's configuration

        Parameters
        ----------
        point: declaracad.occ.shape.Point
            The point to convert

        Returns
        -------
        converted_point: Tuple
            Tuple of converted values

        """
        config = self.config
        precision = config.PRECISIONS.get(config.precision)
        o = config.origin
        x = o.x - point.x if config.mirror_x else point.x - o.x
        y = o.y - point.y if config.mirror_y else point.y - o.y
        z = o.z - point.z if config.mirror_z else point.z - o.z
        x = gcode.convert(x, config.scale_x, precision)
        y = gcode.convert(y, config.scale_y, precision)
        z = gcode.convert(z, config.scale_z, precision)
        if config.swap_xy:
            x, y = y, x
        return (x, y, z)

    async def rapid_move_to(self, point):
        """ Send a G0 to the point

        """
        x, y, z = self.convert(point)
        await self.write(f'G0 X{x} Y{y} Z{z}\n')
Ejemplo n.º 6
0
class ProcessLineReceiver(Atom, asyncio.SubprocessProtocol):
    """ A process protocol that pushes output into a list of each line.
    Observe the `output` member in a view to have it update with live output.
    """

    #: Process transport
    process_transport = Value()
    transport = Value()

    #: Status code
    exit_code = Int()

    #: Holds process output
    output = ContainerList()

    #: Redirect error to output
    err_to_out = Bool(True)

    #: Split on each line
    delimiter = Bytes(b'\n')

    def connection_made(self, transport):
        """ Save a reference to the transports

        Parameters
        ----------
        transport: asyncio.SubprocessTransport
            The transport for stdin, stdout, and stderr pipes

        """
        self.process_transport = transport
        self.transport = transport.get_pipe_transport(0)

    def pipe_data_received(self, fd, data):
        """ Forward calls to data_received or err_received based one the fd

        Parameters
        ----------
        fd: Int
            The fd of the pipe
        data: Bytes
            The data received

        """
        if fd == 1:
            self.data_received(data)
        elif fd == 2:
            if self.err_to_out:
                self.data_received(data)
            else:
                self.err_received(data)

    def data_received(self, data):
        """ Called for stdout data and stderr data if err_to_out is True

        Parameters
        ----------
        data: Bytes
            The data received

        """
        self.output.append(data)

    def err_received(self, data):
        """ Called for stderr data if err_to_out is set to False

        Parameters
        ----------
        data: Bytes
            The data received

        """
        self.output.append(data)

    def terminate(self):
        if self.process_transport:
            try:
                self.process_transport.terminate()
            except ProcessLookupError as e:
                pass
Ejemplo n.º 7
0
class File(JSONModel):
    id = Instance(uuid.UUID, factory=uuid.uuid4)
    name = Str()
    data = Bytes()
Ejemplo n.º 8
0
class Model(with_metaclass(ModelMeta, Atom)):
    """ An atom model that can be serialized and deserialized to and from 
    a database.
    
    """
    __slots__ = '__weakref__'
    
    #: ID of this object in the database. Subclasses can redefine this as needed
    _id = Bytes()

    #: A unique ID used to handle cyclical serialization and deserialization
    #: Do NOT use python's id() as these are reused and can cause conflicts
    __ref__ = Bytes(factory=lambda: os.urandom(16))
    
    # ==========================================================================
    # Serialization API
    # ==========================================================================
    
    #: Handles encoding and decoding. Subclasses should redefine this to a
    #: subclass of ModelSerializer
    serializer = None

    def __getstate__(self, scope=None):
        state = super(Model, self).__getstate__()
        flatten = self.serializer.flatten
        
        scope = scope or {}
        ref = self.__ref__
        scope[ref] = self
        state = {f: flatten(state[f], scope) for f in self.__fields__}
        state['__model__'] = self.__model__
        state['__ref__'] = ref # ID for circular references
        if self._id is not None:
            state['_id'] = self._id
        return state

    async def __setstate__(self, state, scope=None):
        """ Restore an object from the a state from the database. This is
        async as it will lookup any referenced objects from the DB.
        
        """
        unflatten = self.serializer.unflatten
        name = state.get('__model__')

        if name is None:
            raise ValueError("State must contain the __model__ key")
        if name != self.__model__:
            raise ValueError(f"Trying to use {name} state for "
                             f"{self.__model__} object")
        scope = scope or {}
        ref = state.pop('__ref__', None)
        if ref is not None:
            scope[ref] = self
        members = self.members()
        for k, v in state.items():
            
            # Don't use getattr because it triggers a default value lookup
            if members.get(k):
                try:
                    obj = await unflatten(v, scope)
                    setattr(self, k, obj)
                except Exception as e:
                    exc = traceback.format_exc()
                    WebApplication.instance().logger.error(
                        f"Error setting state:"
                        f"{self.__model__}.{k} = {pformat(obj)}:"
                        f"\nSelf: {ref}: {scope.get(ref)}"
                        f"\nValue: {pformat(v)}"
                        f"\nScope: {pformat(scope)}"
                        f"\nState: {pformat(state)}"
                        f"\n{exc}"
                    )
    
    # ==========================================================================
    # Database API
    # ==========================================================================
    
    #: Handles database access. Subclasses should redefine this.
    objects = None
    
    @classmethod
    async def restore(cls, state):
        """ Restore an object from the database """
        obj = cls.__new__(cls)
        await obj.__setstate__(state)
        return obj
    
    async def save(self):
        """ Alias to delete this object to the database """
        raise NotImplementedError

    async def delete(self):
        """ Alias to delete this object in the database """
        raise NotImplementedError
Ejemplo n.º 9
0
class AbstractUser(SQLModel):
    email = Str().tag(length=64)
    hashed_password = Bytes()

    class Meta:
        abstract = True
Ejemplo n.º 10
0
class Attachment(SQLModel):
    id = Int().tag(primary_key=True)
    email = Instance(Email).tag(nullable=False)
    name = Str().tag(length=100)
    size = Int()
    data = Bytes()
Ejemplo n.º 11
0
 (Range(high=2), [-1, 2], [-1, 2], [3]),
 (
     Range(sys.maxsize, sys.maxsize + 2),
     [sys.maxsize, sys.maxsize + 2],
     [sys.maxsize, sys.maxsize + 2],
     [sys.maxsize - 1, sys.maxsize + 3],
 ),
 (Float(), [1, int(1), 1.1], [1.0, 1.0, 1.1], [""]),
 (Float(strict=True), [1.1], [1.1], [1]),
 (FloatRange(0.0, 0.5), [0.0, 0.5], [0.0, 0.5], [-0.1, 0.6]),
 (FloatRange(0.5, 0.0), [0.0, 0.5], [0.0, 0.5], [-0.1, 0.6]),
 (FloatRange(0.0), [0.0, 0.6], [0.0, 0.6], [-0.1, ""]),
 (FloatRange(high=0.5), [-0.3, 0.5], [-0.3, 0.5], [0.6]),
 (FloatRange(1.0, 10.0, strict=True), [1.0, 3.7], [1.0, 3.7
                                                   ], [2, 4, 0, -11]),
 (Bytes(strict=False), [b"a", "a"], [b"a"] * 2, [1]),
 (Bytes(), [b"a"], [b"a"], ["a"]),
 (Str(strict=False), [b"a", "a"], ["a"] * 2, [1]),
 (Str(), ["a"], ["a"], [b"a"]),
 (Enum(1, 2, "a"), [1, 2, "a"], [1, 2, "a"], [3]),
 (Callable(), [int, None], [int, None], [1]),
 # 3.9 subs and 3.10 union tests in test_typing_utils are sufficient
 (Coerced(set), [{1}, [1], (1, )], [{1}] * 3, [1]),
 (Coerced(int, coercer=c), ["101"], [5], []),
 (Coerced((int, float), coercer=c), ["101"], [5], []),
 (Coerced(int, coercer=lambda x: []), [], [], [""]),  # type: ignore
 (Coerced(TSet[int]), [{1}, [1], (1, )], [{1}] * 3, [1]),
 (Tuple(), [(1, )], [(1, )], [[1]]),
 (Tuple(Int()), [(1, )], [(1, )], [(1.0, )]),
 (Tuple(int), [(1, )], [(1, )], [(1.0, ), (None, )]),
 (Tuple(TSet[int]), [({1}, )], [({1}, )], [(1.0, ), (None, )]),