<commit_before>import time
import sys
import os
import re

curdir = os.path.dirname(__file__)
sys.path += [curdir, os.path.dirname(curdir)]
import utils
from log import log

from classes import *
from ts6 import TS6Protocol

class HybridProtocol(TS6Protocol):
    def __init__(self, irc):
        # This protocol module inherits from the TS6 protocol.
        super().__init__(irc)

        self.casemapping = 'ascii'
        self.caps = {}
        self.hook_map = {'EOB': 'ENDBURST', 'TBURST': 'TOPIC'}

    def connect(self):
        """Initializes a connection to a server."""
        ts = self.irc.start_ts

        f = self.irc.send
        # Valid keywords (from mostly InspIRCd's named modes):
        # admin allowinvite autoop ban banexception blockcolor
        # c_registered exemptchanops filter forward flood halfop history invex
        # inviteonly joinflood key kicknorejoin limit moderated nickflood
        # noctcp noextmsg nokick noknock nonick nonotice official-join op
        # operonly opmoderated owner permanent private redirect regonly
        # regmoderated secret sslonly stripcolor topiclock voice

        # https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
        cmodes = {
            # TS6 generic modes:
            'op': 'o', 'halfop': 'h', 'voice': 'v', 'ban': 'b', 'key': 'k',
            'limit': 'l', 'moderated': 'm', 'noextmsg': 'n',
            'secret': 's', 'topiclock': 't',
            # hybrid-specific modes:
            'blockcolor': 'c', 'inviteonly': 'i', 'noctcp': 'C',
            'regmoderated': 'M', 'operonly': 'O', 'regonly': 'R',
            'sslonly': 'S', 'banexception': 'e', 'paranoia': 'p',
            'registered': 'r', 'invex': 'I',
            # Now, map all the ABCD type modes:
            '*A': 'beI', '*B': 'k', '*C': 'l', '*D': 'cimnprstCMORS'
        }

        self.irc.cmodes.update(cmodes)

        # Same thing with umodes:
        # bot callerid cloak deaf_commonchan helpop hidechans hideoper invisible oper
        # regdeaf servprotect showwhois snomask u_registered u_stripcolor wallops
        umodes = {
            'oper': 'o', 'invisible': 'i', 'wallops': 'w', 'chary_locops': 'l',
            'cloak': 'x', 'hidechans': 'p', 'regdeaf': 'R', 'deaf': 'D',
            'callerid': 'g', 'showadmin': 'a', 'softcallerid': 'G', 'hideops': 'H',
            'webirc': 'W', 'client_connections': 'c', 'bad_client_connections': 'u',
            'rejected_clients': 'j', 'skill_notices': 'k', 'fullauthblock': 'f',
            'remote_client_connections': 'F', 'admin_requests': 'y', 'debug': 'd',
            'nickchange_notices': 'n', 'hideidle': 'q', 'registered': 'r',
            'smessages': 's', 'ssl': 'S', 'sjoins': 'e', 'botfloods': 'b',
            # Now, map all the ABCD type modes:
            '*A': '', '*B': '', '*C': '', '*D': 'oiwlxpRDg'
        }

        self.irc.umodes.update(umodes)

        # halfops is mandatory on Hybrid
        self.irc.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}

        # https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
        f('PASS %s TS 6 %s' % (self.irc.serverdata["sendpass"], self.irc.sid))

        # We request the following capabilities (for hybrid):

        # ENCAP: message encapsulation for certain commands
        # EX: Support for ban exemptions (+e)
        # IE: Support for invite exemptions (+e)
        # CHW: Allow sending messages to @#channel and the like.
        # KNOCK: Support for /knock
        # SVS: Deal with extended NICK/UID messages that contain service IDs/stamps
        # TBURST: Topic Burst command; we send this in topicBurst
        # DLN: DLINE command
        # UNDLN: UNDLINE command
        # KLN: KLINE command
        # UNKLN: UNKLINE command
        # HOPS: Supports HALFOPS
        # CHW: Can do channel wall (@#)
        # CLUSTER: Supports server clustering
        # EOB: Supports EOB (end of burst) command
        f('CAPAB :TBURST DLN KNOCK UNDLN UNKLN KLN ENCAP IE EX HOPS CHW SVS CLUSTER EOB QS')

        f('SERVER %s 0 :%s' % (self.irc.serverdata["hostname"],
                               self.irc.serverdata.get('serverdesc') or self.irc.botdata['serverdesc']))

        # send endburst now
        self.irc.send(':%s EOB' % (self.irc.sid,))

    def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
            server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
            manipulatable=False):
        """Spawns a client with nick <nick> on the given IRC connection.
        Note: No nick collision / valid nickname checks are done here; it is
        up to plugins to make sure they don't introduce anything invalid."""
        server = server or self.irc.sid
        if not self.irc.isInternalServer(server):
            raise ValueError('Server %r is not a PyLink internal PseudoServer!' % server)
        # Create an UIDGenerator instance for every SID, so that each gets
        # distinct values.
        uid = self.uidgen.setdefault(server, utils.TS6UIDGenerator(server)).next_uid()
        # EUID:
        # parameters: nickname, hopcount, nickTS, umodes, username,
        # visible hostname, IP address, UID, real hostname, account name, gecos
        ts = ts or int(time.time())
        realname = realname or self.irc.botdata['realname']
        realhost = realhost or host
        raw_modes = utils.joinModes(modes)
        u = self.irc.users[uid] = IrcUser(nick, ts, uid, ident=ident, host=host, realname=realname,
            realhost=realhost, ip=ip, manipulatable=manipulatable)
        utils.applyModes(self.irc, uid, modes)
        self.irc.servers[server].users.add(uid)
        self._send(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
                "* :{realname}".format(ts=ts, host=host,
                nick=nick, ident=ident, uid=uid,
                modes=raw_modes, ip=ip, realname=realname))
        return u

    def updateClient(self, target, field, text):
        """Updates the ident, host, or realname of a PyLink client."""
        # https://github.com/ircd-hybrid/ircd-hybrid/blob/58323b8/modules/m_svsmode.c#L40-L103
        # parv[0] = command
        # parv[1] = nickname <-- UID works too -GLolol
        # parv[2] = TS <-- Of the user, not the current time. -GLolol
        # parv[3] = mode
        # parv[4] = optional argument (services account, vhost)
        field = field.upper()

        ts = irc.users[target].ts

        if field == 'HOST':
            self.irc.users[target].host = text
            # On Hybrid, it appears that host changing is actually just forcing umode
            # "+x <hostname>" on the target. -GLolol
            self._send(self.irc.sid, 'SVSMODE %s %s +x %s' % (target, ts, text))
        else:
            raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field)

    def topicBurst(self, numeric, target, text):
        """Sends a topic change from a PyLink server. This is usually used on burst."""
        # <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
        if not self.irc.isInternalServer(numeric):
            raise LookupError('No such PyLink server exists.')

        ts = self.irc.channels[target].ts
        servername = self.irc.servers[numeric].name

        self._send(numeric, 'TBURST %s %s %s %s :%s' % (ts, target, int(time.time()), servername, text))
        self.irc.channels[target].topic = text
        self.irc.channels[target].topicset = True

    # command handlers

    def handle_capab(self, numeric, command, args):
        # We only get a list of keywords here. Hybrid obviously assumes that
        # we know what modes it supports (indeed, this is a standard list).
        # <- CAPAB :UNDLN UNKLN KLN TBURST KNOCK ENCAP DLN IE EX HOPS CHW SVS CLUSTER EOB QS
        self.irc.caps = caps = args[0].split()
        for required_cap in ('EX', 'IE', 'SVS', 'EOB', 'HOPS', 'QS', 'TBURST', 'SVS'):
             if required_cap not in caps:
                 raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))

        log.debug('(%s) self.irc.connected set!', self.irc.name)
        self.irc.connected.set()

    def handle_uid(self, numeric, command, args):
        """
        Handles Hybrid-style UID commands (user introduction). This is INCOMPATIBLE
        with standard TS6 implementations, as the arguments are slightly different.
        """
        # <- :0UY UID dan 1 1451041551 +Facdeiklosuw ~ident localhost 127.0.0.1 0UYAAAAAB * :realname
        nick = args[0]
        ts, modes, ident, host, ip, uid, account, realname = args[2:10]
        if account == '*':
            account = None
        log.debug('(%s) handle_uid: got args nick=%s ts=%s uid=%s ident=%s '
                  'host=%s realname=%s ip=%s', self.irc.name, nick, ts, uid,
                  ident, host, realname, ip)

        self.irc.users[uid] = IrcUser(nick, ts, uid, ident, host, realname, host, ip)

        parsedmodes = utils.parseModes(self.irc, uid, [modes])
        log.debug('(%s) handle_uid: Applying modes %s for %s', self.irc.name, parsedmodes, uid)
        utils.applyModes(self.irc, uid, parsedmodes)
        self.irc.servers[numeric].users.add(uid)

        # Call the OPERED UP hook if +o is being added to the mode list.
        if ('+o', None) in parsedmodes:
            self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC_Operator'}])

        # Set the account name if present
        if account:
            self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])

        return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip}

    def handle_tburst(self, numeric, command, args):
        """Handles incoming topic burst (TBURST) commands."""
        # <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
        channel = args[1].lower()
        ts = args[2]
        setter = args[3]
        topic = args[-1]
        self.irc.channels[channel].topic = topic
        self.irc.channels[channel].topicset = True
        return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}

    def handle_eob(self, numeric, command, args):
        log.debug('(%s) end of burst received', self.irc.name)
        return {}

