<commit_before>import platform
import textwrap
from typing import Any, Dict, Optional, Set, Tuple, Type, Union

from pygments.lexers import get_lexer_by_name, guess_lexer_for_filename
from pygments.style import Style as PygmentsStyle
from pygments.styles import get_style_by_name
from pygments.token import Token
from pygments.util import ClassNotFound

from ._loop import loop_first
from .color import Color, blend_rgb, parse_rgb_hex
from .console import Console, ConsoleOptions, RenderResult, Segment
from .jupyter import JupyterMixin
from .measure import Measurement
from .style import Style
from .text import Text

WINDOWS = platform.system() == "Windows"
DEFAULT_THEME = "monokai"

class Syntax(JupyterMixin):
    """Construct a Syntax object to render syntax highlighted code.

    Args:
        code (str): Code to highlight.
        lexer_name (str): Lexer to use (see https://pygments.org/docs/lexers/)
        theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "monokai".
        dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
        line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
        start_line (int, optional): Starting number for line numbers. Defaults to 1.
        line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
        highlight_lines (Set[int]): A set of line numbers to highlight.
        code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
        tab_size (int, optional): Size of tabs. Defaults to 4.
        word_wrap (bool, optional): Enable word wrapping.
    """

    _pygments_style_class: Type[PygmentsStyle]

    def __init__(
        self,
        code: str,
        lexer_name: str,
        *,
        theme: Union[str, Type[PygmentsStyle]] = DEFAULT_THEME,
        dedent: bool = False,
        line_numbers: bool = False,
        start_line: int = 1,
        line_range: Tuple[int, int] = None,
        highlight_lines: Set[int] = None,
        code_width: Optional[int] = None,
        tab_size: int = 4,
        word_wrap: bool = False
    ) -> None:
        self.code = code
        self.lexer_name = lexer_name
        self.dedent = dedent
        self.line_numbers = line_numbers
        self.start_line = start_line
        self.line_range = line_range
        self.highlight_lines = highlight_lines or set()
        self.code_width = code_width
        self.tab_size = tab_size
        self.word_wrap = word_wrap

        self._style_cache: Dict[Any, Style] = {}

        if not isinstance(theme, str) and issubclass(theme, PygmentsStyle):
            self._pygments_style_class = theme
        else:
            try:
                self._pygments_style_class = get_style_by_name(theme)
            except ClassNotFound:
                self._pygments_style_class = get_style_by_name("default")
        self._background_color = self._pygments_style_class.background_color

    @classmethod
    def from_path(
        cls,
        path: str,
        encoding: str = "utf-8",
        theme: Union[str, Type[PygmentsStyle]] = DEFAULT_THEME,
        dedent: bool = True,
        line_numbers: bool = False,
        line_range: Tuple[int, int] = None,
        start_line: int = 1,
        highlight_lines: Set[int] = None,
        code_width: Optional[int] = None,
        tab_size: int = 4,
        word_wrap: bool = False,
    ) -> "Syntax":
        """Construct a Syntax object from a file.

        Args:
            path (str): Path to file to highlight.
            encoding (str): Encoding of file.
            lexer_name (str): Lexer to use (see https://pygments.org/docs/lexers/)
            theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "emacs".
            dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
            line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
            start_line (int, optional): Starting number for line numbers. Defaults to 1.
            line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
            highlight_lines (Set[int]): A set of line numbers to highlight.
            code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
            tab_size (int, optional): Size of tabs. Defaults to 4.
            word_wrap (bool, optional): Enable word wrapping of code.

        Returns:
            [Syntax]: A Syntax object that may be printed to the console
        """
        with open(path, "rt", encoding=encoding) as code_file:
            code = code_file.read()
        try:
            lexer = guess_lexer_for_filename(path, code)
            lexer_name = lexer.name
        except ClassNotFound:
            lexer_name = "default"
        return cls(
            code,
            lexer_name,
            theme=theme,
            dedent=dedent,
            line_numbers=line_numbers,
            line_range=line_range,
            start_line=start_line,
            highlight_lines=highlight_lines,
            code_width=code_width,
            word_wrap=word_wrap,
        )

    def _get_theme_style(self, token_type) -> Style:
        if token_type in self._style_cache:
            style = self._style_cache[token_type]
        else:
            try:
                pygments_style = self._pygments_style_class.style_for_token(token_type)
            except KeyError:
                style = Style()
            else:
                color = pygments_style["color"]
                bgcolor = pygments_style["bgcolor"]
                style = Style(
                    color="#" + color if color else "#000000",
                    bgcolor="#" + bgcolor if bgcolor else self._background_color,
                    bold=pygments_style["bold"],
                    italic=pygments_style["italic"],
                    underline=pygments_style["underline"],
                )
            self._style_cache[token_type] = style

        return style

    def _get_default_style(self) -> Style:
        style = self._get_theme_style(Token.Text)
        style = style + Style(bgcolor=self._pygments_style_class.background_color)
        return style

    def _highlight(self, lexer_name: str) -> Text:
        default_style = self._get_default_style()
        try:
            lexer = get_lexer_by_name(lexer_name)
        except ClassNotFound:
            return Text(
                self.code, justify="left", style=default_style, tab_size=self.tab_size
            )
        text = Text(justify="left", style=default_style, tab_size=self.tab_size)
        append = text.append
        _get_theme_style = self._get_theme_style
        for token_type, token in lexer.get_tokens(self.code):
            append(token, _get_theme_style(token_type))
        return text

    def _get_line_numbers_color(self, blend: float = 0.3) -> Color:
        background_color = parse_rgb_hex(
            self._pygments_style_class.background_color[1:]
        )
        foreground_color = self._get_theme_style(Token.Text)._color
        if foreground_color is None:
            return Color.default()
        # TODO: Handle no full colors here
        assert foreground_color.triplet is not None
        new_color = blend_rgb(
            background_color, foreground_color.triplet, cross_fade=blend
        )
        return Color.from_triplet(new_color)

    @property
    def _numbers_column_width(self) -> int:
        """Get the number of characters used to render the numbers column."""
        if self.line_numbers:
            return len(str(self.start_line + self.code.count("\n"))) + 2
        return 0

    def _get_number_styles(self, console: Console) -> Tuple[Style, Style, Style]:
        """Get background, number, and highlight styles for line numbers."""
        background_style = Style(bgcolor=self._pygments_style_class.background_color)
        if console.color_system in ("256", "truecolor"):
            number_style = Style.chain(
                background_style,
                self._get_theme_style(Token.Text),
                Style(color=self._get_line_numbers_color()),
            )
            highlight_number_style = Style.chain(
                background_style,
                self._get_theme_style(Token.Text),
                Style(bold=True, color=self._get_line_numbers_color(0.9)),
            )
        else:
            number_style = highlight_number_style = Style()
        return background_style, number_style, highlight_number_style

    def __rich_measure__(self, console: "Console", max_width: int) -> "Measurement":
        if self.code_width is not None:
            width = self.code_width + self._numbers_column_width
            return Measurement(self._numbers_column_width, width)
        return Measurement(self._numbers_column_width, max_width)

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        code_width = (
            (options.max_width - self._numbers_column_width - 1)
            if self.code_width is None
            else self.code_width
        )
        code = self.code
        if self.dedent:
            code = textwrap.dedent(code)
        text = self._highlight(self.lexer_name)
        if not self.line_numbers:
            if self.code_width is None:
                yield text
            else:
                yield from console.render(
                    text, options=options.update(width=code_width)
                )
            return

        lines = text.split("\n")

        line_offset = 0
        if self.line_range:
            start_line, end_line = self.line_range
            line_offset = max(0, start_line - 1)
            lines = lines[line_offset:end_line]

        numbers_column_width = self._numbers_column_width
        render_options = options.update(width=code_width)

        (
            background_style,
            number_style,
            highlight_number_style,
        ) = self._get_number_styles(console)

        highlight_line = self.highlight_lines.__contains__
        _Segment = Segment
        padding = _Segment(" " * numbers_column_width + " ", background_style)
        new_line = _Segment("\n")

        line_pointer = "❱ "

        for line_no, line in enumerate(lines, self.start_line + line_offset):
            if self.word_wrap:
                wrapped_lines = console.render_lines(
                    line, render_options, style=background_style
                )
            else:
                segments = list(line.render(console, end=""))
                wrapped_lines = [
                    Segment.adjust_line_length(
                        segments, render_options.max_width, style=background_style
                    )
                ]
            for first, wrapped_line in loop_first(wrapped_lines):
                if first:
                    line_column = str(line_no).rjust(numbers_column_width - 2) + " "
                    if highlight_line(line_no):
                        yield _Segment(line_pointer, number_style)
                        yield _Segment(
                            line_column, highlight_number_style,
                        )
                    else:
                        yield _Segment("  ", highlight_number_style)
                        yield _Segment(
                            line_column, number_style,
                        )
                else:
                    yield padding
                yield from wrapped_line
                yield new_line

if __name__ == "__main__":  # pragma: no cover

    import argparse

    parser = argparse.ArgumentParser(
        description="Render Markdown to the console with Rich"
    )
    parser.add_argument("path", metavar="PATH", help="path to file")
    parser.add_argument(
        "-c",
        "--force-color",
        dest="force_color",
        action="store_true",
        help="force color for non-terminals",
    )
    parser.add_argument(
        "-l",
        "--line-numbers",
        dest="line_numbers",
        action="store_true",
        help="render line numbers",
    )
    parser.add_argument(
        "-w",
        "--width",
        type=int,
        dest="width",
        default=None,
        help="width of output (default will auto-detect)",
    )
    parser.add_argument(
        "-r",
        "--wrap",
        dest="word_wrap",
        action="store_true",
        default=False,
        help="word wrap long lines",
    )
    parser.add_argument(
        "-t", "--theme", dest="theme", default="monokai", help="pygments theme"
    )
    args = parser.parse_args()

    from rich.console import Console

    console = Console(force_terminal=args.force_color, width=args.width)

    syntax = Syntax.from_path(
        args.path,
        line_numbers=args.line_numbers,
        word_wrap=args.word_wrap,
        theme=args.theme,
    )
    console.print(syntax)
<commit_msg>Syntax module: correct description<commit_after>import platform
import textwrap
from typing import Any, Dict, Optional, Set, Tuple, Type, Union

from pygments.lexers import get_lexer_by_name, guess_lexer_for_filename
from pygments.style import Style as PygmentsStyle
from pygments.styles import get_style_by_name
from pygments.token import Token
from pygments.util import ClassNotFound

