<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2015 Hewlett Packard Enterprise Development LP <asicapi@hp.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

"""
topology manager module.

This module takes care of understanding the topology defined in the test
suite, commanding its construction to appropriate topology platform plugin
and finally commanding its destruction to the plugin also.
"""

from __future__ import unicode_literals, absolute_import
from __future__ import print_function, division

import logging
from datetime import datetime
from traceback import format_exc
from collections import OrderedDict
from string import ascii_lowercase, digits

from pynml.manager import ExtendedNMLManager

from .platforms.base import BaseNode
from .platforms.manager import platforms, DEFAULT_PLATFORM

log = logging.getLogger(__name__)

IDENTIFIER = ascii_lowercase + digits + '_'

class ParseException(Exception):
    """
    Custom exception thrown by the parser if it could succeded.

    :var lineno: Line number of the parsing error.
    :var raw_line: Raw line that failed to be parsed.
    :var exc: Internal failure traceback of the parser.
    """
    def __init__(self, lineno, raw_line, exc):
        super(ParseException, self).__init__(
            'Unable to parse line #{}: "{}"'.format(
                lineno, raw_line
            )
        )
        self.lineno = lineno
        self.raw_line = raw_line
        self.exc = exc

class TopologyManager(object):
    """
    Main Topology Manager object.

    This object is responsible to build a topology given a description.

    There is three options to build a topology:

    - Using the simplified textual description using a Graphviz like syntax.
      To use this option call the :meth:`parse`.
    - Using a basic Python dictionary to load the description of the topology.
      To use this option call the :meth:`load`.
    - Build a full NML topology using NML objects and relations and register
      all the objects in the namespace using the enbbedded
      :class:`pynml.manager.ExtendedNMLManager`, for example:

      ::

         mgr = TopologyManager()
         sw1 = mgr.create_node(name='My Switch 1')
         sw2 = mgr.create_node(name='My Switch 2')
         sw1p1 = mgr.create_biport(sw1)
         # ...

      See :class:`pynml.manager.ExtendedNMLManager` for more information of
      this usage.

    :param str engine: Name of the engine platform to build the topology.
     See :func:`platforms` for how to get and discover available platforms.
    """

    def __init__(self, engine=DEFAULT_PLATFORM, **kwargs):
        self.nml = ExtendedNMLManager(**kwargs)
        self.engine = engine
        self.nodes = OrderedDict()
        self._platform = None
        self._built = False

    def is_identifier(self, raw):
        """
        Check if a string is a identifier.

        An identifier is any non empty string which letters are ascii
        lowercase, digits or the underscore character, but needs to start with
        a letter.
        """
        if not raw:
            return False
        if not raw[0] in ascii_lowercase:
            return False
        return all(l in IDENTIFIER for l in raw)

    def load(self, dictmeta):
        """
        FIXME write some documentation.

        FIXME actualy read the dictmeta and load the topology
        FIXME Specify dictionary format
        """
        print('*' * 79)
        print(dictmeta)

        # Load nodes
        for nodes_spec in dictmeta.get('nodes', []):
            attributes = nodes_spec['attributes']
            for node_id in nodes_spec['nodes']:
                self.nml.create_node(identifier=node_id, **attributes)

    def parse(self, txtmeta, load=True):
        """
        Parse a textual topology meta-description.

        This topology representation format allows to quickly specify simple
        topologies that are only composed of simple nodes and links between
        them. For a more feature full format consider the ``metadict`` format
        or using the :class:`pynml.manager.ExtendedNMLManager` directly.

        The format for the textual description of a topology is similar to
        Graphviz syntax and allows to define nodes with shared attributes and
        links between endpoints with shared attributes too.

        ::

            # Nodes
            [type=switch attr1=1] sw1 sw2
            hs1

            # Links
            [linkattr1=20] sw1 -- sw2
            [linkattr2=40] sw1: -- sw2:3

        In the above example two nodes with the attributes ``type`` and
        ``attr1`` are specified. Then a third node `hs1` with no particular
        attributes is defined. In the same way, a link between endpoints MAY
        have attributes.

        An endpoint is a combination of a node and a port number, but the
        port number is optional. If the endpoint just specifies the node
        (``sw1``, ``sw1:``) then the next available port is implied.
        """

        def parse_attrs(subline, raw_line, lineno):
            """
            Parse all attributes in a subline:

            ::

                '[myattr1="A String" myattr2=bar' ->
                {
                    'myattr1': '"A String"'
                    'myattr2': 'bar'
                }
            """
            attrs = OrderedDict()

            attrs_parts = subline.replace('[', '', 1).split()
            for part in attrs_parts:

                key, value = [p.strip() for p in part.split('=')]

                # Check for value overriding
                if key in attrs:
                    log.warning(
                        'Repeated key in line #{}: "{}"'.format(
                            lineno, raw_line
                        )
                    )
                    log.warning(
                        'Overriding "{}" value from "{}" to "{}"'.format(
                            key, attrs[key], value
                        )
                    )

                attrs[key] = value

            return attrs

        def parse_nodes(line, raw_line, lineno):
            """
            Parse a nodes description:

            ::

                '[...] sw1 hs1' ->
                {
                    'nodes': ('sw1', 'hs1'),
                    'attributes': {
                        ...
                    }
                }
            """
            attributes = OrderedDict()
            nodes_part = line

            # Parse attributes if present
            if ']' in line:
                attrs_part, nodes_part = line.split(']')
                attributes = parse_attrs(attrs_part, raw_line, lineno)

            nodes = nodes_part.split()
            nodes_set = set(nodes)

            if len(nodes) != len(nodes_set):
                log.warning(
                    'Repeated node names in line #{}: "{}"'.format(
                        lineno, raw_line
                    )
                )

            return {
                'nodes': nodes_set,
                'attributes': attributes
            }

        def parse_link(line, raw_line, lineno):
            """
            Parse a link description:

            ::

                '[...] sw1:1 -- hs1:1' ->
                {
                    'endpoints': (('sw1', 1), ('hs1', 1)),
                    'attributes': {
                        ...
                    }
                }

                '[...] sw1: -- hs1' ->
                {
                    'endpoints': (('sw1', None), ('hs1', None)),
                    'attributes': {
                        ...
                    }
                }
            """
            attributes = OrderedDict()
            link_part = line

            # Parse attributes if present
            if ']' in line:
                attrs_part, link_part = line.split(']')
                attributes = parse_attrs(attrs_part, raw_line, lineno)

            # Split endpoints
            endp_a, endp_b = link_part.split('--')
            endpoints = []

            # Parse endpoints
            for endp in [endp_a, endp_b]:

                endp = endp.strip()
                node_port = endp.split(':')

                # Check consistency
                if len(node_port) not in [1, 2] or not node_port[0]:
                    msg = 'Bad link specification at line #{}: "{}"'.format(
                        lineno, raw_line
                    )
                    log.error(msg)
                    raise Exception(msg)

                # First syntax 'sw1'
                if len(node_port) == 1:
                    node_port.append(None)

                # Second syntax 'sw1:'
                elif not node_port[1]:
                    node_port[1] = None

                # Third syntax 'sw:1'
                else:
                    node_port[1] = int(node_port[1])

                endpoints.append(tuple(node_port))

            return {
                'endpoints': tuple(endpoints),
                'attributes': attributes
            }

        # Parse txtmeta
        data = {
            'nodes': [],
            'links': []
        }

        for lineno, raw_line in enumerate(txtmeta.splitlines(), 1):

            try:

                # Ignore comments and empty line
                line = raw_line.strip()
                if not line or line.startswith('#'):
                    continue

                # Parse links
                if '--' in line:
                    link_spec = parse_link(line, raw_line, lineno)
                    data['links'].append(link_spec)
                    log.debug('Line number {}:\n{}'.format(lineno, link_spec))
                    continue

                # Parse nodes
                nodes_spec = parse_nodes(line, raw_line, lineno)
                data['nodes'].append(nodes_spec)
                log.debug('Line number {}:\n{}'.format(lineno, nodes_spec))
                continue
            except Exception:
                e = ParseException(lineno, raw_line, format_exc())
                log.error(str(e))
                log.debug(e.exc)
                raise e

        if load:
            self.load(data)

        return data

    def is_built(self):
        """
        Check if the current topology was built.

        :rtype: bool
        :return: True if the topology was succesfully built.
        """
        return self._built

    def build(self):
        """
        Build the topology hold by this manager.

        This method instance the platform engine and request the build of the
        topology defined.
        """
        timestamp = datetime.now().replace(microsecond=0).isoformat()

        # Instance platform
        plugin = platforms()[self.engine]
        self._platform = plugin(timestamp, self.nml)

        self._platform.pre_build()

        for node in self.nml.nodes():
            enode = self._platform.add_node(node)

            # Check that engine node implements the minimum interface
            if not isinstance(enode, BaseNode):
                msg = 'Platform {} returned an invalid engine node.'.format(
                    self.engine
                )
                log.critical(msg)
                raise Exception(msg)

            self.nodes[enode.identifier] = enode

        for node, biport in self.nml.biports():
            self._platform.add_biport(node, biport)

        for node_porta, node_portb, bilink in self.nml.bilinks():
            self._platform.add_bilink(node_porta, node_portb, bilink)

        self._platform.post_build()

        self._built = True

    def unbuild(self):
        """
        Undo the topology.

        This removes all references to the engine nodes and request the
        platform to destroy the topology.
        """
        if not self._built:
            raise RuntimeError(
                'You cannot unbuild and never built topology.'
            )

        # Remove own reference to enodes
        self.nodes = OrderedDict()

        # Call platform destroy hook
        self._platform.destroy()

        # Explicitly delete platform
        del self._platform
        self._platform = None

    def get(self, identifier):
        """
        Get a engine platform with given identifier.

        :param str identifier: The node identifier.
        :rtype: A subclass of :class:`topology.platforms.base.BaseNode`
        :return: The engine node implementing the communication with the node.
        """
        return self.nodes.get(identifier, None)

__all__ = ['ParseException', 'TopologyManager']
<commit_msg>new: dev: Added a doctest to the manager module to test the workflow.<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2015 Hewlett Packard Enterprise Development LP <asicapi@hp.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