Class = HybridProtocol
<commit_msg>hybrid: Fix typo in 0be83449fb0 (#198)<commit_after>import time
import sys
import os
import re

curdir = os.path.dirname(__file__)
sys.path += [curdir, os.path.dirname(curdir)]
import utils
from log import log

from classes import *
from ts6 import TS6Protocol

class HybridProtocol(TS6Protocol):
    def __init__(self, irc):
        # This protocol module inherits from the TS6 protocol.
        super().__init__(irc)

        self.casemapping = 'ascii'
        self.caps = {}
        self.hook_map = {'EOB': 'ENDBURST', 'TBURST': 'TOPIC'}

    def connect(self):
        """Initializes a connection to a server."""
        ts = self.irc.start_ts

        f = self.irc.send
        # Valid keywords (from mostly InspIRCd's named modes):
        # admin allowinvite autoop ban banexception blockcolor
        # c_registered exemptchanops filter forward flood halfop history invex
        # inviteonly joinflood key kicknorejoin limit moderated nickflood
        # noctcp noextmsg nokick noknock nonick nonotice official-join op
        # operonly opmoderated owner permanent private redirect regonly
        # regmoderated secret sslonly stripcolor topiclock voice

        # https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
        cmodes = {
            # TS6 generic modes:
            'op': 'o', 'halfop': 'h', 'voice': 'v', 'ban': 'b', 'key': 'k',
            'limit': 'l', 'moderated': 'm', 'noextmsg': 'n',
            'secret': 's', 'topiclock': 't',
            # hybrid-specific modes:
            'blockcolor': 'c', 'inviteonly': 'i', 'noctcp': 'C',
            'regmoderated': 'M', 'operonly': 'O', 'regonly': 'R',
            'sslonly': 'S', 'banexception': 'e', 'paranoia': 'p',
            'registered': 'r', 'invex': 'I',
            # Now, map all the ABCD type modes:
            '*A': 'beI', '*B': 'k', '*C': 'l', '*D': 'cimnprstCMORS'
        }

        self.irc.cmodes.update(cmodes)

        # Same thing with umodes:
        # bot callerid cloak deaf_commonchan helpop hidechans hideoper invisible oper
        # regdeaf servprotect showwhois snomask u_registered u_stripcolor wallops
        umodes = {
            'oper': 'o', 'invisible': 'i', 'wallops': 'w', 'chary_locops': 'l',
            'cloak': 'x', 'hidechans': 'p', 'regdeaf': 'R', 'deaf': 'D',
            'callerid': 'g', 'showadmin': 'a', 'softcallerid': 'G', 'hideops': 'H',
            'webirc': 'W', 'client_connections': 'c', 'bad_client_connections': 'u',
            'rejected_clients': 'j', 'skill_notices': 'k', 'fullauthblock': 'f',
            'remote_client_connections': 'F', 'admin_requests': 'y', 'debug': 'd',
            'nickchange_notices': 'n', 'hideidle': 'q', 'registered': 'r',
            'smessages': 's', 'ssl': 'S', 'sjoins': 'e', 'botfloods': 'b',
            # Now, map all the ABCD type modes:
            '*A': '', '*B': '', '*C': '', '*D': 'oiwlxpRDg'
        }

        self.irc.umodes.update(umodes)

        # halfops is mandatory on Hybrid
        self.irc.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}

        # https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
        f('PASS %s TS 6 %s' % (self.irc.serverdata["sendpass"], self.irc.sid))

        # We request the following capabilities (for hybrid):

        # ENCAP: message encapsulation for certain commands
        # EX: Support for ban exemptions (+e)
        # IE: Support for invite exemptions (+e)
        # CHW: Allow sending messages to @#channel and the like.
        # KNOCK: Support for /knock
        # SVS: Deal with extended NICK/UID messages that contain service IDs/stamps
        # TBURST: Topic Burst command; we send this in topicBurst
        # DLN: DLINE command
        # UNDLN: UNDLINE command
        # KLN: KLINE command
        # UNKLN: UNKLINE command
        # HOPS: Supports HALFOPS
        # CHW: Can do channel wall (@#)
        # CLUSTER: Supports server clustering
        # EOB: Supports EOB (end of burst) command
        f('CAPAB :TBURST DLN KNOCK UNDLN UNKLN KLN ENCAP IE EX HOPS CHW SVS CLUSTER EOB QS')

        f('SERVER %s 0 :%s' % (self.irc.serverdata["hostname"],
                               self.irc.serverdata.get('serverdesc') or self.irc.botdata['serverdesc']))

        # send endburst now
        self.irc.send(':%s EOB' % (self.irc.sid,))

    def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
            server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
            manipulatable=False):
        """Spawns a client with nick <nick> on the given IRC connection.
        Note: No nick collision / valid nickname checks are done here; it is
        up to plugins to make sure they don't introduce anything invalid."""
        server = server or self.irc.sid
        if not self.irc.isInternalServer(server):
            raise ValueError('Server %r is not a PyLink internal PseudoServer!' % server)
        # Create an UIDGenerator instance for every SID, so that each gets
        # distinct values.
        uid = self.uidgen.setdefault(server, utils.TS6UIDGenerator(server)).next_uid()
        # EUID:
        # parameters: nickname, hopcount, nickTS, umodes, username,
        # visible hostname, IP address, UID, real hostname, account name, gecos
        ts = ts or int(time.time())
        realname = realname or self.irc.botdata['realname']
        realhost = realhost or host
        raw_modes = utils.joinModes(modes)
        u = self.irc.users[uid] = IrcUser(nick, ts, uid, ident=ident, host=host, realname=realname,
            realhost=realhost, ip=ip, manipulatable=manipulatable)
        utils.applyModes(self.irc, uid, modes)
        self.irc.servers[server].users.add(uid)
        self._send(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
                "* :{realname}".format(ts=ts, host=host,
                nick=nick, ident=ident, uid=uid,
                modes=raw_modes, ip=ip, realname=realname))
        return u

    def updateClient(self, target, field, text):
        """Updates the ident, host, or realname of a PyLink client."""
        # https://github.com/ircd-hybrid/ircd-hybrid/blob/58323b8/modules/m_svsmode.c#L40-L103
        # parv[0] = command
        # parv[1] = nickname <-- UID works too -GLolol
        # parv[2] = TS <-- Of the user, not the current time. -GLolol
        # parv[3] = mode
        # parv[4] = optional argument (services account, vhost)
        field = field.upper()

        ts = self.irc.users[target].ts

        if field == 'HOST':
            self.irc.users[target].host = text
            # On Hybrid, it appears that host changing is actually just forcing umode
            # "+x <hostname>" on the target. -GLolol
            self._send(self.irc.sid, 'SVSMODE %s %s +x %s' % (target, ts, text))
        else:
            raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field)

    def topicBurst(self, numeric, target, text):
        """Sends a topic change from a PyLink server. This is usually used on burst."""
        # <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
        if not self.irc.isInternalServer(numeric):
            raise LookupError('No such PyLink server exists.')

        ts = self.irc.channels[target].ts
        servername = self.irc.servers[numeric].name

        self._send(numeric, 'TBURST %s %s %s %s :%s' % (ts, target, int(time.time()), servername, text))
        self.irc.channels[target].topic = text
        self.irc.channels[target].topicset = True

    # command handlers

    def handle_capab(self, numeric, command, args):
        # We only get a list of keywords here. Hybrid obviously assumes that
        # we know what modes it supports (indeed, this is a standard list).
        # <- CAPAB :UNDLN UNKLN KLN TBURST KNOCK ENCAP DLN IE EX HOPS CHW SVS CLUSTER EOB QS
        self.irc.caps = caps = args[0].split()
        for required_cap in ('EX', 'IE', 'SVS', 'EOB', 'HOPS', 'QS', 'TBURST', 'SVS'):
             if required_cap not in caps:
                 raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))

        log.debug('(%s) self.irc.connected set!', self.irc.name)
        self.irc.connected.set()

    def handle_uid(self, numeric, command, args):
        """
        Handles Hybrid-style UID commands (user introduction). This is INCOMPATIBLE
        with standard TS6 implementations, as the arguments are slightly different.
        """
        # <- :0UY UID dan 1 1451041551 +Facdeiklosuw ~ident localhost 127.0.0.1 0UYAAAAAB * :realname
        nick = args[0]
        ts, modes, ident, host, ip, uid, account, realname = args[2:10]
        if account == '*':
            account = None
        log.debug('(%s) handle_uid: got args nick=%s ts=%s uid=%s ident=%s '
                  'host=%s realname=%s ip=%s', self.irc.name, nick, ts, uid,
                  ident, host, realname, ip)

        self.irc.users[uid] = IrcUser(nick, ts, uid, ident, host, realname, host, ip)

        parsedmodes = utils.parseModes(self.irc, uid, [modes])
        log.debug('(%s) handle_uid: Applying modes %s for %s', self.irc.name, parsedmodes, uid)
        utils.applyModes(self.irc, uid, parsedmodes)
        self.irc.servers[numeric].users.add(uid)

        # Call the OPERED UP hook if +o is being added to the mode list.
        if ('+o', None) in parsedmodes:
            self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC_Operator'}])

        # Set the account name if present
        if account:
            self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])

        return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip}

    def handle_tburst(self, numeric, command, args):
        """Handles incoming topic burst (TBURST) commands."""
        # <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
        channel = args[1].lower()
        ts = args[2]
        setter = args[3]
        topic = args[-1]
        self.irc.channels[channel].topic = topic
        self.irc.channels[channel].topicset = True
        return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}

    def handle_eob(self, numeric, command, args):
        log.debug('(%s) end of burst received', self.irc.name)
        return {}

Class = HybridProtocol
<|endoftext|>


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

import argparse
import curses
import curses.ascii
import os
import sys
import text_unidecode
import typing

# constants

DESCRIPTION = "shakyo2d is a tool to learn about something just copying it " \
              "by hand."

TTY_DEVICE_FILE = "/dev/tty" # POSIX compliant

QUIT_CHARS = {chr(curses.ascii.ESC), curses.ascii.ctrl('[')}
DELETE_CHARS = {chr(curses.ascii.DEL), chr(curses.ascii.BS),
                chr(curses.KEY_BACKSPACE), chr(curses.KEY_DC)}
CLEAR_CHAR = curses.ascii.ctrl('u')
CHEAT_CHAR = curses.ascii.ctrl('n')

CAN_CHEAT = False
SPACES_PER_TAB = 2

ATTR_CORRECT = curses.A_NORMAL
ATTR_ERROR = curses.A_REVERSE

# the order of bugs

def error(*message):
  print("ERROR:", *message, file=sys.stderr)

def fail(*message):
  error(*message)
  exit(1)

# classes