from ._loop import loop_first
from .color import Color, blend_rgb, parse_rgb_hex
from .console import Console, ConsoleOptions, RenderResult, Segment
from .jupyter import JupyterMixin
from .measure import Measurement
from .style import Style
from .text import Text

WINDOWS = platform.system() == "Windows"
DEFAULT_THEME = "monokai"

class Syntax(JupyterMixin):
    """Construct a Syntax object to render syntax highlighted code.

    Args:
        code (str): Code to highlight.
        lexer_name (str): Lexer to use (see https://pygments.org/docs/lexers/)
        theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "monokai".
        dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
        line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
        start_line (int, optional): Starting number for line numbers. Defaults to 1.
        line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
        highlight_lines (Set[int]): A set of line numbers to highlight.
        code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
        tab_size (int, optional): Size of tabs. Defaults to 4.
        word_wrap (bool, optional): Enable word wrapping.
    """

    _pygments_style_class: Type[PygmentsStyle]

    def __init__(
        self,
        code: str,
        lexer_name: str,
        *,
        theme: Union[str, Type[PygmentsStyle]] = DEFAULT_THEME,
        dedent: bool = False,
        line_numbers: bool = False,
        start_line: int = 1,
        line_range: Tuple[int, int] = None,
        highlight_lines: Set[int] = None,
        code_width: Optional[int] = None,
        tab_size: int = 4,
        word_wrap: bool = False
    ) -> None:
        self.code = code
        self.lexer_name = lexer_name
        self.dedent = dedent
        self.line_numbers = line_numbers
        self.start_line = start_line
        self.line_range = line_range
        self.highlight_lines = highlight_lines or set()
        self.code_width = code_width
        self.tab_size = tab_size
        self.word_wrap = word_wrap

        self._style_cache: Dict[Any, Style] = {}

        if not isinstance(theme, str) and issubclass(theme, PygmentsStyle):
            self._pygments_style_class = theme
        else:
            try:
                self._pygments_style_class = get_style_by_name(theme)
            except ClassNotFound:
                self._pygments_style_class = get_style_by_name("default")
        self._background_color = self._pygments_style_class.background_color

    @classmethod
    def from_path(
        cls,
        path: str,
        encoding: str = "utf-8",
        theme: Union[str, Type[PygmentsStyle]] = DEFAULT_THEME,
        dedent: bool = True,
        line_numbers: bool = False,
        line_range: Tuple[int, int] = None,
        start_line: int = 1,
        highlight_lines: Set[int] = None,
        code_width: Optional[int] = None,
        tab_size: int = 4,
        word_wrap: bool = False,
    ) -> "Syntax":
        """Construct a Syntax object from a file.

        Args:
            path (str): Path to file to highlight.
            encoding (str): Encoding of file.
            lexer_name (str): Lexer to use (see https://pygments.org/docs/lexers/)
            theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "emacs".
            dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
            line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
            start_line (int, optional): Starting number for line numbers. Defaults to 1.
            line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
            highlight_lines (Set[int]): A set of line numbers to highlight.
            code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
            tab_size (int, optional): Size of tabs. Defaults to 4.
            word_wrap (bool, optional): Enable word wrapping of code.

        Returns:
            [Syntax]: A Syntax object that may be printed to the console
        """
        with open(path, "rt", encoding=encoding) as code_file:
            code = code_file.read()
        try:
            lexer = guess_lexer_for_filename(path, code)
            lexer_name = lexer.name
        except ClassNotFound:
            lexer_name = "default"
        return cls(
            code,
            lexer_name,
            theme=theme,
            dedent=dedent,
            line_numbers=line_numbers,
            line_range=line_range,
            start_line=start_line,
            highlight_lines=highlight_lines,
            code_width=code_width,
            word_wrap=word_wrap,
        )

    def _get_theme_style(self, token_type) -> Style:
        if token_type in self._style_cache:
            style = self._style_cache[token_type]
        else:
            try:
                pygments_style = self._pygments_style_class.style_for_token(token_type)
            except KeyError:
                style = Style()
            else:
                color = pygments_style["color"]
                bgcolor = pygments_style["bgcolor"]
                style = Style(
                    color="#" + color if color else "#000000",
                    bgcolor="#" + bgcolor if bgcolor else self._background_color,
                    bold=pygments_style["bold"],
                    italic=pygments_style["italic"],
                    underline=pygments_style["underline"],
                )
            self._style_cache[token_type] = style

        return style

    def _get_default_style(self) -> Style:
        style = self._get_theme_style(Token.Text)
        style = style + Style(bgcolor=self._pygments_style_class.background_color)
        return style

    def _highlight(self, lexer_name: str) -> Text:
        default_style = self._get_default_style()
        try:
            lexer = get_lexer_by_name(lexer_name)
        except ClassNotFound:
            return Text(
                self.code, justify="left", style=default_style, tab_size=self.tab_size
            )
        text = Text(justify="left", style=default_style, tab_size=self.tab_size)
        append = text.append
        _get_theme_style = self._get_theme_style
        for token_type, token in lexer.get_tokens(self.code):
            append(token, _get_theme_style(token_type))
        return text

    def _get_line_numbers_color(self, blend: float = 0.3) -> Color:
        background_color = parse_rgb_hex(
            self._pygments_style_class.background_color[1:]
        )
        foreground_color = self._get_theme_style(Token.Text)._color
        if foreground_color is None:
            return Color.default()
        # TODO: Handle no full colors here
        assert foreground_color.triplet is not None
        new_color = blend_rgb(
            background_color, foreground_color.triplet, cross_fade=blend
        )
        return Color.from_triplet(new_color)

    @property
    def _numbers_column_width(self) -> int:
        """Get the number of characters used to render the numbers column."""
        if self.line_numbers:
            return len(str(self.start_line + self.code.count("\n"))) + 2
        return 0

    def _get_number_styles(self, console: Console) -> Tuple[Style, Style, Style]:
        """Get background, number, and highlight styles for line numbers."""
        background_style = Style(bgcolor=self._pygments_style_class.background_color)
        if console.color_system in ("256", "truecolor"):
            number_style = Style.chain(
                background_style,
                self._get_theme_style(Token.Text),
                Style(color=self._get_line_numbers_color()),
            )
            highlight_number_style = Style.chain(
                background_style,
                self._get_theme_style(Token.Text),
                Style(bold=True, color=self._get_line_numbers_color(0.9)),
            )
        else:
            number_style = highlight_number_style = Style()
        return background_style, number_style, highlight_number_style

    def __rich_measure__(self, console: "Console", max_width: int) -> "Measurement":
        if self.code_width is not None:
            width = self.code_width + self._numbers_column_width
            return Measurement(self._numbers_column_width, width)
        return Measurement(self._numbers_column_width, max_width)

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        code_width = (
            (options.max_width - self._numbers_column_width - 1)
            if self.code_width is None
            else self.code_width
        )
        code = self.code
        if self.dedent:
            code = textwrap.dedent(code)
        text = self._highlight(self.lexer_name)
        if not self.line_numbers:
            if self.code_width is None:
                yield text
            else:
                yield from console.render(
                    text, options=options.update(width=code_width)
                )
            return

        lines = text.split("\n")

        line_offset = 0
        if self.line_range:
            start_line, end_line = self.line_range
            line_offset = max(0, start_line - 1)
            lines = lines[line_offset:end_line]

        numbers_column_width = self._numbers_column_width
        render_options = options.update(width=code_width)

        (
            background_style,
            number_style,
            highlight_number_style,
        ) = self._get_number_styles(console)

        highlight_line = self.highlight_lines.__contains__
        _Segment = Segment
        padding = _Segment(" " * numbers_column_width + " ", background_style)
        new_line = _Segment("\n")

        line_pointer = "❱ "

        for line_no, line in enumerate(lines, self.start_line + line_offset):
            if self.word_wrap:
                wrapped_lines = console.render_lines(
                    line, render_options, style=background_style
                )
            else:
                segments = list(line.render(console, end=""))
                wrapped_lines = [
                    Segment.adjust_line_length(
                        segments, render_options.max_width, style=background_style
                    )
                ]
            for first, wrapped_line in loop_first(wrapped_lines):
                if first:
                    line_column = str(line_no).rjust(numbers_column_width - 2) + " "
                    if highlight_line(line_no):
                        yield _Segment(line_pointer, number_style)
                        yield _Segment(
                            line_column, highlight_number_style,
                        )
                    else:
                        yield _Segment("  ", highlight_number_style)
                        yield _Segment(
                            line_column, number_style,
                        )
                else:
                    yield padding
                yield from wrapped_line
                yield new_line

if __name__ == "__main__":  # pragma: no cover

    import argparse

    parser = argparse.ArgumentParser(
        description="Render syntax to the console with Rich"
    )
    parser.add_argument("path", metavar="PATH", help="path to file")
    parser.add_argument(
        "-c",
        "--force-color",
        dest="force_color",
        action="store_true",
        help="force color for non-terminals",
    )
    parser.add_argument(
        "-l",
        "--line-numbers",
        dest="line_numbers",
        action="store_true",
        help="render line numbers",
    )
    parser.add_argument(
        "-w",
        "--width",
        type=int,
        dest="width",
        default=None,
        help="width of output (default will auto-detect)",
    )
    parser.add_argument(
        "-r",
        "--wrap",
        dest="word_wrap",
        action="store_true",
        default=False,
        help="word wrap long lines",
    )
    parser.add_argument(
        "-t", "--theme", dest="theme", default="monokai", help="pygments theme"
    )
    args = parser.parse_args()

    from rich.console import Console

    console = Console(force_terminal=args.force_color, width=args.width)

    syntax = Syntax.from_path(
        args.path,
        line_numbers=args.line_numbers,
        word_wrap=args.word_wrap,
        theme=args.theme,
    )
    console.print(syntax)
<|endoftext|>


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

HAVE_PIDS = True

import sys, struct
try:
    import prodids
except:
    print("[.] Could not find product ID database.")
    HAVE_PIDS = False

class FileSizeError(Exception):
    pass

class MZSignatureError(Exception):
    pass

class MZPointerError(Exception):
    pass

class PESignatureError(Exception):
    pass

class RichSignatureError(Exception):
    pass

class DanSSignatureError(Exception):
    pass

class HeaderPaddingError(Exception):
    pass

class RichLengthError(Exception):
    pass

class FileReadError(Exception):
    pass