"""
topology manager module.

This module takes care of understanding the topology defined in the test
suite, commanding its construction to appropriate topology platform plugin
and finally commanding its destruction to the plugin also.
"""

from __future__ import unicode_literals, absolute_import
from __future__ import print_function, division

import logging
from datetime import datetime
from traceback import format_exc
from collections import OrderedDict
from string import ascii_lowercase, digits

from pynml.manager import ExtendedNMLManager

from .platforms.base import BaseNode
from .platforms.manager import platforms, DEFAULT_PLATFORM

log = logging.getLogger(__name__)

IDENTIFIER = ascii_lowercase + digits + '_'

def is_identifier(raw):
    """
    Check if a string is a identifier.

    >>> is_identifier('a_abbc')
    True
    >>> is_identifier('Foo')
    False
    >>> is_identifier('1abc')
    False
    >>> is_identifier('a-abbc')
    False
    >>> is_identifier('BAR')
    False

    An identifier is any non empty string which letters are ascii
    lowercase, digits or the underscore character, but needs to start with
    a letter.
    """
    if not raw:
        return False
    if not raw[0] in ascii_lowercase:
        return False
    return all(l in IDENTIFIER for l in raw)

class ParseException(Exception):
    """
    Custom exception thrown by the parser if it could succeded.

    :var lineno: Line number of the parsing error.
    :var raw_line: Raw line that failed to be parsed.
    :var exc: Internal failure traceback of the parser.
    """
    def __init__(self, lineno, raw_line, exc):
        super(ParseException, self).__init__(
            'Unable to parse line #{}: "{}"'.format(
                lineno, raw_line
            )
        )
        self.lineno = lineno
        self.raw_line = raw_line
        self.exc = exc

class TopologyManager(object):
    """
    Main Topology Manager object.

    This object is responsible to build a topology given a description.

    There is three options to build a topology:

    - Using the simplified textual description using a Graphviz like syntax.
      To use this option call the :meth:`parse`.
    - Using a basic Python dictionary to load the description of the topology.
      To use this option call the :meth:`load`.
    - Build a full NML topology using NML objects and relations and register
      all the objects in the namespace using the enbbedded
      :class:`pynml.manager.ExtendedNMLManager`, for example:

      ::

         mgr = TopologyManager()
         sw1 = mgr.create_node(name='My Switch 1')
         sw2 = mgr.create_node(name='My Switch 2')
         sw1p1 = mgr.create_biport(sw1)
         # ...

      See :class:`pynml.manager.ExtendedNMLManager` for more information of
      this usage.

    :param str engine: Name of the engine platform to build the topology.
     See :func:`platforms` for how to get and discover available platforms.
    """

    def __init__(self, engine=DEFAULT_PLATFORM, **kwargs):
        self.nml = ExtendedNMLManager(**kwargs)
        self.engine = engine
        self.nodes = OrderedDict()
        self._platform = None
        self._built = False

    def load(self, dictmeta):
        """
        FIXME write some documentation.

        FIXME actualy read the dictmeta and load the topology
        FIXME Specify dictionary format
        """
        print('*' * 79)
        print(dictmeta)

        # Load nodes
        for nodes_spec in dictmeta.get('nodes', []):
            attributes = nodes_spec['attributes']
            for node_id in nodes_spec['nodes']:
                self.nml.create_node(identifier=node_id, **attributes)

    def parse(self, txtmeta, load=True):
        """
        Parse a textual topology meta-description.

        This topology representation format allows to quickly specify simple
        topologies that are only composed of simple nodes and links between
        them. For a more feature full format consider the ``metadict`` format
        or using the :class:`pynml.manager.ExtendedNMLManager` directly.

        The format for the textual description of a topology is similar to
        Graphviz syntax and allows to define nodes with shared attributes and
        links between endpoints with shared attributes too.

        ::

            # Nodes
            [type=switch attr1=1] sw1 sw2
            hs1

            # Links
            [linkattr1=20] sw1 -- sw2
            [linkattr2=40] sw1: -- sw2:3

        In the above example two nodes with the attributes ``type`` and
        ``attr1`` are specified. Then a third node `hs1` with no particular
        attributes is defined. In the same way, a link between endpoints MAY
        have attributes.

        An endpoint is a combination of a node and a port number, but the
        port number is optional. If the endpoint just specifies the node
        (``sw1``, ``sw1:``) then the next available port is implied.
        """

        def parse_attrs(subline, raw_line, lineno):
            """
            Parse all attributes in a subline:

            ::

                '[myattr1="A String" myattr2=bar' ->
                {
                    'myattr1': '"A String"'
                    'myattr2': 'bar'
                }
            """
            attrs = OrderedDict()

            attrs_parts = subline.replace('[', '', 1).split()
            for part in attrs_parts:

                key, value = [p.strip() for p in part.split('=')]

                # Check for value overriding
                if key in attrs:
                    log.warning(
                        'Repeated key in line #{}: "{}"'.format(
                            lineno, raw_line
                        )
                    )
                    log.warning(
                        'Overriding "{}" value from "{}" to "{}"'.format(
                            key, attrs[key], value
                        )
                    )

                attrs[key] = value

            return attrs

        def parse_nodes(line, raw_line, lineno):
            """
            Parse a nodes description:

            ::

                '[...] sw1 hs1' ->
                {
                    'nodes': ('sw1', 'hs1'),
                    'attributes': {
                        ...
                    }
                }
            """
            attributes = OrderedDict()
            nodes_part = line

            # Parse attributes if present
            if ']' in line:
                attrs_part, nodes_part = line.split(']')
                attributes = parse_attrs(attrs_part, raw_line, lineno)

            nodes = nodes_part.split()
            nodes_set = set(nodes)

            if len(nodes) != len(nodes_set):
                log.warning(
                    'Repeated node names in line #{}: "{}"'.format(
                        lineno, raw_line
                    )
                )

            return {
                'nodes': nodes_set,
                'attributes': attributes
            }

        def parse_link(line, raw_line, lineno):
            """
            Parse a link description:

            ::

                '[...] sw1:1 -- hs1:1' ->
                {
                    'endpoints': (('sw1', 1), ('hs1', 1)),
                    'attributes': {
                        ...
                    }
                }

                '[...] sw1: -- hs1' ->
                {
                    'endpoints': (('sw1', None), ('hs1', None)),
                    'attributes': {
                        ...
                    }
                }
            """
            attributes = OrderedDict()
            link_part = line

            # Parse attributes if present
            if ']' in line:
                attrs_part, link_part = line.split(']')
                attributes = parse_attrs(attrs_part, raw_line, lineno)

            # Split endpoints
            endp_a, endp_b = link_part.split('--')
            endpoints = []

            # Parse endpoints
            for endp in [endp_a, endp_b]:

                endp = endp.strip()
                node_port = endp.split(':')

                # Check consistency
                if len(node_port) not in [1, 2] or not node_port[0]:
                    msg = 'Bad link specification at line #{}: "{}"'.format(
                        lineno, raw_line
                    )
                    log.error(msg)
                    raise Exception(msg)

                # First syntax 'sw1'
                if len(node_port) == 1:
                    node_port.append(None)

                # Second syntax 'sw1:'
                elif not node_port[1]:
                    node_port[1] = None

                # Third syntax 'sw:1'
                else:
                    node_port[1] = int(node_port[1])

                endpoints.append(tuple(node_port))

            return {
                'endpoints': tuple(endpoints),
                'attributes': attributes
            }

        # Parse txtmeta
        data = {
            'nodes': [],
            'links': []
        }

        for lineno, raw_line in enumerate(txtmeta.splitlines(), 1):

            try:

                # Ignore comments and empty line
                line = raw_line.strip()
                if not line or line.startswith('#'):
                    continue

                # Parse links
                if '--' in line:
                    link_spec = parse_link(line, raw_line, lineno)
                    data['links'].append(link_spec)
                    log.debug('Line number {}:\n{}'.format(lineno, link_spec))
                    continue

                # Parse nodes
                nodes_spec = parse_nodes(line, raw_line, lineno)
                data['nodes'].append(nodes_spec)
                log.debug('Line number {}:\n{}'.format(lineno, nodes_spec))
                continue
            except Exception:
                e = ParseException(lineno, raw_line, format_exc())
                log.error(str(e))
                log.debug(e.exc)
                raise e

        if load:
            self.load(data)

        return data

    def is_built(self):
        """
        Check if the current topology was built.

        :rtype: bool
        :return: True if the topology was succesfully built.
        """
        return self._built

    def build(self):
        """
        Build the topology hold by this manager.

        This method instance the platform engine and request the build of the
        topology defined.
        """
        timestamp = datetime.now().replace(microsecond=0).isoformat()

        # Instance platform
        plugin = platforms()[self.engine]
        self._platform = plugin(timestamp, self.nml)

        self._platform.pre_build()

        for node in self.nml.nodes():
            enode = self._platform.add_node(node)

            # Check that engine node implements the minimum interface
            if not isinstance(enode, BaseNode):
                msg = 'Platform {} returned an invalid engine node.'.format(
                    self.engine
                )
                log.critical(msg)
                raise Exception(msg)

            self.nodes[enode.identifier] = enode

        for node, biport in self.nml.biports():
            self._platform.add_biport(node, biport)

        for node_porta, node_portb, bilink in self.nml.bilinks():
            self._platform.add_bilink(node_porta, node_portb, bilink)

        self._platform.post_build()

        self._built = True

    def unbuild(self):
        """
        Undo the topology.

        This removes all references to the engine nodes and request the
        platform to destroy the topology.
        """
        if not self._built:
            raise RuntimeError(
                'You cannot unbuild and never built topology.'
            )

        # Remove own reference to enodes
        self.nodes = OrderedDict()

        # Call platform destroy hook
        self._platform.destroy()

        # Explicitly delete platform
        del self._platform
        self._platform = None

    def get(self, identifier):
        """
        Get a engine platform with given identifier.

        :param str identifier: The node identifier.
        :rtype: A subclass of :class:`topology.platforms.base.BaseNode`
        :return: The engine node implementing the communication with the node.
        """
        return self.nodes.get(identifier, None)