class ConsoleApi:
  def __init__(self, window):
    window.keypad(True)
    window.scrollok(True)
    self.__window = window

  def __save_position(method):
    def wrapper(self, *args, **keyword_args):
      position = self.__window.getyx()
      method(self, *args, **keyword_args)
      self.__window.move(*position)
    return wrapper

  @property
  def screen_height(self):
    return self.__window.getmaxyx()[0]

  @property
  def screen_width(self):
    return self.__window.getmaxyx()[1]

  def get_char(self) -> typing.Optional[str]:
    char = self.__window.getkey()
    return char if self.__is_ascii_char(char) else None

  @__save_position
  def put_char(self, char: str, attr=curses.A_NORMAL):
    assert self.__is_ascii_char(char)
    self.__window.addch(ord(char), attr)

  @__save_position
  def put_line(self, line_pos, text):
    assert len(text) <= self.screen_width
    self.__window.move(line_pos, 0)
    self.__window.clrtoeol()
    self.__window.addstr(line_pos, 0, text)

  def move(self, y, x):
    self.__window.move(y, x)

  def move_right(self):
    y, x = self.__window.getyx()
    if x == self.screen_width - 1: return
    self.__window.move(y, x + 1)

  def move_left(self):
    y, x = self.__window.getyx()
    if x == 0: return
    self.__window.move(y, x - 1)

  @__save_position
  def clear(self):
    self.__window.clear()

  @__save_position
  def scroll(self):
    self.__window.scroll()

  @staticmethod
  def __is_ascii_char(char):
    return len(char) == 1 and curses.ascii.isascii(char)

class TypingGame:
  def __init__(self, api, example_file):
    self.__api = api
    self.__example_text = FormattedText(example_file,
                                        line_length=(api.screen_width - 1))
    self.__geometry = Geometry(api.screen_height)
    self.__input_text = ""
    if self.__example_text[0] == None:
      raise Exception("No line can be read from example souce.")

  def play(self):
    self.__initialize_screen()

    game_over = False
    while not game_over:
      char = self.__get_char()

      if char in QUIT_CHARS:
        return
      elif char == CLEAR_CHAR:
        self.__clear_input_text()
      elif char in DELETE_CHARS:
        self.__delete_char()
      elif char == CHEAT_CHAR and CAN_CHEAT:
        game_over = self.__cheat()
      elif curses.ascii.isprint(char) or curses.ascii.isspace(char):
        game_over = self.__add_char(char)

  def __get_char(self):
    char = self.__api.get_char()
    while char == None:
      char = self.__api.get_char()
    return char

  def __add_char(self, char: str) -> bool:
    go_to_next_line = char == '\n' \
                      and self.__input_text == self.__example_text[0]
    if go_to_next_line and self.__example_text[1] == None:
      return True

    if go_to_next_line:
      self.__scroll()
    elif len(self.__input_text) == len(self.__example_text[0]):
      pass
    elif char == '\t':
      for _ in range(SPACES_PER_TAB):
        result = self.__add_char(' ')
        assert result == False
    elif curses.ascii.isprint(char):
      self.__input_text += char
      attr = ATTR_CORRECT \
             if char == self.__example_text[0][len(self.__input_text) - 1] \
             else ATTR_ERROR
      self.__api.put_char(char, attr=attr)
      self.__api.move_right()
    return False

  def __delete_char(self):
    if len(self.__input_text) == 0: return
    self.__input_text = self.__input_text[:-1]
    self.__api.move_left()
    self.__api.put_char(self.__example_text[0][len(self.__input_text)])

  def __cheat(self) -> bool:
    if self.__example_text[1] == None:
      return True
    self.__scroll()
    return False

  def __initialize_screen(self):
    self.__api.clear()
    self.__print_all_example_text()
    self.__clear_input_text()

  def __scroll(self):
    self.__api.scroll()
    del self.__example_text[0]
    self.__print_bottom_example_text()
    self.__clear_input_text()

  def __clear_input_text(self):
    self.__input_text = ""
    self.__api.put_line(self.__geometry.current_line, self.__example_text[0])
    self.__api.move(self.__geometry.current_line, 0)

  def __print_bottom_example_text(self):
    index = self.__geometry.bottom_line - self.__geometry.current_line
    if self.__example_text[index] != None:
      self.__api.put_line(self.__geometry.bottom_line,
                          self.__example_text[index])

  def __print_all_example_text(self):
    for index in range(self.__geometry.bottom_line
                       - self.__geometry.current_line):
      if self.__example_text[index] == None: break
      self.__api.put_line(self.__geometry.current_line + index,
                          self.__example_text[index])

class Geometry:
  def __init__(self, screen_height):
    self.current_line = (screen_height - 1) // 2
    self.bottom_line = screen_height - 1

class FormattedText:
  def __init__(self, text_file, line_length=79):
    self.__file = text_file
    self.__line_length = line_length
    self.__buffered_lines = []

  def __del__(self):
    self.__file.close()

  def __getitem__(self, index: int) -> str:
    self.__read_lines_from_file()

    return self.__buffered_lines[index] if index < len(self.__buffered_lines) \
           else None

  def __delitem__(self, index: int):
    del self.__buffered_lines[index]

  def __read_lines_from_file(self):
    lines = self.__file.readlines()

    for line in lines:
      self.__buffer_line(self.__preprocess_text(line))

  def __buffer_line(self, line):
    line = line.rstrip()

    while len(line) > self.__line_length:
      buffered_line, line = self.__split_line(line)
      self.__buffered_lines.append(buffered_line)
    self.__buffered_lines.append(line)

  def __split_line(self, line):
    return line[self.__line_length:].rstrip(), \
           line[:self.__line_length].strip()

  @staticmethod
  def __preprocess_text(text):
    return text_unidecode.unidecode(text.replace('\t', ' ' * SPACES_PER_TAB))

# functions

def reset_stdin():
  TEMPORARY_FD = 3

  os.dup2(sys.stdin.fileno(), TEMPORARY_FD)
  os.close(sys.stdin.fileno())
  sys.stdin = open(TTY_DEVICE_FILE)

  return os.fdopen(TEMPORARY_FD)

def initialize_curses():
  window = curses.initscr()
  curses.noecho()
  curses.cbreak()
  curses.start_color()
  curses.use_default_colors()
  return window

def finalize_curses():
  curses.nocbreak()
  curses.echo()
  curses.endwin() # should be in the very last line

# main routine

def main():
  if not sys.stdout.isatty(): fail("stdout is not a tty.")
  if sys.stdin.isatty(): fail("stdin is a tty.")

  example_file = reset_stdin()

  try:
    # CAUTION:
    # You need to raise some Exception() instead of calling exit() here
    # to prevent curses messing up your terminal.

    window = initialize_curses()

    TypingGame(ConsoleApi(window), example_file).play()
  finally:
    finalize_curses()

def parse_args():
  arg_parser = argparse.ArgumentParser(description=DESCRIPTION)
  arg_parser.add_argument("-c", "--cheat",
                          dest="can_cheat",
                          action="store_true",
                          help="enable the cheat key, {}"
                               .format(curses.ascii.unctrl(CHEAT_CHAR)))
  arg_parser.add_argument("-t", "--spaces-per-tab",
                          type=int, dest="spaces_per_tab",
                          help="set number of spaces per tab")

  args = arg_parser.parse_args()
  if args.spaces_per_tab != None:
    global SPACES_PER_TAB
    SPACES_PER_TAB = args.spaces_per_tab
  if args.can_cheat == True:
    global CAN_CHEAT
    CAN_CHEAT = True

if __name__ == "__main__":
  parse_args()
  main()
<commit_msg>Fix a typo<commit_after>#!/usr/bin/env python3

import argparse
import curses
import curses.ascii
import os
import sys
import text_unidecode
import typing

# constants

DESCRIPTION = "shakyo2d is a tool to learn about something just copying it " \
              "by hand."

TTY_DEVICE_FILE = "/dev/tty" # POSIX compliant

QUIT_CHARS = {chr(curses.ascii.ESC), curses.ascii.ctrl('[')}
DELETE_CHARS = {chr(curses.ascii.DEL), chr(curses.ascii.BS),
                chr(curses.KEY_BACKSPACE), chr(curses.KEY_DC)}
CLEAR_CHAR = curses.ascii.ctrl('u')
CHEAT_CHAR = curses.ascii.ctrl('n')

CAN_CHEAT = False
SPACES_PER_TAB = 2

ATTR_CORRECT = curses.A_NORMAL
ATTR_ERROR = curses.A_REVERSE

# the order of bugs

def error(*message):
  print("ERROR:", *message, file=sys.stderr)

def fail(*message):
  error(*message)
  exit(1)

# classes

class ConsoleApi:
  def __init__(self, window):
    window.keypad(True)
    window.scrollok(True)
    self.__window = window

  def __save_position(method):
    def wrapper(self, *args, **keyword_args):
      position = self.__window.getyx()
      method(self, *args, **keyword_args)
      self.__window.move(*position)
    return wrapper

  @property
  def screen_height(self):
    return self.__window.getmaxyx()[0]

  @property
  def screen_width(self):
    return self.__window.getmaxyx()[1]

  def get_char(self) -> typing.Optional[str]:
    char = self.__window.getkey()
    return char if self.__is_ascii_char(char) else None

  @__save_position
  def put_char(self, char: str, attr=curses.A_NORMAL):
    assert self.__is_ascii_char(char)
    self.__window.addch(ord(char), attr)

  @__save_position
  def put_line(self, line_pos, text):
    assert len(text) <= self.screen_width
    self.__window.move(line_pos, 0)
    self.__window.clrtoeol()
    self.__window.addstr(line_pos, 0, text)

  def move(self, y, x):
    self.__window.move(y, x)

  def move_right(self):
    y, x = self.__window.getyx()
    if x == self.screen_width - 1: return
    self.__window.move(y, x + 1)

  def move_left(self):
    y, x = self.__window.getyx()
    if x == 0: return
    self.__window.move(y, x - 1)

  @__save_position
  def clear(self):
    self.__window.clear()

  @__save_position
  def scroll(self):
    self.__window.scroll()

  @staticmethod
  def __is_ascii_char(char):
    return len(char) == 1 and curses.ascii.isascii(char)