def err2str(code):
    return{
        -1: "Could not open file.",
        -2: "File too small to contain required headers.",
        -3: "MZ signature not found.",
        -4: "MZ Header pointing beyond end of file.",
        -5: "PE signature not found",
        -6: "Rich signature not found. This file probably has no Rich header.",
        -7: "DanS signature not found. Rich header corrupt.",
        -8: "Wrong header padding behind DanS signature. Rich header corrupt.",
        -9: "Rich data length not a multiple of 8. Rich header corrupt.",

        }[code]

class RichLibrary:

    def __u32(self, x):
        return struct.unpack("<I", x)[0]

    def __p32(self, x):
        return struct.pack("<I", x)

    def __rol32(self, v, n):
        return ((v << (n & 0x1f)) & 0xffffffff) | (v >> (32 - (n & 0x1f)))

    def parse(self):
        dat = bytearray(open(self.fname, 'rb').read()[:0x1000])

        ## Do basic sanity checks on the PE
        dat_len = len(dat)
        if dat_len < self.SIZE_DOS_HEADER:
            raise FileSizeError()

        if dat[0:][:2] != b'MZ':
            raise MZSignatureError()

        e_lfanew = self.__u32(dat[self.POS_E_LFANEW:][:4])

        if e_lfanew + 1 > dat_len:
            raise MZPointerError()

        if dat[e_lfanew:][:2] != b'PE':
            raise PESignatureError()

        ## IMPORTANT: Do not assume the data to start at 0x80, this is not always
        ## the case (modified DOS stub). Instead, start searching backwards for
        ## 'Rich', stop at beginning of DOS header.
        rich = 0
        for rich in range(e_lfanew, self.SIZE_DOS_HEADER, -1):
            if dat[rich:][:4] == b'Rich':
                break

        if rich == self.SIZE_DOS_HEADER:
            raise RichSignatureError()

        ## We found a valid 'Rich' signature in the header from here on
        csum = self.__u32(dat[rich + 4:][:4])

        ## xor backwards with csum until either 'DanS' or end of the DOS header,
        ## invert the result to get original order
        upack = [ self.__u32(dat[i:][:4]) ^ csum for i in range(rich - 4, self.SIZE_DOS_HEADER, -4) ][::-1]
        if self.__u32(b'DanS') not in upack:
            raise DanSSignatureError()

        upack = upack[upack.index(self.__u32(b'DanS')):]
        dans = e_lfanew - len(upack) * 4 - (e_lfanew - rich)

        ## DanS is _always_ followed by three zero dwords
        if not all([upack[i] == 0 for i in range(1, 4)]):
            raise HeaderPaddingError()

        upack = upack[4:]

        if len(upack) & 1:
            raise RichLengthError()

        cmpids = []

        ## Bonus feature: Calculate and check the check sum csum
        chk = dans
        for i in range(dans):
            ## Mask out the e_lfanew field as it's not initialized yet
            if i in range(0x3c, 0x40):
                continue
            chk += self.__rol32(dat[i], i)

        for i in range(0, len(upack), 2):
            cmpids.append({
                'mcv': (upack[i + 0] >>  0) & 0xffff,
                'pid': (upack[i + 0] >> 16) & 0xffff,
                'cnt': (upack[i + 1] >>  0)
            })
            ## Exclude the "generic file" descriptor from the check sum
            #if cmpids[-1]['mcv'] & 0xffff != 0:
            chk += self.__rol32(upack[i + 0], upack[i + 1])

        ## Truncate calculated checksum to 32 bit
        chk &= 0xffffffff

        return {'error': 0, 'cmpids': cmpids, 'csum_calc': chk, 'csum_file': csum,
                'offset': dans}

    def __pprint_cmpids(self, cmpids):
        print("-" * (20 + 16 + 16 + 32 + 39))
        print("{:>20s}{:>16s}{:>16s}{:>32s}{:>39s}".format("Compiler Patchlevel", "Product ID",
            "Count", "MS Internal Name", "Visual Studio Release"))
        print("-" * (20 + 16 + 16 + 32 + 39))

        for e in cmpids:
            if HAVE_PIDS:
                try:
                    int_name = prodids.int_names[e['pid']]
                except:
                    int_name = '<unknown>'
                vs_version = prodids.vs_version(e['pid'])

            print("{:>20s}{:>16s}{:>16s}{:>32s}{:>39s}".format(
                "{:5d}".format(e['mcv']),
                "0x{:04x}".format(e['pid']),
                "0x{:08x}".format(e['cnt']),
                "{}".format(int_name),
                "{:18s} ({})".format(*vs_version)))
        print("-" * (20 + 16 + 16 + 32 + 39))

    def pprint_header(self, data):
        self.__pprint_cmpids(data['cmpids'])
        if data['csum_calc'] == data['csum_file']:
            print("\x1b[32mChecksums match! (0x{:08x})".format(data['csum_calc']))
        else:
            print("\x1b[33mChecksum corrupt! (calc 0x{:08x}, file "
            "0x{:08x})".format(data['csum_calc'], data['csum_file']))
        print("\x1b[39m" + "-" * (20 + 16 + 16 + 32 + 39))

    def __init__(self, path):
        self.data = {}
        self.SIZE_DOS_HEADER = 0x40
        self.POS_E_LFANEW = 0x3c

        self.fname = path
<commit_msg>seberated out checksum calc<commit_after>#!/usr/bin/env python3

HAVE_PIDS = True

import sys, struct
try:
    import prodids
except:
    print("[.] Could not find product ID database.")
    HAVE_PIDS = False

class FileSizeError(Exception):
    pass

class MZSignatureError(Exception):
    pass

class MZPointerError(Exception):
    pass

class PESignatureError(Exception):
    pass

class RichSignatureError(Exception):
    pass

class DanSSignatureError(Exception):
    pass

class HeaderPaddingError(Exception):
    pass

class RichLengthError(Exception):
    pass

class FileReadError(Exception):
    pass

def err2str(code):
    return{
        -1: "Could not open file.",
        -2: "File too small to contain required headers.",
        -3: "MZ signature not found.",
        -4: "MZ Header pointing beyond end of file.",
        -5: "PE signature not found",
        -6: "Rich signature not found. This file probably has no Rich header.",
        -7: "DanS signature not found. Rich header corrupt.",
        -8: "Wrong header padding behind DanS signature. Rich header corrupt.",
        -9: "Rich data length not a multiple of 8. Rich header corrupt.",
        }[code]

class RichLibrary:

    def __u32(self, x):
        return struct.unpack("<I", x)[0]

    def __p32(self, x):
        return struct.pack("<I", x)

    def __rol32(self, v, n):
        return ((v << (n & 0x1f)) & 0xffffffff) | (v >> (32 - (n & 0x1f)))

    def generate_csum(self, raw_dat, compids, off):
        csum = off

        for i in range(off):
            ## Mask out the e_lfanew field as it's not initialized yet
            if i in range(0x3c, 0x40):
                continue
            csum += self.__rol32(raw_dat[i], i)

        for c in compids:
            csum += self.__rol32(c['pid'] << 16 | c['mcv'], c['cnt'])

        ## Truncate calculated checksum to 32 bit
        return csum & 0xffffffff

    def parse(self):
        dat = bytearray(open(self.fname, 'rb').read()[:0x1000])

        ## Do basic sanity checks on the PE
        dat_len = len(dat)
        if dat_len < self.SIZE_DOS_HEADER:
            raise FileSizeError()

        if dat[0:][:2] != b'MZ':
            raise MZSignatureError()

        e_lfanew = self.__u32(dat[self.POS_E_LFANEW:][:4])

        if e_lfanew + 1 > dat_len:
            raise MZPointerError()

        if dat[e_lfanew:][:2] != b'PE':
            raise PESignatureError()

        ## IMPORTANT: Do not assume the data to start at 0x80, this is not always
        ## the case (modified DOS stub). Instead, start searching backwards for
        ## 'Rich', stop at beginning of DOS header.
        rich = 0
        for rich in range(e_lfanew, self.SIZE_DOS_HEADER, -1):
            if dat[rich:][:4] == b'Rich':
                break

        if rich == self.SIZE_DOS_HEADER:
            raise RichSignatureError()

        ## We found a valid 'Rich' signature in the header from here on
        csum = self.__u32(dat[rich + 4:][:4])

        ## xor backwards with csum until either 'DanS' or end of the DOS header,
        ## invert the result to get original order
        upack = [ self.__u32(dat[i:][:4]) ^ csum for i in range(rich - 4, self.SIZE_DOS_HEADER, -4) ][::-1]
        if self.__u32(b'DanS') not in upack:
            raise DanSSignatureError()

        upack = upack[upack.index(self.__u32(b'DanS')):]
        dans = e_lfanew - len(upack) * 4 - (e_lfanew - rich)

        ## DanS is _always_ followed by three zero dwords
        if not all([upack[i] == 0 for i in range(1, 4)]):
            raise HeaderPaddingError()

        upack = upack[4:]

        if len(upack) & 1:
            raise RichLengthError()

        cmpids = []
        for i in range(0, len(upack), 2):
            cmpids.append({
                'mcv': (upack[i + 0] >>  0) & 0xffff,
                'pid': (upack[i + 0] >> 16) & 0xffff,
                'cnt': (upack[i + 1] >>  0)
            })

        ## Bonus feature: Calculate and check the check sum csum
        chk = self.generate_csum(dat, cmpids, dans)

        return {'error': 0, 'cmpids': cmpids, 'csum_calc': chk, 'csum_file': csum,
                'offset': dans}

    def __pprint_cmpids(self, cmpids):
        print("-" * (20 + 16 + 16 + 32 + 39))
        print("{:>20s}{:>16s}{:>16s}{:>32s}{:>39s}".format("Compiler Patchlevel", "Product ID",
            "Count", "MS Internal Name", "Visual Studio Release"))
        print("-" * (20 + 16 + 16 + 32 + 39))

        for e in cmpids:
            if HAVE_PIDS:
                try:
                    int_name = prodids.int_names[e['pid']]
                except:
                    int_name = '<unknown>'
                vs_version = prodids.vs_version(e['pid'])

            print("{:>20s}{:>16s}{:>16s}{:>32s}{:>39s}".format(
                "{:5d}".format(e['mcv']),
                "0x{:04x}".format(e['pid']),
                "0x{:08x}".format(e['cnt']),
                "{}".format(int_name),
                "{:18s} ({})".format(*vs_version)))
        print("-" * (20 + 16 + 16 + 32 + 39))

    def pprint_header(self, data):
        self.__pprint_cmpids(data['cmpids'])
        if data['csum_calc'] == data['csum_file']:
            print("\x1b[32mChecksums match! (0x{:08x})".format(data['csum_calc']))
        else:
            print("\x1b[33mChecksum corrupt! (calc 0x{:08x}, file "
            "0x{:08x})".format(data['csum_calc'], data['csum_file']))
        print("\x1b[39m" + "-" * (20 + 16 + 16 + 32 + 39))

    def __init__(self, path):
        self.data = {}
        self.SIZE_DOS_HEADER = 0x40
        self.POS_E_LFANEW = 0x3c

        self.fname = path
