<commit_before>#!/usr/bin/python

## Some info should go here.

#TODO: add these features:
# [ ] HDMI overscan
# [ ] AirPlay mirroring
# [ ] set individual display settings

## Imports
import argparse
import os
import sys

import Quartz

## Global Variables
attributes = {
    'long_name' : 'Display Manager',
    'name'      : os.path.basename(sys.argv[0]),
    'version'   : '0.3.0'
}

MAX_DISPLAYS = 32

## Struct-like thing for easy display.
class DisplayMode(object):
    def __init__(self, mode):
        self.width      = Quartz.CGDisplayModeGetWidth(mode)
        self.height     = Quartz.CGDisplayModeGetHeight(mode)
        self.pixels     = self.width * self.height
        self.bpp        = get_pixel_depth_from_encoding(Quartz.CGDisplayModeCopyPixelEncoding(mode))
        self.refresh    = Quartz.CGDisplayModeGetRefreshRate(mode)
        self.raw_mode   = mode
    def __str__(self):
        return "{width}x{height}; pixel depth: {bpp}; refresh rate: {refresh}".format(
            width   = self.width,
            height  = self.height,
            bpp     = self.bpp,
            refresh = self.refresh
        )
    def __repr__(self):
        return self.__str__()

## Helper Functions
def get_pixel_depth_from_encoding(encoding):
    if encoding == "PPPPPPPP":
        return 8
    elif encoding == "-RRRRRGGGGGBBBBB":
        return 16
    elif encoding == "--------RRRRRRRRGGGGGGGGBBBBBBBB":
        return 32
    else:
        raise RuntimeError("Unknown pixel encoding: {}".format(encoding))

def get_displays_list():
    (error, online_displays, displays_count) = Quartz.CGGetOnlineDisplayList(MAX_DISPLAYS, None, None)
    if error:
        raise RuntimeError("Unable to get displays list.")
    return online_displays

def get_all_modes_for_display(display):
    modes = [DisplayMode(mode) for mode in Quartz.CGDisplayCopyAllDisplayModes(display, None)]
    modes.sort(key = lambda mode: mode.pixels, reverse = True)
    return modes

def get_current_mode_for_display(display):
    mode = DisplayMode(Quartz.CGDisplayCopyDisplayMode(display))
    return mode

def get_all_current_configurations():
    modes = []
    for display in get_displays_list():
        modes.append( (display, get_current_mode_for_display(display)) )
    return modes

def get_all_modes_for_all_displays():
    modes = []
    for display in get_displays_list():
        modes.append( (display, get_all_modes_for_display(display)) )
    return modes

def get_mode_closest_to_values(modes, width, height, depth, refresh):
    pixels = width * height
    close_matches = []
    for mode in modes:
        if mode.width == width and mode.height == height and mode.depth == depth and mode.refresh == refresh:
            return mode
        if mode.pixels == pixels:
            close_matches.append(mode)
    if len(close_matches) == 0:
        larger = None
        smaller = None
        for mode in modes:
            if mode.pixels > pixels:
                larger = mode
            else:
                smaller = mode
                break
        # Check some edge cases.
        if smaller and not larger:
            # The desired resolution is greater than any available.
            return smaller
        if larger and not smaller:
            # The desired resolution is lesser than any available.
            return larger
        larger_dif = abs(larger.pixels - pixels)
        smaller_dif = abs(smaller.pixels - pixels)
        # Check which size is closer
        if smaller_dif < larger_dif:
            # The smaller size is closer.
            return smaller
        if larger_dif < smaller_dif:
            # The larger size is closer.
            return larger
    if len(close_matches) == 1:
        # We only had one match, so let's use it!
        return close_matches[0]
    if len(close_matches) > 1:
        # We had multiple matches at the same pixel count.
        res_matches = []
        for match in close_matches:
            if match.width == width and match.height == height:
                res_matches.append(match)

# def get_highest_supported_resolution_for_display(display):
#     return get_all_modes_for_display(display)[0]
#
# def get_highest_supported_resolution_for_all_displays():
#     modes = []
#     for display in get_displays_list():
#         modes.append( (display, get_highest_supported_resolution_for_display(display)) )
#     return modes

def sub_set(command, width, height, depth, refresh):
    pass

def sub_show(command, width, height, depth, refresh):
    if command == "all":
        all_modes = get_all_modes_for_all_displays()
        print("Showing all possible display configurations.")
        print('-' * 80)
        for pair in all_modes:
            print("Display: {}".format(pair[0]))
            for mode in pair[1]:
                print("    {}".format(mode))
    elif command == "closest":
        if not all([width, height, depth, refresh]):
            print("Must have all of (width, height, depth, refresh) for closest matching.")
            sys.exit(2)
    elif command == "highest":
        all_modes = get_all_modes_for_all_displays()
        print("Showing highest supported display configurations.")
        print('-' * 80)
        for pair in all_modes:
            print("Display: {}".format(pair[0]))
            print("    {}".format(pair[1][0]))
    elif command == "exact":
        current_modes = get_all_current_configurations()
        print("Showing current display configurations.")
        print('-' * 80)
        for pair in current_modes:
            print("Display: {}".format(pair[0]))
            print("    {}".format(pair[1]))

## Helpful command-line information

def version():
    """
    :return: A string containing the version information for this program.
    """
    return (
        "{name}, version {version}\n".format(
            name=attributes['long_name'],
            version=attributes['version']
        )
    )

def usage(command=None):
    """
    Prints out the usage information.
    """
    # Give the version information always.
    print(version())

    if command == 'set':
        print('''\
usage: {name} set {{ help | closest | highest | exact }}
        [-w width] [-h height] [-d depth] [-r refresh]

SUBCOMMANDS
    help        Print this help information.
    closest     Set the display settings to the supported resolution that is
                closest to the specified values.
    highest     Set the display settings to the highest supported resolution.
    exact       Set the display settings to the specified values without
                checking whether that resolution is supported by the display.

OPTIONS
    -w width    Resolution width
    -h height   Resolution height
    -d depth    Color depth
    -r refresh  Refresh rate
''').format(name=attributes['name'])
    elif command == 'show':
        print('''\
usage: {name} show {{ help | all | closest | highest | exact }}
        [-w width] [-h height] [-d depth] [-r refresh]

SUBCOMMANDS
    help        Print this help information.
    all         Show all supported resolutions for the display.
    closest     Show the closest matching supported resolution to the specified
                values.
    highest     Show the highest supported resolution.
    exact       Show the current display configuration.

OPTIONS
    -w width    Resolution width
    -h height   Resolution height
    -d depth    Color depth
    -r refresh  Refresh rate
''')
    else:
        print('''\
usage: {name} {{ help | version | set | show }}

Use any of the subcommands with 'help' to get more information:
    help    Print this help information.
    version Print the version information.
    set     Set the display configuration.
    show    See available display configurations.
''').format(name=attributes['name'])

class ArgumentParser(argparse.ArgumentParser):
    """
    Custom argument parser for printing error messages a bit more nicely.
    """
    def error(self, message):
        print("Error: {}\n".format(message))
        usage()
        self.exit(2)

if __name__ == '__main__':
    # If they don't give any arguments, help them out.
    if len(sys.argv) < 2:
        usage()
        sys.exit(1)

    if len(sys.argv) == 2 and sys.argv[1] == '--help':
        usage()
        sys.exit(0)

    # Do actual argument parsing.
    parser = ArgumentParser(add_help=False)
    parser.add_argument('-v', '--version', action='store_true')

    # Check whether user wanted version information.
    # Print the version information and quit.
    args = parser.parse_known_args()
    if args[0].version:
        print(version())
        sys.exit(0)

    # Add the subparsers.
    subparsers = parser.add_subparsers(dest='subcommand')

    # Subparser for 'version'.
    parser_version = subparsers.add_parser('version', add_help=False)

    # Subparser for 'help'.
    parser_help = subparsers.add_parser('help', add_help=False)
    parser_help.add_argument('command', choices=['set', 'show'], nargs='?', default=None)

    # Subparser for 'set'.
    parser_set = subparsers.add_parser('set', add_help=False)
    parser_set.add_argument('command', choices=['help', 'closest', 'highest', 'exact'], nargs='?', default='closest')

    # Subparser for 'show'.
    parser_show = subparsers.add_parser('show', add_help=False)
    parser_show.add_argument('command', choices=['help', 'all', 'closest', 'highest', 'exact'], nargs='?', default='all')

    # Both 'set' and 'show' have similar supported arguments.
    for subparser in [parser_set, parser_show]:
        subparser.add_argument('--help', action='store_true')
        subparser.add_argument('-w', '--width', type=int)
        subparser.add_argument('-h', '--height', type=int)
        subparser.add_argument('-d', '--depth', type=int)
        subparser.add_argument('-r', '--refresh', type=int)

    # Parse the arguments.
    # Note that we have to use the leftover arguments from the
    # parser.parse_known_args() call up above.
    args = parser.parse_args(args[1])

    # If they used the 'help' subcommand, use it smartly.
    if args.subcommand == 'help':
        usage(command=args.command)
        sys.exit(0)

    if args.subcommand == 'version':
        print(version())
        sys.exit(0)

    # Check if they wanted help with the subcommand.
    if args.command == 'help' or args.help:
        usage(command=args.subcommand)
        sys.exit(0)

    # Check we have either all or none of the manual specifications.
    manual = [args.width, args.height, args.depth, args.refresh]
    if any(manual) and not all(manual):
        usage()
        print("Error: Must have either all or none of the manual specifications.")
        sys.exit(1)

    if args.subcommand == 'set':
        sub_set(args.command, args.width, args.height, args.depth, args.refresh)
    else:
        sub_show(args.command, args.width, args.height, args.depth, args.refresh)
<commit_msg>Significant updates to show<commit_after>#!/usr/bin/python

## Some info should go here.

#TODO: add these features:
# [ ] HDMI overscan
# [ ] AirPlay mirroring
# [ ] set individual display settings

## Imports
import argparse
import os
import sys

import Quartz