__all__ = ['ParseException', 'TopologyManager']
<|endoftext|>


<commit_before>VERSION="0.3.0"
<commit_msg>Version bump<commit_after>VERSION="0.3.5"
<|endoftext|>


<commit_before>#copyright ReportLab Inc. 2000-2001
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/test/test_platypus_paragraphs.py?cvsroot=reportlab
#$Header: /tmp/reportlab/reportlab/test/test_platypus_paragraphs.py,v 1.11 2002/11/04 00:11:49 andy_robinson Exp $
"""Tests for the reportlab.platypus.paragraphs module.
"""

import sys, os, tempfile
from string import split, strip, join, whitespace
from operator import truth
from types import StringType, ListType

from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses

from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.platypus.paraparser import ParaParser
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import Color
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.utils import _className
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate \
     import PageTemplate, BaseDocTemplate
from reportlab.platypus import tableofcontents
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.tables import TableStyle, Table
from reportlab.platypus.paragraph import *
from reportlab.platypus.paragraph import _getFragWords

def myMainPageFrame(canvas, doc):
    "The page frame used for all PDF documents."

    canvas.saveState()

    canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
    canvas.setFont('Times-Roman', 12)
    pageNumber = canvas.getPageNumber()
    canvas.drawString(10*cm, cm, str(pageNumber))

    canvas.restoreState()

class MyDocTemplate(BaseDocTemplate):
    _invalidInitArgs = ('pageTemplates',)

    def __init__(self, filename, **kw):
        frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
        self.allowSplitting = 0
        apply(BaseDocTemplate.__init__, (self, filename), kw)
        template = PageTemplate('normal', [frame1], myMainPageFrame)
        self.addPageTemplates(template)

class ParagraphSplitTestCase(unittest.TestCase):
    "Test multi-page splitting of paragraphs (eyeball-test)."

    def test0(self):
        "This makes one long multi-page paragraph."

        # Build story.
        story = []
        styleSheet = getSampleStyleSheet()
        bt = styleSheet['BodyText']
        text = '''If you imagine that the box of X's tothe left is
an image, what I want to be able to do is flow a
series of paragraphs around the image
so that once the bottom of the image is reached, then text will flow back to the
left margin. I know that it would be possible to something like this
using tables, but I can't see how to have a generic solution.
There are two examples of this in the demonstration section of the reportlab
site.
If you look at the "minimal" euro python conference brochure, at the end of the
timetable section (page 8), there are adverts for "AdSu" and "O'Reilly". I can
see how the AdSu one might be done generically, but the O'Reilly, unsure...
I guess I'm hoping that I've missed something, and that
it's actually easy to do using platypus.
'''
        from reportlab.platypus.flowables import ParagraphAndImage, Image
        story.append(ParagraphAndImage(Paragraph(text,bt),Image('pythonpowered.gif')))
        phrase = 'This should be a paragraph spanning at least three pages. '
        description = phrase * 250
        story.append(Paragraph(description, bt))

        doc = MyDocTemplate('test_platypus_paragraphs.pdf')
        doc.multiBuild(story)

class FragmentTestCase(unittest.TestCase):
    "Test fragmentation of paragraphs."

    def test0(self):
        "Test empty paragraph."

        styleSheet = getSampleStyleSheet()
        B = styleSheet['BodyText']
        text = ''
        P = Paragraph(text, B)
        frags = map(lambda f:f.text, P.frags)
        assert frags == []

    def test1(self):
        "Test simple paragraph."

        styleSheet = getSampleStyleSheet()
        B = styleSheet['BodyText']
        text = "XYZ"
        P = Paragraph(text, B)
        frags = map(lambda f:f.text, P.frags)
        assert frags == ['X', 'Y', 'Z']

#noruntests
def makeSuite():
    return makeSuiteForClasses(FragmentTestCase, ParagraphSplitTestCase)

#noruntests
if __name__ == "__main__":
    unittest.TextTestRunner().run(makeSuite())

<commit_msg>Now works in compact distro<commit_after>#copyright ReportLab Inc. 2000-2001
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/test/test_platypus_paragraphs.py?cvsroot=reportlab
#$Header: /tmp/reportlab/reportlab/test/test_platypus_paragraphs.py,v 1.12 2004/03/23 15:19:21 rgbecker Exp $
"""Tests for the reportlab.platypus.paragraphs module.
"""

import sys, os, tempfile
from string import split, strip, join, whitespace
from operator import truth
from types import StringType, ListType

from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses

from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.platypus.paraparser import ParaParser
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import Color
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.utils import _className
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate \
     import PageTemplate, BaseDocTemplate
from reportlab.platypus import tableofcontents
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.tables import TableStyle, Table
from reportlab.platypus.paragraph import *
from reportlab.platypus.paragraph import _getFragWords

def myMainPageFrame(canvas, doc):
    "The page frame used for all PDF documents."

    canvas.saveState()

    canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
    canvas.setFont('Times-Roman', 12)
    pageNumber = canvas.getPageNumber()
    canvas.drawString(10*cm, cm, str(pageNumber))

    canvas.restoreState()

class MyDocTemplate(BaseDocTemplate):
    _invalidInitArgs = ('pageTemplates',)

    def __init__(self, filename, **kw):
        frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
        self.allowSplitting = 0
        apply(BaseDocTemplate.__init__, (self, filename), kw)
        template = PageTemplate('normal', [frame1], myMainPageFrame)
        self.addPageTemplates(template)

class ParagraphSplitTestCase(unittest.TestCase):
    "Test multi-page splitting of paragraphs (eyeball-test)."

    def test0(self):
        "This makes one long multi-page paragraph."

        # Build story.
        story = []
        styleSheet = getSampleStyleSheet()
        bt = styleSheet['BodyText']
        text = '''If you imagine that the box of X's tothe left is
an image, what I want to be able to do is flow a
series of paragraphs around the image
so that once the bottom of the image is reached, then text will flow back to the
left margin. I know that it would be possible to something like this
using tables, but I can't see how to have a generic solution.
There are two examples of this in the demonstration section of the reportlab
site.
If you look at the "minimal" euro python conference brochure, at the end of the
timetable section (page 8), there are adverts for "AdSu" and "O'Reilly". I can
see how the AdSu one might be done generically, but the O'Reilly, unsure...
I guess I'm hoping that I've missed something, and that
it's actually easy to do using platypus.
'''
        from reportlab.platypus.flowables import ParagraphAndImage, Image
        from reportlab.lib.utils import _RL_DIR
        gif = os.path.join(_RL_DIR,'test','pythonpowered.gif')
        story.append(ParagraphAndImage(Paragraph(text,bt),Image(gif)))
        phrase = 'This should be a paragraph spanning at least three pages. '
        description = phrase * 250
        story.append(Paragraph(description, bt))

        doc = MyDocTemplate('test_platypus_paragraphs.pdf')
        doc.multiBuild(story)

class FragmentTestCase(unittest.TestCase):
    "Test fragmentation of paragraphs."

    def test0(self):
        "Test empty paragraph."

        styleSheet = getSampleStyleSheet()
        B = styleSheet['BodyText']
        text = ''
        P = Paragraph(text, B)
        frags = map(lambda f:f.text, P.frags)
        assert frags == []

    def test1(self):
        "Test simple paragraph."

        styleSheet = getSampleStyleSheet()
        B = styleSheet['BodyText']
        text = "XYZ"
        P = Paragraph(text, B)
        frags = map(lambda f:f.text, P.frags)
        assert frags == ['X', 'Y', 'Z']

#noruntests
def makeSuite():
    return makeSuiteForClasses(FragmentTestCase, ParagraphSplitTestCase)

#noruntests
if __name__ == "__main__":
    unittest.TextTestRunner().run(makeSuite())

<|endoftext|>


<commit_before>"""Financial instrument types used by Interactive Brokers."""

from dataclasses import dataclass, field
from typing import List, NamedTuple, Optional

import ib_insync.util as util

__all__ = (
    'Contract Stock Option Future ContFuture Forex Index CFD '
    'Commodity Bond FuturesOption MutualFund Warrant Bag Crypto '
    'TagValue ComboLeg DeltaNeutralContract ContractDetails '
    'ContractDescription ScanData').split()