<|endoftext|>


<commit_before># This file is part of RinohType, the Python document preparation system.
#
# Copyright (c) Brecht Machiels.
#
# Use of this source code is subject to the terms of the GNU Affero General
# Public License v3. See the LICENSE file or http://www.gnu.org/licenses/.

"""
Base classes and exceptions for styled document elements.

* :class:`Style`: Dictionary storing a set of style attributes
* :class:`Styled`: A styled entity, having a :class:`Style` associated with it
* :class:`StyleStore`: Dictionary storing a set of related `Style`s by name
* :const:`PARENT_STYLE`: Special style that forwards style lookups to the parent
                        :class:`Styled`
* :exc:`ParentStyleException`: Thrown when style attribute lookup needs to be
                               delegated to the parent :class:`Styled`
"""

from collections import OrderedDict

from .element import DocumentElement
from .util import cached

__all__ = ['Style', 'Styled', 'Var',
           'StyledMatcher', 'StyleSheet', 'ClassSelector', 'ContextSelector',
           'PARENT_STYLE', 'StyleException']

class StyleException(Exception):
    """Style lookup requires special handling."""

class ParentStyleException(StyleException):
    """Style attribute not found. Consult the parent :class:`Styled`."""

class BaseStyleException(StyleException):
    """The attribute is not specified in this :class:`Style`. Find the attribute
    in a base style."""

    def __init__(self, base_name, attribute):
        self.base_name = base_name
        self.attribute = attribute

class DefaultStyleException(StyleException):
    """The attribute is not specified in this :class:`Style` or any of its base
    styles. Return the default value for the attribute."""

class Style(dict):
    """"Dictionary storing style attributes.

    Attrributes can also be accessed as attributes."""

    attributes = {}
    """Dictionary holding the supported style attributes for this :class:`Style`
    class (keys) and their default values (values)"""

    default_base = None

    def __init__(self, base=None, **attributes):
        """Style attributes are as passed as keyword arguments. Supported
        attributes include those defined in the :attr:`attributes` attribute of
        this style class and those defined in style classes this one inherits
        from.

        Optionally, a `base` (:class:`Style`) is passed, where attributes are
        looked up when they have not been specified in this style.
        Alternatively, if `base` is :class:`PARENT_STYLE`, the attribute lookup
        is forwarded to the parent of the element the lookup originates from.
        If `base` is a :class:`str`, it is used to look up the base style in
        the :class:`StyleStore` this style is stored in."""
        self.base = base or self.default_base
        self.name = None
        self.stylesheet = None
        for attribute in attributes:
            if attribute not in self._supported_attributes():
                raise TypeError('%s is not a supported attribute' % attribute)
        super().__init__(attributes)

    def __repr__(self):
        """Return a textual representation of this style."""
        return '{0}({1}) > {2}'.format(self.__class__.__name__, self.name or '',
                                       self.base)

    def __copy__(self):
        copy = self.__class__(base=self.base, **self)
        if self.name is not None:
            copy.name = self.name + ' (copy)'
            copy.stylesheet = self.stylesheet
        return copy

    def __getattr__(self, attribute):
        if attribute in self._supported_attributes():
            return self[attribute]
        else:
            return super().__getattr__(attribute)

    def __getitem__(self, attribute):
        """Return the value of `attribute`.

        If the attribute is not specified in this :class:`Style`, find it in
        this style's base styles (hierarchically), or ultimately raise a
        :class:`DefaultValueException`."""
        try:
            return super().__getitem__(attribute)
        except KeyError:
            if self.base is None:
                raise DefaultStyleException
            elif isinstance(self.base, str):
                raise BaseStyleException(self.base, attribute)
            else:
                return self.base[attribute]

    @classmethod
    def _get_default(cls, attribute):
        """Return the default value for `attribute`.

        If no default is specified in this style, get the default from the
        nearest superclass.
        If `attribute` is not supported, raise a :class:`KeyError`."""
        try:
            for super_cls in cls.__mro__:
                if attribute in super_cls.attributes:
                    return super_cls.attributes[attribute]
        except AttributeError:
            raise KeyError("No attribute '{}' in {}".format(attribute, cls))

    @classmethod
    def _supported_attributes(cls):
        """Return a :class:`set` of the attributes supported by this style
        class."""
        attributes = set()
        try:
            for super_cls in cls.__mro__:
                attributes.update(super_cls.attributes.keys())
        except AttributeError:
            return attributes

    def get_value(self, attribute, document):
        value = self[attribute]
        if isinstance(value, VarBase):
            value = value.get(document)
        return value

class ParentStyle(Style):
    """Special style that delegates attribute lookups by raising a
    :class:`ParentStyleException` on each attempt to access an attribute."""

    def __repr__(self):
        return self.__class__.__name__

    def __getitem__(self, attribute):
        raise ParentStyleException

PARENT_STYLE = ParentStyle()
"""Special style that forwards style lookups to the parent of the
:class:`Styled` from which the lookup originates."""

class Selector(object):
    cls = None

    def __truediv__(self, other):
        try:
            selectors = self.selectors + other.selectors
        except AttributeError:
            assert other == Ellipsis
            selectors = self.selectors + (other, )
        return ContextSelector(*selectors)

    def match(self, styled):
        raise NotImplementedError

class ClassSelectorBase(Selector):
    @property
    def selectors(self):
        return (self, )

    def match(self, styled):
        if not isinstance(styled, self.cls):
            return Specificity(False, False, False)
        class_match = 2 if type(styled) == self.cls else 1
        attributes_result = style_name_result = None
        if self.attributes:
            for attr, value in self.attributes.items():
                if not hasattr(styled, attr) or getattr(styled, attr) != value:
                    attributes_result = False
                    break
            else:
                attributes_result = True
        if self.style_name is not None:
            style_name_result = styled.style == self.style_name
        if False in (attributes_result, style_name_result):
            return Specificity(False, False, False)
        else:
            return Specificity(style_name_result or False,
                               attributes_result or False, class_match)

class ClassSelector(ClassSelectorBase):
    def __init__(self, cls, style_name=None, **attributes):
        super().__init__()
        self.cls = cls
        self.style_name = style_name
        self.attributes = attributes

class ContextSelector(Selector):
    def __init__(self, *selectors):
        super().__init__()
        self.selectors = selectors

    @property
    def cls(self):
        return self.selectors[-1].cls

    def match(self, styled):
        total_score = Specificity(0, 0, 0)
        selectors = reversed(self.selectors)
        selector = next(selectors)
        while True:
            if styled is None:
                return Specificity(0, 0, 0)
            if selector is Ellipsis:
                selector = next(selectors)
                while True:
                    if selector.match(styled):
                        break
                    styled = styled.parent
                    if styled is None:
                        return Specificity(0, 0, 0)
            score = selector.match(styled)
            if not score:
                return Specificity(0, 0, 0)
            total_score += score
            styled = styled.parent
            try:
                selector = next(selectors)
            except StopIteration:
                break
        return total_score

class StyledMeta(type, ClassSelectorBase):
    attributes = None
    style_name = None

    @property
    def cls(cls):
        return cls

    def like(cls, style_name=None, **attributes):
        return ClassSelector(cls, style_name, **attributes)

class Styled(DocumentElement, metaclass=StyledMeta):
    """An element that has a :class:`Style` associated with it."""

    style_class = None
    """The :class:`Style` subclass that corresponds to this :class:`Styled`
    subclass."""

    default_style = None

    def __init__(self, style=None, parent=None):
        """Associates `style` with this element. If `style` is `None`, an empty
        :class:`Style` is create, effectively using the defaults defined for the
        associated :class:`Style` class).
        A `parent` can be passed on object initialization, or later by
        assignment to the `parent` attribute."""
        super().__init__(parent=parent)
        if (isinstance(style, Style)
                and not isinstance(style, (self.style_class, ParentStyle))):
            raise TypeError('the style passed to {} should be of type {} '
                            '(a {} was passed instead)'
                            .format(self.__class__.__name__,
                                    self.style_class.__name__,
                                    style.__class__.__name__))
        self.style = style or self.default_style

    @property
    def path(self):
        parent = self.parent.path + ' > ' if self.parent else ''
        style = '[{}]'.format(self.style) if self.style else ''
        return parent + self.__class__.__name__ + style

    @cached
    def get_style(self, attribute, document=None):
        try:
            return self.get_style_recursive(attribute, document)
        except DefaultStyleException:
            # self.warn('Falling back to default style for ({})'
            #           .format(self.path))
            return self.style_class._get_default(attribute)

    def get_base_style_recursive(self, exception, document):
        try:
            base_style = document.stylesheet[exception.base_name]
            return base_style.get_value(exception.attribute, document)
        except ParentStyleException:
            return self.parent.get_style_recursive(exception.attribute,
                                                   document)
        except BaseStyleException as e:
            return self.get_base_style_recursive(e, document)

    def get_style_recursive(self, attribute, document=None):
        try:
            try:
                style = self._style(document)
                return style.get_value(attribute, document)
            except DefaultStyleException:
                if self.style_class.default_base == PARENT_STYLE:
                    raise ParentStyleException
                raise
        except ParentStyleException:
            return self.parent.get_style_recursive(attribute, document)
        except BaseStyleException as exception:
            return self.get_base_style_recursive(exception, document)

    @cached
    def _style(self, document):
        if isinstance(self.style, Style):
            return self.style
        else:
            return document.stylesheet.find_style(self)
        raise DefaultStyleException

class AmbiguousStyleSheetException(Exception):
    pass

class StyledMatcher(dict):
    def __call__(self, name, selector):
        self[name] = selector

    def __setitem__(self, name, selector):
        assert name not in self
        super().__setitem__(name, selector)

    def best_match(self, styled):
        scores = {}
        for name, selector in self.items():
            score = selector.match(styled)
            if score:
                if score in scores:
                    raise AmbiguousStyleSheetException(name, scores[score])
                scores[score] = name
        try:
            max_score = max(scores)
            return Match(scores[max_score], max_score)
        except ValueError:
            return NO_MATCH