## Global Variables
attributes = {
    'long_name' : 'Display Manager',
    'name'      : os.path.basename(sys.argv[0]),
    'version'   : '0.5.0'
}

MAX_DISPLAYS = 32

## Struct-like thing for easy display.
class DisplayMode(object):
    def __init__(self, mode=None, width=None, height=None, bpp=None, refresh=None):
        if mode:
            self.width      = Quartz.CGDisplayModeGetWidth(mode)
            self.height     = Quartz.CGDisplayModeGetHeight(mode)
            self.bpp        = get_pixel_depth_from_encoding(Quartz.CGDisplayModeCopyPixelEncoding(mode))
            self.refresh    = Quartz.CGDisplayModeGetRefreshRate(mode)
            self.raw_mode   = mode
        else:
            for element in [width, height, bpp, refresh]:
                if element is None:
                    raise ValueError("Must give all of width, height, bits per pixel, and refresh rate to construct DisplayMode.")
            self.width      = width
            self.height     = height
            self.bpp        = bpp
            self.refresh    = refresh
            self.raw_mode   = None
        self.pixels     = self.width * self.height
        self.ratio      = float(self.width) / float(self.height)
    def __str__(self):
        return "{width}x{height}; pixel depth: {bpp}; refresh rate: {refresh}; ratio: {ratio}:1".format(
            width   = self.width,
            height  = self.height,
            bpp     = self.bpp,
            refresh = self.refresh,
            ratio   = self.ratio,
        )
    def __repr__(self):
        return self.__str__()
    def __eq__(self, other):
        return (
            isinstance(other, self.__class__)   and
            self.width      == other.width      and
            self.height     == other.height     and
            self.bpp        == other.bpp        and
            self.refresh    == other.refresh
        )
    def __ne__(self, other):
        return not self.__eq__(other)

## Helper Functions
def get_pixel_depth_from_encoding(encoding):
    if encoding == "PPPPPPPP":
        return 8
    elif encoding == "-RRRRRGGGGGBBBBB":
        return 16
    elif encoding == "--------RRRRRRRRGGGGGGGGBBBBBBBB":
        return 32
    else:
        raise RuntimeError("Unknown pixel encoding: {}".format(encoding))

def get_displays_list():
    (error, online_displays, displays_count) = Quartz.CGGetOnlineDisplayList(MAX_DISPLAYS, None, None)
    if error:
        raise RuntimeError("Unable to get displays list.")
    return online_displays

def get_all_modes_for_display(display):
    modes = [DisplayMode(mode=mode) for mode in Quartz.CGDisplayCopyAllDisplayModes(display, None)]
    modes.sort(key = lambda mode: mode.refresh, reverse = True)
    modes.sort(key = lambda mode: mode.bpp, reverse = True)
    modes.sort(key = lambda mode: mode.pixels, reverse = True)
    return modes

def get_current_mode_for_display(display):
    mode = DisplayMode(mode=Quartz.CGDisplayCopyDisplayMode(display))
    return mode

def get_all_current_configurations():
    modes = []
    for display in get_displays_list():
        modes.append( (display, get_current_mode_for_display(display)) )
    return modes

def get_all_modes_for_all_displays():
    modes = []
    for display in get_displays_list():
        modes.append( (display, get_all_modes_for_display(display)) )
    return modes

def get_mode_closest_to_values(modes, width, height, depth, refresh):
    """
    :param modes: A list containing DisplayMode objects.
    """
    # Check we don't have an empty list of modes.
    if not modes:
        return None
    match_mode = DisplayMode(width=width, height=height, bpp=depth, refresh=refresh)
    ratio = width / height
    pixels = width * height
    # Search for an exact match.
    for mode in modes:
        if mode == match_mode:
            return mode
    # No exact match, so let's check if there's a resolution and bit depth match.
    close_matches = []
    for mode in modes:
        if mode.width == width and mode.height == height and mode.bpp == depth:
            # Found one with the correct resolution.
            close_matches.append(mode)
    if close_matches:
        # There's at least one match at the correct resolution and bit depth.
        close_matches.sort(key = lambda mode: mode.refresh, reverse = True)
        larger = None
        smaller = None
        # Find the two closest matches by refresh rate.
        for match in close_matches:
            if match.refresh > refresh:
                larger = match
            else:
                smaller = match
                break
        # Check some edge cases.
        if smaller and not larger:
            # All the available refresh rates are lesser than the desired.
            return smaller
        if larger and not smaller:
            # There's only one element in the list, and it's larger than we
            # ideally wanted. Oh well.
            return larger
        # Okay, now we have two elements, and neither is perfect.
        # Find the closer of the two.
        larger_dif = abs(larger.refresh - refresh)
        smaller_dif = abs(smaller.refresh - refresh)
        if smaller_dif < larger_dif:
            return smaller
        else:
            return larger
    # No matches for WHD, so let's check that bit depth.
    close_matches = []
    for mode in modes:
        if mode.width == width and mode.height == height:
            # Found one with the right resolution.
            close_matches.append(mode)
    if close_matches:
        # We have the correct resolution. Let's find the closest bit depth.
        close_matches.sort(key = lambda mode: mode.bpp, reverse = True)
        larger = None
        smaller = None
        # Find the two closest matches by bit depth.
        for match in close_matches:
            if match.bpp > depth:
                larger = match
            else:
                smaller = match
                break
        # Check some edge cases.
        if smaller and not larger:
            # All the available bit depths are lesser than the desired.
            return smaller
        if larger and not smaller:
            # There's only one element in the list, and it's larger than we
            # ideally wanted. Oh well.
            return larger
        # Okay, now we have two elements, and neither is perfect.
        # Find the closer of the two.
        larger_dif = abs(larger.bpp - depth)
        smaller_dif = abs(smaller.bpp - depth)
        if smaller_dif < larger_dif:
            return smaller
        else:
            return larger
    # At this point, we don't even have a good resolution match.
    # Let's find all the modes with the appropriate ratio, and then find the
    # closest total pixel count.
    close_matches = []
    for mode in modes:
        if mode.ratio == ratio:
            # Got the right width:height ratio.
            close_matches.append(mode)
    if close_matches:
        # Sort by total pixels.
        close_matches.sort(key = lambda mode: mode.pixels, reverse = True)
        larger = None
        smaller = None
        # Find the closest matches by pixel count.
        for match in close_matches:
            if match.pixels > pixels:
                larger = match
            else:
                smaller = match
                break
        # Check some edge cases.
        if smaller and not larger:
            # All the available pixel counts are lesser than the desired.
            return smaller
        if larger and not smaller:
            # There's only one element in the list, and it's larger than we
            # ideally wanted. Oh well.
            return larger
        # Okay, now we have two elements, and neither is perfect.
        # Find the closer of the two.
        larger_dif = abs(larger.pixels - pixels)
        smaller_dif = abs(smaller.pixels - pixels)
        if smaller_dif < larger_dif:
            return smaller
        else:
            return larger
    # We don't have any good resolutions available. Let's throw an error?
    return None

def sub_set(command, width, height, depth, refresh, display=None):
    pass

def sub_show(command, width, height, depth, refresh, display=None):
    main_display = Quartz.CGMainDisplayID()
    if command == "all":
        all_modes = get_all_modes_for_all_displays()
        if display:
            all_modes = [x for x in all_modes if x[0] == display]
        if not all_modes:
            print("No matching displays found.")
            sys.exit(4)
        print("Showing all possible display configurations.")
        print('-' * 80)
        for pair in all_modes:
            print("Display: {}{}".format(pair[0], " (Main Display)" if pair[0] == main_display else ""))
            for mode in pair[1]:
                print("    {}".format(mode))
    elif command == "closest":
        for element in [width, height, depth, refresh]:
            if element is None:
                usage()
                print("Must have all of (width, height, depth, refresh) for closest matching.")
                sys.exit(2)
        all_modes = get_all_modes_for_all_displays()
        if display:
            all_modes = [x for x in all_modes if x[0] == display]
        if not all_modes:
            print("No matching displays found.")
            sys.exit(4)
        print("Finding closest supported display configurations.")
        print('-' * 80)
        for pair in all_modes:
            print("Display: {}{}".format(pair[0], " (Main Display)" if pair[0] == main_display else ""))
            closest = get_mode_closest_to_values(pair[1], width, height, depth, refresh)
            if closest:
                print("    {}".format(closest))
            else:
                print("    (no close matches found)")
    elif command == "highest":
        all_modes = get_all_modes_for_all_displays()
        if display:
            all_modes = [x for x in all_modes if x[0] == display]
        if not all_modes:
            print("No matching displays found.")
            sys.exit(4)
        print("Showing highest supported display configurations.")
        print('-' * 80)
        for pair in all_modes:
            print("Display: {}{}".format(pair[0], " (Main Display)" if pair[0] == main_display else ""))
            print("    {}".format(pair[1][0]))
    elif command == "exact":
        current_modes = get_all_current_configurations()
        print("Showing current display configurations.")
        print('-' * 80)
        for pair in current_modes:
            print("Display: {}".format(pair[0]))
            print("    {}".format(pair[1]))
    elif command == "displays":
        for display in get_displays_list():
            print("Display: {}{}".format(display, " (Main Display)" if display == main_display else ""))

## Helpful command-line information

def version():
    """
    :return: A string containing the version information for this program.
    """
    return (
        "{name}, version {version}\n".format(
            name=attributes['long_name'],
            version=attributes['version']
        )
    )