@dataclass
class Contract:
    """
    ``Contract(**kwargs)`` can create any contract using keyword
    arguments. To simplify working with contracts, there are also more
    specialized contracts that take optional positional arguments.
    Some examples::

        Contract(conId=270639)
        Stock('AMD', 'SMART', 'USD')
        Stock('INTC', 'SMART', 'USD', primaryExchange='NASDAQ')
        Forex('EURUSD')
        CFD('IBUS30')
        Future('ES', '20180921', 'GLOBEX')
        Option('SPY', '20170721', 240, 'C', 'SMART')
        Bond(secIdType='ISIN', secId='US03076KAA60')

    Args:
        conId (int): The unique IB contract identifier.
        symbol (str): The contract (or its underlying) symbol.
        secType (str): The security type:

            * 'STK' = Stock (or ETF)
            * 'OPT' = Option
            * 'FUT' = Future
            * 'IND' = Index
            * 'FOP' = Futures option
            * 'CASH' = Forex pair
            * 'CFD' = CFD
            * 'BAG' = Combo
            * 'WAR' = Warrant
            * 'BOND'= Bond
            * 'CMDTY'= Commodity
            * 'NEWS' = News
            * 'FUND'= Mutual fund
        lastTradeDateOrContractMonth (str): The contract's last trading
            day or contract month (for Options and Futures).
            Strings with format YYYYMM will be interpreted as the
            Contract Month whereas YYYYMMDD will be interpreted as
            Last Trading Day.
        strike (float): The option's strike price.
        right (str): Put or Call.
            Valid values are 'P', 'PUT', 'C', 'CALL', or '' for non-options.
        multiplier (str): he instrument's multiplier (i.e. options, futures).
        exchange (str): The destination exchange.
        currency (str): The underlying's currency.
        localSymbol (str): The contract's symbol within its primary exchange.
            For options, this will be the OCC symbol.
        primaryExchange (str): The contract's primary exchange.
            For smart routed contracts, used to define contract in case
            of ambiguity. Should be defined as native exchange of contract,
            e.g. ISLAND for MSFT. For exchanges which contain a period in name,
            will only be part of exchange name prior to period, i.e. ENEXT
            for ENEXT.BE.
        tradingClass (str): The trading class name for this contract.
            Available in TWS contract description window as well.
            For example, GBL Dec '13 future's trading class is "FGBL".
        includeExpired (bool): If set to true, contract details requests
            and historical data queries can be performed pertaining to
            expired futures contracts. Expired options or other instrument
            types are not available.
        secIdType (str): Security identifier type. Examples for Apple:

                * secIdType='ISIN', secId='US0378331005'
                * secIdType='CUSIP', secId='037833100'
        secId (str): Security identifier.
        comboLegsDescription (str): Description of the combo legs.
        comboLegs (List[ComboLeg]): The legs of a combined contract definition.
        deltaNeutralContract (DeltaNeutralContract): Delta and underlying
            price for Delta-Neutral combo orders.
    """

    secType: str = ''
    conId: int = 0
    symbol: str = ''
    lastTradeDateOrContractMonth: str = ''
    strike: float = 0.0
    right: str = ''
    multiplier: str = ''
    exchange: str = ''
    primaryExchange: str = ''
    currency: str = ''
    localSymbol: str = ''
    tradingClass: str = ''
    includeExpired: bool = False
    secIdType: str = ''
    secId: str = ''
    comboLegsDescrip: str = ''
    comboLegs: List['ComboLeg'] = field(default_factory=list)
    deltaNeutralContract: Optional['DeltaNeutralContract'] = None

    @staticmethod
    def create(**kwargs) -> 'Contract':
        """
        Create and a return a specialized contract based on the given secType,
        or a general Contract if secType is not given.
        """
        secType = kwargs.get('secType', '')
        cls = {
            '': Contract,
            'STK': Stock,
            'OPT': Option,
            'FUT': Future,
            'CONTFUT': ContFuture,
            'CASH': Forex,
            'IND': Index,
            'CFD': CFD,
            'BOND': Bond,
            'CMDTY': Commodity,
            'FOP': FuturesOption,
            'FUND': MutualFund,
            'WAR': Warrant,
            'IOPT': Warrant,
            'BAG': Bag,
            'NEWS': Contract
        }.get(secType, Contract)
        if cls is not Contract:
            kwargs.pop('secType', '')
        return cls(**kwargs)

    def isHashable(self) -> bool:
        """
        See if this contract can be hashed by conId.

        Note: Bag contracts always get conId=28812380, so they're not hashable.
        """
        return bool(
            self.conId and self.conId != 28812380
            and self.secType != 'BAG')

    def __eq__(self, other):
        return (
            isinstance(other, Contract)
            and (
                self.conId and self.conId == other.conId
                or util.dataclassAsDict(self) == util.dataclassAsDict(other)))

    def __hash__(self):
        if not self.isHashable():
            raise ValueError(f'Contract {self} can\'t be hashed')
        if self.secType == 'CONTFUT':
            # CONTFUT gets the same conId as the front contract, invert it here
            h = -self.conId
        else:
            h = self.conId
        return h

    def __repr__(self):
        attrs = util.dataclassNonDefaults(self)
        if self.__class__ is not Contract:
            attrs.pop('secType', '')
        clsName = self.__class__.__qualname__
        kwargs = ', '.join(f'{k}={v!r}' for k, v in attrs.items())
        return f'{clsName}({kwargs})'

    __str__ = __repr__

class Stock(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Stock contract.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, secType='STK', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class Option(Contract):

    def __init__(
            self, symbol: str = '', lastTradeDateOrContractMonth: str = '',
            strike: float = 0.0, right: str = '', exchange: str = '',
            multiplier: str = '', currency: str = '', **kwargs):
        """
        Option contract.

        Args:
            symbol: Symbol name.
            lastTradeDateOrContractMonth: The option's last trading day
                or contract month.

                * YYYYMM format: To specify last month
                * YYYYMMDD format: To specify last trading day
            strike: The option's strike price.
            right: Put or call option.
                Valid values are 'P', 'PUT', 'C' or 'CALL'.
            exchange: Destination exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'OPT', symbol=symbol,
            lastTradeDateOrContractMonth=lastTradeDateOrContractMonth,
            strike=strike, right=right, exchange=exchange,
            multiplier=multiplier, currency=currency, **kwargs)

class Future(Contract):

    def __init__(
            self, symbol: str = '', lastTradeDateOrContractMonth: str = '',
            exchange: str = '', localSymbol: str = '', multiplier: str = '',
            currency: str = '', **kwargs):
        """
        Future contract.

        Args:
            symbol: Symbol name.
            lastTradeDateOrContractMonth: The option's last trading day
                or contract month.

                * YYYYMM format: To specify last month
                * YYYYMMDD format: To specify last trading day
            exchange: Destination exchange.
            localSymbol: The contract's symbol within its primary exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'FUT', symbol=symbol,
            lastTradeDateOrContractMonth=lastTradeDateOrContractMonth,
            exchange=exchange, localSymbol=localSymbol,
            multiplier=multiplier, currency=currency, **kwargs)

class ContFuture(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', localSymbol: str = '',
            multiplier: str = '', currency: str = '', **kwargs):
        """
        Continuous future contract.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            localSymbol: The contract's symbol within its primary exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'CONTFUT', symbol=symbol,
            exchange=exchange, localSymbol=localSymbol,
            multiplier=multiplier, currency=currency, **kwargs)

class Forex(Contract):

    def __init__(
            self, pair: str = '', exchange: str = 'IDEALPRO',
            symbol: str = '', currency: str = '', **kwargs):
        """
        Foreign exchange currency pair.

        Args:
            pair: Shortcut for specifying symbol and currency, like 'EURUSD'.
            exchange: Destination exchange.
            symbol: Base currency.
            currency: Quote currency.
        """
        if pair:
            assert len(pair) == 6
            symbol = symbol or pair[:3]
            currency = currency or pair[3:]
        Contract.__init__(
            self, 'CASH', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

    def __repr__(self):
        attrs = util.dataclassNonDefaults(self)
        attrs.pop('secType')
        s = 'Forex('
        if 'symbol' in attrs and 'currency' in attrs:
            pair = attrs.pop('symbol')
            pair += attrs.pop('currency')
            s += "'" + pair + "'" + (", " if attrs else "")
        s += ', '.join(f'{k}={v!r}' for k, v in attrs.items())
        s += ')'
        return s

    __str__ = __repr__

    def pair(self) -> str:
        """Short name of pair."""
        return self.symbol + self.currency

class Index(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Index.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'IND', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class CFD(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Contract For Difference.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'CFD', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class Commodity(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Commodity.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'CMDTY', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class Bond(Contract):

    def __init__(self, **kwargs):
        """Bond."""
        Contract.__init__(self, 'BOND', **kwargs)

class FuturesOption(Contract):

    def __init__(
            self, symbol: str = '', lastTradeDateOrContractMonth: str = '',
            strike: float = 0.0, right: str = '', exchange: str = '',
            multiplier: str = '', currency: str = '', **kwargs):
        """
        Option on a futures contract.

        Args:
            symbol: Symbol name.
            lastTradeDateOrContractMonth: The option's last trading day
                or contract month.

                * YYYYMM format: To specify last month
                * YYYYMMDD format: To specify last trading day
            strike: The option's strike price.
            right: Put or call option.
                Valid values are 'P', 'PUT', 'C' or 'CALL'.
            exchange: Destination exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'FOP', symbol=symbol,
            lastTradeDateOrContractMonth=lastTradeDateOrContractMonth,
            strike=strike, right=right, exchange=exchange,
            multiplier=multiplier, currency=currency, **kwargs)

class MutualFund(Contract):

    def __init__(self, **kwargs):
        """Mutual fund."""
        Contract.__init__(self, 'FUND', **kwargs)

class Warrant(Contract):

    def __init__(self, **kwargs):
        """Warrant option."""
        Contract.__init__(self, 'WAR', **kwargs)

class Bag(Contract):

    def __init__(self, **kwargs):
        """Bag contract."""
        Contract.__init__(self, 'BAG', **kwargs)