class StyleSheet(OrderedDict):
    """Dictionary storing a set of related :class:`Style`s by name.

    :class:`Style`s stored in a :class:`StyleStore` can refer to their base
    style by name. See :class:`Style`."""

    def __init__(self, name, matcher=None, base=None):
        super().__init__()
        self.name = name
        self.matcher = matcher or base.matcher
        self.base = base
        self.variables = {}

    def __getitem__(self, name):
        try:
            return super().__getitem__(name)
        except KeyError:
            if self.base is not None:
                return self.base[name]
            else:
                raise

    def __setitem__(self, name, style):
        assert name not in self
        style.name = name
        style.stylesheet = self
        super().__setitem__(name, style)

    def __call__(self, name, **kwargs):
        selector = self.matcher[name]
        self[name] = selector.cls.style_class(**kwargs)

    def get_variable(self, name):
        try:
            return self.variables[name]
        except KeyError:
            return self.base.get_variable(name)

    def find_best_match(self, styled):
        try:
            best_match = self.matcher.best_match(styled)
        except AttributeError:
            best_match = NO_MATCH
        if self.base:
            base_best_match = self.base.find_best_match(styled)
            if base_best_match > best_match:
                best_match = base_best_match
        return best_match

    def find_style(self, styled):
        match = self.find_best_match(styled)
        if match:
            # print("({}) matches '{}'".format(styled.path, match.style_name))
            try:
                return self[match.style_name]
            except KeyError:
                print("No style '{}' found in stylesheet"
                      .format(match.style_name))
        raise DefaultStyleException

class VarBase(object):
    def __getattr__(self, name):
        return VarAttribute(self, name)

    def get(self, document):
        raise NotImplementedError

class Var(VarBase):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def get(self, document):
        return document.get_style_var(self.name)

class VarAttribute(VarBase):
    def __init__(self, parent, attribute_name):
        super().__init__()
        self.parent = parent
        self.attribute_name = attribute_name

    def get(self, document):
        return getattr(self.parent.get(document), self.attribute_name)

class Specificity(tuple):
    def __new__(cls, *items):
        return super().__new__(cls, items)

    def __add__(self, other):
        return tuple(a + b for a, b in zip(self, other))

    def __bool__(self):
        return any(self)

class Match(object):
    def __init__(self, style_name, specificity):
        self.style_name = style_name
        self.specificity = specificity

    def __gt__(self, other):
        return self.specificity > other.specificity

    def __bool__(self):
        return bool(self.specificity)

NO_MATCH = Match(None, Specificity(0, 0, 0))
<commit_msg>Fix: Styled(style=PARENT_STYLE) had no effect<commit_after># This file is part of RinohType, the Python document preparation system.
#
# Copyright (c) Brecht Machiels.
#
# Use of this source code is subject to the terms of the GNU Affero General
# Public License v3. See the LICENSE file or http://www.gnu.org/licenses/.

"""
Base classes and exceptions for styled document elements.

* :class:`Style`: Dictionary storing a set of style attributes
* :class:`Styled`: A styled entity, having a :class:`Style` associated with it
* :class:`StyleStore`: Dictionary storing a set of related `Style`s by name
* :const:`PARENT_STYLE`: Special style that forwards style lookups to the parent
                        :class:`Styled`
* :exc:`ParentStyleException`: Thrown when style attribute lookup needs to be
                               delegated to the parent :class:`Styled`
"""

from collections import OrderedDict

from .element import DocumentElement
from .util import cached

__all__ = ['Style', 'Styled', 'Var',
           'StyledMatcher', 'StyleSheet', 'ClassSelector', 'ContextSelector',
           'PARENT_STYLE', 'StyleException']

class StyleException(Exception):
    """Style lookup requires special handling."""

class ParentStyleException(StyleException):
    """Style attribute not found. Consult the parent :class:`Styled`."""

class BaseStyleException(StyleException):
    """The attribute is not specified in this :class:`Style`. Find the attribute
    in a base style."""

    def __init__(self, base_name, attribute):
        self.base_name = base_name
        self.attribute = attribute

class DefaultStyleException(StyleException):
    """The attribute is not specified in this :class:`Style` or any of its base
    styles. Return the default value for the attribute."""

class Style(dict):
    """"Dictionary storing style attributes.

    Attrributes can also be accessed as attributes."""

    attributes = {}
    """Dictionary holding the supported style attributes for this :class:`Style`
    class (keys) and their default values (values)"""

    default_base = None

    def __init__(self, base=None, **attributes):
        """Style attributes are as passed as keyword arguments. Supported
        attributes include those defined in the :attr:`attributes` attribute of
        this style class and those defined in style classes this one inherits
        from.

        Optionally, a `base` (:class:`Style`) is passed, where attributes are
        looked up when they have not been specified in this style.
        Alternatively, if `base` is :class:`PARENT_STYLE`, the attribute lookup
        is forwarded to the parent of the element the lookup originates from.
        If `base` is a :class:`str`, it is used to look up the base style in
        the :class:`StyleStore` this style is stored in."""
        self.base = base or self.default_base
        self.name = None
        self.stylesheet = None
        for attribute in attributes:
            if attribute not in self._supported_attributes():
                raise TypeError('%s is not a supported attribute' % attribute)
        super().__init__(attributes)

    def __repr__(self):
        """Return a textual representation of this style."""
        return '{0}({1}) > {2}'.format(self.__class__.__name__, self.name or '',
                                       self.base)

    def __copy__(self):
        copy = self.__class__(base=self.base, **self)
        if self.name is not None:
            copy.name = self.name + ' (copy)'
            copy.stylesheet = self.stylesheet
        return copy

    def __getattr__(self, attribute):
        if attribute in self._supported_attributes():
            return self[attribute]
        else:
            return super().__getattr__(attribute)

    def __getitem__(self, attribute):
        """Return the value of `attribute`.

        If the attribute is not specified in this :class:`Style`, find it in
        this style's base styles (hierarchically), or ultimately raise a
        :class:`DefaultValueException`."""
        try:
            return super().__getitem__(attribute)
        except KeyError:
            if self.base is None:
                raise DefaultStyleException
            elif isinstance(self.base, str):
                raise BaseStyleException(self.base, attribute)
            else:
                return self.base[attribute]

    @classmethod
    def _get_default(cls, attribute):
        """Return the default value for `attribute`.

        If no default is specified in this style, get the default from the
        nearest superclass.
        If `attribute` is not supported, raise a :class:`KeyError`."""
        try:
            for super_cls in cls.__mro__:
                if attribute in super_cls.attributes:
                    return super_cls.attributes[attribute]
        except AttributeError:
            raise KeyError("No attribute '{}' in {}".format(attribute, cls))

    @classmethod
    def _supported_attributes(cls):
        """Return a :class:`set` of the attributes supported by this style
        class."""
        attributes = set()
        try:
            for super_cls in cls.__mro__:
                attributes.update(super_cls.attributes.keys())
        except AttributeError:
            return attributes

    def get_value(self, attribute, document):
        value = self[attribute]
        if isinstance(value, VarBase):
            value = value.get(document)
        return value

class ParentStyle(Style):
    """Special style that delegates attribute lookups by raising a
    :class:`ParentStyleException` on each attempt to access an attribute."""

    def __repr__(self):
        return self.__class__.__name__

    def __getitem__(self, attribute):
        raise ParentStyleException

    def __bool__(self):
        return True

PARENT_STYLE = ParentStyle()
"""Special style that forwards style lookups to the parent of the
:class:`Styled` from which the lookup originates."""

class Selector(object):
    cls = None

    def __truediv__(self, other):
        try:
            selectors = self.selectors + other.selectors
        except AttributeError:
            assert other == Ellipsis
            selectors = self.selectors + (other, )
        return ContextSelector(*selectors)

    def match(self, styled):
        raise NotImplementedError

class ClassSelectorBase(Selector):
    @property
    def selectors(self):
        return (self, )

    def match(self, styled):
        if not isinstance(styled, self.cls):
            return Specificity(False, False, False)
        class_match = 2 if type(styled) == self.cls else 1
        attributes_result = style_name_result = None
        if self.attributes:
            for attr, value in self.attributes.items():
                if not hasattr(styled, attr) or getattr(styled, attr) != value:
                    attributes_result = False
                    break
            else:
                attributes_result = True
        if self.style_name is not None:
            style_name_result = styled.style == self.style_name
        if False in (attributes_result, style_name_result):
            return Specificity(False, False, False)
        else:
            return Specificity(style_name_result or False,
                               attributes_result or False, class_match)

class ClassSelector(ClassSelectorBase):
    def __init__(self, cls, style_name=None, **attributes):
        super().__init__()
        self.cls = cls
        self.style_name = style_name
        self.attributes = attributes

class ContextSelector(Selector):
    def __init__(self, *selectors):
        super().__init__()
        self.selectors = selectors

    @property
    def cls(self):
        return self.selectors[-1].cls

    def match(self, styled):
        total_score = Specificity(0, 0, 0)
        selectors = reversed(self.selectors)
        selector = next(selectors)
        while True:
            if styled is None:
                return Specificity(0, 0, 0)
            if selector is Ellipsis:
                selector = next(selectors)
                while True:
                    if selector.match(styled):
                        break
                    styled = styled.parent
                    if styled is None:
                        return Specificity(0, 0, 0)
            score = selector.match(styled)
            if not score:
                return Specificity(0, 0, 0)
            total_score += score
            styled = styled.parent
            try:
                selector = next(selectors)
            except StopIteration:
                break
        return total_score

class StyledMeta(type, ClassSelectorBase):
    attributes = None
    style_name = None

    @property
    def cls(cls):
        return cls

    def like(cls, style_name=None, **attributes):
        return ClassSelector(cls, style_name, **attributes)