def usage(command=None):
    """
    Prints out the usage information.
    """
    # Give the version information always.
    print(version())

    if command == 'set':
        print('''\
usage: {name} set {{ help | closest | highest | exact }}
        [-w width] [-h height] [-d depth] [-r refresh]

SUBCOMMANDS
    help        Print this help information.
    closest     Set the display settings to the supported resolution that is
                closest to the specified values.
    highest     Set the display settings to the highest supported resolution.
    exact       Set the display settings to the specified values without
                checking whether that resolution is supported by the display.

OPTIONS
    -w width    Resolution width
    -h height   Resolution height
    -d depth    Color depth
    -r refresh  Refresh rate
''').format(name=attributes['name'])
    elif command == 'show':
        print('''\
usage: {name} show {{ help | all | closest | highest | exact }}
        [-w width] [-h height] [-d depth] [-r refresh]

SUBCOMMANDS
    help        Print this help information.
    all         Show all supported resolutions for the display.
    closest     Show the closest matching supported resolution to the specified
                values.
    highest     Show the highest supported resolution.
    exact       Show the current display configuration.

OPTIONS
    -w width    Resolution width
    -h height   Resolution height
    -d depth    Color depth
    -r refresh  Refresh rate
''')
    else:
        print('''\
usage: {name} {{ help | version | set | show }}

Use any of the subcommands with 'help' to get more information:
    help    Print this help information.
    version Print the version information.
    set     Set the display configuration.
    show    See available display configurations.
''').format(name=attributes['name'])

class ArgumentParser(argparse.ArgumentParser):
    """
    Custom argument parser for printing error messages a bit more nicely.
    """
    def error(self, message):
        print("Error: {}\n".format(message))
        usage()
        self.exit(2)

if __name__ == '__main__':
    # If they don't give any arguments, help them out.
    if len(sys.argv) < 2:
        usage()
        sys.exit(1)

    if len(sys.argv) == 2 and sys.argv[1] == '--help':
        usage()
        sys.exit(0)

    # Do actual argument parsing.
    parser = ArgumentParser(add_help=False)
    parser.add_argument('-v', '--version', action='store_true')

    # Check whether user wanted version information.
    # Print the version information and quit.
    args = parser.parse_known_args()
    if args[0].version:
        print(version())
        sys.exit(0)

    # Add the subparsers.
    subparsers = parser.add_subparsers(dest='subcommand')

    # Subparser for 'version'.
    parser_version = subparsers.add_parser('version', add_help=False)

    # Subparser for 'help'.
    parser_help = subparsers.add_parser('help', add_help=False)
    parser_help.add_argument('command', choices=['set', 'show'], nargs='?', default=None)

    # Subparser for 'set'.
    parser_set = subparsers.add_parser('set', add_help=False)
    parser_set.add_argument('command', choices=['help', 'closest', 'highest', 'exact'], nargs='?', default='closest')

    # Subparser for 'show'.
    parser_show = subparsers.add_parser('show', add_help=False)
    parser_show.add_argument('command', choices=['help', 'all', 'closest', 'highest', 'exact', 'displays'], nargs='?', default='all')

    # Both 'set' and 'show' have similar supported arguments.
    for subparser in [parser_set, parser_show]:
        subparser.add_argument('--help', action='store_true')
        subparser.add_argument('-w', '--width', type=int)
        subparser.add_argument('-h', '--height', type=int)
        subparser.add_argument('-d', '--depth', type=int)
        subparser.add_argument('-r', '--refresh', type=int)
        subparser.add_argument('--display', type=int)

    # Parse the arguments.
    # Note that we have to use the leftover arguments from the
    # parser.parse_known_args() call up above.
    args = parser.parse_args(args[1])

    # If they used the 'help' subcommand, use it smartly.
    if args.subcommand == 'help':
        usage(command=args.command)
        sys.exit(0)

    if args.subcommand == 'version':
        print(version())
        sys.exit(0)

    # Check if they wanted help with the subcommand.
    if args.command == 'help' or args.help:
        usage(command=args.subcommand)
        sys.exit(0)

    # Check we have either all or none of the manual specifications.
    manual = [args.width, args.height, args.depth, args.refresh]
    if any(manual):
        for element in manual:
            if element is None:
                usage()
                print("Error: Must have either all or none of the manual specifications.")
                sys.exit(1)

    if args.subcommand == 'set':
        sub_set(args.command, args.width, args.height, args.depth, args.refresh, args.display)
    else:
        sub_show(args.command, args.width, args.height, args.depth, args.refresh, args.display)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint:disable=unused-wildcard-import,wildcard-import,invalid-name
"""Controls used by the TKinter GUI."""
from __future__ import print_function, unicode_literals, absolute_import

import sys

from . import binding
from core.lnp import lnp

if sys.version_info[0] == 3:  # Alternate import names
    # pylint:disable=import-error
    from tkinter import *
    from tkinter.ttk import *
    import tkinter.simpledialog as simpledialog
else:
    # pylint:disable=import-error
    from Tkinter import *
    from ttk import *
    import tkSimpleDialog as simpledialog

# Monkeypatch simpledialog to use themed dialogs from ttk
if sys.platform != 'darwin':  # OS X looks better without patch
    simpledialog.Toplevel = Toplevel
    simpledialog.Entry = Entry
    simpledialog.Frame = Frame
    simpledialog.Button = Button

# Make Enter on button with focus activate it
TtkButton = Button
#pylint: disable=too-many-public-methods
class Button(TtkButton):  # pylint:disable=function-redefined,missing-docstring
    def __init__(self, master=None, **kw):
        TtkButton.__init__(self, master, **kw)
        if 'command' in kw:
            self.bind('<Return>', lambda e: kw['command']())

# http://effbot.org/zone/tkinter-autoscrollbar.htm
class _AutoScrollbar(Scrollbar):
    """A scrollbar that hides itself if it's not needed."""
    # pylint:disable=arguments-differ
    def set(self, lo, hi):
        """Only show scrollbar when there's more content than will fit."""
        if (float(lo) <= 0.0 and float(hi) >= 1.0) or (
                hasattr(self, 'hidden') and self.hidden):
            self.grid_remove()
        else:
            self.grid()
        Scrollbar.set(self, lo, hi)

# http://www.voidspace.org.uk/python/weblog/arch_d7_2006_07_01.shtml#e387
class _ToolTip(object):
    """Tooltip widget."""
    def __init__(self, widget, text):
        self.widget = widget
        self.tipwindow = None
        self.id = None
        self.event = None
        self.text = text
        self.active = False

    def showtip(self):
        """Displays the tooltip."""
        self.active = True
        if self.tipwindow or not self.text:
            return
        x = self.widget.winfo_pointerx() + 16
        y = self.widget.winfo_pointery() + 16
        self.tipwindow = tw = Toplevel(self.widget)
        tw.wm_overrideredirect(1)
        tw.wm_geometry("+%d+%d" % (x, y))
        try:
            # pylint:disable=protected-access
            # For OS X
            tw.tk.call(
                "::tk::unsupported::MacWindowStyle", "style", tw._w, "help",
                "noActivates")
        except TclError:
            pass
        label = Label(
            tw, text=self.text, justify=LEFT, background="#ffffe0",
            relief=SOLID, borderwidth=1)
        label.pack(ipadx=1)

    def hidetip(self):
        """Hides the tooltip."""
        tw = self.tipwindow
        self.tipwindow = None
        self.active = False
        if tw:
            tw.destroy()

    def settext(self, text):
        """
        Sets the tooltip text and redraws it if necessary.

        Params:
            text
                The new tooltip text.
        """
        if text == self.text:
            return
        self.text = text
        if self.active:
            self.hidetip()
            self.showtip()

_TOOLTIP_DELAY = 500

__ui = None

# pylint:disable=too-few-public-methods
class _FakeControl(object):
    """Fake control returned if an option doesn't exist."""
    # pylint:disable=unused-argument
    @staticmethod
    def grid(*args, **kwargs):
        """Prevents breaking for code that tries to layout the control."""
        return
    pack = grid

fake_control = _FakeControl()

def init(ui):
    """Connect to a TkGui instance."""
    # pylint:disable=global-statement
    global __ui
    __ui = ui

def create_tooltip(widget, text):
    """
    Creates and returns a tooltip for a widget.

    Params:
        widget
            The widget to associate the tooltip to.
        text
            The tooltip text.
    """
    tooltip = _ToolTip(widget, text)
    # pylint:disable=unused-argument
    def enter(event):
        """
        Event handler on mouse enter.

        Params:
            event
                The event data."""
        if tooltip.event:
            widget.after_cancel(tooltip.event)
        tooltip.event = widget.after(_TOOLTIP_DELAY, tooltip.showtip)

    def leave(event):
        """
        Event handler on mouse exit.

        Params:
            event
                The event data.
        """
        if tooltip.event is not None:
            widget.after_cancel(tooltip.event)
            tooltip.event = None
        tooltip.hidetip()
    widget.bind('<Enter>', enter)
    widget.bind('<Leave>', leave)
    return tooltip

def create_control_group(parent, text, dual_column=False):
    """
    Creates and returns a Frame or Labelframe to group controls.

    Params:
        text
            The caption for the Labelframe. If None, returns a Frame.
        dual_column
            If True, configures the frame for a dual-column grid layout.
    """
    f = None
    if text is not None:
        f = Labelframe(parent, text=text)
    else:
        f = Frame(parent)
    if dual_column:
        Grid.columnconfigure(f, 0, weight=1)
        Grid.columnconfigure(f, 1, weight=1)
    return f

def create_option_button(
        parent, text, tooltip, option, update_func=None):
    """
    Creates and returns a button bound to an option.

    Params:
        parent
            The parent control for the button.
        text
            The button text.
        tooltip
            The tooltip for the button.
        option
            The keyword used for the option.
        update_func
            If given, a reference to a function that pre-processes the
            given option for display.
    """
    return create_trigger_option_button(
        parent, text, tooltip, lambda: __ui.cycle_option(option), option,
        update_func)

def create_trigger_button(parent, text, tooltip, command):
    """
    Creates and returns a button that triggers an action when clicked.

    Params:
        parent
            The parent control for the button.
        text
            The button text.
        tooltip
            The tooltip for the button.
        command
            Reference to the function called when the button is clicked.
    """
    b = Button(parent, text=text, command=command)
    create_tooltip(b, tooltip)
    return b