class TypingGame:
  def __init__(self, api, example_file):
    self.__api = api
    self.__example_text = FormattedText(example_file,
                                        line_length=(api.screen_width - 1))
    self.__geometry = Geometry(api.screen_height)
    self.__input_text = ""
    if self.__example_text[0] == None:
      raise Exception("No line can be read from example souce.")

  def play(self):
    self.__initialize_screen()

    game_over = False
    while not game_over:
      char = self.__get_char()

      if char in QUIT_CHARS:
        return
      elif char == CLEAR_CHAR:
        self.__clear_input_text()
      elif char in DELETE_CHARS:
        self.__delete_char()
      elif char == CHEAT_CHAR and CAN_CHEAT:
        game_over = self.__cheat()
      elif curses.ascii.isprint(char) or curses.ascii.isspace(char):
        game_over = self.__add_char(char)

  def __get_char(self):
    char = self.__api.get_char()
    while char == None:
      char = self.__api.get_char()
    return char

  def __add_char(self, char: str) -> bool:
    go_to_next_line = char == '\n' \
                      and self.__input_text == self.__example_text[0]
    if go_to_next_line and self.__example_text[1] == None:
      return True

    if go_to_next_line:
      self.__scroll()
    elif len(self.__input_text) == len(self.__example_text[0]):
      pass
    elif char == '\t':
      for _ in range(SPACES_PER_TAB):
        result = self.__add_char(' ')
        assert result == False
    elif curses.ascii.isprint(char):
      self.__input_text += char
      attr = ATTR_CORRECT \
             if char == self.__example_text[0][len(self.__input_text) - 1] \
             else ATTR_ERROR
      self.__api.put_char(char, attr=attr)
      self.__api.move_right()
    return False

  def __delete_char(self):
    if len(self.__input_text) == 0: return
    self.__input_text = self.__input_text[:-1]
    self.__api.move_left()
    self.__api.put_char(self.__example_text[0][len(self.__input_text)])

  def __cheat(self) -> bool:
    if self.__example_text[1] == None:
      return True
    self.__scroll()
    return False

  def __initialize_screen(self):
    self.__api.clear()
    self.__print_all_example_text()
    self.__clear_input_text()

  def __scroll(self):
    self.__api.scroll()
    del self.__example_text[0]
    self.__print_bottom_example_text()
    self.__clear_input_text()

  def __clear_input_text(self):
    self.__input_text = ""
    self.__api.put_line(self.__geometry.current_line, self.__example_text[0])
    self.__api.move(self.__geometry.current_line, 0)

  def __print_bottom_example_text(self):
    index = self.__geometry.bottom_line - self.__geometry.current_line
    if self.__example_text[index] != None:
      self.__api.put_line(self.__geometry.bottom_line,
                          self.__example_text[index])

  def __print_all_example_text(self):
    for index in range(self.__geometry.bottom_line
                       - self.__geometry.current_line):
      if self.__example_text[index] == None: break
      self.__api.put_line(self.__geometry.current_line + index,
                          self.__example_text[index])

class Geometry:
  def __init__(self, screen_height):
    self.current_line = (screen_height - 1) // 2
    self.bottom_line = screen_height - 1

class FormattedText:
  def __init__(self, text_file, line_length=79):
    self.__file = text_file
    self.__line_length = line_length
    self.__buffered_lines = []

  def __del__(self):
    self.__file.close()

  def __getitem__(self, index: int) -> str:
    self.__read_lines_from_file()

    return self.__buffered_lines[index] if index < len(self.__buffered_lines) \
           else None

  def __delitem__(self, index: int):
    del self.__buffered_lines[index]

  def __read_lines_from_file(self):
    lines = self.__file.readlines()

    for line in lines:
      self.__buffer_line(self.__preprocess_text(line))

  def __buffer_line(self, line):
    line = line.rstrip()

    while len(line) > self.__line_length:
      buffered_line, line = self.__split_line(line)
      self.__buffered_lines.append(buffered_line)
    self.__buffered_lines.append(line)

  def __split_line(self, line):
    return line[self.__line_length:].rstrip(), \
           line[:self.__line_length].strip()

  @staticmethod
  def __preprocess_text(text):
    return text_unidecode.unidecode(text.replace('\t', ' ' * SPACES_PER_TAB))

# functions

def reset_stdin():
  TEMPORARY_FD = 3

  os.dup2(sys.stdin.fileno(), TEMPORARY_FD)
  os.close(sys.stdin.fileno())
  sys.stdin = open(TTY_DEVICE_FILE)

  return os.fdopen(TEMPORARY_FD)

def initialize_curses():
  window = curses.initscr()
  curses.noecho()
  curses.cbreak()
  curses.start_color()
  curses.use_default_colors()
  return window

def finalize_curses():
  curses.nocbreak()
  curses.echo()
  curses.endwin() # should be in the very last line

# main routine

def main():
  if not sys.stdout.isatty(): fail("stdout is not a tty.")
  if sys.stdin.isatty(): fail("stdin is a tty.")

  example_file = reset_stdin()

  try:
    # CAUTION:
    # You need to raise some Exception() instead of calling exit() here
    # to prevent curses from messing up your terminal.

    window = initialize_curses()

    TypingGame(ConsoleApi(window), example_file).play()
  finally:
    finalize_curses()

def parse_args():
  arg_parser = argparse.ArgumentParser(description=DESCRIPTION)
  arg_parser.add_argument("-c", "--cheat",
                          dest="can_cheat",
                          action="store_true",
                          help="enable the cheat key, {}"
                               .format(curses.ascii.unctrl(CHEAT_CHAR)))
  arg_parser.add_argument("-t", "--spaces-per-tab",
                          type=int, dest="spaces_per_tab",
                          help="set number of spaces per tab")

  args = arg_parser.parse_args()
  if args.spaces_per_tab != None:
    global SPACES_PER_TAB
    SPACES_PER_TAB = args.spaces_per_tab
  if args.can_cheat == True:
    global CAN_CHEAT
    CAN_CHEAT = True

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


<commit_before>def stage(settings):
    """ Creates a mvn image and pipeline.
    """
    import os
    import sys
    import shutil
    from subprocess import call, Popen, PIPE
    from turtles import interpolate_file, docker_inspect, ImageMissing
    shares = ['~/.ssh', '~/.m2']
    for share in shares:
        if not os.path.exists(os.path.expanduser(share)):
            sys.exit("For jenkins to work properly you need: " + share)
    image = "turtle-jenkins"
    try:
        docker_inspect(image)
    except ImageMissing:
        here = os.getcwd()
        with open(os.path.join(here, "log.txt"), 'a') as fout:
            print("Preparing for pipeline, logging to", fout.name)
            try:
                os.chdir(os.path.dirname(settings['-p']))
                if call(["git", "clone", "git@github.com:philipbergen/turtles.git"]):
                    sys.exit("Failed: git clone git@github.com:philipbergen/turtles.git")
                interpolate_file("Dockerfile.in", "Dockerfile", log=fout.write)
                if call(["docker", "build", "-t", image, "."], stdout=fout):
                    sys.exit("Docker build failed.")
            finally:
                shutil.rmtree("turtles")
                os.chdir(here)
    if not os.path.exists(".jenkins/plugins"):
        try:
            os.mkdir(".jenkins")
        except OSError:
            pass
        uidgid = "%d:%d" % (os.getuid(), os.getgid())
        print("Will now launch Jenkins, please press run the wizard and shut down jenkins after")
        cmd = ["docker", "run", "--user", uidgid, "-p", "8080:8080", "--rm", "-v",
               os.path.abspath(".jenkins") + ":/var/jenkins_home:rw",
               image]
        call(cmd)
    pope = Popen(["git", "remote", "get-url", "origin"], stdout=PIPE)
    giturl, _ = pope.communicate()
    giturl = giturl.strip()  # git@github.com:philipbergen/turtles.git
    user, ghurl = giturl.split('@', 1)  # git, github.com:philipbergen/turtles.git
    ghurl, ghrepo = ghurl.split(':', 1)  # github.com, philipbergen/turtles.git
    org, name = ghrepo.rsplit('/', 1)  # philipbergen, turtles.git
    name, _ = name.split(".", 1)  # turtles, _
    template = {
        "name": name,
        "user": user,
        "github_org": org,
        "github_url": "https://%s/%s/%s" % (ghurl, org, name),
        "git_url": giturl,
    }
    cfg_path = ".jenkins/jobs/%(name)s/config.xml" % template
    if not os.path.exists(cfg_path):
        try:
            os.makedirs(os.path.dirname(cfg_path))
        except OSError:
            pass
        interpolate_file(os.path.join(os.path.dirname(settings['-p']), "config.xml"), cfg_path,
                         template)
    return {
        "-s": '/usr/local/bin/jenkins.sh',
        "-d": image,
        "-v": ["/var/run/docker.sock:/var/run/docker.sock", ".jenkins:/var/jenkins_home:rw",
               ".:/cwd:rw"] + [
                  '%s:%s:rw' % (share, share.replace('~', '/var/jenkins_home')) for
                  share in shares],
    }
<commit_msg>Make sure strings, not bytes. Py3<commit_after>def stage(settings):
    """ Creates a mvn image and pipeline.
    """
    import os
    import sys
    import shutil
    from subprocess import call, Popen, PIPE
    from turtles import interpolate_file, docker_inspect, ImageMissing
    shares = ['~/.ssh', '~/.m2']
    for share in shares:
        if not os.path.exists(os.path.expanduser(share)):
            sys.exit("For jenkins to work properly you need: " + share)
    image = "turtle-jenkins"
    try:
        docker_inspect(image)
    except ImageMissing:
        here = os.getcwd()
        with open(os.path.join(here, "log.txt"), 'a') as fout:
            print("Preparing for pipeline, logging to", fout.name)
            try:
                os.chdir(os.path.dirname(settings['-p']))
                if call(["git", "clone", "git@github.com:philipbergen/turtles.git"]):
                    sys.exit("Failed: git clone git@github.com:philipbergen/turtles.git")
                interpolate_file("Dockerfile.in", "Dockerfile", log=fout.write)
                if call(["docker", "build", "-t", image, "."], stdout=fout):
                    sys.exit("Docker build failed.")
            finally:
                shutil.rmtree("turtles")
                os.chdir(here)
    if not os.path.exists(".jenkins/plugins"):
        try:
            os.mkdir(".jenkins")
        except OSError:
            pass
        uidgid = "%d:%d" % (os.getuid(), os.getgid())
        print("Will now launch Jenkins, please press run the wizard and shut down jenkins after")
        cmd = ["docker", "run", "--user", uidgid, "-p", "8080:8080", "--rm", "-v",
               os.path.abspath(".jenkins") + ":/var/jenkins_home:rw",
               image]
        call(cmd)
    pope = Popen(["git", "remote", "get-url", "origin"], stdout=PIPE)
    giturl, _ = pope.communicate()
    if not type(giturl) == str:
        giturl = str(giturl, encoding='utf-8')
    giturl = giturl.strip()  # git@github.com:philipbergen/turtles.git
    user, ghurl = giturl.split('@', 1)  # git, github.com:philipbergen/turtles.git
    ghurl, ghrepo = ghurl.split(':', 1)  # github.com, philipbergen/turtles.git
    org, name = ghrepo.rsplit('/', 1)  # philipbergen, turtles.git
    name, _ = name.split(".", 1)  # turtles, _
    template = {
        "name": name,
        "user": user,
        "github_org": org,
        "github_url": "https://%s/%s/%s" % (ghurl, org, name),
        "git_url": giturl,
    }
    cfg_path = ".jenkins/jobs/%(name)s/config.xml" % template
    if not os.path.exists(cfg_path):
        try:
            os.makedirs(os.path.dirname(cfg_path))
        except OSError:
            pass
        interpolate_file(os.path.join(os.path.dirname(settings['-p']), "config.xml"), cfg_path,
                         template)
    return {
        "-s": '/usr/local/bin/jenkins.sh',
        "-d": image,
        "-v": ["/var/run/docker.sock:/var/run/docker.sock", ".jenkins:/var/jenkins_home:rw",
               ".:/cwd:rw"] + [
                  '%s:%s:rw' % (share, share.replace('~', '/var/jenkins_home')) for
                  share in shares],
    }