class Crypto(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = 'SMART',
            currency: str = 'USD', **kwargs):
        """
        Crypto currency contract.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, secType='CRYPTO', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class TagValue(NamedTuple):
    tag: str
    value: str

@dataclass
class ComboLeg:
    conId: int = 0
    ratio: int = 0
    action: str = ''
    exchange: str = ''
    openClose: int = 0
    shortSaleSlot: int = 0
    designatedLocation: str = ''
    exemptCode: int = -1

@dataclass
class DeltaNeutralContract:
    conId: int = 0
    delta: float = 0.0
    price: float = 0.0

@dataclass
class ContractDetails:
    contract: Optional[Contract] = None
    marketName: str = ''
    minTick: float = 0.0
    sizeMinTick: float = 0.0
    orderTypes: str = ''
    validExchanges: str = ''
    priceMagnifier: int = 0
    underConId: int = 0
    longName: str = ''
    contractMonth: str = ''
    industry: str = ''
    category: str = ''
    subcategory: str = ''
    timeZoneId: str = ''
    tradingHours: str = ''
    liquidHours: str = ''
    evRule: str = ''
    evMultiplier: int = 0
    mdSizeMultiplier: int = 0
    aggGroup: int = 0
    underSymbol: str = ''
    underSecType: str = ''
    marketRuleIds: str = ''
    secIdList: List[TagValue] = field(default_factory=list)
    realExpirationDate: str = ''
    lastTradeTime: str = ''
    stockType: str = ''
    cusip: str = ''
    ratings: str = ''
    descAppend: str = ''
    bondType: str = ''
    couponType: str = ''
    callable: bool = False
    putable: bool = False
    coupon: int = 0
    convertible: bool = False
    maturity: str = ''
    issueDate: str = ''
    nextOptionDate: str = ''
    nextOptionType: str = ''
    nextOptionPartial: bool = False
    notes: str = ''

@dataclass
class ContractDescription:
    contract: Optional[Contract] = None
    derivativeSecTypes: List[str] = field(default_factory=list)

@dataclass
class ScanData:
    rank: int
    contractDetails: ContractDetails
    distance: str
    benchmark: str
    projection: str
    legsStr: str
<commit_msg>Add Crypto contract example to docs<commit_after>"""Financial instrument types used by Interactive Brokers."""

from dataclasses import dataclass, field
from typing import List, NamedTuple, Optional

import ib_insync.util as util

__all__ = (
    'Contract Stock Option Future ContFuture Forex Index CFD '
    'Commodity Bond FuturesOption MutualFund Warrant Bag Crypto '
    'TagValue ComboLeg DeltaNeutralContract ContractDetails '
    'ContractDescription ScanData').split()

@dataclass
class Contract:
    """
    ``Contract(**kwargs)`` can create any contract using keyword
    arguments. To simplify working with contracts, there are also more
    specialized contracts that take optional positional arguments.
    Some examples::

        Contract(conId=270639)
        Stock('AMD', 'SMART', 'USD')
        Stock('INTC', 'SMART', 'USD', primaryExchange='NASDAQ')
        Forex('EURUSD')
        CFD('IBUS30')
        Future('ES', '20180921', 'GLOBEX')
        Option('SPY', '20170721', 240, 'C', 'SMART')
        Bond(secIdType='ISIN', secId='US03076KAA60')
        Crypto('BTC')

    Args:
        conId (int): The unique IB contract identifier.
        symbol (str): The contract (or its underlying) symbol.
        secType (str): The security type:

            * 'STK' = Stock (or ETF)
            * 'OPT' = Option
            * 'FUT' = Future
            * 'IND' = Index
            * 'FOP' = Futures option
            * 'CASH' = Forex pair
            * 'CFD' = CFD
            * 'BAG' = Combo
            * 'WAR' = Warrant
            * 'BOND'= Bond
            * 'CMDTY'= Commodity
            * 'NEWS' = News
            * 'FUND'= Mutual fund
        lastTradeDateOrContractMonth (str): The contract's last trading
            day or contract month (for Options and Futures).
            Strings with format YYYYMM will be interpreted as the
            Contract Month whereas YYYYMMDD will be interpreted as
            Last Trading Day.
        strike (float): The option's strike price.
        right (str): Put or Call.
            Valid values are 'P', 'PUT', 'C', 'CALL', or '' for non-options.
        multiplier (str): he instrument's multiplier (i.e. options, futures).
        exchange (str): The destination exchange.
        currency (str): The underlying's currency.
        localSymbol (str): The contract's symbol within its primary exchange.
            For options, this will be the OCC symbol.
        primaryExchange (str): The contract's primary exchange.
            For smart routed contracts, used to define contract in case
            of ambiguity. Should be defined as native exchange of contract,
            e.g. ISLAND for MSFT. For exchanges which contain a period in name,
            will only be part of exchange name prior to period, i.e. ENEXT
            for ENEXT.BE.
        tradingClass (str): The trading class name for this contract.
            Available in TWS contract description window as well.
            For example, GBL Dec '13 future's trading class is "FGBL".
        includeExpired (bool): If set to true, contract details requests
            and historical data queries can be performed pertaining to
            expired futures contracts. Expired options or other instrument
            types are not available.
        secIdType (str): Security identifier type. Examples for Apple:

                * secIdType='ISIN', secId='US0378331005'
                * secIdType='CUSIP', secId='037833100'
        secId (str): Security identifier.
        comboLegsDescription (str): Description of the combo legs.
        comboLegs (List[ComboLeg]): The legs of a combined contract definition.
        deltaNeutralContract (DeltaNeutralContract): Delta and underlying
            price for Delta-Neutral combo orders.
    """

    secType: str = ''
    conId: int = 0
    symbol: str = ''
    lastTradeDateOrContractMonth: str = ''
    strike: float = 0.0
    right: str = ''
    multiplier: str = ''
    exchange: str = ''
    primaryExchange: str = ''
    currency: str = ''
    localSymbol: str = ''
    tradingClass: str = ''
    includeExpired: bool = False
    secIdType: str = ''
    secId: str = ''
    comboLegsDescrip: str = ''
    comboLegs: List['ComboLeg'] = field(default_factory=list)
    deltaNeutralContract: Optional['DeltaNeutralContract'] = None

    @staticmethod
    def create(**kwargs) -> 'Contract':
        """
        Create and a return a specialized contract based on the given secType,
        or a general Contract if secType is not given.
        """
        secType = kwargs.get('secType', '')
        cls = {
            '': Contract,
            'STK': Stock,
            'OPT': Option,
            'FUT': Future,
            'CONTFUT': ContFuture,
            'CASH': Forex,
            'IND': Index,
            'CFD': CFD,
            'BOND': Bond,
            'CMDTY': Commodity,
            'FOP': FuturesOption,
            'FUND': MutualFund,
            'WAR': Warrant,
            'IOPT': Warrant,
            'BAG': Bag,
            'NEWS': Contract
        }.get(secType, Contract)
        if cls is not Contract:
            kwargs.pop('secType', '')
        return cls(**kwargs)

    def isHashable(self) -> bool:
        """
        See if this contract can be hashed by conId.

        Note: Bag contracts always get conId=28812380, so they're not hashable.
        """
        return bool(
            self.conId and self.conId != 28812380
            and self.secType != 'BAG')

    def __eq__(self, other):
        return (
            isinstance(other, Contract)
            and (
                self.conId and self.conId == other.conId
                or util.dataclassAsDict(self) == util.dataclassAsDict(other)))

    def __hash__(self):
        if not self.isHashable():
            raise ValueError(f'Contract {self} can\'t be hashed')
        if self.secType == 'CONTFUT':
            # CONTFUT gets the same conId as the front contract, invert it here
            h = -self.conId
        else:
            h = self.conId
        return h

    def __repr__(self):
        attrs = util.dataclassNonDefaults(self)
        if self.__class__ is not Contract:
            attrs.pop('secType', '')
        clsName = self.__class__.__qualname__
        kwargs = ', '.join(f'{k}={v!r}' for k, v in attrs.items())
        return f'{clsName}({kwargs})'

    __str__ = __repr__

class Stock(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Stock contract.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, secType='STK', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class Option(Contract):

    def __init__(
            self, symbol: str = '', lastTradeDateOrContractMonth: str = '',
            strike: float = 0.0, right: str = '', exchange: str = '',
            multiplier: str = '', currency: str = '', **kwargs):
        """
        Option contract.

        Args:
            symbol: Symbol name.
            lastTradeDateOrContractMonth: The option's last trading day
                or contract month.

                * YYYYMM format: To specify last month
                * YYYYMMDD format: To specify last trading day
            strike: The option's strike price.
            right: Put or call option.
                Valid values are 'P', 'PUT', 'C' or 'CALL'.
            exchange: Destination exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'OPT', symbol=symbol,
            lastTradeDateOrContractMonth=lastTradeDateOrContractMonth,
            strike=strike, right=right, exchange=exchange,
            multiplier=multiplier, currency=currency, **kwargs)

class Future(Contract):

    def __init__(
            self, symbol: str = '', lastTradeDateOrContractMonth: str = '',
            exchange: str = '', localSymbol: str = '', multiplier: str = '',
            currency: str = '', **kwargs):
        """
        Future contract.

        Args:
            symbol: Symbol name.
            lastTradeDateOrContractMonth: The option's last trading day
                or contract month.

                * YYYYMM format: To specify last month
                * YYYYMMDD format: To specify last trading day
            exchange: Destination exchange.
            localSymbol: The contract's symbol within its primary exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'FUT', symbol=symbol,
            lastTradeDateOrContractMonth=lastTradeDateOrContractMonth,
            exchange=exchange, localSymbol=localSymbol,
            multiplier=multiplier, currency=currency, **kwargs)

class ContFuture(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', localSymbol: str = '',
            multiplier: str = '', currency: str = '', **kwargs):
        """
        Continuous future contract.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            localSymbol: The contract's symbol within its primary exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'CONTFUT', symbol=symbol,
            exchange=exchange, localSymbol=localSymbol,
            multiplier=multiplier, currency=currency, **kwargs)