#pylint: disable=too-many-arguments
def create_trigger_option_button(
        parent, text, tooltip, command, option, update_func=None):
    """
    Creates and returns a button bound to an option, with a special action
    triggered on click.

    Params:
        parent
            The parent control for the button.
        text
            The button text.
        tooltip
            The tooltip for the button.
        command
            Reference to the function called when the button is clicked.
        option
            The keyword used for the option.
        update_func
            If given, a reference to a function that pre-processes the
            given option for display.
    """
    if not binding.version_has_option(option):
        return fake_control
    b = create_trigger_button(parent, text, tooltip, command)
    binding.bind(b, option, update_func)
    return b

def create_scrollbar(parent, control, **gridargs):
    """
    Creates and layouts a vertical scrollbar associated to <control>.

    Params:
        parent
            The parent control for the scrollbar.
        control
            The control to attach the scrollbar to.
        gridargs
            Keyword arguments used to apply grid layout to the scrollbar.
    """
    s = _AutoScrollbar(parent, orient=VERTICAL, command=control.yview)
    control['yscrollcommand'] = s.set
    s.grid(sticky="ns", **gridargs)
    s.grid_remove()
    return s

def create_file_list(parent, title, listvar, **args):
    """
        Creates a file list with a scrollbar. Returns a tuple
        (frame, listbox).

        Params:
            parent
                The parent control for the list.
            title
                The title for the frame.
            listvar
                The Variable containing the list items.
            args
                Additions keyword arguments for the file list itself.
    """
    if 'height' not in args:
        args['height'] = 4
    lf = create_control_group(parent, title)
    lf.pack(side=BOTTOM, fill=BOTH, expand=Y, anchor="s")
    Grid.columnconfigure(lf, 0, weight=2)
    Grid.rowconfigure(lf, 1, weight=1)
    lb = Listbox(
        lf, listvariable=listvar, activestyle='dotbox', exportselection=0,
        **args)
    lb.grid(column=0, row=0, rowspan=2, sticky="nsew")
    create_scrollbar(lf, lb, column=1, row=0, rowspan=2)
    return (lf, lb)

def create_readonly_file_list_buttons(
        parent, title, listvar, load_fn, refresh_fn, **args):
    """
        Creates a file list with load and refresh buttons. Returns a tuple
        (frame, listbox, buttons).

        Params:
            parent
                The parent control for the list.
            title
                The title for the frame.
            listvar
                The Variable containing the list items.
            load_fn
                Reference to a function to be called when the Load button
                is clicked.
            refresh_fn
                Reference to a function to be called when the Refresh button
                is clicked.
            args
                Additions keyword arguments for the file list itself.
    """
    (lf, lb) = create_file_list(parent, title, listvar, **args)
    buttons = Frame(lf)
    load = create_trigger_button(buttons, 'Load', 'Load selected', load_fn)
    load.pack(side=TOP)
    refresh = create_trigger_button(
        buttons, 'Refresh', 'Refresh list', refresh_fn)
    refresh.pack(side=TOP)
    buttons.grid(column=2, row=0, sticky="n")
    return (lf, lb, buttons)

#pylint: disable=too-many-arguments
def create_file_list_buttons(
        parent, title, listvar, load_fn, refresh_fn, save_fn,
        delete_fn, **args):
    """
        Creates a file list with load, refresh, save and delete buttons.
        Returns a tuple (frame, listbox, buttons).

        Params:
            parent
                The parent control for the list.
            title
                The title for the frame.
            listvar
                The Variable containing the list items.
            load_fn
                Reference to a function to be called when the Load button
                is clicked.
            refresh_fn
                Reference to a function to be called when the Refresh button
                is clicked.
            save_fn
                Reference to a function to be called when the Save button
                is clicked.
            delete_fn
                Reference to a function to be called when the Delete button
                is clicked.
            args
                Additions keyword arguments for the file list itself.
    """
    (lf, lb, buttons) = create_readonly_file_list_buttons(
        parent, title, listvar, load_fn, refresh_fn, **args)
    save = create_trigger_button(buttons, 'Save', 'Save current', save_fn)
    save.pack(side=TOP)
    delete = create_trigger_button(
        buttons, 'Delete', 'Delete selected', delete_fn)
    delete.pack(side=TOP)
    return (lf, lb, buttons)

def create_toggle_list(parent, columns, framegridopts, listopts={}):
    """
    Creates and returns a two-column Treeview in a frame to show toggleable
    items in a list.

    Params:
        parent
            The parent control for the Treeview.
        columns
            Column data for the Treeview.
        framegridopts
            Additional options for grid layout of the frame.
        listopts
            Additional options for the Treeview.
    """
    # pylint:disable=star-args,dangerous-default-value
    lf = Frame(parent)
    lf.grid(**framegridopts)
    Grid.rowconfigure(lf, 0, weight=1)
    Grid.columnconfigure(lf, 0, weight=1)
    lst = Treeview(lf, columns=columns, show=['headings'], **listopts)
    lst.grid(column=0, row=0, sticky="nsew")
    create_scrollbar(lf, lst, column=1, row=0)
    return lst

def create_numeric_entry(parent, variable, option, tooltip):
    """
    Creates and returns an Entry suitable for input of small, numeric values
    and hooks up notification of changes.

    Params:
        parent
            The parent control for the Entry.
        variable
            The StringVar used to store the value internally.
        option
            The keyword used for the option.
        tooltip
            The tooltip for the Entry.
    """
    if not binding.version_has_option(option):
        return fake_control
    e = Entry(
        parent, width=4, validate='key',
        validatecommand=__ui.vcmd, textvariable=variable)
    variable.trace(
        "w", lambda name, index, mode: __ui.change_entry(option, variable))
    create_tooltip(e, tooltip)
    binding.bind(e, option)
    return e

# vim:expandtab
<commit_msg>Remove unused import<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint:disable=unused-wildcard-import,wildcard-import,invalid-name
"""Controls used by the TKinter GUI."""
from __future__ import print_function, unicode_literals, absolute_import

import sys

from . import binding

if sys.version_info[0] == 3:  # Alternate import names
    # pylint:disable=import-error
    from tkinter import *
    from tkinter.ttk import *
    import tkinter.simpledialog as simpledialog
else:
    # pylint:disable=import-error
    from Tkinter import *
    from ttk import *
    import tkSimpleDialog as simpledialog

# Monkeypatch simpledialog to use themed dialogs from ttk
if sys.platform != 'darwin':  # OS X looks better without patch
    simpledialog.Toplevel = Toplevel
    simpledialog.Entry = Entry
    simpledialog.Frame = Frame
    simpledialog.Button = Button

# Make Enter on button with focus activate it
TtkButton = Button
#pylint: disable=too-many-public-methods
class Button(TtkButton):  # pylint:disable=function-redefined,missing-docstring
    def __init__(self, master=None, **kw):
        TtkButton.__init__(self, master, **kw)
        if 'command' in kw:
            self.bind('<Return>', lambda e: kw['command']())

# http://effbot.org/zone/tkinter-autoscrollbar.htm
class _AutoScrollbar(Scrollbar):
    """A scrollbar that hides itself if it's not needed."""
    # pylint:disable=arguments-differ
    def set(self, lo, hi):
        """Only show scrollbar when there's more content than will fit."""
        if (float(lo) <= 0.0 and float(hi) >= 1.0) or (
                hasattr(self, 'hidden') and self.hidden):
            self.grid_remove()
        else:
            self.grid()
        Scrollbar.set(self, lo, hi)

# http://www.voidspace.org.uk/python/weblog/arch_d7_2006_07_01.shtml#e387
class _ToolTip(object):
    """Tooltip widget."""
    def __init__(self, widget, text):
        self.widget = widget
        self.tipwindow = None
        self.id = None
        self.event = None
        self.text = text
        self.active = False

    def showtip(self):
        """Displays the tooltip."""
        self.active = True
        if self.tipwindow or not self.text:
            return
        x = self.widget.winfo_pointerx() + 16
        y = self.widget.winfo_pointery() + 16
        self.tipwindow = tw = Toplevel(self.widget)
        tw.wm_overrideredirect(1)
        tw.wm_geometry("+%d+%d" % (x, y))
        try:
            # pylint:disable=protected-access
            # For OS X
            tw.tk.call(
                "::tk::unsupported::MacWindowStyle", "style", tw._w, "help",
                "noActivates")
        except TclError:
            pass
        label = Label(
            tw, text=self.text, justify=LEFT, background="#ffffe0",
            relief=SOLID, borderwidth=1)
        label.pack(ipadx=1)

    def hidetip(self):
        """Hides the tooltip."""
        tw = self.tipwindow
        self.tipwindow = None
        self.active = False
        if tw:
            tw.destroy()

    def settext(self, text):
        """
        Sets the tooltip text and redraws it if necessary.

        Params:
            text
                The new tooltip text.
        """
        if text == self.text:
            return
        self.text = text
        if self.active:
            self.hidetip()
            self.showtip()

_TOOLTIP_DELAY = 500

__ui = None

# pylint:disable=too-few-public-methods
class _FakeControl(object):
    """Fake control returned if an option doesn't exist."""
    # pylint:disable=unused-argument
    @staticmethod
    def grid(*args, **kwargs):
        """Prevents breaking for code that tries to layout the control."""
        return
    pack = grid

fake_control = _FakeControl()

def init(ui):
    """Connect to a TkGui instance."""
    # pylint:disable=global-statement
    global __ui
    __ui = ui

def create_tooltip(widget, text):
    """
    Creates and returns a tooltip for a widget.

    Params:
        widget
            The widget to associate the tooltip to.
        text
            The tooltip text.
    """
    tooltip = _ToolTip(widget, text)
    # pylint:disable=unused-argument
    def enter(event):
        """
        Event handler on mouse enter.

        Params:
            event
                The event data."""
        if tooltip.event:
            widget.after_cancel(tooltip.event)
        tooltip.event = widget.after(_TOOLTIP_DELAY, tooltip.showtip)

    def leave(event):
        """
        Event handler on mouse exit.

        Params:
            event
                The event data.
        """
        if tooltip.event is not None:
            widget.after_cancel(tooltip.event)
            tooltip.event = None
        tooltip.hidetip()
    widget.bind('<Enter>', enter)
    widget.bind('<Leave>', leave)
    return tooltip