<|endoftext|>


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

import client
import requests
import six

def sha_walker(sha_to_walks, dci_client, repository, product_id, test_id):
    sha = sha_to_walks.pop()
    if not sha:
        return
    commit = gh_s.get(
        'https://api.github.com/'
        'repos/%s/git/commits/%s' % (repository, sha)).json()
    if 'parents' in commit:
        for parent in commit['parents']:
            print(parent['sha'])
            sha_to_walks.append(parent['sha'])

    # NOTE(Gonéri): Is the commit already here?
    r = dci_client.get("/versions/%s" % sha)
    if r.status_code == 404:
        message = commit['message']
        title = message.split('\n')[0]
        print('%s - %s' % (sha, title))
        r = dci_client.post("/versions", {
            "product_id": product_id,
            "name": title,
            "title": title,
            "message": message,
            "sha": sha,
            "data": {
                "sha2": sha
            }
        })
        version_id = r.json()['id']
        dci_client.post("/testversions", {
            "test_id": test_id,
            "version_id": version_id,
        })

def fetch(gh_s, dci_client, product, repositories):
    r = dci_client.get('/tests/tox')
    if r.status_code == 404:
        r = dci_client.post("/tests", {
            "name": "tox",
        })
    test_id = r.json()['id']

    for repository in repositories:
        r = dci_client.get("/products", where={
            'name': "%s-%s" % (product, repository)})
        if r.status_code == 200 and r.json()['_meta']['total'] == 1:
            product_id = r.json()['_items'][0]['id']
        else:
            r = dci_client.post("/products", {
                "name": "%s-%s" % (product, repository),
                "data": {
                    "git_url": "https://github.com/%s" % repository}}
            )
            product_id = r.json()['id']
        r = gh_s.get(
            'https://api.github.com/repos/' +
            repository +
            '/branches')
        if r.status_code == 404:
            print("Repository not found: %s" % repository)
            continue
        branches = {a['name']: a['commit'] for a in r.json()}
        sha_to_walks = [branches['master']['sha']]
        while sha_to_walks:
            sha_walker(sha_to_walks, dci_client,
                       repository, product_id, test_id)

products = {
    'dci-control-server': [
        'enovance/dci-control-server', 'goneri/dci-control-server']}
gh_s = requests.Session()
# gh_s.auth = ('user', 'xxx')
dci_client = client.DCIClient()

for product, repositories in six.iteritems(products):
    fetch(gh_s, dci_client, product, repositories)
<commit_msg>print the error message in case of server error<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2015 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import client
import requests
import six

def sha_walker(sha_to_walks, dci_client, repository, product_id, test_id):
    sha = sha_to_walks.pop()
    if not sha:
        return
    commit = gh_s.get(
        'https://api.github.com/'
        'repos/%s/git/commits/%s' % (repository, sha)).json()
    if 'parents' in commit:
        for parent in commit['parents']:
            print(parent['sha'])
            sha_to_walks.append(parent['sha'])

    # NOTE(Gonéri): Is the commit already here?
    r = dci_client.get("/versions/%s" % sha)
    if r.status_code == 404:
        message = commit['message']
        title = message.split('\n')[0]
        print('%s - %s' % (sha, title))
        r = dci_client.post("/versions", {
            "product_id": product_id,
            "name": title,
            "title": title,
            "message": message,
            "sha": sha,
            "data": {
                "sha2": sha
            }
        })
        version_id = r.json()['id']
        dci_client.post("/testversions", {
            "test_id": test_id,
            "version_id": version_id,
        })

def fetch(gh_s, dci_client, product, repositories):
    r = dci_client.get('/tests/tox')
    if r.status_code == 404:
        r = dci_client.post("/tests", {
            "name": "tox",
        })
    try:
        test_id = r.json()['id']
    except KeyError as e:
        print(r.text)
        raise e

    for repository in repositories:
        r = dci_client.get("/products", where={
            'name': "%s-%s" % (product, repository)})
        if r.status_code == 200 and r.json()['_meta']['total'] == 1:
            product_id = r.json()['_items'][0]['id']
        else:
            r = dci_client.post("/products", {
                "name": "%s-%s" % (product, repository),
                "data": {
                    "git_url": "https://github.com/%s" % repository}}
            )
            product_id = r.json()['id']
        r = gh_s.get(
            'https://api.github.com/repos/' +
            repository +
            '/branches')
        if r.status_code == 404:
            print("Repository not found: %s" % repository)
            continue
        branches = {a['name']: a['commit'] for a in r.json()}
        sha_to_walks = [branches['master']['sha']]
        while sha_to_walks:
            sha_walker(sha_to_walks, dci_client,
                       repository, product_id, test_id)

products = {
    'dci-control-server': [
        'enovance/dci-control-server', 'goneri/dci-control-server']}
gh_s = requests.Session()
# gh_s.auth = ('user', 'xxx')
dci_client = client.DCIClient()

for product, repositories in six.iteritems(products):
    fetch(gh_s, dci_client, product, repositories)
<|endoftext|>


<commit_before>#!/usr/bin/env python3
# Copyright (C) 2015 taylor.fish (https://github.com/taylordotfish)
# Copyright (C) 2015 nc Krantz-Fire (https://pineco.net/)
# Added --prefix option.
#
# This file is part of shellbot.
#
# shellbot is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# shellbot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with shellbot.  If not, see <http://www.gnu.org/licenses/>.
#
# See EXCEPTIONS for additional permissions.
"""
Usage:
  shellbot <host> <port> [-u user] [-d directory] [-q] [-i]
           [-n nick] [-m max] [-t timeout] [-p prefix] [-c channel]...

Options:
  -u <user>       Run commands as the specified user. Prevents the shellbot
                  process from being killed. Must be run as root.
  -d <directory>  The current working directory for all commands.
  -q --queries    Run commands in private queries as well as channels.
  -i --identify   Identify with NickServ. Accepts a password through stdin.
  -n <nick>       The nickname to use [default: shellbot].
  -m <max>        The maximum number of lines of output to send [default: 10].
  -t <timeout>    How many seconds to wait before killing processes
                  [default: 4].
  -p <prefix>     The prefix which identifies commands to run [default: !$].
  -c <channel>    An IRC channel to join.
"""
from pyrcb import IrcBot
from command import run_shell
from docopt import docopt
from datetime import datetime
import os
import re
import sys
import threading

# If modified, replace the source URL with one to the modified version.
help_message = """\
Source: https://github.com/taylordotfish/shellbot (AGPLv3 or later)
Use in private queries is {0}.
To run a command, send "{1} [command]".
"""

class Shellbot(IrcBot):
    def __init__(self, max_lines, timeout, prefix, queries, user, cwd):
        super(Shellbot, self).__init__()
        self.max_lines = max_lines
        self.timeout = timeout
        self.prefix = prefix + " "
        self.allow_queries = queries
        self.cmd_user = user
        self.cwd = cwd

    def on_query(self, message, nickname):
        if message.lower() == "help":
            help_lines = help_message.format(
                ["disabled", "enabled"][self.allow_queries],
                self.prefix).splitlines()
            for line in help_lines:
                self.send(nickname, line)
        else:
            self.send(nickname, '"/msg {0} help" for help'
                                .format(self.nickname))

    def on_message(self, message, nickname, target, is_query):
        if not message.startswith(self.prefix):
            if is_query:
                self.on_query(message, nickname)
            return
        if is_query and not self.allow_queries:
            self.send(nickname, "Use in private queries is disabled.")
            return
        print("[{3}] [{0}] <{1}> {2}".format(
            target, nickname, message, datetime.now().replace(microsecond=0)))
        threading.Thread(target=self.run_command,
                         args=(message[len(self.prefix):], target)).start()

    def run_command(self, command, target):
        # Strip ANSI escape sequences.
        lines = [re.sub(r"\x1b.*?[a-zA-Z]", "", l) for l in run_shell(
            command, self.cmd_user, self.cwd, self.timeout, self.timeout / 2)]
        lines = [l for l in lines if l]

        for line in lines[:self.max_lines]:
            self.send(target, line)
            print(">>> " + line)
        if len(lines) > self.max_lines:
            message = "...output trimmed to {0} lines".format(self.max_lines)
            self.send(target, message)
            print(">>> " + message)
        if not lines:
            message = "Command produced no output."
            self.send(target, message)
            print(">>> " + message)

def main():
    args = docopt(__doc__)
    if args["-u"] and os.geteuid() != 0:
        print('Must be run as root when "-u" is specified.')
        return
    if not args["-u"] and os.geteuid() == 0:
        print('Cannot be run as root unless "-u" is specified.')
        return

    bot = Shellbot(int(args["-m"]), float(args["-t"]), args["-p"],
                   args["--queries"], args["-u"], args["-d"])
    bot.connect(args["<host>"], int(args["<port>"]))

    if args["--identify"]:
        print("Password: ", end="", file=sys.stderr)
        bot.password(input())
        print("Received password.", file=sys.stderr)
    bot.register(args["-n"])

    for channel in args["-c"]:
        bot.join(channel)
    bot.listen()

if __name__ == "__main__":
    main()