class Styled(DocumentElement, metaclass=StyledMeta):
    """An element that has a :class:`Style` associated with it."""

    style_class = None
    """The :class:`Style` subclass that corresponds to this :class:`Styled`
    subclass."""

    default_style = None

    def __init__(self, style=None, parent=None):
        """Associates `style` with this element. If `style` is `None`, an empty
        :class:`Style` is create, effectively using the defaults defined for the
        associated :class:`Style` class).
        A `parent` can be passed on object initialization, or later by
        assignment to the `parent` attribute."""
        super().__init__(parent=parent)
        if (isinstance(style, Style)
                and not isinstance(style, (self.style_class, ParentStyle))):
            raise TypeError('the style passed to {} should be of type {} '
                            '(a {} was passed instead)'
                            .format(self.__class__.__name__,
                                    self.style_class.__name__,
                                    style.__class__.__name__))
        self.style = style or self.default_style

    @property
    def path(self):
        parent = self.parent.path + ' > ' if self.parent else ''
        style = '[{}]'.format(self.style) if self.style else ''
        return parent + self.__class__.__name__ + style

    @cached
    def get_style(self, attribute, document=None):
        try:
            return self.get_style_recursive(attribute, document)
        except DefaultStyleException:
            # self.warn('Falling back to default style for ({})'
            #           .format(self.path))
            return self.style_class._get_default(attribute)

    def get_base_style_recursive(self, exception, document):
        try:
            base_style = document.stylesheet[exception.base_name]
            return base_style.get_value(exception.attribute, document)
        except ParentStyleException:
            return self.parent.get_style_recursive(exception.attribute,
                                                   document)
        except BaseStyleException as e:
            return self.get_base_style_recursive(e, document)

    def get_style_recursive(self, attribute, document=None):
        try:
            try:
                style = self._style(document)
                return style.get_value(attribute, document)
            except DefaultStyleException:
                if self.style_class.default_base == PARENT_STYLE:
                    raise ParentStyleException
                raise
        except ParentStyleException:
            return self.parent.get_style_recursive(attribute, document)
        except BaseStyleException as exception:
            return self.get_base_style_recursive(exception, document)

    @cached
    def _style(self, document):
        if isinstance(self.style, Style):
            return self.style
        else:
            return document.stylesheet.find_style(self)
        raise DefaultStyleException

class AmbiguousStyleSheetException(Exception):
    pass

class StyledMatcher(dict):
    def __call__(self, name, selector):
        self[name] = selector

    def __setitem__(self, name, selector):
        assert name not in self
        super().__setitem__(name, selector)

    def best_match(self, styled):
        scores = {}
        for name, selector in self.items():
            score = selector.match(styled)
            if score:
                if score in scores:
                    raise AmbiguousStyleSheetException(name, scores[score])
                scores[score] = name
        try:
            max_score = max(scores)
            return Match(scores[max_score], max_score)
        except ValueError:
            return NO_MATCH

class StyleSheet(OrderedDict):
    """Dictionary storing a set of related :class:`Style`s by name.

    :class:`Style`s stored in a :class:`StyleStore` can refer to their base
    style by name. See :class:`Style`."""

    def __init__(self, name, matcher=None, base=None):
        super().__init__()
        self.name = name
        self.matcher = matcher or base.matcher
        self.base = base
        self.variables = {}

    def __getitem__(self, name):
        try:
            return super().__getitem__(name)
        except KeyError:
            if self.base is not None:
                return self.base[name]
            else:
                raise

    def __setitem__(self, name, style):
        assert name not in self
        style.name = name
        style.stylesheet = self
        super().__setitem__(name, style)

    def __call__(self, name, **kwargs):
        selector = self.matcher[name]
        self[name] = selector.cls.style_class(**kwargs)

    def get_variable(self, name):
        try:
            return self.variables[name]
        except KeyError:
            return self.base.get_variable(name)

    def find_best_match(self, styled):
        try:
            best_match = self.matcher.best_match(styled)
        except AttributeError:
            best_match = NO_MATCH
        if self.base:
            base_best_match = self.base.find_best_match(styled)
            if base_best_match > best_match:
                best_match = base_best_match
        return best_match

    def find_style(self, styled):
        match = self.find_best_match(styled)
        if match:
            # print("({}) matches '{}'".format(styled.path, match.style_name))
            try:
                return self[match.style_name]
            except KeyError:
                print("No style '{}' found in stylesheet"
                      .format(match.style_name))
        raise DefaultStyleException

class VarBase(object):
    def __getattr__(self, name):
        return VarAttribute(self, name)

    def get(self, document):
        raise NotImplementedError

class Var(VarBase):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def get(self, document):
        return document.get_style_var(self.name)

class VarAttribute(VarBase):
    def __init__(self, parent, attribute_name):
        super().__init__()
        self.parent = parent
        self.attribute_name = attribute_name

    def get(self, document):
        return getattr(self.parent.get(document), self.attribute_name)

class Specificity(tuple):
    def __new__(cls, *items):
        return super().__new__(cls, items)

    def __add__(self, other):
        return tuple(a + b for a, b in zip(self, other))

    def __bool__(self):
        return any(self)

class Match(object):
    def __init__(self, style_name, specificity):
        self.style_name = style_name
        self.specificity = specificity

    def __gt__(self, other):
        return self.specificity > other.specificity

    def __bool__(self):
        return bool(self.specificity)

NO_MATCH = Match(None, Specificity(0, 0, 0))
<|endoftext|>


<commit_before>
from __future__ import absolute_import
import os
from picamera import PiCamera
from time import sleep
from celery import Celery
from django.apps import AppConfig
from django.conf import settings

if not settings.configured:
    # set the default Django settings module for the 'celery' program.
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local')  # pragma: no cover

app = Celery('pi_security_cam')

class CeleryConfig(AppConfig):
    name = 'pi_security_cam.taskapp'
    verbose_name = 'Celery Config'

    def ready(self):
        # Using a string here means the worker will not have to
        # pickle the object when using Windows.
        app.config_from_object('django.conf:settings')
        app.autodiscover_tasks(lambda: settings.INSTALLED_APPS, force=True)

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))  # pragma: no cover

@app.task(bind=True)
def take_photo(self, photo_path=None):
    if not photo_path:
        photo_path = '/tmp/photo.jpg'

    camera = PiCamera()
    camera.start_preview()
    sleep(2)
    camera.capture(photo_path)
    camera.stop_preview()
    sleep(2)<commit_msg>Rotated the image 180 degrees.<commit_after>
from __future__ import absolute_import
import os
from picamera import PiCamera
from time import sleep
from celery import Celery
from django.apps import AppConfig
from django.conf import settings
from PIL import Image as PilImage

if not settings.configured:
    # set the default Django settings module for the 'celery' program.
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local')  # pragma: no cover

app = Celery('pi_security_cam')

class CeleryConfig(AppConfig):
    name = 'pi_security_cam.taskapp'
    verbose_name = 'Celery Config'

    def ready(self):
        # Using a string here means the worker will not have to
        # pickle the object when using Windows.
        app.config_from_object('django.conf:settings')
        app.autodiscover_tasks(lambda: settings.INSTALLED_APPS, force=True)

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))  # pragma: no cover

@app.task(bind=True)
def take_photo(self, photo_path=None):
    if not photo_path:
        photo_path = '/tmp/photo.jpg'

    camera = PiCamera()
    camera.start_preview()
    sleep(2)
    camera.capture(photo_path)
    camera.stop_preview()
    im = PilImage.open(photo_path)
    rotated_image = im.rotate(180)
    rotated_image.save(photo_path)
    sleep(2)<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function, unicode_literals
import numpy as np
import gdspy
import uuid
import picwriter.toolkit as tk

class WaveguideTemplate:
    """ Standard template for waveguides (as well as other structures) that contains some standard information about the fabrication process and waveguides.

        Keyword Args:
           * **bend_radius** (float): Radius of curvature for waveguide bends (circular)
           * **wg_width** (float): Width of the waveguide as shown on the mask
           * **clad_width** (float): Width of the cladding (region next to waveguide, mainly used for positive-type photoresists + etching, or negative-type and liftoff)
           * **resist** (string): Must be either '+' or '-'.  Specifies the type of photoresist used
           * **fab** (string): If 'ETCH', then keeps resist as is, otherwise changes it from '+' to '-' (or vice versa).  This is mainly used to reverse the type of mask used if the fabrication type is 'LIFTOFF'
           * **wg_layer** (int): Layer type used for waveguides
           * **wg_datatype** (int): Data type used for waveguides
           * **clad_layer** (int): Layer type used for cladding
           * **clad_datatype** (int): Data type used for cladding

    """
    def __init__(self, bend_radius=50.0, wg_width=2.0, clad_width=10.0,
                 resist='+', fab='ETCH', wg_layer=1, wg_datatype=0, clad_layer=2, clad_datatype=0):
        self.wg_width = wg_width
        self.bend_radius = bend_radius
        self.clad_width = clad_width
        if resist != '+' and resist != '-':
            raise ValueError("Warning, invalid input for type resist in "
                             "WaveguideTemplate")
        if fab=='ETCH':
            self.resist = resist #default state assumes 'etching'
        else: #reverse waveguide type if liftoff or something else
            self.resist = '+' if resist=='-' else '-'

        self.wg_layer = wg_layer
        self.wg_datatype = wg_datatype
        self.clad_layer = clad_layer
        self.clad_datatype = clad_datatype