def create_control_group(parent, text, dual_column=False):
    """
    Creates and returns a Frame or Labelframe to group controls.

    Params:
        text
            The caption for the Labelframe. If None, returns a Frame.
        dual_column
            If True, configures the frame for a dual-column grid layout.
    """
    f = None
    if text is not None:
        f = Labelframe(parent, text=text)
    else:
        f = Frame(parent)
    if dual_column:
        Grid.columnconfigure(f, 0, weight=1)
        Grid.columnconfigure(f, 1, weight=1)
    return f

def create_option_button(
        parent, text, tooltip, option, update_func=None):
    """
    Creates and returns a button bound to an option.

    Params:
        parent
            The parent control for the button.
        text
            The button text.
        tooltip
            The tooltip for the button.
        option
            The keyword used for the option.
        update_func
            If given, a reference to a function that pre-processes the
            given option for display.
    """
    return create_trigger_option_button(
        parent, text, tooltip, lambda: __ui.cycle_option(option), option,
        update_func)

def create_trigger_button(parent, text, tooltip, command):
    """
    Creates and returns a button that triggers an action when clicked.

    Params:
        parent
            The parent control for the button.
        text
            The button text.
        tooltip
            The tooltip for the button.
        command
            Reference to the function called when the button is clicked.
    """
    b = Button(parent, text=text, command=command)
    create_tooltip(b, tooltip)
    return b

#pylint: disable=too-many-arguments
def create_trigger_option_button(
        parent, text, tooltip, command, option, update_func=None):
    """
    Creates and returns a button bound to an option, with a special action
    triggered on click.

    Params:
        parent
            The parent control for the button.
        text
            The button text.
        tooltip
            The tooltip for the button.
        command
            Reference to the function called when the button is clicked.
        option
            The keyword used for the option.
        update_func
            If given, a reference to a function that pre-processes the
            given option for display.
    """
    if not binding.version_has_option(option):
        return fake_control
    b = create_trigger_button(parent, text, tooltip, command)
    binding.bind(b, option, update_func)
    return b

def create_scrollbar(parent, control, **gridargs):
    """
    Creates and layouts a vertical scrollbar associated to <control>.

    Params:
        parent
            The parent control for the scrollbar.
        control
            The control to attach the scrollbar to.
        gridargs
            Keyword arguments used to apply grid layout to the scrollbar.
    """
    s = _AutoScrollbar(parent, orient=VERTICAL, command=control.yview)
    control['yscrollcommand'] = s.set
    s.grid(sticky="ns", **gridargs)
    s.grid_remove()
    return s

def create_file_list(parent, title, listvar, **args):
    """
        Creates a file list with a scrollbar. Returns a tuple
        (frame, listbox).

        Params:
            parent
                The parent control for the list.
            title
                The title for the frame.
            listvar
                The Variable containing the list items.
            args
                Additions keyword arguments for the file list itself.
    """
    if 'height' not in args:
        args['height'] = 4
    lf = create_control_group(parent, title)
    lf.pack(side=BOTTOM, fill=BOTH, expand=Y, anchor="s")
    Grid.columnconfigure(lf, 0, weight=2)
    Grid.rowconfigure(lf, 1, weight=1)
    lb = Listbox(
        lf, listvariable=listvar, activestyle='dotbox', exportselection=0,
        **args)
    lb.grid(column=0, row=0, rowspan=2, sticky="nsew")
    create_scrollbar(lf, lb, column=1, row=0, rowspan=2)
    return (lf, lb)

def create_readonly_file_list_buttons(
        parent, title, listvar, load_fn, refresh_fn, **args):
    """
        Creates a file list with load and refresh buttons. Returns a tuple
        (frame, listbox, buttons).

        Params:
            parent
                The parent control for the list.
            title
                The title for the frame.
            listvar
                The Variable containing the list items.
            load_fn
                Reference to a function to be called when the Load button
                is clicked.
            refresh_fn
                Reference to a function to be called when the Refresh button
                is clicked.
            args
                Additions keyword arguments for the file list itself.
    """
    (lf, lb) = create_file_list(parent, title, listvar, **args)
    buttons = Frame(lf)
    load = create_trigger_button(buttons, 'Load', 'Load selected', load_fn)
    load.pack(side=TOP)
    refresh = create_trigger_button(
        buttons, 'Refresh', 'Refresh list', refresh_fn)
    refresh.pack(side=TOP)
    buttons.grid(column=2, row=0, sticky="n")
    return (lf, lb, buttons)

#pylint: disable=too-many-arguments
def create_file_list_buttons(
        parent, title, listvar, load_fn, refresh_fn, save_fn,
        delete_fn, **args):
    """
        Creates a file list with load, refresh, save and delete buttons.
        Returns a tuple (frame, listbox, buttons).

        Params:
            parent
                The parent control for the list.
            title
                The title for the frame.
            listvar
                The Variable containing the list items.
            load_fn
                Reference to a function to be called when the Load button
                is clicked.
            refresh_fn
                Reference to a function to be called when the Refresh button
                is clicked.
            save_fn
                Reference to a function to be called when the Save button
                is clicked.
            delete_fn
                Reference to a function to be called when the Delete button
                is clicked.
            args
                Additions keyword arguments for the file list itself.
    """
    (lf, lb, buttons) = create_readonly_file_list_buttons(
        parent, title, listvar, load_fn, refresh_fn, **args)
    save = create_trigger_button(buttons, 'Save', 'Save current', save_fn)
    save.pack(side=TOP)
    delete = create_trigger_button(
        buttons, 'Delete', 'Delete selected', delete_fn)
    delete.pack(side=TOP)
    return (lf, lb, buttons)

def create_toggle_list(parent, columns, framegridopts, listopts={}):
    """
    Creates and returns a two-column Treeview in a frame to show toggleable
    items in a list.

    Params:
        parent
            The parent control for the Treeview.
        columns
            Column data for the Treeview.
        framegridopts
            Additional options for grid layout of the frame.
        listopts
            Additional options for the Treeview.
    """
    # pylint:disable=star-args,dangerous-default-value
    lf = Frame(parent)
    lf.grid(**framegridopts)
    Grid.rowconfigure(lf, 0, weight=1)
    Grid.columnconfigure(lf, 0, weight=1)
    lst = Treeview(lf, columns=columns, show=['headings'], **listopts)
    lst.grid(column=0, row=0, sticky="nsew")
    create_scrollbar(lf, lst, column=1, row=0)
    return lst

def create_numeric_entry(parent, variable, option, tooltip):
    """
    Creates and returns an Entry suitable for input of small, numeric values
    and hooks up notification of changes.

    Params:
        parent
            The parent control for the Entry.
        variable
            The StringVar used to store the value internally.
        option
            The keyword used for the option.
        tooltip
            The tooltip for the Entry.
    """
    if not binding.version_has_option(option):
        return fake_control
    e = Entry(
        parent, width=4, validate='key',
        validatecommand=__ui.vcmd, textvariable=variable)
    variable.trace(
        "w", lambda name, index, mode: __ui.change_entry(option, variable))
    create_tooltip(e, tooltip)
    binding.bind(e, option)
    return e

# vim:expandtab
<|endoftext|>


<commit_before>from .environment import *
from .store import *
from .value import *

import viper.parser.ast.nodes as ns

from viper.parser.ast.nodes import AST

from typing import List, NamedTuple, Optional, Tuple

STARTERS = [
    ns.SingleNewline, ns.SingleLine, ns.FileInput, ns.FileNewline, ns.FileStmt
]

STMTS = [
    ns.Stmt, ns.PlainStmt, ns.IfStmt, ns.ElifStmt, ns.ElseStmt, ns.Definition, ns.Parameter, ns.Arguments, ns.StmtBlock,
]

EXPRS = [
    ns.Expr, ns.IfExpr, ns.ElifExpr, ns.ElseExpr, ns.TestExprList, ns.TestExpr, ns.OrTestExpr, ns.AndTestExpr,
    ns.NotTestExpr, ns.OpExpr, ns.SubOpExpr, ns.AtomExpr, ns.Atom, ns.Trailer, ns.ExprBlock,
    ns.LhsExpr,
]

class EvalResult(NamedTuple):
    env: Environment
    store: Store
    val: Optional[Value]

class EvalStmtResult(NamedTuple):
    env: Environment
    store: Store
    val: Optional[Value]

class EvalExprResult(NamedTuple):
    val: Value
    store: Store

class EvalLhsResult(NamedTuple):
    maybe_env: Optional[Environment]
    store: Store

def start_eval(code: AST, env: Environment = None, store: Store = None,
               previous_result: EvalResult = None) -> EvalResult:
    if previous_result is not None:
        env = previous_result.env
        store = previous_result.store
    if env is None:
        env = empty_env()
    if store is None:
        store = empty_store()
    if any(map(lambda s: isinstance(code, s), STARTERS)):
        return eval_starter(code, env, store)
    elif any(map(lambda s: isinstance(code, s), STMTS)):
        stmt_res = eval_stmt(code, env, store)
        return EvalResult(stmt_res.env, stmt_res.store, stmt_res.val)
    elif any(map(lambda e: isinstance(code, e), EXPRS)):
        expr_res = eval_expr(code, env, store)
        return EvalResult(env, expr_res.store, expr_res.val)
    else:
        raise NotImplementedError(f"No evaluation rules for ASTs of type: {type(code).__name__}")

def eval_starter(starter: AST, env: Environment, store: Store) -> EvalResult:
    if isinstance(starter, ns.SingleNewline):
        return EvalResult(env, store, None)
    elif isinstance(starter, ns.SingleLine):
        return start_eval(starter.line, env, store)
    elif isinstance(starter, ns.FileInput):
        val = None
        for line in starter.lines:
            eval_res = eval_starter(line, env, store)
            val = eval_res.val
            env = eval_res.env
            store = eval_res.store
        return EvalResult(env, store, val)
    elif isinstance(starter, ns.FileNewline):
        return EvalResult(env, store, None)
    elif isinstance(starter, ns.FileStmt):
        return start_eval(starter.stmt, env, store)
    else:
        raise NotImplementedError(f"No evaluation rules for starters of type: {type(starter).__name__}")