<commit_msg>Update help message<commit_after>#!/usr/bin/env python3
# Copyright (C) 2015 taylor.fish (https://github.com/taylordotfish)
# Copyright (C) 2015 nc Krantz-Fire (https://pineco.net/)
# Added --prefix option.
#
# This file is part of shellbot.
#
# shellbot is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# shellbot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with shellbot.  If not, see <http://www.gnu.org/licenses/>.
#
# See EXCEPTIONS for additional permissions.
"""
Usage:
  shellbot <host> <port> [options] [-c CHANNEL]...

Options:
  -q --queries   Run commands in private queries as well as channels.
  -i --identify  Identify with NickServ. Accepts a password through stdin.
  -n NICKNAME    The nickname to use [default: shellbot].
  -c CHANNEL     An IRC channel to join.
  -m MAX-LINES   The maximum number of lines of output to send [default: 10].
  -t TIMEOUT     How many seconds to wait before killing processes
  -p PREFIX      The prefix which identifies commands to run [default: !$].
  -u USER        Run commands as the specified user. Prevents the shellbot
                 process from being killed. Must be run as root.
  -d DIRECTORY   The current working directory for all commands.
                 [default: 4].
"""
from pyrcb import IrcBot
from command import run_shell
from docopt import docopt
from datetime import datetime
import os
import re
import sys
import threading

# If modified, replace the source URL with one to the modified version.
help_message = """\
Source: https://github.com/taylordotfish/shellbot (AGPLv3 or later)
Use in private queries is {0}.
To run a command, send "{1} [command]".
"""

class Shellbot(IrcBot):
    def __init__(self, max_lines, timeout, prefix, queries, user, cwd):
        super(Shellbot, self).__init__()
        self.max_lines = max_lines
        self.timeout = timeout
        self.prefix = prefix + " "
        self.allow_queries = queries
        self.cmd_user = user
        self.cwd = cwd

    def on_query(self, message, nickname):
        if message.lower() == "help":
            help_lines = help_message.format(
                ["disabled", "enabled"][self.allow_queries],
                self.prefix).splitlines()
            for line in help_lines:
                self.send(nickname, line)
        else:
            self.send(nickname, '"/msg {0} help" for help'
                                .format(self.nickname))

    def on_message(self, message, nickname, target, is_query):
        if not message.startswith(self.prefix):
            if is_query:
                self.on_query(message, nickname)
            return
        if is_query and not self.allow_queries:
            self.send(nickname, "Use in private queries is disabled.")
            return
        print("[{3}] [{0}] <{1}> {2}".format(
            target, nickname, message, datetime.now().replace(microsecond=0)))
        threading.Thread(target=self.run_command,
                         args=(message[len(self.prefix):], target)).start()

    def run_command(self, command, target):
        # Strip ANSI escape sequences.
        lines = [re.sub(r"\x1b.*?[a-zA-Z]", "", l) for l in run_shell(
            command, self.cmd_user, self.cwd, self.timeout, self.timeout / 2)]
        lines = [l for l in lines if l]

        for line in lines[:self.max_lines]:
            self.send(target, line)
            print(">>> " + line)
        if len(lines) > self.max_lines:
            message = "...output trimmed to {0} lines".format(self.max_lines)
            self.send(target, message)
            print(">>> " + message)
        if not lines:
            message = "Command produced no output."
            self.send(target, message)
            print(">>> " + message)

def main():
    args = docopt(__doc__)
    if args["-u"] and os.geteuid() != 0:
        print('Must be run as root when "-u" is specified.')
        return
    if not args["-u"] and os.geteuid() == 0:
        print('Cannot be run as root unless "-u" is specified.')
        return

    bot = Shellbot(int(args["-m"]), float(args["-t"]), args["-p"],
                   args["--queries"], args["-u"], args["-d"])
    bot.connect(args["<host>"], int(args["<port>"]))

    if args["--identify"]:
        print("Password: ", end="", file=sys.stderr)
        bot.password(input())
        print("Received password.", file=sys.stderr)
    bot.register(args["-n"])

    for channel in args["-c"]:
        bot.join(channel)
    bot.listen()

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


<commit_before>import json
import time
from uuid import uuid4

from btrc import CouchbaseClient, StatsReporter
from couchbase import Couchbase
from logger import logger

from perfrunner.settings import SF_STORAGE

class BtrcReporter(object):

    def __init__(self, test):
        self.test = test

    def reset_utilzation_stats(self):
        for target in self.test.target_iterator:
            logger.info('Resetting utilization stats from {0}/{1}'.format(
                        target.node, target.bucket))
            cb = CouchbaseClient(target.node, target.bucket,
                                 target.username, target.password)
            cb.reset_utilization_stats()

    def save_utilzation_stats(self):
        for target in self.test.target_iterator:
            logger.info('Saving utilization stats from {0}/{1}'.format(
                        target.node, target.bucket))
            cb = CouchbaseClient(target.node, target.bucket,
                                 target.username, target.password)
            reporter = StatsReporter(cb)
            reporter.report_stats('util_stats')

    def save_btree_stats(self):
        for target in self.test.target_iterator:
            logger.info('Saving B-tree stats from {0}/{1}'.format(
                        target.node, target.bucket))
            cb = CouchbaseClient(target.node, target.bucket,
                                 target.username, target.password)
            reporter = StatsReporter(cb)
            reporter.report_stats('btree_stats')

class SFReporter(object):

    def __init__(self, test):
        self.test = test

    def _add_cluster(self):
        cluster = self.test.cluster_spec.name
        params = self.test.cluster_spec.get_parameters()
        try:
            cb = Couchbase.connect(bucket='clusters', **SF_STORAGE)
            cb.set(cluster, params)
        except Exception, e:
            logger.warn('Failed to add cluster, {0}'.format(e))
        else:
            logger.info('Successfully posted: {0}, {1}'.format(
                cluster, params))

    def _add_metric(self, metric, metric_info):
        if metric_info is None:
            metric_info = {
                'title': self.test.test_config.get_test_descr(),
                'cluster': self.test.cluster_spec.name,
                'larger_is_better': self.test.test_config.get_regression_criterion()
            }
        try:
            cb = Couchbase.connect(bucket='metrics', **SF_STORAGE)
            cb.set(metric, metric_info)
        except Exception, e:
            logger.warn('Failed to add cluster, {0}'.format(e))
        else:
            logger.info('Successfully posted: {0}, {1}'.format(metric,
                                                               metric_info))

    def _prepare_data(self, metric, value):
        key = uuid4().hex
        master_node = self.test.cluster_spec.get_masters().values()[0]
        build = self.test.rest.get_version(master_node)
        data = {'build': build, 'metric': metric, 'value': value}
        data.update(self.test.reports)
        return key, data

    def _mark_previous_as_obsolete(self, cb, benckmark):
        for row in cb.query('benchmarks', 'values_by_build_and_metric',
                            key=[benckmark['metric'], benckmark['build']]):
            doc = cb.get(row.docid)
            doc.value.update({'obsolete': True})
            cb.set(row.docid, doc.value)

    def _log_benchmark(self, metric, value):
        _, benckmark = self._prepare_data(metric, value)
        logger.info('Dry run stats: {0}'.format(benckmark))

    def _post_benckmark(self, metric, value):
        key, benckmark = self._prepare_data(metric, value)
        try:
            cb = Couchbase.connect(bucket='benchmarks', **SF_STORAGE)
            self._mark_previous_as_obsolete(cb, benckmark)
            cb.set(key, benckmark)
        except Exception, e:
            logger.warn('Failed to post results, {0}'.format(e))
        else:
            logger.info('Successfully posted: {0}'.format(benckmark))

    def post_to_sf(self, value, metric=None, metric_info=None):
        if metric is None:
            metric = '{0}_{1}'.format(self.test.test_config.name,
                                      self.test.cluster_spec.name)

        stats_settings = self.test.test_config.get_stats_settings()

        if stats_settings.post_to_sf:
            self._add_metric(metric, metric_info)
            self._add_cluster()
            self._post_benckmark(metric, value)
        else:
            self._log_benchmark(metric, value)

class LogReporter(object):

    def __init__(self, test):
        self.test = test

    def save_web_logs(self):
        for target in self.test.target_iterator:
            logs = self.test.rest.get_logs(target.node)
            fname = 'web_log_{0}.json'.format(target.node.split(':')[0])
            with open(fname, 'w') as fh:
                fh.write(json.dumps(logs, indent=4, sort_keys=True))

    def save_master_events(self):
        for target in self.test.target_iterator:
            master_events = self.test.rest.get_master_events(target.node)
            fname = 'master_events_{0}.log'.format(target.node.split(':')[0])
            with open(fname, 'w') as fh:
                fh.write(master_events)

class Reporter(BtrcReporter, SFReporter, LogReporter):

    def start(self):
        self.ts = time.time()

    def finish(self, action):
        elapsed = round((time.time() - self.ts) / 60, 1)
        logger.info(
            'Time taken to perform "{0}": {1} min'.format(action, elapsed)
        )
        return elapsed
<commit_msg>log results in more pretty format<commit_after>import json
import time
from uuid import uuid4

from btrc import CouchbaseClient, StatsReporter
from couchbase import Couchbase
from logger import logger

from perfrunner.settings import SF_STORAGE

class BtrcReporter(object):

    def __init__(self, test):
        self.test = test

    def reset_utilzation_stats(self):
        for target in self.test.target_iterator:
            logger.info('Resetting utilization stats from {0}/{1}'.format(
                        target.node, target.bucket))
            cb = CouchbaseClient(target.node, target.bucket,
                                 target.username, target.password)
            cb.reset_utilization_stats()

    def save_utilzation_stats(self):
        for target in self.test.target_iterator:
            logger.info('Saving utilization stats from {0}/{1}'.format(
                        target.node, target.bucket))
            cb = CouchbaseClient(target.node, target.bucket,
                                 target.username, target.password)
            reporter = StatsReporter(cb)
            reporter.report_stats('util_stats')

    def save_btree_stats(self):
        for target in self.test.target_iterator:
            logger.info('Saving B-tree stats from {0}/{1}'.format(
                        target.node, target.bucket))
            cb = CouchbaseClient(target.node, target.bucket,
                                 target.username, target.password)
            reporter = StatsReporter(cb)
            reporter.report_stats('btree_stats')