class Waveguide(gdspy.Cell):
    """ Standard Waveguide Cell class (subclass of gdspy.Cell).

        Args:
           * **trace** (list):  List of coordinates used to generate the waveguide (such as '[(x1,y1), (x2,y2), ...]').
           * **wgt** (WaveguideTemplate):  WaveguideTemplate object

        Members:
           * **portlist** (dict): Dictionary with the relevant port information

        Portlist format:
           * portlist['input'] = {'port': (x1,y1), 'direction': 'dir1'}
           * portlist['output'] = {'port': (x2, y2), 'direction': 'dir2'}

        Where in the above (x1,y1) are the first elements of 'trace', (x2, y2) are the last elements of 'trace', and 'dir1', 'dir2' are of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, *or* an angle in *radians*. 'Direction' points *towards* the component that the waveguide will connect to.

    """
    def __init__(self, trace, wgt):
        gdspy.Cell.__init__(self,"Waveguide--"+str(uuid.uuid4()))

        self.portlist = {}

        self.trace = trace
        self.wgt = wgt
        self.resist = wgt.resist
        self.wg_spec = {'layer': wgt.wg_layer, 'datatype': wgt.wg_datatype}
        self.clad_spec = {'layer': wgt.clad_layer, 'datatype': wgt.clad_datatype} #Used for 'xor' operation

        self.type_check_trace()
        self.build_cell()
        self.build_ports()

    def type_check_trace(self):
        trace = []
        """ Round each trace value to the nearest 1e-6 -- prevents
        some typechecking errors
        """
        for t in self.trace:
            trace.append((round(t[0], 6), round(t[1], 5)))
        self.trace = trace

        """ Type-check trace ¯\_(ツ)_/¯
        """
        prev_dx, prev_dy = 1,1 #initialize to arbitrary value
        for i in range(len(self.trace)-1):
            dx = abs(self.trace[i+1][0]-self.trace[i][0])+1E-10
            dy = abs(self.trace[i+1][1]-self.trace[i][1])+1E-10
            if ((prev_dx <= 1e-6 and dx<=1e-6) or (prev_dy <= 1e-6 and dy<=1e-6)):
                raise ValueError("Warning! Unnecessary waypoint specified.  All"
                                 " waypoints must specify a valid bend")
            prev_dx, prev_dy = dx, dy

    def build_cell(self):
        # Sequentially build all the geometric shapes using gdspy path functions
        # for waveguide, then add it to the Cell
        br = self.wgt.bend_radius

        if len(self.trace)==2:
            path = gdspy.Path(self.wgt.wg_width, self.trace[0])
            path.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **self.wg_spec)
            path2 = gdspy.Path(self.wgt.wg_width+2*self.wgt.clad_width, self.trace[0])
            path2.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **self.clad_spec)

        else:
            path = gdspy.Path(self.wgt.wg_width, self.trace[0])
            path2 = gdspy.Path(self.wgt.wg_width+2*self.wgt.clad_width, self.trace[0])

            prev_dl = 0.0
            for i in range(len(self.trace)-2):
                start_angle = tk.get_exact_angle(self.trace[i], self.trace[i+1])
                next_angle = tk.get_exact_angle(self.trace[i+1], self.trace[i+2])

                #dl is the amount of distance that is taken *off* the waveguide from the curved section
                dl = abs(br*np.tan((next_angle-start_angle)/2.0))
                if (dl+prev_dl) > tk.dist(self.trace[i], self.trace[i+1]):
                    raise ValueError("Warning! The waypoints "+str(self.trace[i])+" and "+str(self.trace[i+1])+" are too close to accommodate "
                                     " the necessary bend-radius of "+str(br)+", the points were closer than "+str(dl+prev_dl))

                path.segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
                             direction=start_angle, **self.wg_spec)
                path2.segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
                             direction=start_angle, **self.clad_spec)

                # The following makes sure the turn-by angle is *always* between -pi and +pi
                turnby = (next_angle - start_angle)%(2*np.pi)
                turnby = turnby-2*np.pi if turnby > np.pi else turnby

                path.turn(br, turnby, number_of_points=0.1, **self.wg_spec)
                path2.turn(br, turnby, number_of_points=0.1, **self.clad_spec)
                prev_dl = dl

            path.segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
                         direction=next_angle, **self.wg_spec)
            path2.segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
                         direction=next_angle, **self.clad_spec)

        self.add(path)
        self.add(path2)

    def build_ports(self):
        # Portlist format:
        # example: example:  {'port':(x_position, y_position), 'direction': 'NORTH'}
        self.portlist["input"] = {'port':(self.trace[0][0], self.trace[0][1]),
                                  'direction': tk.get_exact_angle(self.trace[1], self.trace[0])}
        self.portlist["output"] = {'port':(self.trace[-1][0], self.trace[-1][1]),
                                   'direction':tk.get_exact_angle(self.trace[-2], self.trace[-1])}

if __name__ == "__main__":
    top = gdspy.Cell("top")
    wgt = WaveguideTemplate(bend_radius=50, resist='+', fab="ETCH")

    wg1=Waveguide([(0,0), (150,0), (150,100), (250,100),(350,0), (200,-150)], wgt)
    tk.add(top, wg1)
    print(wg1.portlist)

    gdspy.LayoutViewer()
    # gdspy.write_gds('waveguide.gds', unit=1.0e-6, precision=1.0e-9)
<commit_msg>Added slot waveguide capabilities to WaveguideTemplate<commit_after># -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function, unicode_literals
import numpy as np
import gdspy
import uuid
import picwriter.toolkit as tk

class WaveguideTemplate:
    """ Standard template for waveguides (as well as other structures) that contains some standard information about the fabrication process and waveguides.

        Keyword Args:
           * **wg_type** (string): Type of waveguide used.  Options are "strip" and "slot".  Defaults to "strip".
           * **bend_radius** (float): Radius of curvature for waveguide bends (circular).  Defaults to 50.
           * **wg_width** (float): Width of the waveguide as shown on the mask.  Defaults to 2.
           * **slot** (float): Size of the waveguide slot region.  This is only used if `wg_type`=`'slot'`.  Defaults to 0.1.
           * **clad_width** (float): Width of the cladding (region next to waveguide, mainly used for positive-type photoresists + etching, or negative-type and liftoff).  Defaults to 10.
           * **resist** (string): Must be either '+' or '-'.  Specifies the type of photoresist used.  Defaults to '+'
           * **fab** (string): If 'ETCH', then keeps resist as is, otherwise changes it from '+' to '-' (or vice versa).  This is mainly used to reverse the type of mask used if the fabrication type is 'LIFTOFF'.   Defaults to "ETCH".
           * **wg_layer** (int): Layer type used for waveguides.  Defaults to 1.
           * **wg_datatype** (int): Data type used for waveguides.  Defaults to 0.
           * **clad_layer** (int): Layer type used for cladding.  Defaults to 2.
           * **clad_datatype** (int): Data type used for cladding.  Defaults to 0.

    """
    def __init__(self, wg_type='strip', bend_radius=50.0, wg_width=2.0, clad_width=10.0,
                 resist='+', fab='ETCH', slot=0.1,
                 wg_layer=1, wg_datatype=0, clad_layer=2, clad_datatype=0):
        self.wg_width = wg_width
        if wg_type in ['strip', 'slot']:
            self.wg_type = wg_type
        else:
            raise ValueError("Warning, invalid input for kwarg wg_type.")
        if self.wg_type =='slot':
            self.slot = slot
            self.rail = (self.wg_width - self.slot)/2.0
            self.rail_dist = self.wg_width -self.rail
        self.bend_radius = bend_radius
        self.clad_width = clad_width
        if resist != '+' and resist != '-':
            raise ValueError("Warning, invalid input for kwarg resist in "
                             "WaveguideTemplate")
        if fab=='ETCH':
            self.resist = resist #default state assumes 'etching'
        else: #reverse waveguide type if liftoff or something else
            self.resist = '+' if resist=='-' else '-'

        self.wg_layer = wg_layer
        self.wg_datatype = wg_datatype
        self.clad_layer = clad_layer
        self.clad_datatype = clad_datatype

class Waveguide(gdspy.Cell):
    """ Standard Waveguide Cell class (subclass of gdspy.Cell).

        Args:
           * **trace** (list):  List of coordinates used to generate the waveguide (such as '[(x1,y1), (x2,y2), ...]').
           * **wgt** (WaveguideTemplate):  WaveguideTemplate object

        Members:
           * **portlist** (dict): Dictionary with the relevant port information

        Portlist format:
           * portlist['input'] = {'port': (x1,y1), 'direction': 'dir1'}
           * portlist['output'] = {'port': (x2, y2), 'direction': 'dir2'}

        Where in the above (x1,y1) are the first elements of 'trace', (x2, y2) are the last elements of 'trace', and 'dir1', 'dir2' are of type `'NORTH'`, `'WEST'`, `'SOUTH'`, `'EAST'`, *or* an angle in *radians*. 'Direction' points *towards* the component that the waveguide will connect to.

    """
    def __init__(self, trace, wgt):
        gdspy.Cell.__init__(self,"Waveguide--"+str(uuid.uuid4()))

        self.portlist = {}

        self.trace = trace
        self.wgt = wgt
        self.resist = wgt.resist
        self.wg_spec = {'layer': wgt.wg_layer, 'datatype': wgt.wg_datatype}
        self.clad_spec = {'layer': wgt.clad_layer, 'datatype': wgt.clad_datatype} #Used for 'xor' operation

        self.type_check_trace()
        self.build_cell()
        self.build_ports()

    def type_check_trace(self):
        trace = []
        """ Round each trace value to the nearest 1e-6 -- prevents
        some typechecking errors
        """
        for t in self.trace:
            trace.append((round(t[0], 6), round(t[1], 5)))
        self.trace = trace

        """ Type-check trace ¯\_(ツ)_/¯
        """
        prev_dx, prev_dy = 1,1 #initialize to arbitrary value
        for i in range(len(self.trace)-1):
            dx = abs(self.trace[i+1][0]-self.trace[i][0])+1E-10
            dy = abs(self.trace[i+1][1]-self.trace[i][1])+1E-10
            if ((prev_dx <= 1e-6 and dx<=1e-6) or (prev_dy <= 1e-6 and dy<=1e-6)):
                raise ValueError("Warning! Unnecessary waypoint specified.  All"
                                 " waypoints must specify a valid bend")
            prev_dx, prev_dy = dx, dy

    def build_cell(self):
        # Sequentially build all the geometric shapes using gdspy path functions
        # for waveguide, then add it to the Cell
        br = self.wgt.bend_radius

        if len(self.trace)==2:
            if self.wgt.wg_type=='strip':
                path = gdspy.Path(self.wgt.wg_width, self.trace[0])
                path.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **self.wg_spec)
            elif self.wgt.wg_type=='slot':
                path = gdspy.Path(self.wgt.rail, self.trace[0], number_of_paths=2, distance=self.wgt.rail_dist)
                path.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **self.wg_spec)
            path2 = gdspy.Path(self.wgt.wg_width+2*self.wgt.clad_width, self.trace[0])
            path2.segment(tk.dist(self.trace[0], self.trace[1]), direction=tk.get_exact_angle(self.trace[0], self.trace[1]), **self.clad_spec)

        else:
            if self.wgt.wg_type=='strip':
                path = gdspy.Path(self.wgt.wg_width, self.trace[0])
            elif self.wgt.wg_type=='slot':
                path = gdspy.Path(self.wgt.rail, self.trace[0], number_of_paths=2, distance=self.wgt.rail_dist)
            path2 = gdspy.Path(self.wgt.wg_width+2*self.wgt.clad_width, self.trace[0])

            prev_dl = 0.0
            for i in range(len(self.trace)-2):
                start_angle = tk.get_exact_angle(self.trace[i], self.trace[i+1])
                next_angle = tk.get_exact_angle(self.trace[i+1], self.trace[i+2])

                #dl is the amount of distance that is taken *off* the waveguide from the curved section
                dl = abs(br*np.tan((next_angle-start_angle)/2.0))
                if (dl+prev_dl) > tk.dist(self.trace[i], self.trace[i+1]):
                    raise ValueError("Warning! The waypoints "+str(self.trace[i])+" and "+str(self.trace[i+1])+" are too close to accommodate "
                                     " the necessary bend-radius of "+str(br)+", the points were closer than "+str(dl+prev_dl))

                path.segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
                             direction=start_angle, **self.wg_spec)
                path2.segment(tk.dist(self.trace[i], self.trace[i+1])-dl-prev_dl,
                             direction=start_angle, **self.clad_spec)

                # The following makes sure the turn-by angle is *always* between -pi and +pi
                turnby = (next_angle - start_angle)%(2*np.pi)
                turnby = turnby-2*np.pi if turnby > np.pi else turnby

                path.turn(br, turnby, number_of_points=0.1, **self.wg_spec)
                path2.turn(br, turnby, number_of_points=0.1, **self.clad_spec)
                prev_dl = dl

            path.segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
                         direction=next_angle, **self.wg_spec)
            path2.segment(tk.dist(self.trace[-2], self.trace[-1])-prev_dl,
                         direction=next_angle, **self.clad_spec)

        self.add(path)
        self.add(path2)

    def build_ports(self):
        # Portlist format:
        # example: example:  {'port':(x_position, y_position), 'direction': 'NORTH'}
        self.portlist["input"] = {'port':(self.trace[0][0], self.trace[0][1]),
                                  'direction': tk.get_exact_angle(self.trace[1], self.trace[0])}
        self.portlist["output"] = {'port':(self.trace[-1][0], self.trace[-1][1]),
                                   'direction':tk.get_exact_angle(self.trace[-2], self.trace[-1])}