class Forex(Contract):

    def __init__(
            self, pair: str = '', exchange: str = 'IDEALPRO',
            symbol: str = '', currency: str = '', **kwargs):
        """
        Foreign exchange currency pair.

        Args:
            pair: Shortcut for specifying symbol and currency, like 'EURUSD'.
            exchange: Destination exchange.
            symbol: Base currency.
            currency: Quote currency.
        """
        if pair:
            assert len(pair) == 6
            symbol = symbol or pair[:3]
            currency = currency or pair[3:]
        Contract.__init__(
            self, 'CASH', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

    def __repr__(self):
        attrs = util.dataclassNonDefaults(self)
        attrs.pop('secType')
        s = 'Forex('
        if 'symbol' in attrs and 'currency' in attrs:
            pair = attrs.pop('symbol')
            pair += attrs.pop('currency')
            s += "'" + pair + "'" + (", " if attrs else "")
        s += ', '.join(f'{k}={v!r}' for k, v in attrs.items())
        s += ')'
        return s

    __str__ = __repr__

    def pair(self) -> str:
        """Short name of pair."""
        return self.symbol + self.currency

class Index(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Index.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'IND', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class CFD(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Contract For Difference.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'CFD', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class Commodity(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = '', currency: str = '',
            **kwargs):
        """
        Commodity.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'CMDTY', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class Bond(Contract):

    def __init__(self, **kwargs):
        """Bond."""
        Contract.__init__(self, 'BOND', **kwargs)

class FuturesOption(Contract):

    def __init__(
            self, symbol: str = '', lastTradeDateOrContractMonth: str = '',
            strike: float = 0.0, right: str = '', exchange: str = '',
            multiplier: str = '', currency: str = '', **kwargs):
        """
        Option on a futures contract.

        Args:
            symbol: Symbol name.
            lastTradeDateOrContractMonth: The option's last trading day
                or contract month.

                * YYYYMM format: To specify last month
                * YYYYMMDD format: To specify last trading day
            strike: The option's strike price.
            right: Put or call option.
                Valid values are 'P', 'PUT', 'C' or 'CALL'.
            exchange: Destination exchange.
            multiplier: The contract multiplier.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, 'FOP', symbol=symbol,
            lastTradeDateOrContractMonth=lastTradeDateOrContractMonth,
            strike=strike, right=right, exchange=exchange,
            multiplier=multiplier, currency=currency, **kwargs)

class MutualFund(Contract):

    def __init__(self, **kwargs):
        """Mutual fund."""
        Contract.__init__(self, 'FUND', **kwargs)

class Warrant(Contract):

    def __init__(self, **kwargs):
        """Warrant option."""
        Contract.__init__(self, 'WAR', **kwargs)

class Bag(Contract):

    def __init__(self, **kwargs):
        """Bag contract."""
        Contract.__init__(self, 'BAG', **kwargs)

class Crypto(Contract):

    def __init__(
            self, symbol: str = '', exchange: str = 'SMART',
            currency: str = 'USD', **kwargs):
        """
        Crypto currency contract.

        Args:
            symbol: Symbol name.
            exchange: Destination exchange.
            currency: Underlying currency.
        """
        Contract.__init__(
            self, secType='CRYPTO', symbol=symbol,
            exchange=exchange, currency=currency, **kwargs)

class TagValue(NamedTuple):
    tag: str
    value: str

@dataclass
class ComboLeg:
    conId: int = 0
    ratio: int = 0
    action: str = ''
    exchange: str = ''
    openClose: int = 0
    shortSaleSlot: int = 0
    designatedLocation: str = ''
    exemptCode: int = -1

@dataclass
class DeltaNeutralContract:
    conId: int = 0
    delta: float = 0.0
    price: float = 0.0

@dataclass
class ContractDetails:
    contract: Optional[Contract] = None
    marketName: str = ''
    minTick: float = 0.0
    sizeMinTick: float = 0.0
    orderTypes: str = ''
    validExchanges: str = ''
    priceMagnifier: int = 0
    underConId: int = 0
    longName: str = ''
    contractMonth: str = ''
    industry: str = ''
    category: str = ''
    subcategory: str = ''
    timeZoneId: str = ''
    tradingHours: str = ''
    liquidHours: str = ''
    evRule: str = ''
    evMultiplier: int = 0
    mdSizeMultiplier: int = 0
    aggGroup: int = 0
    underSymbol: str = ''
    underSecType: str = ''
    marketRuleIds: str = ''
    secIdList: List[TagValue] = field(default_factory=list)
    realExpirationDate: str = ''
    lastTradeTime: str = ''
    stockType: str = ''
    cusip: str = ''
    ratings: str = ''
    descAppend: str = ''
    bondType: str = ''
    couponType: str = ''
    callable: bool = False
    putable: bool = False
    coupon: int = 0
    convertible: bool = False
    maturity: str = ''
    issueDate: str = ''
    nextOptionDate: str = ''
    nextOptionType: str = ''
    nextOptionPartial: bool = False
    notes: str = ''

@dataclass
class ContractDescription:
    contract: Optional[Contract] = None
    derivativeSecTypes: List[str] = field(default_factory=list)

@dataclass
class ScanData:
    rank: int
    contractDetails: ContractDetails
    distance: str
    benchmark: str
    projection: str
    legsStr: str
<|endoftext|>


<commit_before># Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

from UM.Math.Vector import Vector
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Logger import Logger

from enum import Enum

from UM.View.GL.OpenGL import OpenGL

import numpy
import numpy.linalg
import scipy.spatial
import hashlib
from copy import deepcopy
from time import time
numpy.seterr(all="ignore") # Ignore warnings (dev by zero)

class MeshType(Enum):
    faces = 1 # Start at one, as 0 is false (so if this is used in a if statement, it's always true)
    pointcloud = 2

##  Class to hold a list of verts and possibly how (and if) they are connected.
#
#   This class stores three numpy arrays that contain the data for a mesh. Vertices
#   are stored as a two-dimensional array of floats with the rows being individual
#   vertices and the three columns being the X, Y and Z components of the vertices.
#   Normals are stored in the same manner and kept in sync with the vertices. Indices
#   are stored as a two-dimensional array of integers with the rows being the individual
#   faces and the three columns being the indices that refer to the individual vertices.
class MeshData:
    def __init__(self, vertices=None, normals=None, indices=None, colors=None, uvs=None, file_name=None,
                 center_position=None):
        self._vertices = immutableNDArray(vertices)
        self._normals = immutableNDArray(normals)
        self._indices = immutableNDArray(indices)
        self._colors = immutableNDArray(colors)
        self._uvs = immutableNDArray(uvs)
        self._vertex_count = len(self._vertices) if self._vertices is not None else 0
        self._face_count = len(self._indices) if self._indices is not None else 0
        self._type = MeshType.faces
        self._file_name = None
        # original center position
        self._center_position = center_position
        self._convex_hull = None    # type: scipy.spatial.qhull.ConvexHull
        self._convex_hull_vertices = None

    def __deepcopy__(self, memo):
        copy = MeshData()
        copy._vertices = deepcopy(self._vertices, memo)
        copy._normals = deepcopy(self._normals, memo)
        copy._indices = deepcopy(self._indices, memo)
        copy._colors = deepcopy(self._colors, memo)
        copy._uvs = deepcopy(self._uvs, memo)
        copy._vertex_count = deepcopy(self._vertex_count, memo)
        copy._face_count = deepcopy(self._face_count, memo)
        copy._type = deepcopy(self._type, memo)
        copy._file_name = deepcopy(self._file_name, memo)
        self._center_position = deepcopy (self._center_position, memo)
        return copy

    def setCenterPosition(self, position):
        self._center_position = position

    def getHash(self):
        m = hashlib.sha256()
        m.update(self.getVerticesAsByteArray())
        return m.hexdigest()

    def getCenterPosition(self):
        return self._center_position

    def getType(self):
        return self._type

    def getFaceCount(self):
        return self._face_count

    ##  Get the array of vertices
    def getVertices(self):
        if self._vertices is None:
            return None

        return self._vertices[0 : self._vertex_count] #Only return up until point where data was filled

    ##  Get the number of vertices
    def getVertexCount(self):
        return self._vertex_count

    ##  Get a vertex by index
    def getVertex(self, index):
        try:
            return self._vertices[index]
        except IndexError:
            return None

    ##  Return whether this mesh has vertex normals.
    def hasNormals(self):
        return self._normals is not None

    ##  Return the list of vertex normals.
    def getNormals(self):
        return self._normals

    ##  Return whether this mesh has indices.
    def hasIndices(self):
        return self._indices is not None

    ##  Get the array of indices
    #   \return \type{numpy.ndarray}
    def getIndices(self):
        return self._indices[0:self._face_count]

    def hasColors(self):
        return self._colors is not None

    def getColors(self):
        return self._colors[0:self._vertex_count]

    def hasUVCoordinates(self):
        return self._uvs is not None

    def getFileName(self):
        return self._file_name

    ##  Transform the meshdata by given Matrix
    #   \param transformation 4x4 homogenous transformation matrix
    def getTransformed(self, transformation):
        if self._vertices is not None:
            data = numpy.pad(self._vertices[0:self._vertex_count], ((0,0), (0,1)), "constant", constant_values=(0.0, 0.0))
            data = data.dot(transformation.getTransposed().getData())
            data += transformation.getData()[:,3]
            data = data[:,0:3]

            mesh = deepcopy(self)
            mesh._vertices = data
            return mesh
        else:
            return MeshData(vertices = self._vertices)

    ##  Get the extents of this mesh.
    #
    #   \param matrix The transformation matrix from model to world coordinates.
    def getExtents(self, matrix = None):
        if self._vertices is None:
            return None

        data = numpy.pad(self.getConvexHullVertices(), ((0, 0), (0, 1)), "constant", constant_values=(0.0, 1.0))

        if matrix is not None:
            transposed = matrix.getTransposed().getData()
            data = data.dot(transposed)
            data += transposed[:, 3]
            data = data[:, 0:3]

        min = data.min(axis=0)
        max = data.max(axis=0)

        return AxisAlignedBox(minimum=Vector(min[0], min[1], min[2]), maximum=Vector(max[0], max[1], max[2]))

    def setVertexUVCoordinates(self, index, u, v):
        if self._uvs is None:
            self._uvs = numpy.zeros((10, 2), dtype=numpy.float32)

        if len(self._uvs) < len(self._vertices):
            self._uvs.resize((len(self._vertices), 2))

        self._uvs[index, 0] = u
        self._uvs[index, 1] = v

    ##
    # /param colors is a vertexCount by 4 numpy array with floats in range of 0 to 1.
    def setColors(self, colors):
        self._colors = colors

    ##  Get all vertices of this mesh as a bytearray
    #
    #   \return A bytearray object with 3 floats per vertex.
    def getVerticesAsByteArray(self):
        if self._vertices is not None:
            return self._vertices[0 : self._vertex_count].tostring()

    ##  Get all normals of this mesh as a bytearray
    #
    #   \return A bytearray object with 3 floats per normal.
    def getNormalsAsByteArray(self):
        if self._normals is not None:
            return self._normals[0 : self._vertex_count].tostring()

    ##  Get all indices as a bytearray
    #
    #   \return A bytearray object with 3 ints per face.
    def getIndicesAsByteArray(self):
        if self._indices is not None:
            return self._indices[0:self._face_count].tostring()

    def getColorsAsByteArray(self):
        if self._colors is not None:
            return self._colors[0 : self._vertex_count].tostring()

    def getUVCoordinatesAsByteArray(self):
        if self._uvs is not None:
            return self._uvs[0 : self._vertex_count].tostring()

    #######################################################################
    # Convex hull handling
    #######################################################################

    def _computeConvexHull(self):
        points = self.getVertices()
        if points is None:
            return

        start_time = time()
        self._convex_hull = scipy.spatial.ConvexHull(points)
        end_time = time()
        Logger.log("d", "Calculating 3D convex hull took %s seconds. %s input vertices. %s output vertices.",
                   end_time - start_time, len(points), len(self._convex_hull.vertices))

    ##  Gets the Convex Hull points
    #
    def getConvexHull(self):
        # Returns type: scipy.spatial.qhull.ConvexHull
        if self._convex_hull is None:
            self._computeConvexHull()
        return self._convex_hull

    def getConvexHullVertices(self):
        if self._convex_hull_vertices is None:
            convex_hull = self.getConvexHull()
            self._convex_hull_vertices = numpy.take(convex_hull.points, convex_hull.vertices, axis=0)
        return self._convex_hull_vertices

    def toString(self):
        return "MeshData(_vertices=" + str(self._vertices) + ", _normals=" + str(self._normals) + ", _indices=" + \
               str(self._indices) + ", _colors=" + str(self._colors) + ", _uvs=" + str(self._uvs) + ") "
##
#
def immutableNDArray(nda):
    if nda is None:
        return None
    if not nda.flags.writeable:
        return nda
    copy = deepcopy(nda)
    copy.flags.writeable = False
    return copy
<commit_msg>Reduce the amount of unneeded array slicing and copying in MeshData. Contributes to CURA-1504<commit_after># Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.

from UM.Math.Vector import Vector
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Logger import Logger

from enum import Enum

from UM.View.GL.OpenGL import OpenGL

import numpy
import numpy.linalg
import scipy.spatial
import hashlib
from copy import deepcopy
from time import time
numpy.seterr(all="ignore") # Ignore warnings (dev by zero)

class MeshType(Enum):
    faces = 1 # Start at one, as 0 is false (so if this is used in a if statement, it's always true)
    pointcloud = 2

##  Class to hold a list of verts and possibly how (and if) they are connected.
#
#   This class stores three numpy arrays that contain the data for a mesh. Vertices
#   are stored as a two-dimensional array of floats with the rows being individual
#   vertices and the three columns being the X, Y and Z components of the vertices.
#   Normals are stored in the same manner and kept in sync with the vertices. Indices
#   are stored as a two-dimensional array of integers with the rows being the individual
#   faces and the three columns being the indices that refer to the individual vertices.
class MeshData:
    def __init__(self, vertices=None, normals=None, indices=None, colors=None, uvs=None, file_name=None,
                 center_position=None):
        self._vertices = immutableNDArray(vertices)
        self._normals = immutableNDArray(normals)
        self._indices = immutableNDArray(indices)
        self._colors = immutableNDArray(colors)
        self._uvs = immutableNDArray(uvs)
        self._vertex_count = len(self._vertices) if self._vertices is not None else 0
        self._face_count = len(self._indices) if self._indices is not None else 0
        self._type = MeshType.faces
        self._file_name = None
        # original center position
        self._center_position = center_position
        self._convex_hull = None    # type: scipy.spatial.qhull.ConvexHull
        self._convex_hull_vertices = None

    def __deepcopy__(self, memo):
        copy = MeshData()
        copy._vertices = deepcopy(self._vertices, memo)
        copy._normals = deepcopy(self._normals, memo)
        copy._indices = deepcopy(self._indices, memo)
        copy._colors = deepcopy(self._colors, memo)
        copy._uvs = deepcopy(self._uvs, memo)
        copy._vertex_count = deepcopy(self._vertex_count, memo)
        copy._face_count = deepcopy(self._face_count, memo)
        copy._type = deepcopy(self._type, memo)
        copy._file_name = deepcopy(self._file_name, memo)
        self._center_position = deepcopy (self._center_position, memo)
        return copy

    def setCenterPosition(self, position):
        self._center_position = position

    def getHash(self):
        m = hashlib.sha256()
        m.update(self.getVerticesAsByteArray())
        return m.hexdigest()

    def getCenterPosition(self):
        return self._center_position

    def getType(self):
        return self._type

    def getFaceCount(self):
        return self._face_count

    ##  Get the array of vertices
    def getVertices(self):
        return self._vertices

    ##  Get the number of vertices
    def getVertexCount(self):
        return self._vertex_count

    ##  Get a vertex by index
    def getVertex(self, index):
        try:
            return self._vertices[index]
        except IndexError:
            return None

    ##  Return whether this mesh has vertex normals.
    def hasNormals(self):
        return self._normals is not None

    ##  Return the list of vertex normals.
    def getNormals(self):
        return self._normals

    ##  Return whether this mesh has indices.
    def hasIndices(self):
        return self._indices is not None

    ##  Get the array of indices
    #   \return \type{numpy.ndarray}
    def getIndices(self):
        return self._indices

    def hasColors(self):
        return self._colors is not None

    def getColors(self):
        return self._colors[0:self._vertex_count]

    def hasUVCoordinates(self):
        return self._uvs is not None

    def getFileName(self):
        return self._file_name

    ##  Transform the meshdata by given Matrix
    #   \param transformation 4x4 homogenous transformation matrix
    def getTransformed(self, transformation):
        if self._vertices is not None:
            data = numpy.pad(self._vertices, ((0,0), (0,1)), "constant", constant_values=(0.0, 0.0))
            data = data.dot(transformation.getTransposed().getData())
            data += transformation.getData()[:,3]
            data = data[:,0:3]

            mesh = deepcopy(self)
            mesh._vertices = data
            return mesh
        else:
            return MeshData(vertices = self._vertices)

    ##  Get the extents of this mesh.
    #
    #   \param matrix The transformation matrix from model to world coordinates.
    def getExtents(self, matrix = None):
        if self._vertices is None:
            return None

        data = numpy.pad(self.getConvexHullVertices(), ((0, 0), (0, 1)), "constant", constant_values=(0.0, 1.0))

        if matrix is not None:
            transposed = matrix.getTransposed().getData()
            data = data.dot(transposed)
            data += transposed[:, 3]
            data = data[:, 0:3]

        min = data.min(axis=0)
        max = data.max(axis=0)

        return AxisAlignedBox(minimum=Vector(min[0], min[1], min[2]), maximum=Vector(max[0], max[1], max[2]))

    def setVertexUVCoordinates(self, index, u, v):
        if self._uvs is None:
            self._uvs = numpy.zeros((10, 2), dtype=numpy.float32)

        if len(self._uvs) < len(self._vertices):
            self._uvs.resize((len(self._vertices), 2))

        self._uvs[index, 0] = u
        self._uvs[index, 1] = v

    ##
    # /param colors is a vertexCount by 4 numpy array with floats in range of 0 to 1.
    def setColors(self, colors):
        self._colors = colors

    ##  Get all vertices of this mesh as a bytearray
    #
    #   \return A bytearray object with 3 floats per vertex.
    def getVerticesAsByteArray(self):
        if self._vertices is None:
            return None
        # FIXME cache result
        return self._vertices.tostring()

    ##  Get all normals of this mesh as a bytearray
    #
    #   \return A bytearray object with 3 floats per normal.
    def getNormalsAsByteArray(self):
        if self._normals is None:
            return None
        # FIXME cache result
        return self._normals.tostring()

    ##  Get all indices as a bytearray
    #
    #   \return A bytearray object with 3 ints per face.
    def getIndicesAsByteArray(self):
        if self._indices is None:
            return None
        # FIXME cache result
        return self._indices.tostring()

    def getColorsAsByteArray(self):
        if self._colors is None:
            return None
        # FIXME cache result
        return self._colors.tostring()

    def getUVCoordinatesAsByteArray(self):
        if self._uvs is not None:
            return self._uvs[0 : self._vertex_count].tostring()

    #######################################################################
    # Convex hull handling
    #######################################################################

    def _computeConvexHull(self):
        points = self.getVertices()
        if points is None:
            return

        start_time = time()
        self._convex_hull = scipy.spatial.ConvexHull(points)
        end_time = time()
        Logger.log("d", "Calculating 3D convex hull took %s seconds. %s input vertices. %s output vertices.",
                   end_time - start_time, len(points), len(self._convex_hull.vertices))

    ##  Gets the Convex Hull points
    #
    def getConvexHull(self):
        # Returns type: scipy.spatial.qhull.ConvexHull
        if self._convex_hull is None:
            self._computeConvexHull()
        return self._convex_hull

    def getConvexHullVertices(self):
        if self._convex_hull_vertices is None:
            convex_hull = self.getConvexHull()
            self._convex_hull_vertices = numpy.take(convex_hull.points, convex_hull.vertices, axis=0)
        return self._convex_hull_vertices

    def toString(self):
        return "MeshData(_vertices=" + str(self._vertices) + ", _normals=" + str(self._normals) + ", _indices=" + \
               str(self._indices) + ", _colors=" + str(self._colors) + ", _uvs=" + str(self._uvs) + ") "
##
#
def immutableNDArray(nda):
    if nda is None:
        return None
    if not nda.flags.writeable:
        return nda
    copy = deepcopy(nda)
    copy.flags.writeable = False
    return copy
<|endoftext|>


<commit_before># coding=utf-8

import unittest
import sys

sys.path.append ("../UI")
import UI
import time

# Things to test:
#   Creating a window
#
#
#
#
#   Closing the window

class OpenUITestCase (unittest.TestCase):
    def runTest (self):
        display = UI.UI ()
        assert display.gui_thread.is_alive ()

# Create Display Object
# Add Books
# Display Library

# Create Display Object
# Display Library
# Add Books

class LibraryScreenTestCase (unittest.TestCase):
    def _ABADL_Book (self, id, display):
        callback = lambda: -1

        display.add_book_to_library (callback, "res/test_bookcover.png")

        time.sleep (0.1)

        children = display.libraryscreen.sizer.GetChildren ()

        assert (len (children) == id + 1)

        children[id].Window.enqueue_callback ()
        with display.callback_queue_lock:
            assert (callback in display.callback_queue)

        assert (children[id].Window.IsShown ())

    def testAddBooksAndDisplayLibrary (self):
        display = UI.UI ()

        self._ABADL_Book (0, display)

        assert (not display.libraryscreen.IsShown ())
        display.display_library ()
        time.sleep (0.1)
        assert (display.libraryscreen.IsShown ())

        self._ABADL_Book(1, display)

def suite ():
    suite = unittest.TestSuite ()
    suite.addTest (OpenUITestCase ())
    suite.addTest (LibraryScreenTestCase ())
    return suite

if __name__ == "__main__":
    unittest.main()<commit_msg>Added check to ensure GUI thread is alive when testing adding books and displaying the library.<commit_after># coding=utf-8

import unittest
import sys

sys.path.append ("../UI")
import UI
import time

# Things to test:
#   Creating a window
#
#
#
#
#   Closing the window

class OpenUITestCase (unittest.TestCase):
    def runTest (self):
        display = UI.UI ()
        assert display.gui_thread.is_alive ()

# Create Display Object
# Add Books
# Display Library

# Create Display Object
# Display Library
# Add Books

class LibraryScreenTestCase (unittest.TestCase):
    def _ABADL_Book (self, id, display):
        callback = lambda: -1

        display.add_book_to_library (callback, "res/test_bookcover.png")

        time.sleep (0.1)

        children = display.libraryscreen.sizer.GetChildren ()

        assert (len (children) == id + 1)

        children[id].Window.enqueue_callback ()
        with display.callback_queue_lock:
            assert (callback in display.callback_queue)

        assert (children[id].Window.IsShown ())

    def testAddBooksAndDisplayLibrary (self):
        display = UI.UI ()

        self._ABADL_Book (0, display)

        assert (not display.libraryscreen.IsShown ())
        display.display_library ()
        time.sleep (0.1)
        assert (display.libraryscreen.IsShown ())

        self._ABADL_Book(1, display)

        assert display.gui_thread.is_alive ()

def suite ():
    suite = unittest.TestSuite ()
    suite.addTest (OpenUITestCase ())
    suite.addTest (LibraryScreenTestCase ())
    return suite

if __name__ == "__main__":
    unittest.main()<|endoftext|>


<commit_before>#!/usr/bin/env python3

"""
Dialog interperter.
"""
__author__ = "Mark Birger"
__date__ = "19 Nov 2014"

from parser import Parser
from scope import Scope
from returns import Returns
import speech
import link_parser

import multiprocessing

class Dialog:
    """
    Dialog interperter class.
    """
    def __init__(self, scope):
        self.expected = []
        self.scope = Scope(scope)
        self.returns = Returns()

    def load(self, filename):
        """
        Loads dialog from the file.
        Uses Parser module.
        """
        parser = Parser(filename, self.scope, self.returns)
        self.expected.extend(parser.result())

    def _extend_expected(self, quesitons):
        # TODO: more elegant way to extends expected questions
        expected_strings = [str(x) for x in self.expected]
        for new in quesitons:
            if not str(new) in expected_strings:
                print("+\t%s"%(new))
                self.expected.append(new)

    def start_spoken(self):
        """
        Interprets dialog with natural speech.
        """
        occupation = multiprocessing.Event()
        listener_queue = multiprocessing.Queue(maxsize=0)
        recognizer_queue = multiprocessing.Queue(maxsize=0)
        speaker_queue = multiprocessing.Queue(maxsize=0)
        speaker = multiprocessing.Process(
            target=speech.speaker,
            args=(occupation, speaker_queue, ))
        recognizer = multiprocessing.Process(
            target=speech.recognizer,
            args=(recognizer_queue, listener_queue, ))
        listener = multiprocessing.Process(
            target=speech.listener,
            args=(occupation, recognizer_queue, ))
        speaker.start()
        recognizer.start()
        listener.start()
        occupation.set()
        print("======")
        for state in self.expected:
            print("\t%s" % (state))
        print("======")
        while True:
            # process routines answers
            answers = self.returns.get_returns()
            for answer in answers:
                tosay, questions = answer.accept()
                speaker_queue.put(tosay)
                print("Bot> "+tosay)
                self._extend_expected(questions)
            # process input
            if not listener_queue.empty():
                input_phrase = listener_queue.get()
                input_phrase = link_parser.parse(input_phrase)
                states_probability = []
                for state in self.expected:
                    # print(state, state.compare(input_phrase))
                    states_probability.append((state, state.compare(input_phrase)))
                states_probability = sorted(states_probability, key=lambda x: x[1], reverse=True)
                print("======")
                for state in states_probability:
                    print("%.2f\t%s" % (state[1], state[0]))
                print("======")
                state = states_probability[0][0]
                if states_probability[0][1] < 0.2:
                    print("Bot> ???")
                else:
                    tosay, questions = state.accept(input_phrase)
                    for answer in tosay:
                        if answer != "":
                            speaker_queue.put(answer)
                            print("Bot> "+answer)
                    self._extend_expected(questions)

    def start_text(self):
        """
        Interprets dialog in text mode.
        """
        print("======")
        for state in self.expected:
            print("\t%s" % (state))
        print("======")
        while True:
            # process routines answers
            answers = self.returns.get_returns()
            for answer in answers:
                tosay, questions = answer.accept()
                print("Bot> "+tosay)
                self._extend_expected(questions)
            # process input
            input_phrase = input("You> ")
            input_phrase = link_parser.parse(input_phrase)
            states_probability = []
            for state in self.expected:
                # print(state, state.compare(input_phrase))
                states_probability.append((state, state.compare(input_phrase)))
            states_probability = sorted(states_probability, key=lambda x: x[1], reverse=True)
            print("======")
            for state in states_probability:
                print("%.2f\t%s" % (state[1], state[0]))
            print("======")
            state = states_probability[0][0]
            if states_probability[0][1] < 0.2:
                print("Bot> ???")
            else:
                tosay, questions = state.accept(input_phrase)
                for answer in tosay:
                    if answer != "":
                        print("Bot> "+answer)
                self._extend_expected(questions)

<commit_msg>Two-way communication decorator<commit_after>#!/usr/bin/env python3

"""
Dialog interperter.
"""
__author__ = "Mark Birger"
__date__ = "19 Nov 2014"

from parser import Parser
from scope import Scope
from returns import Returns
import speech
import link_parser

import multiprocessing

class Dialog:
    """
    Dialog interperter class.
    """
    def __init__(self, scope):
        self.expected = []
        self.scope = Scope(scope)
        self.returns = Returns()

    def load(self, filename):
        """
        Loads dialog from the file.
        Uses Parser module.
        """
        parser = Parser(filename, self.scope, self.returns)
        self.expected.extend(parser.result())

    def _extend_expected(self, quesitons):
        """
        Method extends set of expected phrases.
        """
        # TODO: more elegant way to extends expected questions
        expected_strings = [str(x) for x in self.expected]
        for new in quesitons:
            if not str(new) in expected_strings:
                print("+\t%s"%(new))
                self.expected.append(new)

    def start_spoken(self):
        """
        Interprets dialog with natural speech.
        """
        occupation = multiprocessing.Event()
        listener_queue = multiprocessing.Queue(maxsize=0)
        recognizer_queue = multiprocessing.Queue(maxsize=0)
        speaker_queue = multiprocessing.Queue(maxsize=0)
        speaker = multiprocessing.Process(
            target=speech.speaker,
            args=(occupation, speaker_queue, ))
        recognizer = multiprocessing.Process(
            target=speech.recognizer,
            args=(recognizer_queue, listener_queue, ))
        listener = multiprocessing.Process(
            target=speech.listener,
            args=(occupation, recognizer_queue, ))
        speaker.start()
        recognizer.start()
        listener.start()
        occupation.set()
        print("======")
        for state in self.expected:
            print("\t%s" % (state))
        print("======")
        while True:
            # process routines answers
            answers = self.returns.get_returns()
            for answer in answers:
                tosay, questions = answer.accept()
                speaker_queue.put(tosay)
                print("Bot> "+tosay)
                self._extend_expected(questions)
            # process input
            if not listener_queue.empty():
                input_phrase = listener_queue.get()
                input_phrase = link_parser.parse(input_phrase)
                states_probability = []
                for state in self.expected:
                    # print(state, state.compare(input_phrase))
                    states_probability.append((state, state.compare(input_phrase)))
                states_probability = sorted(states_probability, key=lambda x: x[1], reverse=True)
                print("======")
                for state in states_probability:
                    print("%.2f\t%s" % (state[1], state[0]))
                print("======")
                state = states_probability[0][0]
                if states_probability[0][1] < 0.2:
                    print("Bot> ???")
                else:
                    tosay, questions = state.accept(input_phrase)
                    for answer in tosay:
                        if answer != "":
                            speaker_queue.put(answer)
                            print("Bot> "+answer)
                    self._extend_expected(questions)

    def start_text(self):
        """
        Interprets dialog in text mode.
        """
        print("======")
        for state in self.expected:
            print("\t%s" % (state))
        print("======")
        while True:
            # process routines answers
            answers = self.returns.get_returns()
            for answer in answers:
                tosay, questions = answer.accept()
                print("Bot> "+tosay)
                self._extend_expected(questions)
            # process input
            input_phrase = input("You> ")
            input_phrase = link_parser.parse(input_phrase)
            states_probability = []
            for state in self.expected:
                # print(state, state.compare(input_phrase))
                states_probability.append((state, state.compare(input_phrase)))
            states_probability = sorted(states_probability, key=lambda x: x[1], reverse=True)
            print("======")
            for state in states_probability:
                print("%.2f\t%s" % (state[1], state[0]))
            print("======")
            state = states_probability[0][0]
            if states_probability[0][1] < 0.2:
                print("Bot> ???")
            else:
                tosay, questions = state.accept(input_phrase)
                for answer in tosay:
                    if answer != "":
                        print("Bot> "+answer)
                self._extend_expected(questions)

def handle(callbacks, before=lambda scope: None, after=lambda scope: None):
    def decorator(async_func):
        def inner(requests, responses, global_scope):
            initial_scope = {}
            for name, obj in global_scope.items():
                if not name.startswith('__'):
                    initial_scope[name] = obj
            scope = type('', (), initial_scope)()
            before(scope)
            while True:
                while not requests.empty():
                    request = requests.get()
                    if request in callbacks:
                        callbacks[request](scope)
                    else:
                        print("Warining: unhandled request")
                        if "" in callbacks:
                            callbacks[""](scope)
                        else:
                            pass
                if scope._exit:
                    break
                async_func(requests, responses, scope)
                if scope._exit:
                    break
            after(scope)
        return inner
    return decorator
<|endoftext|>