def eval_stmt(stmt: AST, env: Environment, store: Store) -> EvalStmtResult:
    if isinstance(stmt, ns.SimpleStmt):
        return eval_stmt(stmt.stmt, env, store)
    elif isinstance(stmt, ns.ReturnStmt):
        raise NotImplementedError
    elif isinstance(stmt, ns.AssignStmt):
        # Evaluate right-hand side first.
        val, store = eval_expr(stmt.expr, env, store)
        # Pass the value to the left-hand side evaluation.
        maybe_env, store = eval_lhs_expr(stmt.lhs, env, store, val)
        if maybe_env is not None:
            env = maybe_env
        return EvalStmtResult(env, store, None)
    elif isinstance(stmt, ns.EmptyStmt):
        return EvalStmtResult(env, store, None)
    elif isinstance(stmt, ns.IfStmt):
        val, store2 = eval_expr(stmt.cond, env, store)
        if isinstance(val, TrueVal):
            return eval_stmt(stmt.then_body, env, store2)
        elif isinstance(val, FalseVal):
            for elif_stmt in stmt.elif_stmts:
                val, store3 = eval_expr(elif_stmt.cond, env, store)
                if isinstance(val, TrueVal):
                    return eval_stmt(elif_stmt.elif_body, env, store3)
            if stmt.else_stmt is not None:
                return eval_stmt(stmt.else_stmt.else_body, env, store)
            else:
                return EvalStmtResult(env, store, None)
        else:
            raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
    elif isinstance(stmt, ns.FuncDef):
        closure = CloVal(stmt.params, stmt.body, env)
        env, store = bind_val(stmt.name.text, closure, env, store)
        return EvalStmtResult(env, store, None)
    elif isinstance(stmt, ns.ClassDef):
        raise NotImplementedError
    elif isinstance(stmt, ns.InterfaceDef):
        raise NotImplementedError
    elif isinstance(stmt, ns.DataDef):
        raise NotImplementedError
    elif isinstance(stmt, ns.Arguments):
        raise NotImplementedError
    elif isinstance(stmt, ns.SimpleStmtBlock):
        return eval_stmt(stmt.stmt, env, store)
    elif isinstance(stmt, ns.CompoundStmtBlock):
        for sub_stmt in stmt.stmts:
            env, store = eval_stmt(sub_stmt, env, store)
        return EvalStmtResult(env, store, None)
    else:
        raise NotImplementedError(f"No implementation for statement of type: {type(stmt).__name__}")

def eval_lhs_expr(expr: AST, env: Environment, store: Store, val: Value) -> EvalLhsResult:
    """
    Attempts to perform a binding of a value. If the shape of the value and the shape of the left-hand side
    are compatible, the binding is performed. An environment and store are returned. If the shapes are not
    compatible, only the store is returned and the environment will be a None.

    :param expr: a left-hand side expression
    :param env: an environment
    :param store: a store
    :param val: a value to bind
    :return: a tuple of a Maybe(env) (as an Environment or a None) and a store
    """
    if isinstance(expr, ns.Pattern):
        return eval_pattern(expr, env, store, val)
    else:
        raise NotImplementedError(f"No implementation for left-hand-side expression of type: {type(expr).__name__}")

def eval_expr(expr: AST, env: Environment, store: Store) -> EvalExprResult:
    if isinstance(expr, ns.IfExpr):
        val, store2 = eval_expr(expr.cond, env, store)
        if isinstance(val, TrueVal):
            return eval_expr(expr.then_body, env, store2)
        elif isinstance(val, FalseVal):
            for elif_expr in expr.elif_exprs:
                val, store3 = eval_expr(elif_expr.cond, env, store)
                if isinstance(val, TrueVal):
                    return eval_expr(elif_expr.elif_body, env, store3)
            if expr.else_expr is not None:
                return eval_expr(expr.else_expr.else_body, env, store)
            else:
                return EvalExprResult(UnitVal(), store)
    elif isinstance(expr, ns.TestExprList):
        vals, store = accumulate_values_from_exprs(expr.tests, env, store)
        return wrap_values(vals, store)
    elif isinstance(expr, ns.TestExpr):
        return eval_expr(expr.test, env, store)
    elif isinstance(expr, ns.OrTestExpr):
        vals, store = accumulate_values_from_exprs(expr.tests, env, store)
        val, store = wrap_values(vals, store)
        if isinstance(val, TupleVal):
            for subval in val.vals:
                if not isinstance(subval, BoolVal):
                    raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
                if isinstance(subval, TrueVal):
                    return EvalExprResult(TrueVal(), store)
            return EvalExprResult(FalseVal(), store)
        else:
            return EvalExprResult(val, store)
    elif isinstance(expr, ns.AndTestExpr):
        vals, store = accumulate_values_from_exprs(expr.tests, env, store)
        val, store = wrap_values(vals, store)
        if isinstance(val, TupleVal):
            for subval in val.vals:
                if not isinstance(subval, BoolVal):
                    raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
                if isinstance(subval, FalseVal):
                    return EvalExprResult(FalseVal(), store)
            return EvalExprResult(TrueVal(), store)
        else:
            return EvalExprResult(val, store)
    elif isinstance(expr, ns.NegatedTestExpr):
        val, store = eval_expr(expr.op_expr, env, store)
        if not isinstance(val, BoolVal):
            raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
        if isinstance(val, TrueVal):
            return EvalExprResult(FalseVal(), store)
        else:
            return EvalExprResult(TrueVal(), store)
    elif isinstance(expr, ns.NotNegatedTestExpr):
        return eval_expr(expr.op_expr, env, store)
    elif isinstance(expr, ns.OpExpr):
        if expr.left_op is not None:
            raise NotImplementedError
        val, store = eval_expr(expr.atom, env, store)
        if expr.sub_op_exprs:
            raise NotImplementedError
        if expr.right_op is not None:
            raise NotImplementedError
        return eval_expr(expr.atom, env, store)
    elif isinstance(expr, ns.AtomExpr):
        val, store = eval_expr(expr.atom, env, store)
        for trailer in expr.trailers:
            if isinstance(trailer, ns.Call):
                if isinstance(val, CloVal):
                    args, store = accumulate_values_from_exprs(trailer.args, env, store)
                    if len(val.params) != len(args):
                        if len(args) > len(val.params):
                            raise RuntimeError(f"Too many arguments specified for call to function {val.name}")  # TODO: Use a custom error.
                        else:
                            # TODO: Automatic currying could be implemented here.
                            raise RuntimeError(f"Not enough arguments specified for call to function {val.name}")  # TODO: Use a custom error.
                    inner_env = val.env
                    for i in range(len(val.params)):
                        param = val.params[i]
                        arg = args[i]
                        inner_env, store = bind_val(param.internal.text, arg, inner_env, store)
                    stmt_res = eval_stmt(val.code, inner_env, store)
                    return EvalExprResult(stmt_res.val, stmt_res.store)
                else:
                    raise RuntimeError(f"Cannot apply arguments to non-closure value of type: {type(val).__name__}")  # TODO: Use a custom error.
            elif isinstance(trailer, ns.Field):
                raise NotImplementedError
            else:
                raise NotImplementedError(f"No implementation for trailer of type: {type(trailer).__name__}")
        return EvalExprResult(val, store)
    elif isinstance(expr, ns.ParenAtom):
        if expr.tests is None:
            return EvalExprResult(UnitVal(), store)
        else:
            return eval_expr(expr.tests, env, store)
    elif isinstance(expr, ns.NameAtom):
        name = expr.name.text
        if name in env:
            return EvalExprResult(store[env[name]], store)
        else:
            raise RuntimeError(f"No such name in environment: {name}")
    elif isinstance(expr, ns.NumberAtom):
        return EvalExprResult(NumVal(expr.num.text), store)
    elif isinstance(expr, ns.EllipsisAtom):
        return EvalExprResult(EllipsisVal(), store)
    elif isinstance(expr, ns.TrueAtom):
        return EvalExprResult(TrueVal(), store)
    elif isinstance(expr, ns.FalseAtom):
        return EvalExprResult(FalseVal(), store)
    elif isinstance(expr, ns.SimpleExprBlock):
        return eval_expr(expr.expr, env, store)
    elif isinstance(expr, ns.IndentedExprBlock):
        return eval_expr(expr.expr, env, store)
    else:
        raise NotImplementedError(f"No implementation for expression of type: {type(expr).__name__}")

def eval_pattern(ptrn: ns.Pattern, env: Environment, store: Store, val: Value) -> EvalLhsResult:
    """
    Binds a value to a pattern, if the value's type and the pattern's shape align.

    :param ptrn: a pattern to match against
    :param env: an environment
    :param store: a store
    :param val: a value to bind
    :return: a tuple of a Maybe(env) (as an Environment or a None) and a store
    """
    if isinstance(ptrn, ns.TypedVariablePattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.TypedAnonymousPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.TypedFieldPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.SimpleVariablePattern):
        name = ptrn.id.id.text
        env, store = bind_val(name, val, env, store)
        return EvalLhsResult(env, store)
    elif isinstance(ptrn, ns.SimpleAnonymousPattern):
        return EvalLhsResult(env, store)
    elif isinstance(ptrn, ns.SimpleFieldPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.SimpleParenPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.PatternList):
        raise NotImplementedError
    else:
        raise NotImplementedError(f"No impementation for pattern of type: {type(ptrn).__name__}")

def bind_val(name: str, val: Value, env: Environment, store: Store) -> Tuple[Environment, Store]:
    """
    Binds a value to a name. If the name exists in the environment, the previous binding is overwritten.
    Otherwise, a new binding is created.
    """
    if name in env:
        store = extend_store(store, val, env[name])
    else:
        env = extend_env(env, name, store.next_addr)
        store = extend_store(store, val)
    return env, store

def accumulate_values_from_exprs(exprs: List[AST], env: Environment, store: Store) -> Tuple[List[Value], Store]:
    values: List[Value] = []
    for expr in exprs:
        val, store = eval_expr(expr, env, store)
        values.append(val)
    return values, store