if __name__ == "__main__":
    top = gdspy.Cell("top")
    wgt = WaveguideTemplate(wg_type='slot', wg_width=1.0, bend_radius=50, resist='+', fab="ETCH")

    wg1=Waveguide([(0,0), (150,0), (150,100), (250,100),(350,0), (200,-150)], wgt)
    tk.add(top, wg1)
    print(wg1.portlist)

    gdspy.LayoutViewer()
    # gdspy.write_gds('waveguide.gds', unit=1.0e-6, precision=1.0e-9)
<|endoftext|>


<commit_before>import datetime
import importlib
import logging
import os
import platform
import socket
import sys

import pikka_bird_collector
import pikka_bird_collector.config

COLLECTORS = [
    'mongodb',
    'mysql',
    'postgresql',
    'rabbitmq',
    'redis',
    'system']

COLLECTORS_MODULE_P = 'pikka_bird_collector.collectors.'

for c in COLLECTORS:
   importlib.import_module(COLLECTORS_MODULE_P + c)

class Collector():
    """
        Main collector, which calls individual service Collectors and merges the
        results. The environment, containing such things as PID, hostname, and
        kernel version, are passed to each collector.
        """

    def __init__(self, config=None, logger=None):
        """
            PARAMETERS:
                path : string
                    filename of config to parse
                logger : logger
                    logger
            """
        self.config = pikka_bird_collector.config.Config(config)
        self.logger = logger or logging.getLogger()

        self.__set_environment()

    def collect(self):
        """
            Collect metrics for all invididual service Collectors, returning the
            reports in a format suitable for sending to the Server, complete
            with dates converted to strings. All times are in UTC, always.

            RETURN:
                : dict
                    collected reports, ready for sending to the Server
            """

        reports = {}

        collecting_at = datetime.datetime.utcnow()
        self.logger.info("COLLECTING")

        for c in COLLECTORS:
            klass = getattr(sys.modules[COLLECTORS_MODULE_P + c], c.title())

            service   = klass.service()
            collector = klass(self.environment, self.config.settings(service))

            if collector.enabled():
                self.logger.info("COLLECTING %s" % service)

                reports[service] = collector.collect()

                self.logger.debug("METRICS %s %s" % (service, reports[service]))
            else:
                self.logger.debug("SKIPPED %s" % service)

        collected_at = datetime.datetime.utcnow()
        self.logger.info("COLLECTED (%d s)" % (collected_at - collecting_at).seconds)

        return {
            'collecting_at': collecting_at.isoformat(),
            'collected_at':  collected_at.isoformat(),
            'environment':   self.environment,
            'reports':       reports}

    def __set_environment(self):
        self.environment = {
            'hostname': socket.gethostname(),
            'pid':      os.getpid(),
            'version':  pikka_bird_collector.__version__,
            'platform': {
                'system':  platform.system(),
                'release': platform.release(),
                'version': platform.version()}}

        self.logger.info("ENVIRONMENT %s" % self.environment)
<commit_msg>Refactor tweak collectors order, making System first.<commit_after>import datetime
import importlib
import logging
import os
import platform
import socket
import sys

import pikka_bird_collector
import pikka_bird_collector.config

COLLECTORS = [
    'system', # keep first; sort rest
    'mongodb',
    'mysql',
    'postgresql',
    'rabbitmq',
    'redis']

COLLECTORS_MODULE_P = 'pikka_bird_collector.collectors.'

for c in COLLECTORS:
   importlib.import_module(COLLECTORS_MODULE_P + c)

class Collector():
    """
        Main collector, which calls individual service Collectors and merges the
        results. The environment, containing such things as PID, hostname, and
        kernel version, are passed to each collector.
        """

    def __init__(self, config=None, logger=None):
        """
            PARAMETERS:
                path : string
                    filename of config to parse
                logger : logger
                    logger
            """
        self.config = pikka_bird_collector.config.Config(config)
        self.logger = logger or logging.getLogger()

        self.__set_environment()

    def collect(self):
        """
            Collect metrics for all invididual service Collectors, returning the
            reports in a format suitable for sending to the Server, complete
            with dates converted to strings. All times are in UTC, always.

            RETURN:
                : dict
                    collected reports, ready for sending to the Server
            """

        reports = {}

        collecting_at = datetime.datetime.utcnow()
        self.logger.info("COLLECTING")

        for c in COLLECTORS:
            klass = getattr(sys.modules[COLLECTORS_MODULE_P + c], c.title())

            service   = klass.service()
            collector = klass(self.environment, self.config.settings(service))

            if collector.enabled():
                self.logger.info("COLLECTING %s" % service)

                reports[service] = collector.collect()

                self.logger.debug("METRICS %s %s" % (service, reports[service]))
            else:
                self.logger.debug("SKIPPED %s" % service)

        collected_at = datetime.datetime.utcnow()
        self.logger.info("COLLECTED (%d s)" % (collected_at - collecting_at).seconds)

        return {
            'collecting_at': collecting_at.isoformat(),
            'collected_at':  collected_at.isoformat(),
            'environment':   self.environment,
            'reports':       reports}

    def __set_environment(self):
        self.environment = {
            'hostname': socket.gethostname(),
            'pid':      os.getpid(),
            'version':  pikka_bird_collector.__version__,
            'platform': {
                'system':  platform.system(),
                'release': platform.release(),
                'version': platform.version()}}

        self.logger.info("ENVIRONMENT %s" % self.environment)
<|endoftext|>


<commit_before>from pathlib import Path

class RequestIdStore:

    from abc import abstractmethod

    @abstractmethod
    def nextId(self, clientId, signerId) -> int:
        pass

    @abstractmethod
    def currentId(self, clientId, signerId) -> int:
        pass

class FileRequestIdStore(RequestIdStore):

    def __init__(self, filePath):
        self.isOpen = False
        self.storeFilePath = filePath
        self._storage = {}

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    def open(self):
        if self.isOpen:
            raise RuntimeError("Storage is already open!")
        self._loadStorage()
        self.isOpen = True

    def close(self):
        self.flush()
        self._storage = {}
        self.isOpen = False

    def flush(self):
        if self.isOpen:
            self._saveStorage()

    def _loadStorage(self):
        storageFile = Path(self.storeFilePath)
        if storageFile.exists():
            with storageFile.open() as file:
                for line in file:
                    (clientId, signerId, lastRequest) = line.split(";")
                    self._storage[clientId, signerId] = int(lastRequest)

    def _saveStorage(self):
        with open(self.storeFilePath, 'w') as file:
            for (clientId, signerId), lastRequest in self._storage.items():
                file.write("{};{};{}\n".format(clientId, signerId, lastRequest))

    def nextId(self, clientId, signerId) -> int:
        lastRequestId = self._storage.get((clientId, signerId))
        nextId = lastRequestId + 1 if lastRequestId is not None else 0
        self._storage[clientId, signerId] = nextId
        return nextId

    def currentId(self, clientId, signerId) -> int:
        return self._storage.get((clientId, signerId))<commit_msg>set configurable delimiter for values<commit_after>from pathlib import Path

class RequestIdStore:

    from abc import abstractmethod

    @abstractmethod
    def nextId(self, clientId, signerId) -> int:
        pass

    @abstractmethod
    def currentId(self, clientId, signerId) -> int:
        pass

class FileRequestIdStore(RequestIdStore):

    def __init__(self, filePath, valueSeparator = '|'):
        self.isOpen = False
        self.storeFilePath = filePath
        self._storage = {}
        self._valueSeparator = valueSeparator

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    def open(self):
        if self.isOpen:
            raise RuntimeError("Storage is already open!")
        self._loadStorage()
        self.isOpen = True

    def close(self):
        self.flush()
        self._storage = {}
        self.isOpen = False

    def flush(self):
        if self.isOpen:
            self._saveStorage()

    def _loadStorage(self):
        storageFile = Path(self.storeFilePath)
        if storageFile.exists():
            with storageFile.open() as file:
                for line in file:
                    (clientId, signerId, lastRequest) = \
                        line.split(self._valueSeparator)
                    self._storage[clientId, signerId] = int(lastRequest)

    def _saveStorage(self):
        with open(self.storeFilePath, 'w') as file:
            for (clientId, signerId), lastRequest in self._storage.items():
                values = [str(x) for x in [clientId, signerId, lastRequest]]
                line = self._valueSeparator.join(values)
                file.write(line + "\n")

    def nextId(self, clientId, signerId) -> int:
        lastRequestId = self._storage.get((clientId, signerId))
        nextId = lastRequestId + 1 if lastRequestId is not None else 0
        self._storage[clientId, signerId] = nextId
        return nextId

    def currentId(self, clientId, signerId) -> int:
        return self._storage.get((clientId, signerId))<|endoftext|>