class SFReporter(object):

    def __init__(self, test):
        self.test = test

    def _add_cluster(self):
        cluster = self.test.cluster_spec.name
        params = self.test.cluster_spec.get_parameters()
        try:
            cb = Couchbase.connect(bucket='clusters', **SF_STORAGE)
            cb.set(cluster, params)
        except Exception, e:
            logger.warn('Failed to add cluster, {0}'.format(e))
        else:
            logger.info('Successfully posted: {0}, {1}'.format(
                cluster, params))

    def _add_metric(self, metric, metric_info):
        if metric_info is None:
            metric_info = {
                'title': self.test.test_config.get_test_descr(),
                'cluster': self.test.cluster_spec.name,
                'larger_is_better': self.test.test_config.get_regression_criterion()
            }
        try:
            cb = Couchbase.connect(bucket='metrics', **SF_STORAGE)
            cb.set(metric, metric_info)
        except Exception, e:
            logger.warn('Failed to add cluster, {0}'.format(e))
        else:
            logger.info('Successfully posted: {0}, {1}'.format(metric,
                                                               metric_info))

    def _prepare_data(self, metric, value):
        key = uuid4().hex
        master_node = self.test.cluster_spec.get_masters().values()[0]
        build = self.test.rest.get_version(master_node)
        data = {'build': build, 'metric': metric, 'value': value}
        data.update(self.test.reports)
        return key, data

    def _mark_previous_as_obsolete(self, cb, benckmark):
        for row in cb.query('benchmarks', 'values_by_build_and_metric',
                            key=[benckmark['metric'], benckmark['build']]):
            doc = cb.get(row.docid)
            doc.value.update({'obsolete': True})
            cb.set(row.docid, doc.value)

    def _log_benchmark(self, metric, value):
        _, benckmark = self._prepare_data(metric, value)
        logger.info('Dry run stats: {0}'.format(
            json.dumps(benckmark, indent=4, sort_keys=True)
        ))

    def _post_benckmark(self, metric, value):
        key, benckmark = self._prepare_data(metric, value)
        try:
            cb = Couchbase.connect(bucket='benchmarks', **SF_STORAGE)
            self._mark_previous_as_obsolete(cb, benckmark)
            cb.set(key, benckmark)
        except Exception, e:
            logger.warn('Failed to post results, {0}'.format(e))
        else:
            logger.info('Successfully posted: {0}'.format(
                json.dumps(benckmark, indent=4, sort_keys=True)
            ))

    def post_to_sf(self, value, metric=None, metric_info=None):
        if metric is None:
            metric = '{0}_{1}'.format(self.test.test_config.name,
                                      self.test.cluster_spec.name)

        stats_settings = self.test.test_config.get_stats_settings()

        if stats_settings.post_to_sf:
            self._add_metric(metric, metric_info)
            self._add_cluster()
            self._post_benckmark(metric, value)
        else:
            self._log_benchmark(metric, value)

class LogReporter(object):

    def __init__(self, test):
        self.test = test

    def save_web_logs(self):
        for target in self.test.target_iterator:
            logs = self.test.rest.get_logs(target.node)
            fname = 'web_log_{0}.json'.format(target.node.split(':')[0])
            with open(fname, 'w') as fh:
                fh.write(json.dumps(logs, indent=4, sort_keys=True))

    def save_master_events(self):
        for target in self.test.target_iterator:
            master_events = self.test.rest.get_master_events(target.node)
            fname = 'master_events_{0}.log'.format(target.node.split(':')[0])
            with open(fname, 'w') as fh:
                fh.write(master_events)

class Reporter(BtrcReporter, SFReporter, LogReporter):

    def start(self):
        self.ts = time.time()

    def finish(self, action):
        elapsed = round((time.time() - self.ts) / 60, 1)
        logger.info(
            'Time taken to perform "{0}": {1} min'.format(action, elapsed)
        )
        return elapsed
<|endoftext|>


<commit_before>from PyQt4.QtOpenGL import QGLWidget
from PyQt4.QtOpenGL import QGLFormat
from PyQt4.QtOpenGL import QGL
from PyQt4.QtCore import Qt

from pivy.coin import SoRenderManager
from pivy.coin import SoEventManager
#from pivy.coin import SoSceneManager
from pivy.coin import SoSearchAction
from pivy.coin import SoCamera
from pivy.coin import SoPerspectiveCamera
from pivy.coin import SoTransform
from pivy.coin import ScXML
from pivy.coin import SoScXMLStateMachine
from pivy.coin import cast
from pivy.coin import SoGLCacheContextElement

from pivy.coin import SoDirectionalLight
from pivy.coin import SoSeparator
from pivy.coin import SbColor4f
from pivy.coin import SbName
from pivy.coin import SbViewportRegion

from devices import DeviceManager
from devices import MouseHandler

from eventhandlers import EventManager

# FIXME jkg: change to private/static method?
def renderCB(closure, manager):
    print "rendercb"
    assert(closure)
    thisp = closure
    thisp.makeCurrent()
    thisp.actualRedraw()
    if (thisp.doubleBuffer()):
        thisp.swapBuffers()
    thisp.doneCurrent()

class QuarterWidget(QGLWidget):
    def __init__(self, parent = None, sharewidget = None):
        QGLWidget.__init__(self, parent)

        # from QuarterWidgetP
        self.cachecontext_list = []
        self.cachecontext = self.findCacheContext(self, sharewidget)
        #self.statecursormap = StateCursorMap()

        self.scene = None
        self.contextmenu = None
        self.contextmenuenabled = True

        self.sorendermanager = SoRenderManager()
        self.soeventmanager = SoEventManager()

        #Mind the order of initialization as the XML state machine uses
        #callbacks which depends on other state being initialized
        self.eventmanager = EventManager(self)
        self.devicemanager = DeviceManager(self)

        statechangecb = staticmethod(self.statechangecb)

        statemachine = ScXML.readFile("coin:scxml/navigation/examiner.xml")
        if (statemachine and statemachine.isOfType(SoScXMLStateMachine.getClassTypeId())):
            sostatemachine = cast(statemachine, "SoScXMLStateMachine")
            print sostatemachine
            statemachine.addStateChangeCallback(statechangecb, self)
            self.soeventmanager.setNavigationSystem(None)
            self.soeventmanager.addSoScXMLStateMachine(sostatemachine)
            sostatemachine.initialize()
            print "ok"

        self.headlight = SoDirectionalLight()
        self.headlight.ref()

        raise SystemExit

        #self.sorendermanager.setAutoClipping(SoRenderManager.VARIABLE_NEAR_PLANE)
        self.sorendermanager.setRenderCallback(renderCB, self)
        # FIXME jkg: set alpha 0
        self.sorendermanager.setBackgroundColor(SbColor4f(0, 0, 0, 0))
        self.sorendermanager.activate()
        #self.sorendermanager.addPreRenderCallback(prerendercb, self)
        #self.sorendermanager.addPostRenderCallback(postrendercb, self)

        #self.soeventmanager.setNavigationState(SoEventManager.MIXED_NAVIGATION)

        self.devicemanager.registerDevice(MouseHandler())
#        self.devicemanager.registerDevice(KeyboardHandler())
#        self.eventmanager.registerEventHandler(DragDropHandler())

        # set up a cache context for the default SoGLRenderAction
        self.sorendermanager.getGLRenderAction().setCacheContext(self.getCacheContextId())

        self.setStateCursor("interact", Qt.ArrowCursor)
        self.setStateCursor("idle", Qt.OpenHandCursor)
        self.setStateCursor("rotate", Qt.ClosedHandCursor)
        self.setStateCursor("pan", Qt.SizeAllCursor)
        self.setStateCursor("zoom", Qt.SizeVerCursor)
        self.setStateCursor("seek", Qt.CrossCursor)
        self.setStateCursor("spin", Qt.OpenHandCursor)

        self.setMouseTracking(True)
        self.setFocusPolicy(Qt.StrongFocus);

    def setSceneGraph(self, node):
        if node and self.scene==node:
            return

        camera = None
        superscene = None
        viewall = False

        if node:
            node.ref()
            self.scene = node
            self.scene.ref()

            superscene = SoSeparator()
            superscene.addChild(self.headlight)

            camera = self.searchForCamera(node)
            if not camera:
                camera = SoPerspectiveCamera()
                superscene.addChild(camera)
                viewall = True
                # FIXME jkg: remove when viewAll is in place
                xf = SoTransform()
                xf.set("translation 0 0 -4")
                superscene.addChild(xf)

            superscene.addChild(node)
            node.unref()

        self.soeventmanager.setSceneGraph(superscene)
        self.sorendermanager.setSceneGraph(superscene)
        self.soeventmanager.setCamera(camera)
        self.sorendermanager.setCamera(camera)

        if viewall:
            self.viewAll()

        if superscene:
            superscene.touch()

    def viewAll(self):
        print "FIXME jkg: missing nav viewall stuff"

    def initializeGL(self):
        # NOTE 20080507 jkg: got unexplainable hang here yesterday. but not today
        self.setFormat(QGLFormat(QGL.DepthBuffer))

    def resizeGL(self, width, height):
        vp = SbViewportRegion(width, height)
        self.sorendermanager.setViewportRegion(vp)
        self.soeventmanager.setViewportRegion(vp)

    def paintGL(self):
        self.actualRedraw()

    def actualRedraw(self):
        self.sorendermanager.render(True, True)

    def event(self, event):
        """Translates Qt Events into Coin events and passes them on to the
          scenemanager for processing. If the event can not be translated or
          processed, it is forwarded to Qt and the method returns false. This
          method could be overridden in a subclass in order to catch events of
          particular interest to the application programmer."""
        if self.eventmanager.handleEvent(event):
            return

        print "event not handled", event

        raise Exception("hm")
        soevent = self.devicemanager.translateEvent(event)
        if (soevent and self.soeventmanager.processEvent(soevent)):
            return True

        QGLWidget.event(self, event)

    def setStateCursor(self, state, cursor):
        """  You can set the cursor you want to use for a given navigation
          state. See the Coin documentation on navigation for information
          about available states"""
        # will overwrite the value of an existing item
        #self.statecursormap->insert(state, cursor);
        pass

    # QuarterWidgetP

    def searchForCamera(self, root):
        sa = SoSearchAction()
        sa.setInterest(SoSearchAction.FIRST)
        sa.setType(SoCamera.getClassTypeId())
        sa.apply(root)

#include "QuarterWidgetP.h"
#include <Quarter/QuarterWidget.h>
#include <Quarter/devices/DeviceManager.h>

#include <QtGui/QCursor>
#include <QtCore/QMap>

#include <Inventor/nodes/SoCamera.h>
#include <Inventor/nodes/SoNode.h>
#include <Inventor/actions/SoSearchAction.h>
#include <Inventor/elements/SoGLCacheContextElement.h>
#include <Inventor/lists/SbList.h>
#include <Inventor/SoEventManager.h>
#include <Inventor/scxml/SoScXMLStateMachine.h>