def wrap_values(values: List[Value], store: Store) -> EvalExprResult:
    if len(values) == 1:
        return EvalExprResult(values[0], store)
    else:
        return EvalExprResult(TupleVal(*values), store)
<commit_msg>Fix eval_stmt call for CompoundStmtBlock<commit_after>from .environment import *
from .store import *
from .value import *

import viper.parser.ast.nodes as ns

from viper.parser.ast.nodes import AST

from typing import List, NamedTuple, Optional, Tuple

STARTERS = [
    ns.SingleNewline, ns.SingleLine, ns.FileInput, ns.FileNewline, ns.FileStmt
]

STMTS = [
    ns.Stmt, ns.PlainStmt, ns.IfStmt, ns.ElifStmt, ns.ElseStmt, ns.Definition, ns.Parameter, ns.Arguments, ns.StmtBlock,
]

EXPRS = [
    ns.Expr, ns.IfExpr, ns.ElifExpr, ns.ElseExpr, ns.TestExprList, ns.TestExpr, ns.OrTestExpr, ns.AndTestExpr,
    ns.NotTestExpr, ns.OpExpr, ns.SubOpExpr, ns.AtomExpr, ns.Atom, ns.Trailer, ns.ExprBlock,
    ns.LhsExpr,
]

class EvalResult(NamedTuple):
    env: Environment
    store: Store
    val: Optional[Value]

class EvalStmtResult(NamedTuple):
    env: Environment
    store: Store
    val: Optional[Value]

class EvalExprResult(NamedTuple):
    val: Value
    store: Store

class EvalLhsResult(NamedTuple):
    maybe_env: Optional[Environment]
    store: Store

def start_eval(code: AST, env: Environment = None, store: Store = None,
               previous_result: EvalResult = None) -> EvalResult:
    if previous_result is not None:
        env = previous_result.env
        store = previous_result.store
    if env is None:
        env = empty_env()
    if store is None:
        store = empty_store()
    if any(map(lambda s: isinstance(code, s), STARTERS)):
        return eval_starter(code, env, store)
    elif any(map(lambda s: isinstance(code, s), STMTS)):
        stmt_res = eval_stmt(code, env, store)
        return EvalResult(stmt_res.env, stmt_res.store, stmt_res.val)
    elif any(map(lambda e: isinstance(code, e), EXPRS)):
        expr_res = eval_expr(code, env, store)
        return EvalResult(env, expr_res.store, expr_res.val)
    else:
        raise NotImplementedError(f"No evaluation rules for ASTs of type: {type(code).__name__}")

def eval_starter(starter: AST, env: Environment, store: Store) -> EvalResult:
    if isinstance(starter, ns.SingleNewline):
        return EvalResult(env, store, None)
    elif isinstance(starter, ns.SingleLine):
        return start_eval(starter.line, env, store)
    elif isinstance(starter, ns.FileInput):
        val = None
        for line in starter.lines:
            eval_res = eval_starter(line, env, store)
            val = eval_res.val
            env = eval_res.env
            store = eval_res.store
        return EvalResult(env, store, val)
    elif isinstance(starter, ns.FileNewline):
        return EvalResult(env, store, None)
    elif isinstance(starter, ns.FileStmt):
        return start_eval(starter.stmt, env, store)
    else:
        raise NotImplementedError(f"No evaluation rules for starters of type: {type(starter).__name__}")

def eval_stmt(stmt: AST, env: Environment, store: Store) -> EvalStmtResult:
    if isinstance(stmt, ns.SimpleStmt):
        return eval_stmt(stmt.stmt, env, store)
    elif isinstance(stmt, ns.ReturnStmt):
        raise NotImplementedError
    elif isinstance(stmt, ns.AssignStmt):
        # Evaluate right-hand side first.
        val, store = eval_expr(stmt.expr, env, store)
        # Pass the value to the left-hand side evaluation.
        maybe_env, store = eval_lhs_expr(stmt.lhs, env, store, val)
        if maybe_env is not None:
            env = maybe_env
        return EvalStmtResult(env, store, None)
    elif isinstance(stmt, ns.EmptyStmt):
        return EvalStmtResult(env, store, None)
    elif isinstance(stmt, ns.IfStmt):
        val, store2 = eval_expr(stmt.cond, env, store)
        if isinstance(val, TrueVal):
            return eval_stmt(stmt.then_body, env, store2)
        elif isinstance(val, FalseVal):
            for elif_stmt in stmt.elif_stmts:
                val, store3 = eval_expr(elif_stmt.cond, env, store)
                if isinstance(val, TrueVal):
                    return eval_stmt(elif_stmt.elif_body, env, store3)
            if stmt.else_stmt is not None:
                return eval_stmt(stmt.else_stmt.else_body, env, store)
            else:
                return EvalStmtResult(env, store, None)
        else:
            raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
    elif isinstance(stmt, ns.FuncDef):
        closure = CloVal(stmt.params, stmt.body, env)
        env, store = bind_val(stmt.name.text, closure, env, store)
        return EvalStmtResult(env, store, None)
    elif isinstance(stmt, ns.ClassDef):
        raise NotImplementedError
    elif isinstance(stmt, ns.InterfaceDef):
        raise NotImplementedError
    elif isinstance(stmt, ns.DataDef):
        raise NotImplementedError
    elif isinstance(stmt, ns.Arguments):
        raise NotImplementedError
    elif isinstance(stmt, ns.SimpleStmtBlock):
        return eval_stmt(stmt.stmt, env, store)
    elif isinstance(stmt, ns.CompoundStmtBlock):
        for sub_stmt in stmt.stmts:
            stmt_res = eval_stmt(sub_stmt, env, store)
            env = stmt_res.env
            store = stmt_res.store
        return EvalStmtResult(env, store, None)
    else:
        raise NotImplementedError(f"No implementation for statement of type: {type(stmt).__name__}")

def eval_lhs_expr(expr: AST, env: Environment, store: Store, val: Value) -> EvalLhsResult:
    """
    Attempts to perform a binding of a value. If the shape of the value and the shape of the left-hand side
    are compatible, the binding is performed. An environment and store are returned. If the shapes are not
    compatible, only the store is returned and the environment will be a None.

    :param expr: a left-hand side expression
    :param env: an environment
    :param store: a store
    :param val: a value to bind
    :return: a tuple of a Maybe(env) (as an Environment or a None) and a store
    """
    if isinstance(expr, ns.Pattern):
        return eval_pattern(expr, env, store, val)
    else:
        raise NotImplementedError(f"No implementation for left-hand-side expression of type: {type(expr).__name__}")

def eval_expr(expr: AST, env: Environment, store: Store) -> EvalExprResult:
    if isinstance(expr, ns.IfExpr):
        val, store2 = eval_expr(expr.cond, env, store)
        if isinstance(val, TrueVal):
            return eval_expr(expr.then_body, env, store2)
        elif isinstance(val, FalseVal):
            for elif_expr in expr.elif_exprs:
                val, store3 = eval_expr(elif_expr.cond, env, store)
                if isinstance(val, TrueVal):
                    return eval_expr(elif_expr.elif_body, env, store3)
            if expr.else_expr is not None:
                return eval_expr(expr.else_expr.else_body, env, store)
            else:
                return EvalExprResult(UnitVal(), store)
    elif isinstance(expr, ns.TestExprList):
        vals, store = accumulate_values_from_exprs(expr.tests, env, store)
        return wrap_values(vals, store)
    elif isinstance(expr, ns.TestExpr):
        return eval_expr(expr.test, env, store)
    elif isinstance(expr, ns.OrTestExpr):
        vals, store = accumulate_values_from_exprs(expr.tests, env, store)
        val, store = wrap_values(vals, store)
        if isinstance(val, TupleVal):
            for subval in val.vals:
                if not isinstance(subval, BoolVal):
                    raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
                if isinstance(subval, TrueVal):
                    return EvalExprResult(TrueVal(), store)
            return EvalExprResult(FalseVal(), store)
        else:
            return EvalExprResult(val, store)
    elif isinstance(expr, ns.AndTestExpr):
        vals, store = accumulate_values_from_exprs(expr.tests, env, store)
        val, store = wrap_values(vals, store)
        if isinstance(val, TupleVal):
            for subval in val.vals:
                if not isinstance(subval, BoolVal):
                    raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
                if isinstance(subval, FalseVal):
                    return EvalExprResult(FalseVal(), store)
            return EvalExprResult(TrueVal(), store)
        else:
            return EvalExprResult(val, store)
    elif isinstance(expr, ns.NegatedTestExpr):
        val, store = eval_expr(expr.op_expr, env, store)
        if not isinstance(val, BoolVal):
            raise RuntimeError(f"Not a boolean value: {val}")  # TODO: Use a custom error.
        if isinstance(val, TrueVal):
            return EvalExprResult(FalseVal(), store)
        else:
            return EvalExprResult(TrueVal(), store)
    elif isinstance(expr, ns.NotNegatedTestExpr):
        return eval_expr(expr.op_expr, env, store)
    elif isinstance(expr, ns.OpExpr):
        if expr.left_op is not None:
            raise NotImplementedError
        val, store = eval_expr(expr.atom, env, store)
        if expr.sub_op_exprs:
            raise NotImplementedError
        if expr.right_op is not None:
            raise NotImplementedError
        return eval_expr(expr.atom, env, store)
    elif isinstance(expr, ns.AtomExpr):
        val, store = eval_expr(expr.atom, env, store)
        for trailer in expr.trailers:
            if isinstance(trailer, ns.Call):
                if isinstance(val, CloVal):
                    args, store = accumulate_values_from_exprs(trailer.args, env, store)
                    if len(val.params) != len(args):
                        if len(args) > len(val.params):
                            raise RuntimeError(f"Too many arguments specified for call to function {val.name}")  # TODO: Use a custom error.
                        else:
                            # TODO: Automatic currying could be implemented here.
                            raise RuntimeError(f"Not enough arguments specified for call to function {val.name}")  # TODO: Use a custom error.
                    inner_env = val.env
                    for i in range(len(val.params)):
                        param = val.params[i]
                        arg = args[i]
                        inner_env, store = bind_val(param.internal.text, arg, inner_env, store)
                    stmt_res = eval_stmt(val.code, inner_env, store)
                    return EvalExprResult(stmt_res.val, stmt_res.store)
                else:
                    raise RuntimeError(f"Cannot apply arguments to non-closure value of type: {type(val).__name__}")  # TODO: Use a custom error.
            elif isinstance(trailer, ns.Field):
                raise NotImplementedError
            else:
                raise NotImplementedError(f"No implementation for trailer of type: {type(trailer).__name__}")
        return EvalExprResult(val, store)
    elif isinstance(expr, ns.ParenAtom):
        if expr.tests is None:
            return EvalExprResult(UnitVal(), store)
        else:
            return eval_expr(expr.tests, env, store)
    elif isinstance(expr, ns.NameAtom):
        name = expr.name.text
        if name in env:
            return EvalExprResult(store[env[name]], store)
        else:
            raise RuntimeError(f"No such name in environment: {name}")
    elif isinstance(expr, ns.NumberAtom):
        return EvalExprResult(NumVal(expr.num.text), store)
    elif isinstance(expr, ns.EllipsisAtom):
        return EvalExprResult(EllipsisVal(), store)
    elif isinstance(expr, ns.TrueAtom):
        return EvalExprResult(TrueVal(), store)
    elif isinstance(expr, ns.FalseAtom):
        return EvalExprResult(FalseVal(), store)
    elif isinstance(expr, ns.SimpleExprBlock):
        return eval_expr(expr.expr, env, store)
    elif isinstance(expr, ns.IndentedExprBlock):
        return eval_expr(expr.expr, env, store)
    else:
        raise NotImplementedError(f"No implementation for expression of type: {type(expr).__name__}")