#include "ContextMenu.h"

    def getCacheContextId(self):
        return self.cachecontext.id

    def findCacheContext(self, widget, sharewidget):

        class QuarterWidgetP_cachecontext:
            def __init__(self):
                self.widgetlist = []
                self.id = None

        for cachecontext in self.cachecontext_list:
            for widget in cachecontext.widgetlist:
                if (widget == sharewidget):
                    cachecontext.widgetlist.append(widget)
                    return cachecontext;
        cachecontext = QuarterWidgetP_cachecontext()
        cachecontext.id = SoGLCacheContextElement.getUniqueCacheContext()
        cachecontext.widgetlist.append(widget)
        self.cachecontext_list.append(cachecontext)

        return cachecontext

    def prerendercb(userdata, manager):
        #thisp = static_cast<QuarterWidgetP *>(userdata);
        thisp = userdata
        evman = thisp.soeventmanager
        assert(evman)

        for c in range(evman.getNumSoScXMLStateMachines()):
            statemachine = evman.getSoScXMLStateMachine(c)
            statemachine.preGLRender()

    def postrendercb(userdata, manager):
        #QuarterWidgetP * thisp = static_cast<QuarterWidgetP *>(userdata);
        evman = thisp.soeventmanager
        assert(evman)
        for c in range(evman.getNumSoScXMLStateMachines()):
            statemachine = evman.getSoScXMLStateMachine(c)
            statemachine.postGLRender()

    def statechangecb(userdata, statemachine, stateid, enter, foo):
        contextmenurequest = SbName("contextmenurequest")
        #QuarterWidgetP * thisp = static_cast<QuarterWidgetP *>(userdata);
        assert(thisp and thisp.master)
        if (enter):
            state = SbName(stateid)
        if (thisp.contextmenuenabled and state == contextmenurequest):
            if (not thisp.contextmenu):
                self.contextmenu = ContextMenu(self)
            self.contextmenu.exec_(self.devicemanager.getLastGlobalPosition())
#        if (statecursormap.contains(state)):
#            cursor = statecursormap.value(state)
#            self.setCursor(cursor)
<commit_msg>cleanup, use state machine for viewAll<commit_after>from PyQt4.QtOpenGL import QGLWidget
from PyQt4.QtOpenGL import QGLFormat
from PyQt4.QtOpenGL import QGL
from PyQt4.QtCore import Qt

from pivy.coin import SoRenderManager
from pivy.coin import SoEventManager
#from pivy.coin import SoSceneManager
from pivy.coin import SoSearchAction
from pivy.coin import SoCamera
from pivy.coin import SoPerspectiveCamera
from pivy.coin import SoTransform
from pivy.coin import ScXML
from pivy.coin import SoScXMLStateMachine
from pivy.coin import cast
from pivy.coin import SoGLCacheContextElement

from pivy.coin import SoDirectionalLight
from pivy.coin import SoSeparator
from pivy.coin import SbColor4f
from pivy.coin import SbName
from pivy.coin import SbViewportRegion

from devices import DeviceManager
from devices import MouseHandler

from eventhandlers import EventManager

# FIXME jkg: (1) this is not called and (2) change to private/static method?
def renderCB(closure, rendermanagerdummy):
    thisp = closure
    print thisp
    assert(thisp)
    thisp.makeCurrent()
    thisp.actualRedraw()
    if (thisp.doubleBuffer()):
        thisp.swapBuffers()
    thisp.doneCurrent()

# FIXME jkg: lacking ContextMenu
def statechangecb(userdata, statemachine, stateid, enter, foo):
    contextmenurequest = SbName("contextmenurequest")
    thisp = userdata
    assert(thisp)
    state = SbName()
    if enter:
        state = SbName(stateid)
    if thisp.contextmenuenabled and state == contextmenurequest:
        if not thisp.contextmenu:
            thisp.contextmenu = ContextMenu(self)
        thisp.contextmenu.exec_(self.devicemanager.getLastGlobalPosition())
        if state in statecursormap.keys():
            cursor = thisp.statecursormap[state]
            thisp.setCursor(cursor)

def prerendercb(userdata, manager):
    thisp = userdata
    evman = thisp.soeventmanager
    assert(thisp and evman)
    for c in range(evman.getNumSoScXMLStateMachines()):
        statemachine = evman.getSoScXMLStateMachine(c)
        statemachine.preGLRender()

def postrendercb(userdata, manager):
    thisp = userdata
    evman = thisp.soeventmanager
    assert(evman)
    for c in range(evman.getNumSoScXMLStateMachines()):
        statemachine = evman.getSoScXMLStateMachine(c)
        statemachine.postGLRender()

class QuarterWidget(QGLWidget):
    def __init__(self, parent = None, sharewidget = None):
        QGLWidget.__init__(self, parent)

        # from QuarterWidgetP
        self.cachecontext_list = []
        self.cachecontext = self.findCacheContext(self, sharewidget)
        self.statecursormap = {}

        self.scene = None
        self.contextmenu = None
        self.contextmenuenabled = True

        self.sorendermanager = SoRenderManager()
        self.soeventmanager = SoEventManager()

        #Mind the order of initialization as the XML state machine uses
        #callbacks which depends on other state being initialized
        self.eventmanager = EventManager(self)
        self.devicemanager = DeviceManager(self)

        statemachine = ScXML.readFile("coin:scxml/navigation/examiner.xml")
        if (statemachine and statemachine.isOfType(SoScXMLStateMachine.getClassTypeId())):
            sostatemachine = cast(statemachine, "SoScXMLStateMachine")
            statemachine.addStateChangeCallback(statechangecb, self)
            self.soeventmanager.setNavigationSystem(None)
            self.soeventmanager.addSoScXMLStateMachine(sostatemachine)
            sostatemachine.initialize()

        self.headlight = SoDirectionalLight()
        self.headlight.ref()

        self.sorendermanager.setAutoClipping(SoRenderManager.VARIABLE_NEAR_PLANE)
        self.sorendermanager.setRenderCallback(renderCB, self)
        self.sorendermanager.setBackgroundColor(SbColor4f(0, 0, 0, 0))
        self.sorendermanager.activate()
        self.sorendermanager.addPreRenderCallback(prerendercb, self)
        self.sorendermanager.addPostRenderCallback(postrendercb, self)

        self.soeventmanager.setNavigationState(SoEventManager.MIXED_NAVIGATION)

        self.devicemanager.registerDevice(MouseHandler())

#        self.devicemanager.registerDevice(KeyboardHandler())
#        self.eventmanager.registerEventHandler(DragDropHandler())

        # set up a cache context for the default SoGLRenderAction
        self.sorendermanager.getGLRenderAction().setCacheContext(self.getCacheContextId())

        self.setStateCursor("interact", Qt.ArrowCursor)
        self.setStateCursor("idle", Qt.OpenHandCursor)
        self.setStateCursor("rotate", Qt.ClosedHandCursor)
        self.setStateCursor("pan", Qt.SizeAllCursor)
        self.setStateCursor("zoom", Qt.SizeVerCursor)
        self.setStateCursor("seek", Qt.CrossCursor)
        self.setStateCursor("spin", Qt.OpenHandCursor)

        self.setMouseTracking(True)
        self.setFocusPolicy(Qt.StrongFocus);

    def setSceneGraph(self, node):
        if node and self.scene==node:
            return

        camera = None
        superscene = None
        viewall = False

        if node:
            node.ref()
            self.scene = node
            self.scene.ref()

            superscene = SoSeparator()
            superscene.addChild(self.headlight)

            camera = self.searchForCamera(node)
            if not camera:
                camera = SoPerspectiveCamera()
                superscene.addChild(camera)
                viewall = True

            superscene.addChild(node)
            node.unref()

        self.soeventmanager.setSceneGraph(superscene)
        self.sorendermanager.setSceneGraph(superscene)
        self.soeventmanager.setCamera(camera)
        self.sorendermanager.setCamera(camera)

        if viewall:
            self.viewAll()

        if superscene:
            superscene.touch()

    def viewAll(self):
        """ Reposition the current camera to display the entire scene"""
        if self.soeventmanager.getNavigationSystem():
            self.soeventmanager.getNavigationSystem().viewAll()

        viewallevent = SbName("sim.coin3d.coin.navigation.ViewAll")
        for c in range(self.soeventmanager.getNumSoScXMLStateMachines()):
            sostatemachine = self.soeventmanager.getSoScXMLStateMachine(c)
            if (sostatemachine.isActive()):
                sostatemachine.queueEvent(viewallevent)
                sostatemachine.processEventQueue()

    def initializeGL(self):
        self.setFormat(QGLFormat(QGL.DepthBuffer))

    def resizeGL(self, width, height):
        vp = SbViewportRegion(width, height)
        self.sorendermanager.setViewportRegion(vp)
        self.soeventmanager.setViewportRegion(vp)

    def paintGL(self):
        self.actualRedraw()

    def actualRedraw(self):
        self.sorendermanager.render(True, True)

    def event(self, qevent):
        """Translates Qt Events into Coin events and passes them on to the
          scenemanager for processing. If the event can not be translated or
          processed, it is forwarded to Qt and the method returns false. This
          method could be overridden in a subclass in order to catch events of
          particular interest to the application programmer."""

        if self.eventmanager.handleEvent(qevent):
            return True

        soevent = self.devicemanager.translateEvent(qevent)
        if (soevent and self.soeventmanager.processEvent(soevent)):
            return True

        QGLWidget.event(self, qevent)
        return True

    def setStateCursor(self, state, cursor):
        self.statecursormap[state] = cursor

    # QuarterWidgetP
    def searchForCamera(self, root):
        sa = SoSearchAction()
        sa.setInterest(SoSearchAction.FIRST)
        sa.setType(SoCamera.getClassTypeId())
        sa.apply(root)

    def getCacheContextId(self):
        return self.cachecontext.id

    def findCacheContext(self, widget, sharewidget):

        class QuarterWidgetP_cachecontext:
            def __init__(self):
                self.widgetlist = []
                self.id = None

        for cachecontext in self.cachecontext_list:
            for widget in cachecontext.widgetlist:
                if (widget == sharewidget):
                    cachecontext.widgetlist.append(widget)
                    return cachecontext;
        cachecontext = QuarterWidgetP_cachecontext()
        cachecontext.id = SoGLCacheContextElement.getUniqueCacheContext()
        cachecontext.widgetlist.append(widget)
        self.cachecontext_list.append(cachecontext)

        return cachecontext
<|endoftext|>