def eval_pattern(ptrn: ns.Pattern, env: Environment, store: Store, val: Value) -> EvalLhsResult:
    """
    Binds a value to a pattern, if the value's type and the pattern's shape align.

    :param ptrn: a pattern to match against
    :param env: an environment
    :param store: a store
    :param val: a value to bind
    :return: a tuple of a Maybe(env) (as an Environment or a None) and a store
    """
    if isinstance(ptrn, ns.TypedVariablePattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.TypedAnonymousPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.TypedFieldPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.SimpleVariablePattern):
        name = ptrn.id.id.text
        env, store = bind_val(name, val, env, store)
        return EvalLhsResult(env, store)
    elif isinstance(ptrn, ns.SimpleAnonymousPattern):
        return EvalLhsResult(env, store)
    elif isinstance(ptrn, ns.SimpleFieldPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.SimpleParenPattern):
        raise NotImplementedError
    elif isinstance(ptrn, ns.PatternList):
        raise NotImplementedError
    else:
        raise NotImplementedError(f"No impementation for pattern of type: {type(ptrn).__name__}")

def bind_val(name: str, val: Value, env: Environment, store: Store) -> Tuple[Environment, Store]:
    """
    Binds a value to a name. If the name exists in the environment, the previous binding is overwritten.
    Otherwise, a new binding is created.
    """
    if name in env:
        store = extend_store(store, val, env[name])
    else:
        env = extend_env(env, name, store.next_addr)
        store = extend_store(store, val)
    return env, store

def accumulate_values_from_exprs(exprs: List[AST], env: Environment, store: Store) -> Tuple[List[Value], Store]:
    values: List[Value] = []
    for expr in exprs:
        val, store = eval_expr(expr, env, store)
        values.append(val)
    return values, store

def wrap_values(values: List[Value], store: Store) -> EvalExprResult:
    if len(values) == 1:
        return EvalExprResult(values[0], store)
    else:
        return EvalExprResult(TupleVal(*values), store)
<|endoftext|>


<commit_before>#------------------------------------------------------------------------------
#
#  Copyright (c) 2015, Enthought, Inc.
#  All rights reserved.
#
#  This software is provided without warranty under the terms of the BSD
#  license included in LICENSE.txt and may be redistributed only
#  under the conditions described in the aforementioned license.  The license
#  is also available online at http://www.enthought.com/licenses/BSD.txt
#
#  Thanks for using Enthought open source!
#
#------------------------------------------------------------------------------
import sys

def mock_modules():
    """ Mock missing modules if necessary """

    MOCK_MODULES = []
    MOCK_TYPES = []

    try:
        import pwd
    except ImportError:
        MOCK_MODULES = ['pwd']
        MOCK_TYPES = []
    else:
        del pwd

    try:
        import grp
    except ImportError:
        MOCK_MODULES.append('grp')
    else:
        del grp

    try:
        import pamela
    except ImportError:
        MOCK_MODULES.append('pamela')
    else:
        del pamela

    try:
        import jupyterhub
    except ImportError:
        MOCK_MODULES.append('jupyterhub')
    else:
        del pamela

    TYPES = {
        mock_type: type(mock_type, bases, {'__module__': path})
        for path, mock_type, bases in MOCK_TYPES}

    class DocMock(object):

        def __init__(self, *args, **kwds):
            if '__doc_mocked_name__' in kwds:
                self.__docmock_name__ = kwds['__docmocked_name__']
            else:
                self.__docmock_name__ = 'Unknown'

        def __getattr__(self, name):
            if name in ('__file__', '__path__'):
                return '/dev/null'
            elif name == '__all__':
                return []
            else:
                return TYPES.get(name, DocMock(__docmock_name__=name))

        def __call__(self, *args, **kwards):
            return DocMock()

        def __iter__(self):
            return self

        def __next__(self):
            raise StopIteration()

        def next(self):
            raise StopIteration()

        @property
        def __name__(self):
            return self.__docmock_name__

        def __repr__(self):
            return '<DocMock.{}>'.format(self.__name__)

    sys.modules.update(
        (mod_name, DocMock(mocked_name=mod_name)) for mod_name in MOCK_MODULES)
    print('mocking modules {} and types {}'.format(MOCK_MODULES, MOCK_TYPES))
<commit_msg>Verify that jhub is not there.<commit_after>#------------------------------------------------------------------------------
#
#  Copyright (c) 2015, Enthought, Inc.
#  All rights reserved.
#
#  This software is provided without warranty under the terms of the BSD
#  license included in LICENSE.txt and may be redistributed only
#  under the conditions described in the aforementioned license.  The license
#  is also available online at http://www.enthought.com/licenses/BSD.txt
#
#  Thanks for using Enthought open source!
#
#------------------------------------------------------------------------------
import sys

def mock_modules():
    """ Mock missing modules if necessary """

    MOCK_MODULES = []
    MOCK_TYPES = []

    try:
        import pwd
    except ImportError:
        MOCK_MODULES = ['pwd']
        MOCK_TYPES = []
    else:
        del pwd

    try:
        import grp
    except ImportError:
        MOCK_MODULES.append('grp')
    else:
        del grp

    try:
        import pamela
    except ImportError:
        MOCK_MODULES.append('pamela')
    else:
        del pamela

    try:
        import jupyterhub
        print("uh? Jupyterhub package found ", jupyterhub.__version__)
    except ImportError:
        MOCK_MODULES.append('jupyterhub')
    else:
        del jupyterhub

    TYPES = {
        mock_type: type(mock_type, bases, {'__module__': path})
        for path, mock_type, bases in MOCK_TYPES}

    class DocMock(object):

        def __init__(self, *args, **kwds):
            if '__doc_mocked_name__' in kwds:
                self.__docmock_name__ = kwds['__docmocked_name__']
            else:
                self.__docmock_name__ = 'Unknown'

        def __getattr__(self, name):
            if name in ('__file__', '__path__'):
                return '/dev/null'
            elif name == '__all__':
                return []
            else:
                return TYPES.get(name, DocMock(__docmock_name__=name))

        def __call__(self, *args, **kwards):
            return DocMock()

        def __iter__(self):
            return self

        def __next__(self):
            raise StopIteration()

        def next(self):
            raise StopIteration()

        @property
        def __name__(self):
            return self.__docmock_name__

        def __repr__(self):
            return '<DocMock.{}>'.format(self.__name__)

    sys.modules.update(
        (mod_name, DocMock(mocked_name=mod_name)) for mod_name in MOCK_MODULES)
    print('mocking modules {} and types {}'.format(MOCK_MODULES, MOCK_TYPES))
<|endoftext|>


<commit_before>from trakt.client import TraktClient
from trakt.helpers import has_attribute

__version__ = '0.6.1-beta'

class TraktMeta(type):
    def __getattr__(self, name):
        if has_attribute(self, name):
            return super(TraktMeta, self).__getattribute__(name)

        if self.client is None:
            self.construct()

        return getattr(self.client, name)

    def __setattr__(self, name, value):
        if has_attribute(self, name):
            return super(TraktMeta, self).__setattr__(name, value)

        if self.client is None:
            self.construct()

        setattr(self.client, name, value)

    def __getitem__(self, key):
        if self.client is None:
            self.construct()

        return self.client[key]

class Trakt(object):
    __metaclass__ = TraktMeta

    client = None

    @classmethod
    def construct(cls):
        cls.client = TraktClient()
<commit_msg>Bumped version to 0.7.0<commit_after>from trakt.client import TraktClient
from trakt.helpers import has_attribute

__version__ = '0.7.0'

class TraktMeta(type):
    def __getattr__(self, name):
        if has_attribute(self, name):
            return super(TraktMeta, self).__getattribute__(name)

        if self.client is None:
            self.construct()

        return getattr(self.client, name)

    def __setattr__(self, name, value):
        if has_attribute(self, name):
            return super(TraktMeta, self).__setattr__(name, value)

        if self.client is None:
            self.construct()

        setattr(self.client, name, value)

    def __getitem__(self, key):
        if self.client is None:
            self.construct()

        return self.client[key]

class Trakt(object):
    __metaclass__ = TraktMeta

    client = None

    @classmethod
    def construct(cls):
        cls.client = TraktClient()
<|endoftext|>