<commit_before>debug = False

import pygame, sys, math, random, time
from Meme import *
from Bossmeme import *
from Wall import *
from Level import *
from Player import *
from Arm import *
from Bullet import *
from GunPickup import *
from Goal import *
from BetterBossMeme import *
from EndFlag import *

pygame.init()

clock = pygame.time.Clock()

width = 1280
height = 720
size = width, height
screen = pygame.display.set_mode(size)

bgColor = r,g,b = 0, 0, 0

all = pygame.sprite.Group()
players = pygame.sprite.Group()
memes = pygame.sprite.Group()
bosses = pygame.sprite.Group()
walls = pygame.sprite.Group()
backgrounds = pygame.sprite.Group()
bigwalls = pygame.sprite.Group()
movingObjects = pygame.sprite.Group()
bullets = pygame.sprite.Group()
pickups = pygame.sprite.Group()
goals = pygame.sprite.Group()
EndFlags = pygame.sprite.Group()
boss = pygame.sprite.Group()
bossbullets = pygame.sprite.Group()

PlayerMeme.containers = players, all
Arm.containers = players, all
Bullet.containers = bullets, movingObjects, all
BossBullet.containers = bossbullets, movingObjects, all
Background.containers = backgrounds, movingObjects, all
Meme.containers = memes, movingObjects, all
BossMeme.containers = bosses, movingObjects, all
Wall.containers = walls,movingObjects, all
Wall_5x5.containers = bigwalls, movingObjects, all
Ground.containers = walls, movingObjects, all
GunPickup.containers = pickups, movingObjects, all
Goal.containers = goals, movingObjects, all
EndFlag.containers = EndFlags, movingObjects, all

#video transcode command:
"""
ffmpeg -i memefinityloading.mp4 -vcodec mpeg1video -b:v 2048k -acodec libmp3lame -s 1280x720 -intra memefinityloading.mpg
"""

#pygame.mixer.quit()

movie = pygame.movie.Movie('rsc/memefinityloading.mpg')
movie_screen = pygame.Surface(size)
movie_rect = movie_screen.get_rect()
movie.set_display(movie_screen)
movie.play()
pygame.mixer.music.load("rsc/audio/Intro Music.ogg")
pygame.mixer.music.set_endevent(pygame.constants.USEREVENT)
pygame.mixer.music.play()

playing = True
while playing:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            movie.stop()
            pygame.mixer.music.stop()
            pygame.quit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                movie.stop()
                pygame.mixer.music.stop()
                playing = False

    if not movie.get_busy() and playing:
        movie.stop()
        playing = False
    screen.blit(movie_screen, movie_rect)
    pygame.display.update()
    clock.tick(60)

while True:
    menu = True
    bg = Background("playbutton.png", size)
    while menu:
        for event in pygame.event.get():
            if event.type == pygame.QUIT: sys.exit()
            if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        menu = False
        screen.blit(bg.image, bg.rect)
        pygame.display.update()
        clock.tick(60)

    levelNumber = 1 #REMOVE THIS IT WILL CAUSE PROBLEMS IN THE LATER

    bg = Background("BgLevel1.png")
    level = Level(levelNumber, size)

    if levelNumber == 2:
        level = Level(levelNumber, size)
        bg = Background("BgLevel1.png")

    if levelNumber == 3:
        level = Level(levelNumber, size)
        bg = Background("BgLevel1.png")

    print len(walls.sprites())

    #Meme(size, 1,
                  #[random.randint(1, 10), random.randint(1, 10)],
                  #[random.randint(0, width-100), random.randint(0, height-100)],
                  #random.randint(20, 100))

    #player = PlayerMeme(size, 5, [width/2+50,height/2])
    #arm = Arm(size, player)
    using = "keyboard"

    for p in players.sprites():
        if p.kind == "arm":
            arm = p
        else:
            player = p
    glev = 0

    print player, arm

    shooting = None
    rightIsDown = False
    leftIsDown = False
    downLast = "right"

    if debug: startTime = time.time()

    fireRate = arm.gun.fireRate
    fireTimer = 0

    print "Pickups: ", pickups.sprites()[0].rect.center
    print "player: ", players.sprites()[0].rect.center

    pygame.mixer.music.load("rsc/audio/Level "+ str(levelNumber) + " Audio.ogg")
    pygame.mixer.music.set_endevent(pygame.constants.USEREVENT)
    pygame.mixer.music.play()
    print "Volume:", pygame.mixer.music.get_volume()
    print "Is playing:", pygame.mixer.music.get_busy()

    while player.living :
        if debug: print "last loop total: ", time.time() - startTime
        if debug: print "--------------------------------------------------------------------------------"
        if debug: startTime = time.time()
        for event in pygame.event.get():
            if event.type == pygame.QUIT: sys.exit()
            if using == "keyboard":
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_w or event.key == pygame.K_UP or event.key == pygame.K_SPACE:
                        player.go("up", walls)
                    if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
                        rightIsDown = True
                        downLast = "right"
                    if event.key == pygame.K_a or event.key == pygame.K_LEFT:
                        leftIsDown = True
                        downLast = "left"
                    if event.key == pygame.K_u:
                        print player.playerSpeedx()
                    if event.key == pygame.K_m:
                        print "music pos: ", pygame.mixer.music.get_pos()
                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_w or event.key == pygame.K_UP:
                        player.go("stop up")
                    if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
                        rightIsDown = False
                    if event.key == pygame.K_a or event.key == pygame.K_LEFT:
                        leftIsDown = False
                if event.type == pygame.MOUSEMOTION:
                    pygame.mouse.set_visible(True)
                    arm.aim(event.pos)
                    player.aim(event.pos)
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        shooting = "alt"
                    if event.button == 2:
                        shooting = "beam"
                    if event.button == 3:
                        shooting = "alt"
                if event.type == pygame.MOUSEBUTTONUP:
                    if event.button == 1:
                        shooting = None
                    if event.button == 2:
                        shooting = None
                    if event.button == 3:
                        shooting = None

        if debug: print "after events: ", time.time() - startTime
        if rightIsDown and not leftIsDown:
            player.go("right")
        elif leftIsDown and not rightIsDown:
            player.go("left")
        elif rightIsDown and leftIsDown:
            player.go(downLast)
        else:
            player.go("stop "+downLast)

        if shooting:
            if shooting == "normal":
                Bullet(player.rect.center, arm.angle)
                shooting = None
            elif shooting == "alt":
                if fireTimer < fireRate:
                    fireTimer += 1
                else:
                    fireTimer = 0
                    Bullet(player.rect.center, arm.angle)
            elif shooting == "beam":
                if fireTimer < fireRate:
                    fireTimer += 1
                else:
                    fire = 0
                    Bullet(player.rect.center, arm.angle)

        if debug: print "after input handled: ", time.time() - startTime

        player.update(walls)
        arm.update()
        for bullet in bullets:
            bullet.update()
        for bossbullet in bossbullets:
            bossbullet.update()
        for meme in memes:
            meme.update(walls)
            meme.bounceScreen(size)
        for boss in bosses:
            boss.update(walls, player)
            boss.bounceScreen(size)

        if debug: print "after updates done: ", time.time() - startTime

        if player.rect.right >= 500:
                diff = player.rect.right - 500
                player.rect.right = 500
                level.shiftWorld([movingObjects], -diff)
        if player.rect.left <= 120:
                diff = 120 - player.rect.left
                player.rect.left = 120
                level.shiftWorld([movingObjects], diff)

        if debug: print "after scrolling done: ", time.time() - startTime
        memesHit = pygame.sprite.spritecollide(player, memes, True)
        bulletsHitMemes = pygame.sprite.groupcollide(bullets, memes, True, True)
        bulletsHitWalls = pygame.sprite.groupcollide(bullets, walls, True, False)
        playerHitspickups = pygame.sprite.spritecollide(player, pickups, True)
        playerHitgoals = pygame.sprite.spritecollide(player, goals, False)
        playerHitsbosses = pygame.sprite.spritecollide(player, bosses, False)
        bulletsHitbosses = pygame.sprite.groupcollide(bullets, bosses, True, False)
        playerHitbossbullets = pygame.sprite.spritecollide(player, bossbullets, True)
        bossbulletsHitWalls = pygame.sprite.groupcollide(bossbullets, walls, True, False)
        PlayerHitsEndFlag = pygame.sprite.spritecollide(player, EndFlags, False)

        if debug: print "after collision groups created: ", time.time() - startTime

        for meme in memesHit:
            meme.bounceMeme(PlayerMeme)
            player.hitMeme(meme)
            meme.speedx = -meme.speedx

        for boss in playerHitsbosses:
            player.hitMeme(boss)

        for EndFlag in PlayerHitsEndFlag:
            player.hitMeme(boss)

        for bullet in bulletsHitbosses:
            for boss in bulletsHitbosses[bullet]:
                boss.hitBullet(bullet)

        for bossbullet in playerHitbossbullets:
            player.hitBossbullet(bossbullet)

        for pickup in playerHitspickups:
            player.heal(pickup.value)

        for goal in playerHitgoals:
            level.unloadLevel(all)
            levelNumber += 1
            level = Level(levelNumber, size)
            bg = Background("BgLevel1.png")
            for p in players.sprites():
                if p.kind == "arm":
                    arm = p
                else:
                    player = p
            pygame.mixer.music.load("rsc/audio/Level "+ str(levelNumber) + " Audio.ogg")
            pygame.mixer.music.play(-1)
            print pygame.mixer.music.get_volume()
            print pygame.mixer.music.get_busy()

        #https://github.com/KRHS-GameProgramming-2016/Spikes-Evil-Maze-Game/blob/master/Game.py#L65

        if debug: print "after meme/player collision group: ", time.time() - startTime

        bgColor = r,g,b
        screen.fill(bgColor)
        screen.blit(bg.image, bg.rect)
        if debug: print "after bg render: ", time.time() - startTime
        for meme in memes:
            screen.blit(meme.image, meme.rect)
        for meme in bosses:
            screen.blit(meme.image, meme.rect)
        for bullet in bullets:
            screen.blit(bullet.image, bullet.rect)
        for bossbullet in bossbullets:
            screen.blit(bossbullet.image, bossbullet.rect)
        for pickup in pickups:
            screen.blit(pickup.image, pickup.rect)
        screen.blit(player.image, player.rect)
        screen.blit(arm.image, arm.rect)
        for wall in walls:
            screen.blit(wall.image, wall.rect)
        for wall in bigwalls:
            screen.blit(wall.image, wall.rect)
        for goal in goals:
            screen.blit(goal.image, goal.rect)
        pygame.display.flip()
        clock.tick(60)

        if debug: print "after render: ", time.time() - startTime, ", fps:", clock.get_fps()

    level.unloadLevel(all)

while BetterBossMeme.living :
    shooting ="alt"

<commit_msg>The intro music has been separated from the video and beam commented out<commit_after>debug = False

import pygame, sys, math, random, time
from Meme import *
from Bossmeme import *
from Wall import *
from Level import *
from Player import *
from Arm import *
from Bullet import *
from GunPickup import *
from Goal import *
from BetterBossMeme import *
from EndFlag import *

pygame.init()

clock = pygame.time.Clock()

width = 1280
height = 720
size = width, height
screen = pygame.display.set_mode(size)

bgColor = r,g,b = 0, 0, 0

all = pygame.sprite.Group()
players = pygame.sprite.Group()
memes = pygame.sprite.Group()
bosses = pygame.sprite.Group()
walls = pygame.sprite.Group()
backgrounds = pygame.sprite.Group()
bigwalls = pygame.sprite.Group()
movingObjects = pygame.sprite.Group()
bullets = pygame.sprite.Group()
pickups = pygame.sprite.Group()
goals = pygame.sprite.Group()
EndFlags = pygame.sprite.Group()
boss = pygame.sprite.Group()
bossbullets = pygame.sprite.Group()

PlayerMeme.containers = players, all
Arm.containers = players, all
Bullet.containers = bullets, movingObjects, all
BossBullet.containers = bossbullets, movingObjects, all
Background.containers = backgrounds, movingObjects, all
Meme.containers = memes, movingObjects, all
BossMeme.containers = bosses, movingObjects, all
Wall.containers = walls,movingObjects, all
Wall_5x5.containers = bigwalls, movingObjects, all
Ground.containers = walls, movingObjects, all
GunPickup.containers = pickups, movingObjects, all
Goal.containers = goals, movingObjects, all
EndFlag.containers = EndFlags, movingObjects, all

#video transcode command:
"""
ffmpeg -i memefinityloading.mp4 -vcodec mpeg1video -b:v 2048k -acodec libmp3lame -s 1280x720 -intra memefinityloading.mpg
"""

#pygame.mixer.quit()

movie = pygame.movie.Movie('rsc/memefinityloading.mpg')
movie_screen = pygame.Surface(size)
movie_rect = movie_screen.get_rect()
movie.set_display(movie_screen)
movie.play()
pygame.mixer.music.load("rsc/audio/Intro Music.ogg")
pygame.mixer.music.set_endevent(pygame.constants.USEREVENT)
pygame.mixer.music.play()

playing = True
while playing:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            movie.stop()
            pygame.mixer.music.stop()
            pygame.quit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                movie.stop()
                pygame.mixer.music.stop()
                playing = False

    if not movie.get_busy() and playing:
        movie.stop()
        playing = False
    screen.blit(movie_screen, movie_rect)
    pygame.display.update()
    clock.tick(60)

while True:
    menu = True
    bg = Background("playbutton.png", size)
    while menu:
        for event in pygame.event.get():
            if event.type == pygame.QUIT: sys.exit()
            if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        menu = False
        screen.blit(bg.image, bg.rect)
        pygame.display.update()
        clock.tick(60)

    levelNumber = 1 #REMOVE THIS IT WILL CAUSE PROBLEMS IN THE LATER

    bg = Background("BgLevel1.png")
    level = Level(levelNumber, size)

    if levelNumber == 2:
        level = Level(levelNumber, size)
        bg = Background("BgLevel1.png")

    if levelNumber == 3:
        level = Level(levelNumber, size)
        bg = Background("BgLevel1.png")

    print len(walls.sprites())

    #Meme(size, 1,
                  #[random.randint(1, 10), random.randint(1, 10)],
                  #[random.randint(0, width-100), random.randint(0, height-100)],
                  #random.randint(20, 100))

    #player = PlayerMeme(size, 5, [width/2+50,height/2])
    #arm = Arm(size, player)
    using = "keyboard"

    for p in players.sprites():
        if p.kind == "arm":
            arm = p
        else:
            player = p
    glev = 0

    print player, arm

    shooting = None
    rightIsDown = False
    leftIsDown = False
    downLast = "right"

    if debug: startTime = time.time()

    fireRate = arm.gun.fireRate
    fireTimer = 0

    print "Pickups: ", pickups.sprites()[0].rect.center
    print "player: ", players.sprites()[0].rect.center

    pygame.mixer.music.load("rsc/audio/Level "+ str(levelNumber) + " Audio.ogg")
    pygame.mixer.music.set_endevent(pygame.constants.USEREVENT)
    pygame.mixer.music.play()
    print "Volume:", pygame.mixer.music.get_volume()
    print "Is playing:", pygame.mixer.music.get_busy()

    while player.living :
        if debug: print "last loop total: ", time.time() - startTime
        if debug: print "--------------------------------------------------------------------------------"
        if debug: startTime = time.time()
        for event in pygame.event.get():
            if event.type == pygame.QUIT: sys.exit()
            if using == "keyboard":
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_w or event.key == pygame.K_UP or event.key == pygame.K_SPACE:
                        player.go("up", walls)
                    if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
                        rightIsDown = True
                        downLast = "right"
                    if event.key == pygame.K_a or event.key == pygame.K_LEFT:
                        leftIsDown = True
                        downLast = "left"
                    if event.key == pygame.K_u:
                        print player.playerSpeedx()
                    if event.key == pygame.K_m:
                        print "music pos: ", pygame.mixer.music.get_pos()
                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_w or event.key == pygame.K_UP:
                        player.go("stop up")
                    if event.key == pygame.K_d or event.key == pygame.K_RIGHT:
                        rightIsDown = False
                    if event.key == pygame.K_a or event.key == pygame.K_LEFT:
                        leftIsDown = False
                if event.type == pygame.MOUSEMOTION:
                    pygame.mouse.set_visible(True)
                    arm.aim(event.pos)
                    player.aim(event.pos)
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        shooting = "alt"
                    if event.button == 2:
                        shooting = "beam"
                    if event.button == 3:
                        shooting = "alt"
                if event.type == pygame.MOUSEBUTTONUP:
                    if event.button == 1:
                        shooting = None
                    if event.button == 2:
                        shooting = None
                    if event.button == 3:
                        shooting = None

        if debug: print "after events: ", time.time() - startTime
        if rightIsDown and not leftIsDown:
            player.go("right")
        elif leftIsDown and not rightIsDown:
            player.go("left")
        elif rightIsDown and leftIsDown:
            player.go(downLast)
        else:
            player.go("stop "+downLast)

        if shooting:
            if shooting == "normal":
                Bullet(player.rect.center, arm.angle)
                shooting = None
            elif shooting == "alt":
                if fireTimer < fireRate:
                    fireTimer += 1
                else:
                    fireTimer = 0
                    Bullet(player.rect.center, arm.angle)
            #elif shooting == "beam":
                #if fireTimer < fireRate:
                    #fireTimer += 1
                #else:
                    #fire = 0
                    #Bullet(player.rect.center, arm.angle)

        if debug: print "after input handled: ", time.time() - startTime

        player.update(walls)
        arm.update()
        for bullet in bullets:
            bullet.update()
        for bossbullet in bossbullets:
            bossbullet.update()
        for meme in memes:
            meme.update(walls)
            meme.bounceScreen(size)
        for boss in bosses:
            boss.update(walls, player)
            boss.bounceScreen(size)

        if debug: print "after updates done: ", time.time() - startTime

        if player.rect.right >= 500:
                diff = player.rect.right - 500
                player.rect.right = 500
                level.shiftWorld([movingObjects], -diff)
        if player.rect.left <= 120:
                diff = 120 - player.rect.left
                player.rect.left = 120
                level.shiftWorld([movingObjects], diff)

        if debug: print "after scrolling done: ", time.time() - startTime
        memesHit = pygame.sprite.spritecollide(player, memes, True)
        bulletsHitMemes = pygame.sprite.groupcollide(bullets, memes, True, True)
        bulletsHitWalls = pygame.sprite.groupcollide(bullets, walls, True, False)
        playerHitspickups = pygame.sprite.spritecollide(player, pickups, True)
        playerHitgoals = pygame.sprite.spritecollide(player, goals, False)
        playerHitsbosses = pygame.sprite.spritecollide(player, bosses, False)
        bulletsHitbosses = pygame.sprite.groupcollide(bullets, bosses, True, False)
        playerHitbossbullets = pygame.sprite.spritecollide(player, bossbullets, True)
        bossbulletsHitWalls = pygame.sprite.groupcollide(bossbullets, walls, True, False)
        PlayerHitsEndFlag = pygame.sprite.spritecollide(player, EndFlags, False)

        if debug: print "after collision groups created: ", time.time() - startTime

        for meme in memesHit:
            meme.bounceMeme(PlayerMeme)
            player.hitMeme(meme)
            meme.speedx = -meme.speedx

        for boss in playerHitsbosses:
            player.hitMeme(boss)

        for EndFlag in PlayerHitsEndFlag:
            player.hitMeme(boss)

        for bullet in bulletsHitbosses:
            for boss in bulletsHitbosses[bullet]:
                boss.hitBullet(bullet)

        for bossbullet in playerHitbossbullets:
            player.hitBossbullet(bossbullet)

        for pickup in playerHitspickups:
            player.heal(pickup.value)

        for goal in playerHitgoals:
            level.unloadLevel(all)
            levelNumber += 1
            level = Level(levelNumber, size)
            bg = Background("BgLevel1.png")
            for p in players.sprites():
                if p.kind == "arm":
                    arm = p
                else:
                    player = p
            pygame.mixer.music.load("rsc/audio/Level "+ str(levelNumber) + " Audio.ogg")
            pygame.mixer.music.play(-1)
            print pygame.mixer.music.get_volume()
            print pygame.mixer.music.get_busy()

        #https://github.com/KRHS-GameProgramming-2016/Spikes-Evil-Maze-Game/blob/master/Game.py#L65

        if debug: print "after meme/player collision group: ", time.time() - startTime

        bgColor = r,g,b
        screen.fill(bgColor)
        screen.blit(bg.image, bg.rect)
        if debug: print "after bg render: ", time.time() - startTime
        for meme in memes:
            screen.blit(meme.image, meme.rect)
        for meme in bosses:
            screen.blit(meme.image, meme.rect)
        for bullet in bullets:
            screen.blit(bullet.image, bullet.rect)
        for bossbullet in bossbullets:
            screen.blit(bossbullet.image, bossbullet.rect)
        for pickup in pickups:
            screen.blit(pickup.image, pickup.rect)
        screen.blit(player.image, player.rect)
        screen.blit(arm.image, arm.rect)
        for wall in walls:
            screen.blit(wall.image, wall.rect)
        for wall in bigwalls:
            screen.blit(wall.image, wall.rect)
        for goal in goals:
            screen.blit(goal.image, goal.rect)
        pygame.display.flip()
        clock.tick(60)

        if debug: print "after render: ", time.time() - startTime, ", fps:", clock.get_fps()

    level.unloadLevel(all)

while BetterBossMeme.living :
    shooting ="alt"

<|endoftext|>


<commit_before># Copyright (c) 2007-2011 gocept gmbh & co. kg
# See also LICENSE.txt

import zeit.cms.testing

class TestTree(zeit.cms.testing.SeleniumTestCase):

    def test_tree_keeps_state(self):
        s = self.selenium

        # Delete the tree state cookie to have a defined starting point
        s.deleteCookie('zeit.cms.repository.treeState', '/')
        self.open('/', auth='zmgr:mgrpw')
        s.verifyTextPresent('online')

        # Open `online`
        s.click('//li[@uniqueid="http://xml.zeit.de/online"]')
        s.waitForElementPresent(
            '//li[@uniqueid="http://xml.zeit.de/online/2007"]')

        # Tree is still open after reload."
        self.open('/', auth='zmgr:mgrpw')
        s.waitForElementPresent(
            '//li[@uniqueid="http://xml.zeit.de/online/2007"]')
<commit_msg>Re #11470: Fix selenium test by waiting<commit_after># Copyright (c) 2007-2011 gocept gmbh & co. kg
# See also LICENSE.txt

import zeit.cms.testing

class TestTree(zeit.cms.testing.SeleniumTestCase):

    def test_tree_keeps_state(self):
        s = self.selenium

        # Delete the tree state cookie to have a defined starting point
        s.deleteCookie('zeit.cms.repository.treeState', '/')
        self.open('/', auth='zmgr:mgrpw')
        s.waitForTextPresent('online')

        # Open `online`
        s.click('//li[@uniqueid="http://xml.zeit.de/online"]')
        s.waitForElementPresent(
            '//li[@uniqueid="http://xml.zeit.de/online/2007"]')

        # Tree is still open after reload."
        self.open('/', auth='zmgr:mgrpw')
        s.waitForElementPresent(
            '//li[@uniqueid="http://xml.zeit.de/online/2007"]')
<|endoftext|>


<commit_before>''' Demo of a Metro-style pivot view.

Inspired by Sami Makkonen in his blog post here:
http://qt.digia.com/Blogs/Qt-blog/Sami-Makkonen/Dates/2012/1/2012/
'''

import sys
import collections

from PySide import QtCore, QtGui
from PySide.QtCore import Qt

class Style:
    '''Style constants'''

    # margin for all ui components
    component_margin = 24

    body_text_size = 24
    header_text_size = 48

    # ui fonts and color
    header_font = QtGui.QFont("Segoe UI", header_text_size)
    body_font = QtGui.QFont("Segoe UI", body_text_size)
    ui_text_color = Qt.white
    background_style = "background-color: rgba(26,26,26)"
    # ui animation duration
    animation_time = 400

class PivotView(QtGui.QGraphicsView):
    def __init__(self, scene, tabs=None, parent=None):
        super(PivotView, self).__init__(scene, parent)

        self.tabs = tabs or {}

        self.header_items = []
        self.header_animations = []
        self.content_items = []
        self.content_animations = []

        self.current_item = 0
        self.mouse_x_position = 0

        #####
        # set blackish background
        self.setStyleSheet(Style.background_style)

        # create opacity animator
        self.opacity_animator = QtCore.QPropertyAnimation()
        self.opacity_animator.setDuration(Style.animation_time)
        self.opacity_animator.setPropertyName("opacity")
        self.opacity_animator.setEasingCurve(QtCore.QEasingCurve.Linear)
        self.opacity_animator.setStartValue(0.3)
        self.opacity_animator.setEndValue(1.0)

        # create animation groups
        self.group_animation_content = QtCore.QParallelAnimationGroup()
        self.group_animation_header = QtCore.QParallelAnimationGroup()

        # create the rest of the ui
        self._create_top_bar()
        self._create_metro_tab_bar()
        self._create_content_items()

    def _create_top_bar(self):
        bar = QtGui.QGraphicsTextItem()
        bar.setAcceptHoverEvents(False)
        bar.setFont(Style.body_font)
        bar.setPlainText("Python Metro Style Pivot")
        bar.setDefaultTextColor(Style.ui_text_color)
        bar.setPos(Style.component_margin, Style.component_margin)
        self.scene().addItem(bar)

    def _create_metro_tab_bar(self):
        x_pos = Style.component_margin

        # create pivot header items
        for i, text in enumerate(self.tabs.iterkeys()):
            text_item = QtGui.QGraphicsTextItem()
            text_item.setAcceptHoverEvents(False)
            text_item.setPlainText(text)
            text_item.setFont(Style.header_font)
            text_item.adjustSize()
            text_item.setDefaultTextColor(Style.ui_text_color)

            # place header below header text
            text_item.setPos(x_pos, (Style.component_margin* 2 + Style.body_text_size))

            # calculate position for the next item. ComponentMargin + item width + ComponentMargin
            x_pos = x_pos + text_item.textWidth() + Style.component_margin

            anim = QtCore.QPropertyAnimation(text_item, 'pos')
            anim.setDuration(Style.animation_time)
            anim.setPropertyName('pos')
            anim.setEasingCurve(QtCore.QEasingCurve.OutCirc)
            self.group_animation_header.addAnimation(anim)
            self.header_animations.append(anim)

            # remove highlight from all items except the current one
            if i > 0:
                text_item.setOpacity(0.3)

            self.header_items.append(text_item)
            self.scene().addItem(text_item)

    def _create_content_items(self):
        x_pos = Style.component_margin
        # scene width - margins
        text_width = self.scene().width() - 2 * Style.component_margin

        # create pivot items text
        for i, text in enumerate(self.tabs.itervalues()):
            tmp = QtGui.QGraphicsTextItem()
            tmp.setFont(Style.body_font)
            tmp.setAcceptHoverEvents(False)
            tmp.setPlainText(text)
            tmp.setTextWidth(text_width)
            tmp.setDefaultTextColor(Style.ui_text_color)

            # place content below header and pivot
            tmp.setPos(x_pos,(Style.component_margin * 4 +
                              Style.body_text_size + Style.header_text_size))

            # calculate the position for the next item
            x_pos = x_pos + text_width + Style.component_margin

            anim = QtCore.QPropertyAnimation(tmp, 'pos')
            anim.setDuration(Style.animation_time)
            anim.setPropertyName('pos')
            anim.setEasingCurve(QtCore.QEasingCurve.OutCirc)

            self.group_animation_content.addAnimation(anim)
            self.content_animations.append(anim)
            self.content_items.append(tmp)
            self.scene().addItem(tmp)

    def mousePressEvent(self, event):
        self.mouse_x_position = event.pos().x()

    def mouseMoveEvent(self, event):
        # move the header by the width of oen item
        calc_header_x_offset = lambda i: self.header_items[i].textWidth() + Style.component_margin

        # a negative value means the mouse moved left
        delta_x = event.pos().x() - self.mouse_x_position

        # don't start animations twice
        if (self.group_animation_content.state() != QtCore.QAbstractAnimation.Running and
            self.group_animation_header.state() != QtCore.QAbstractAnimation.Running):

            # sweep left
            if delta_x < 0:
                # don't get over the edge
                if self.current_item < len(self.tabs) - 1:
                    # move the header by the width of the current item
                    header_x_offset = -calc_header_x_offset(self.current_item)
                    self.current_item += 1
                    self.start_content_animation(True)
                    self.start_header_animation(header_x_offset)

            # sweep right
            elif delta_x > 0:
                # don't get over the edge
                if self.current_item > 0:
                    self.current_item -= 1
                    # move the header by the width of the item being moved into place
                    header_x_offset = calc_header_x_offset(self.current_item)
                    self.start_content_animation(False)
                    self.start_header_animation(header_x_offset)

        self.mouse_x_position = event.pos().x()

    def start_content_animation(self, sweep_left=True):
        # create animation items
        for i in xrange(len(self.tabs)):
            text = self.content_items[i]
            start = text.pos()
            end = text.pos()

            if sweep_left:
                end.setX(end.x() - text.textWidth() - Style.component_margin)
            else:
                end.setX(end.x() + text.textWidth() + Style.component_margin)

            self.content_animations[i].setStartValue(start)
            self.content_animations[i].setEndValue(end)

        self.group_animation_content.start()

    def start_header_animation(self, x_offset):
        for i in xrange(len(self.tabs)):
            text = self.header_items[i]
            start = text.pos()
            end = text.pos()
            end.setX(end.x() + x_offset)

            self.header_animations[i].setStartValue(start)
            self.header_animations[i].setEndValue(end)

            # reset opacity
            text.setOpacity(0.3)

        self.opacity_animator.setTargetObject(self.header_items[self.current_item])
        self.group_animation_header.start()
        self.opacity_animator.start()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    view_rect = QtCore.QRect(100, 100, 1366, 768)

    scene = QtGui.QGraphicsScene()
    scene.setSceneRect(0.0, 0.0, view_rect.width(), view_rect.height())

    tabs = collections.OrderedDict((('Summary',"Metro is an internal code name of a typography-based design language created by Microsoft, originally for use in Windows Phone 7. A key design principle of Metro is better focus on the content of applications, relying more on typography and less on graphics (\"content before chrome\"). Early uses of the Metro principles began as early as Microsoft Encarta 95 and MSN 2.0, and later evolved into Windows Media Center and Zune. Later the principles of Metro were included in Windows Phone, Microsoft's website, the Xbox 360 dashboard update, and Windows 8."),
            ('History',"Metro is based on the design principles of classic Swiss graphic design. Early glimpses of this style could be seen in Windows Media Center for Windows XP Media Center Edition, which favored text as the primary form of navigation. This interface carried over into later iterations of Media Center. In 2006, Zune refreshed its interface using these principles. Microsoft designers decided to redesign the interface and with more focus on clean typography and less on UI chrome. These principles and the new Zune UI were carried over to Windows Phone 7 (from which much was drawn for Windows 8). The Zune Desktop Client was also redesigned with an emphasis on typography and clean design that was different from the Zune's previous Portable Media Center based UI. Flat colored \"live tiles\" were introduced into the design language during the early Windows Phone's studies. Microsoft has begun integrating these elements of the design language into its other products, with direct influence being seen in newer versions of Windows Live Messenger, Live Mesh, and Windows 8."),
            ('Principles 1',"Microsoft's design team says that the design language is partly inspired by signs commonly found at public transport systems; for instance, those found on the King County Metro transit system, which serves the greater Seattle area where Microsoft is headquartered. The design language places emphasis on good typography and has large text that catches the eye. Microsoft says that the design language is designed to be \"sleek, quick, modern\" and a \"refresh\" from the icon-based interfaces of Windows, Android, and iOS. All instances use fonts based on the Segoe font family designed by Steve Matteson at Agfa Monotype and licensed to Microsoft. For the Zune, Microsoft created a custom version called Zegoe UI, and for Windows Phone, Microsoft created the \"Segoe WP\" font family. The fonts mostly differ only in minor details. More obvious differences between Segoe UI and Segoe WP are apparent in their respective numerical characters. The Segoe UI in Windows 8 had an obvious differences as being similar to Segoe WP. Notable characters had a typographic changes of the characters 1, 2, 4, 5, 7, 8, I, and Q."),
            ('Principles 2',"The design language was designed specifically to consolidate groups of common tasks to speed up usage. This is accomplished by excluding superfluous graphics and instead relying on the actual content to also function as the main UI. The resulting interfaces favour larger hubs over smaller buttons and often feature laterally scrolling canvases. Page titles are usually large and consequently also take advantage of lateral scrolling."),
            ('Principles 3',"Animation plays a large part, with transitions, and user interactions such as presses or swipes recommended to always be acknowledged by some form of natural animation or motion. This is intended to give the user the impression that the UI is \"alive\" and responsive, with \"an added sense of depth.\""),
            ('Principles 4',"Close to the official launch date of Windows 8 (October 26, 2012), more developers and Microsoft partners started working on creating new Metro applications, and many websites with resources related to this topic have been created, as well as the Microsoft's UX guidelines for Windows Store Apps."),
    ))

    view = PivotView(scene, tabs=tabs)
    view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    view.setGeometry(view_rect)
    view.setRenderHints(QtGui.QPainter.Antialiasing)
    view.show()

    app.exec_()<commit_msg>Improved touch controls and animation.<commit_after>''' Demo of a Metro-style pivot view.

Inspired by Sami Makkonen in his blog post here:
http://qt.digia.com/Blogs/Qt-blog/Sami-Makkonen/Dates/2012/1/2012/
'''

import sys
import collections

from PySide import QtCore, QtGui
from PySide.QtCore import Qt

class Style:
    '''Style constants'''

    # margin for all ui components
    component_margin = 24

    body_text_size = 24
    header_text_size = 48

    # ui fonts and color
    header_font = QtGui.QFont("Segoe UI", header_text_size)
    body_font = QtGui.QFont("Segoe UI", body_text_size)
    ui_text_color = Qt.white
    background_style = "background-color: rgba(26,26,26)"

    # ui animation duration
    animation_time = 400

class PivotView(QtGui.QGraphicsView):
    def __init__(self, scene, tabs=None, parent=None):
        super(PivotView, self).__init__(scene, parent)

        self.tabs = tabs or {}

        self.header_items = []
        self.header_animations = []
        self.content_items = []
        self.content_animations = []

        self.current_index = 0
        self.mouse_x_position = 0
        self.prev_x_position = 0

        # set blackish background
        self.setStyleSheet(Style.background_style)

        # create opacity animator
        self.opacity_animator = QtCore.QPropertyAnimation()
        self.opacity_animator.setDuration(Style.animation_time)
        self.opacity_animator.setPropertyName("opacity")
        self.opacity_animator.setEasingCurve(QtCore.QEasingCurve.Linear)
        self.opacity_animator.setStartValue(0.3)
        self.opacity_animator.setEndValue(1.0)

        # create animation groups
        self.content_animation_group = QtCore.QParallelAnimationGroup()
        self.header_animation_group = QtCore.QParallelAnimationGroup()

        # create the rest of the ui
        self._create_top_bar()
        self._create_metro_tab_bar()
        self._create_content_items()

    def _create_top_bar(self):
        bar = QtGui.QGraphicsTextItem()
        bar.setAcceptHoverEvents(False)
        bar.setFont(Style.body_font)
        bar.setPlainText("Python Metro Style Pivot")
        bar.setDefaultTextColor(Style.ui_text_color)
        bar.setPos(Style.component_margin, Style.component_margin)
        self.scene().addItem(bar)

    def _create_metro_tab_bar(self):
        x_pos = Style.component_margin

        # create pivot header items
        for i, text in enumerate(self.tabs.iterkeys()):
            text_item = QtGui.QGraphicsTextItem()
            text_item.setAcceptHoverEvents(False)
            text_item.setPlainText(text)
            text_item.setFont(Style.header_font)
            text_item.adjustSize()
            text_item.setDefaultTextColor(Style.ui_text_color)

            # place header below header text
            text_item.setPos(x_pos, (Style.component_margin* 2 + Style.body_text_size))

            # calculate position for the next item. ComponentMargin + item width + ComponentMargin
            x_pos = x_pos + text_item.textWidth() + Style.component_margin

            anim = QtCore.QPropertyAnimation(text_item, 'pos')
            anim.setDuration(Style.animation_time)
            anim.setPropertyName('pos')
            anim.setEasingCurve(QtCore.QEasingCurve.OutCirc)
            self.header_animation_group.addAnimation(anim)
            self.header_animations.append(anim)

            # remove highlight from all items except the current one
            if i > 0:
                text_item.setOpacity(0.3)

            self.header_items.append(text_item)
            self.scene().addItem(text_item)

    def _create_content_items(self):
        x_pos = Style.component_margin
        # scene width - margins
        text_width = self.scene().width() - 2 * Style.component_margin

        # create pivot items text
        for i, text in enumerate(self.tabs.itervalues()):
            tmp = QtGui.QGraphicsTextItem()
            tmp.setFont(Style.body_font)
            tmp.setAcceptHoverEvents(False)
            tmp.setPlainText(text)
            tmp.setTextWidth(text_width)
            tmp.setDefaultTextColor(Style.ui_text_color)

            # place content below header and pivot
            tmp.setPos(x_pos,(Style.component_margin * 4 +
                              Style.body_text_size + Style.header_text_size))

            # calculate the position for the next item
            x_pos = x_pos + text_width + Style.component_margin

            anim = QtCore.QPropertyAnimation(tmp, 'pos')
            anim.setDuration(Style.animation_time)
            anim.setPropertyName('pos')
            anim.setEasingCurve(QtCore.QEasingCurve.OutCirc)

            self.content_animation_group.addAnimation(anim)
            self.content_animations.append(anim)
            self.content_items.append(tmp)
            self.scene().addItem(tmp)

    def mousePressEvent(self, event):
        self.prev_x_position = self.mouse_x_position = event.pos().x()

    def mouseMoveEvent(self, event):
        # don't do anything if there's an animation going
        if self.content_animation_group.state() != QtCore.QAbstractAnimation.Running:
            delta_x = event.x() - self.prev_x_position
            self.prev_x_position = event.x()

            # Keep the content under the mouse until it's released.
            for item in self.content_items:
                item.moveBy(delta_x, 0)

    def mouseReleaseEvent(self, event):
        # don't start animations twice
        if (self.content_animation_group.state() != QtCore.QAbstractAnimation.Running and
            self.header_animation_group.state() != QtCore.QAbstractAnimation.Running):

            # a negative value means the mouse moved left
            delta_x = event.x() - self.mouse_x_position
            sign_x = -1 if delta_x < 0 else 1

            # if we aren't trying to scroll off the end, move everything
            if ((delta_x < 0 and self.current_index < len(self.tabs) - 1) or
                (delta_x > 0 and self.current_index > 0)):

                # move the content by the width of one item
                content_x_offset = self.content_items[self.current_index].textWidth() + Style.component_margin - abs(delta_x)
                # move the headers by the width of the current item if we're moving them left, or the previous item if we're moving them right
                header_x_offset = self.header_items[self.current_index - (1 if delta_x > 0 else 0)].textWidth() + Style.component_margin

                # if the mouse moved left, move the everything right and vice versa
                self.current_index -= sign_x

                self.start_content_animation(sign_x * content_x_offset)
                self.start_header_animation(sign_x * header_x_offset)
            else:
                # if we're off the end, move the content back into place
                self.start_content_animation(-delta_x)

    def start_content_animation(self, x_offset):
        # create animation items
        for i, item in enumerate(self.content_items):
            start = item.pos()
            end = item.pos()
            end.setX(end.x() + x_offset)

            self.content_animations[i].setStartValue(start)
            self.content_animations[i].setEndValue(end)

        self.content_animation_group.start()

    def start_header_animation(self, x_offset):
        for i, item in enumerate(self.header_items):
            start = item.pos()
            end = item.pos()
            end.setX(end.x() + x_offset)

            self.header_animations[i].setStartValue(start)
            self.header_animations[i].setEndValue(end)

            # reset opacity
            item.setOpacity(0.3)

        self.opacity_animator.setTargetObject(self.header_items[self.current_index])
        self.header_animation_group.start()
        self.opacity_animator.start()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    view_rect = QtCore.QRect(100, 100, 1366, 768)

    scene = QtGui.QGraphicsScene()
    scene.setSceneRect(0.0, 0.0, view_rect.width(), view_rect.height())

    tabs = collections.OrderedDict((('Summary',"Metro is an internal code name of a typography-based design language created by Microsoft, originally for use in Windows Phone 7. A key design principle of Metro is better focus on the content of applications, relying more on typography and less on graphics (\"content before chrome\"). Early uses of the Metro principles began as early as Microsoft Encarta 95 and MSN 2.0, and later evolved into Windows Media Center and Zune. Later the principles of Metro were included in Windows Phone, Microsoft's website, the Xbox 360 dashboard update, and Windows 8."),
            ('History',"Metro is based on the design principles of classic Swiss graphic design. Early glimpses of this style could be seen in Windows Media Center for Windows XP Media Center Edition, which favored text as the primary form of navigation. This interface carried over into later iterations of Media Center. In 2006, Zune refreshed its interface using these principles. Microsoft designers decided to redesign the interface and with more focus on clean typography and less on UI chrome. These principles and the new Zune UI were carried over to Windows Phone 7 (from which much was drawn for Windows 8). The Zune Desktop Client was also redesigned with an emphasis on typography and clean design that was different from the Zune's previous Portable Media Center based UI. Flat colored \"live tiles\" were introduced into the design language during the early Windows Phone's studies. Microsoft has begun integrating these elements of the design language into its other products, with direct influence being seen in newer versions of Windows Live Messenger, Live Mesh, and Windows 8."),
            ('Principles 1',"Microsoft's design team says that the design language is partly inspired by signs commonly found at public transport systems; for instance, those found on the King County Metro transit system, which serves the greater Seattle area where Microsoft is headquartered. The design language places emphasis on good typography and has large text that catches the eye. Microsoft says that the design language is designed to be \"sleek, quick, modern\" and a \"refresh\" from the icon-based interfaces of Windows, Android, and iOS. All instances use fonts based on the Segoe font family designed by Steve Matteson at Agfa Monotype and licensed to Microsoft. For the Zune, Microsoft created a custom version called Zegoe UI, and for Windows Phone, Microsoft created the \"Segoe WP\" font family. The fonts mostly differ only in minor details. More obvious differences between Segoe UI and Segoe WP are apparent in their respective numerical characters. The Segoe UI in Windows 8 had an obvious differences as being similar to Segoe WP. Notable characters had a typographic changes of the characters 1, 2, 4, 5, 7, 8, I, and Q."),
            ('Principles 2',"The design language was designed specifically to consolidate groups of common tasks to speed up usage. This is accomplished by excluding superfluous graphics and instead relying on the actual content to also function as the main UI. The resulting interfaces favor larger hubs over smaller buttons and often feature laterally scrolling canvases. Page titles are usually large and consequently also take advantage of lateral scrolling."),
            ('Principles 3',"Animation plays a large part, with transitions, and user interactions such as presses or swipes recommended to always be acknowledged by some form of natural animation or motion. This is intended to give the user the impression that the UI is \"alive\" and responsive, with \"an added sense of depth.\""),
            ('Principles 4',"Close to the official launch date of Windows 8 (October 26, 2012), more developers and Microsoft partners started working on creating new Metro applications, and many websites with resources related to this topic have been created, as well as the Microsoft's UX guidelines for Windows Store Apps."),
    ))

    view = PivotView(scene, tabs=tabs)
    view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    view.setGeometry(view_rect)
    view.setRenderHints(QtGui.QPainter.Antialiasing)
    view.show()

    app.exec_()<|endoftext|>


<commit_before># Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 eventlet
import jinja2
import json
import six
import traceback
import uuid
import datetime

from st2actions.runners import ActionRunner
from st2common import log as logging
from st2common.constants.action import (LIVEACTION_STATUS_SUCCEEDED, LIVEACTION_STATUS_FAILED)
from st2common.constants.system import SYSTEM_KV_PREFIX
from st2common.content.loader import MetaLoader
from st2common.exceptions import actionrunner as runnerexceptions
from st2common.models.db.action import LiveActionDB
from st2common.models.system import actionchain
from st2common.models.utils import action_param_utils
from st2common.services import action as action_service
from st2common.services.keyvalues import KeyValueLookup
from st2common.util import action_db as action_db_util
from st2common.util import isotime

LOG = logging.getLogger(__name__)
RESULTS_KEY = '__results'

def render_values(values, context):
    env = jinja2.Environment(undefined=jinja2.StrictUndefined)
    rendered_values = {}
    for k, v in six.iteritems(values):
        # jinja2 works with string so transform list and dict to strings.
        reverse_json_dumps = False
        if isinstance(v, dict) or isinstance(v, list):
            v = json.dumps(v)
            reverse_json_dumps = True
        else:
            v = str(v)
        rendered_v = env.from_string(v).render(context)
        # no change therefore no templatization so pick params from original to retain
        # original type
        if rendered_v == v:
            rendered_values[k] = values[k]
            continue
        if reverse_json_dumps:
            rendered_v = json.loads(rendered_v)
        rendered_values[k] = rendered_v
    return rendered_values

class ChainHolder(object):

    def __init__(self, chainspec, chainname):
        self.actionchain = actionchain.ActionChain(**chainspec)
        self.chainname = chainname
        if not self.actionchain.default:
            default = self._get_default(self.actionchain)
            self.actionchain.default = default
        LOG.debug('Using %s as default for %s.', self.actionchain.default, self.chainname)
        if not self.actionchain.default:
            raise Exception('Failed to find default node in %s.' % (self.chainname))
        # finalize the vars and save them around to be used at execution time.
        self.vars = self._get_rendered_vars(self.actionchain.vars) if self.actionchain.vars else {}

    @staticmethod
    def _get_default(_actionchain):
        # default is defined
        if _actionchain.default:
            return _actionchain.default
        # no nodes in chain
        if not _actionchain.chain:
            return None
        # The first node with no references is the default node. Assumptions
        # that support this are :
        # 1. There are no loops in the chain. Even if there are loops there is
        #    at least 1 node which does not end up in this loop.
        # 2. There are no fragments in the chain.
        node_names = set([node.name for node in _actionchain.chain])
        on_success_nodes = set([node.on_success for node in _actionchain.chain])
        on_failure_nodes = set([node.on_failure for node in _actionchain.chain])
        referenced_nodes = on_success_nodes | on_failure_nodes
        possible_default_nodes = node_names - referenced_nodes
        if possible_default_nodes:
            return possible_default_nodes.pop()
        # If no node is found assume the first node in the chain list to be default.
        return _actionchain.chain[0].name

    @staticmethod
    def _get_rendered_vars(vars):
        if not vars:
            return {}
        context = {SYSTEM_KV_PREFIX: KeyValueLookup()}
        return render_values(vars, context)

    def get_node(self, node_name=None):
        for node in self.actionchain.chain:
            if node.name == node_name:
                return node
        return None

    def get_next_node(self, curr_node_name=None, condition='on-success'):
        if not curr_node_name:
            return self.get_node(self.actionchain.default)
        current_node = self.get_node(curr_node_name)
        if condition == 'on-success':
            return self.get_node(current_node.on_success)
        elif condition == 'on-failure':
            return self.get_node(current_node.on_failure)
        raise runnerexceptions.ActionRunnerException('Unknown condition %s.' % condition)

class ActionChainRunner(ActionRunner):

    def __init__(self, runner_id):
        super(ActionChainRunner, self).__init__(runner_id=runner_id)
        self.chain_holder = None
        self._meta_loader = MetaLoader()

    def pre_run(self):
        chainspec_file = self.entry_point
        LOG.debug('Reading action chain from %s for action %s.', chainspec_file,
                  self.action)
        try:
            chainspec = self._meta_loader.load(chainspec_file)
            self.chain_holder = ChainHolder(chainspec, self.action_name)
        except Exception as e:
            LOG.exception('Failed to instantiate ActionChain.')
            raise runnerexceptions.ActionRunnerPreRunError(e.message)

    def run(self, action_parameters):
        action_node = self.chain_holder.get_next_node()

        result = {'tasks': []}  # holds final result we store
        context_result = {}  # holds result which is used for the template context purposes
        fail = True

        while action_node:
            error = None
            liveaction = None
            fail = False
            created_at = datetime.datetime.now()

            try:
                resolved_params = ActionChainRunner._resolve_params(
                    action_node=action_node, original_parameters=action_parameters,
                    results=context_result, chain_vars=self.chain_holder.vars)
                liveaction = ActionChainRunner._run_action(
                    action_node=action_node, parent_execution_id=self.liveaction_id,
                    params=resolved_params)
            except Exception:
                # Save the traceback and error message.
                LOG.exception('Failure in running action %s.', action_node.name)
                error = {'error': traceback.format_exc(10)}
                context_result[action_node.name] = error
            else:
                # Update context result
                context_result[action_node.name] = liveaction.result

                # Render and publish variables
                rendered_publish_vars = ActionChainRunner._render_publish_vars(
                    action_node=action_node, execution_result=liveaction.result,
                    previous_execution_results=context_result, chain_vars=self.chain_holder.vars)

                if rendered_publish_vars:
                    self.chain_holder.vars.update(rendered_publish_vars)
            finally:
                # Record result and resolve a next node based on the task success or failure
                updated_at = datetime.datetime.now()

                format_kwargs = {'action_node': action_node, 'liveaction_db': liveaction,
                                 'created_at': created_at, 'updated_at': updated_at}

                if error:
                    format_kwargs['error'] = error

                task_result = self._format_action_exec_result(**format_kwargs)
                result['tasks'].append(task_result)

                if not liveaction or liveaction.status == LIVEACTION_STATUS_FAILED:
                    fail = True
                    action_node = self.chain_holder.get_next_node(action_node.name, 'on-failure')
                elif liveaction.status == LIVEACTION_STATUS_SUCCEEDED:
                    action_node = self.chain_holder.get_next_node(action_node.name, 'on-success')

        if fail:
            status = LIVEACTION_STATUS_FAILED
        else:
            status = LIVEACTION_STATUS_SUCCEEDED

        return (status, result, None)

    @staticmethod
    def _render_publish_vars(action_node, execution_result, previous_execution_results,
                             chain_vars):
        """
        If no output is specified on the action_node the output is the entire execution_result.
        If any output is specified then only those variables are published as output of an
        execution of this action_node.
        The output variable can refer to a variable from the execution_result,
        previous_execution_results or chain_vars.
        """
        if not action_node.publish:
            return {}
        context = {}
        context.update({action_node.name: execution_result})
        context.update(previous_execution_results)
        context.update(chain_vars)
        context.update({RESULTS_KEY: previous_execution_results})
        context.update({SYSTEM_KV_PREFIX: KeyValueLookup()})
        rendered_result = render_values(action_node.publish, context)
        return rendered_result

    @staticmethod
    def _resolve_params(action_node, original_parameters, results, chain_vars):
        # setup context with original parameters and the intermediate results.
        context = {}
        context.update(original_parameters)
        context.update(results)
        context.update(chain_vars)
        context.update({RESULTS_KEY: results})
        context.update({SYSTEM_KV_PREFIX: KeyValueLookup()})
        rendered_params = render_values(action_node.params, context)
        LOG.debug('Rendered params: %s: Type: %s', rendered_params, type(rendered_params))
        return rendered_params

    @staticmethod
    def _run_action(action_node, parent_execution_id, params, wait_for_completion=True):
        execution = LiveActionDB(action=action_node.ref)
        execution.parameters = action_param_utils.cast_params(action_ref=action_node.ref,
                                                              params=params)
        execution.context = {
            'parent': str(parent_execution_id),
            'chain': vars(action_node)
        }
        execution, _ = action_service.schedule(execution)
        while (wait_for_completion and
               execution.status != LIVEACTION_STATUS_SUCCEEDED and
               execution.status != LIVEACTION_STATUS_FAILED):
            eventlet.sleep(1)
            execution = action_db_util.get_liveaction_by_id(execution.id)
        return execution

    def _format_action_exec_result(self, action_node, liveaction_db, created_at, updated_at,
                                   error=None):
        """
        Format ActionExecution result so it can be used in the final action result output.

        :rtype: ``dict``
        """
        assert(isinstance(created_at, datetime.datetime))
        assert(isinstance(updated_at, datetime.datetime))

        result = {}

        result['id'] = action_node.name
        result['name'] = action_node.name
        result['execution_id'] = str(liveaction_db.id) if liveaction_db else None
        result['mistral_execution_id'] = None
        result['workflow'] = None

        result['created_at'] = isotime.format(dt=created_at)
        result['updated_at'] = isotime.format(dt=updated_at)

        if error or not liveaction_db:
            result['state'] = LIVEACTION_STATUS_SUCCEEDED
        else:
            result['state'] = liveaction_db.status

        if error:
            result['result'] = error
        else:
            result['result'] = liveaction_db.result

        return result

def get_runner():
    return ActionChainRunner(str(uuid.uuid4()))
<commit_msg>Fix a bug with empty error message if entry_point file specified inside the action-chain definition didn't exist or if the action chain workflow parsing failed.<commit_after># Licensed to the StackStorm, Inc ('StackStorm') under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 eventlet
import jinja2
import json
import six
import traceback
import uuid
import datetime

from st2actions.runners import ActionRunner
from st2common import log as logging
from st2common.constants.action import (LIVEACTION_STATUS_SUCCEEDED, LIVEACTION_STATUS_FAILED)
from st2common.constants.system import SYSTEM_KV_PREFIX
from st2common.content.loader import MetaLoader
from st2common.exceptions import actionrunner as runnerexceptions
from st2common.models.db.action import LiveActionDB
from st2common.models.system import actionchain
from st2common.models.utils import action_param_utils
from st2common.services import action as action_service
from st2common.services.keyvalues import KeyValueLookup
from st2common.util import action_db as action_db_util
from st2common.util import isotime

LOG = logging.getLogger(__name__)
RESULTS_KEY = '__results'

def render_values(values, context):
    env = jinja2.Environment(undefined=jinja2.StrictUndefined)
    rendered_values = {}
    for k, v in six.iteritems(values):
        # jinja2 works with string so transform list and dict to strings.
        reverse_json_dumps = False
        if isinstance(v, dict) or isinstance(v, list):
            v = json.dumps(v)
            reverse_json_dumps = True
        else:
            v = str(v)
        rendered_v = env.from_string(v).render(context)
        # no change therefore no templatization so pick params from original to retain
        # original type
        if rendered_v == v:
            rendered_values[k] = values[k]
            continue
        if reverse_json_dumps:
            rendered_v = json.loads(rendered_v)
        rendered_values[k] = rendered_v
    return rendered_values

class ChainHolder(object):

    def __init__(self, chainspec, chainname):
        self.actionchain = actionchain.ActionChain(**chainspec)
        self.chainname = chainname
        if not self.actionchain.default:
            default = self._get_default(self.actionchain)
            self.actionchain.default = default
        LOG.debug('Using %s as default for %s.', self.actionchain.default, self.chainname)
        if not self.actionchain.default:
            raise Exception('Failed to find default node in %s.' % (self.chainname))
        # finalize the vars and save them around to be used at execution time.
        self.vars = self._get_rendered_vars(self.actionchain.vars) if self.actionchain.vars else {}

    @staticmethod
    def _get_default(_actionchain):
        # default is defined
        if _actionchain.default:
            return _actionchain.default
        # no nodes in chain
        if not _actionchain.chain:
            return None
        # The first node with no references is the default node. Assumptions
        # that support this are :
        # 1. There are no loops in the chain. Even if there are loops there is
        #    at least 1 node which does not end up in this loop.
        # 2. There are no fragments in the chain.
        node_names = set([node.name for node in _actionchain.chain])
        on_success_nodes = set([node.on_success for node in _actionchain.chain])
        on_failure_nodes = set([node.on_failure for node in _actionchain.chain])
        referenced_nodes = on_success_nodes | on_failure_nodes
        possible_default_nodes = node_names - referenced_nodes
        if possible_default_nodes:
            return possible_default_nodes.pop()
        # If no node is found assume the first node in the chain list to be default.
        return _actionchain.chain[0].name

    @staticmethod
    def _get_rendered_vars(vars):
        if not vars:
            return {}
        context = {SYSTEM_KV_PREFIX: KeyValueLookup()}
        return render_values(vars, context)

    def get_node(self, node_name=None):
        for node in self.actionchain.chain:
            if node.name == node_name:
                return node
        return None

    def get_next_node(self, curr_node_name=None, condition='on-success'):
        if not curr_node_name:
            return self.get_node(self.actionchain.default)
        current_node = self.get_node(curr_node_name)
        if condition == 'on-success':
            return self.get_node(current_node.on_success)
        elif condition == 'on-failure':
            return self.get_node(current_node.on_failure)
        raise runnerexceptions.ActionRunnerException('Unknown condition %s.' % condition)

class ActionChainRunner(ActionRunner):

    def __init__(self, runner_id):
        super(ActionChainRunner, self).__init__(runner_id=runner_id)
        self.chain_holder = None
        self._meta_loader = MetaLoader()

    def pre_run(self):
        chainspec_file = self.entry_point
        LOG.debug('Reading action chain from %s for action %s.', chainspec_file,
                  self.action)
        try:
            chainspec = self._meta_loader.load(chainspec_file)
            self.chain_holder = ChainHolder(chainspec, self.action_name)
        except Exception as e:
            message = e.message or str(e)
            LOG.exception('Failed to instantiate ActionChain.')
            raise runnerexceptions.ActionRunnerPreRunError(message)

    def run(self, action_parameters):
        action_node = self.chain_holder.get_next_node()

        result = {'tasks': []}  # holds final result we store
        context_result = {}  # holds result which is used for the template context purposes
        fail = True

        while action_node:
            error = None
            liveaction = None
            fail = False
            created_at = datetime.datetime.now()

            try:
                resolved_params = ActionChainRunner._resolve_params(
                    action_node=action_node, original_parameters=action_parameters,
                    results=context_result, chain_vars=self.chain_holder.vars)
                liveaction = ActionChainRunner._run_action(
                    action_node=action_node, parent_execution_id=self.liveaction_id,
                    params=resolved_params)
            except Exception:
                # Save the traceback and error message.
                LOG.exception('Failure in running action %s.', action_node.name)
                error = {'error': traceback.format_exc(10)}
                context_result[action_node.name] = error
            else:
                # Update context result
                context_result[action_node.name] = liveaction.result

                # Render and publish variables
                rendered_publish_vars = ActionChainRunner._render_publish_vars(
                    action_node=action_node, execution_result=liveaction.result,
                    previous_execution_results=context_result, chain_vars=self.chain_holder.vars)

                if rendered_publish_vars:
                    self.chain_holder.vars.update(rendered_publish_vars)
            finally:
                # Record result and resolve a next node based on the task success or failure
                updated_at = datetime.datetime.now()

                format_kwargs = {'action_node': action_node, 'liveaction_db': liveaction,
                                 'created_at': created_at, 'updated_at': updated_at}

                if error:
                    format_kwargs['error'] = error

                task_result = self._format_action_exec_result(**format_kwargs)
                result['tasks'].append(task_result)

                if not liveaction or liveaction.status == LIVEACTION_STATUS_FAILED:
                    fail = True
                    action_node = self.chain_holder.get_next_node(action_node.name, 'on-failure')
                elif liveaction.status == LIVEACTION_STATUS_SUCCEEDED:
                    action_node = self.chain_holder.get_next_node(action_node.name, 'on-success')

        if fail:
            status = LIVEACTION_STATUS_FAILED
        else:
            status = LIVEACTION_STATUS_SUCCEEDED

        return (status, result, None)

    @staticmethod
    def _render_publish_vars(action_node, execution_result, previous_execution_results,
                             chain_vars):
        """
        If no output is specified on the action_node the output is the entire execution_result.
        If any output is specified then only those variables are published as output of an
        execution of this action_node.
        The output variable can refer to a variable from the execution_result,
        previous_execution_results or chain_vars.
        """
        if not action_node.publish:
            return {}
        context = {}
        context.update({action_node.name: execution_result})
        context.update(previous_execution_results)
        context.update(chain_vars)
        context.update({RESULTS_KEY: previous_execution_results})
        context.update({SYSTEM_KV_PREFIX: KeyValueLookup()})
        rendered_result = render_values(action_node.publish, context)
        return rendered_result

    @staticmethod
    def _resolve_params(action_node, original_parameters, results, chain_vars):
        # setup context with original parameters and the intermediate results.
        context = {}
        context.update(original_parameters)
        context.update(results)
        context.update(chain_vars)
        context.update({RESULTS_KEY: results})
        context.update({SYSTEM_KV_PREFIX: KeyValueLookup()})
        rendered_params = render_values(action_node.params, context)
        LOG.debug('Rendered params: %s: Type: %s', rendered_params, type(rendered_params))
        return rendered_params

    @staticmethod
    def _run_action(action_node, parent_execution_id, params, wait_for_completion=True):
        execution = LiveActionDB(action=action_node.ref)
        execution.parameters = action_param_utils.cast_params(action_ref=action_node.ref,
                                                              params=params)
        execution.context = {
            'parent': str(parent_execution_id),
            'chain': vars(action_node)
        }
        execution, _ = action_service.schedule(execution)
        while (wait_for_completion and
               execution.status != LIVEACTION_STATUS_SUCCEEDED and
               execution.status != LIVEACTION_STATUS_FAILED):
            eventlet.sleep(1)
            execution = action_db_util.get_liveaction_by_id(execution.id)
        return execution

    def _format_action_exec_result(self, action_node, liveaction_db, created_at, updated_at,
                                   error=None):
        """
        Format ActionExecution result so it can be used in the final action result output.

        :rtype: ``dict``
        """
        assert(isinstance(created_at, datetime.datetime))
        assert(isinstance(updated_at, datetime.datetime))

        result = {}

        result['id'] = action_node.name
        result['name'] = action_node.name
        result['execution_id'] = str(liveaction_db.id) if liveaction_db else None
        result['mistral_execution_id'] = None
        result['workflow'] = None

        result['created_at'] = isotime.format(dt=created_at)
        result['updated_at'] = isotime.format(dt=updated_at)

        if error or not liveaction_db:
            result['state'] = LIVEACTION_STATUS_SUCCEEDED
        else:
            result['state'] = liveaction_db.status

        if error:
            result['result'] = error
        else:
            result['result'] = liveaction_db.result

        return result

def get_runner():
    return ActionChainRunner(str(uuid.uuid4()))
<|endoftext|>


<commit_before>__author__ = 'loopingz'

# Based on https://code.google.com/p/winsys/source/browse/trunk/random/file_handles.py code

import os, sys
from ctypes import *
from ctypes.wintypes import *
import Queue
import re
import struct
import threading

import ntsecuritycon
import pywintypes
import win32api
import win32con
import win32event
import win32file
import win32security
import winerror
from nxdrive.logging_config import get_logger
from time import sleep

UCHAR = c_ubyte
PVOID = c_void_p
ntdll = windll.ntdll

SystemHandleInformation = 16
STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
STATUS_BUFFER_OVERFLOW = 0x80000005L
STATUS_INVALID_HANDLE = 0xC0000008L
STATUS_BUFFER_TOO_SMALL = 0xC0000023L
STATUS_SUCCESS = 0

CURRENT_PROCESS = win32api.GetCurrentProcess ()

threads = 0
log = get_logger(__name__)

class x_file_handles(Exception):
    pass

def signed_to_unsigned (signed):
    unsigned, = struct.unpack ("L", struct.pack ("l", signed))
    return unsigned

class SYSTEM_HANDLE_TABLE_ENTRY_INFO(Structure):
    _fields_ = [
        ("UniqueProcessId", USHORT),
        ("CreatorBackTraceIndex", USHORT),
        ("ObjectTypeIndex", UCHAR),
        ("HandleAttributes", UCHAR),
        ("HandleValue", USHORT),
        ("Object", PVOID),
        ("GrantedAccess", ULONG),
    ]

class SYSTEM_HANDLE_INFORMATION(Structure):
    _fields_ = [
        ("NumberOfHandles", ULONG),
        ("Handles", SYSTEM_HANDLE_TABLE_ENTRY_INFO * 1),
    ]

class LSA_UNICODE_STRING(Structure):
    _fields_ = [
        ("Length", USHORT),
        ("MaximumLength", USHORT),
        ("Buffer", LPWSTR),
    ]

class PUBLIC_OBJECT_TYPE_INFORMATION(Structure):
    _fields_ = [
        ("Name", LSA_UNICODE_STRING),
        ("Reserved", ULONG * 22),
    ]

class OBJECT_NAME_INFORMATION(Structure):
    _fields_ = [
        ("Name", LSA_UNICODE_STRING),
    ]

class IO_STATUS_BLOCK_UNION(Union):
    _fields_ = [
        ("Status", LONG),
        ("Pointer", PVOID),
    ]

class IO_STATUS_BLOCK(Structure):
    _anonymous_ = ("u",)
    _fields_ = [
        ("u", IO_STATUS_BLOCK_UNION),
        ("Information", POINTER(ULONG)),
    ]

class FILE_NAME_INFORMATION(Structure):
    filename_size = 4096
    _fields_ = [
        ("FilenameLength", ULONG),
        ("FileName", WCHAR * filename_size),
    ]

class GetObjectInfoThread(threading.Thread):
    def __init__(self, pid, handles, filter_type):
        super(GetObjectInfoThread, self).__init__(target=self._get_object_info, args=(self, pid, handles, filter_type))
        self._pid = pid
        self._handles = handles
        self._filter_type = filter_type
        self._result = []
        self.lastGrantedAccess = None

    def get_pid(self):
        return self._pid

    @staticmethod
    def get_type_info(handle):
        public_object_type_information = PUBLIC_OBJECT_TYPE_INFORMATION ()
        size = DWORD(sizeof(public_object_type_information))
        while True:
            result = signed_to_unsigned(
                ntdll.NtQueryObject(
                handle, 2, byref(public_object_type_information), size, None
                )
            )
            if result == STATUS_SUCCESS:
                return public_object_type_information.Name.Buffer
            elif result == STATUS_INFO_LENGTH_MISMATCH:
                size = DWORD(size.value * 4)
                resize(public_object_type_information, size.value)
            elif result == STATUS_INVALID_HANDLE:
                return None
            elif result == 0xc0000005:
                # Access denied
                # Windows 10 result
                return None
            else:
                raise x_file_handles ("NtQueryObject.2", hex (result))

    @staticmethod
    def get_name_info(handle):
        object_name_information = OBJECT_NAME_INFORMATION ()
        size = DWORD (sizeof (object_name_information))
        while True:
            info = ntdll.NtQueryObject (
                    handle, 1, byref (object_name_information), size, None
                )
            result = signed_to_unsigned (
                info
            )
            if result == STATUS_SUCCESS:
                return object_name_information.Name.Buffer
            elif result in (STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH):
                size = DWORD(size.value * 4)
                resize(object_name_information, size.value)
            else:
                return None

    @staticmethod
    def get_object_info(pid, process_handle, handle, filter_type):
        type = GetObjectInfoThread.get_type_info(handle)
        if filter_type is not None and filter_type != type and type is not None:
            return None
        name = GetObjectInfoThread.get_name_info(handle)
        if name is None:
            return None
        return (pid, type, name, handle)

    @staticmethod
    def _get_object_info(obj, pid, handles, filter_type):
        try:
            process_handler = win32api.OpenProcess (win32con.PROCESS_DUP_HANDLE, 0, pid)
        except win32api.error, (errno, errctx, errmsg):
            return
        for handle_obj in handles:
            obj.lastGrantedAccess = handle_obj[1]
            handle = handle_obj[0]
            hDuplicate = WindowsProcessFileHandlerSniffer.get_process_handle(process_handler, handle)
            if hDuplicate is None:
                continue
            handle = int(hDuplicate)
            resource = GetObjectInfoThread.get_object_info(pid, process_handler, handle, filter_type)
            win32api.CloseHandle(hDuplicate)
            if resource is not None:
                obj._result.append(resource)
        win32api.CloseHandle(process_handler)

    def get_results(self):
        return self._result

class WindowsProcessFileHandlerSniffer():

    def __init__(self):
        self.DEVICE_DRIVES = dict()
        self._running = False
        self._threads = []
        self._pid_blacklist = dict()
        self._pid_blacklist[os.getpid()] = True
        # Get WIndows Drive
        for d in "abcdefghijklmnopqrstuvwxyz":
            try:
                device = win32file.QueryDosDevice(d + ":").strip("\x00").lower()
                self.DEVICE_DRIVES[device] = d.upper() + ":"
            except win32file.error, (errno, errctx, errmsg):
                if errno == 2:
                    pass
                else:
                    raise

    @staticmethod
    def get_process_handle(hProcess, handle):
        try:
            return win32api.DuplicateHandle (hProcess, handle, CURRENT_PROCESS,
                                                   0, 0, win32con.DUPLICATE_SAME_ACCESS)
        except win32api.error, (errno, errctx, errmsg):
            if errno in (
                winerror.ERROR_ACCESS_DENIED,
                winerror.ERROR_INVALID_PARAMETER,
                winerror.ERROR_INVALID_HANDLE,
                winerror.ERROR_NOT_SUPPORTED
            ):
                return None
            else:
                raise

    def get_handles(self):
        system_handle_information = SYSTEM_HANDLE_INFORMATION ()
        size = DWORD (sizeof (system_handle_information))
        while True:
            result = ntdll.NtQuerySystemInformation (
                SystemHandleInformation,
                byref (system_handle_information),
                size,
                byref (size)
            )
            result = signed_to_unsigned (result)
            if result == STATUS_SUCCESS:
                break
            elif result == STATUS_INFO_LENGTH_MISMATCH:
                size = DWORD (size.value * 4)
                resize (system_handle_information, size.value)
            else:
                raise x_file_handles ("NtQuerySystemInformation", hex (result))

        result = dict()
        pHandles = cast (
            system_handle_information.Handles,
            POINTER (SYSTEM_HANDLE_TABLE_ENTRY_INFO * system_handle_information.NumberOfHandles)
        )
        for handle in pHandles.contents:
            if handle.UniqueProcessId not in result:
                result[handle.UniqueProcessId] = dict()
                result[handle.UniqueProcessId]["handles"] = []
                result[handle.UniqueProcessId]["risky_handles"] = []

            # Useless for now but keep for later use in case
            if handle.GrantedAccess == 0x1a0089 or handle.GrantedAccess == 0x1a019f or handle.GrantedAccess == 0x12019f\
                     or handle.GrantedAccess == 0x120189 or handle.GrantedAccess == 0x01f01ff\
                     or handle.GrantedAccess == 0x100081:
                # Some process like ssh-agent or chrome make NtQueryObject hang when queried
                result[handle.UniqueProcessId]["risky_handles"].append((handle.HandleValue, handle.GrantedAccess))
                continue

            result[handle.UniqueProcessId]["risky_handles"].append((handle.HandleValue, handle.GrantedAccess))
        return result

    def get_main_open_files(self, pids=None, filter_type='File'):
        handles = self.get_handles()
        info_threads = []
        # Remove stopped pids from blacklist
        remove_pids = []
        for pid in self._pid_blacklist:
            if not pid in handles:
                remove_pids.append(pid)
        for pid in remove_pids:
            del self._pid_blacklist[pid]

        # Go through handles ( stored by pid )
        for pid in handles:
            if pid in self._pid_blacklist:
                continue
            try:
                process_handler = win32api.OpenProcess (win32con.PROCESS_DUP_HANDLE, 0, pid)
            except win32api.error, (errno, errctx, errmsg):
                continue
            for handle_obj in handles[pid]['handles']:
                handle = handle_obj[0]
                hDuplicate = WindowsProcessFileHandlerSniffer.get_process_handle(process_handler, handle)
                if hDuplicate is None:
                    continue
                handle = int(hDuplicate)
                resource = GetObjectInfoThread.get_object_info(pid, process_handler, handle, filter_type)
                win32api.CloseHandle(hDuplicate)
                if resource is not None:
                    yield resource
            win32api.CloseHandle(process_handler)
            if len(handles[pid]['risky_handles']) > 0:
                # As they can hang the kernel we need to launch another thread for those one
                thread = GetObjectInfoThread(pid, handles[pid]['risky_handles'], filter_type)
                # To avoid crash at the end
                thread.setDaemon(True)
                thread.start()
                info_threads.append(thread)
        # Wait 2s
        sleep(2)
        # Go through all the remaining thread
        for thread in info_threads:
            if thread.is_alive():
                # Should not be blacklist the pid
                self._pid_blacklist[thread.get_pid()] = True
                log.trace('Blacklisting process %d because of stuck thread (GrantedAccess: %d)',
                            thread.get_pid(), thread.lastGrantedAccess)
            else:
                for result in thread.get_results():
                    yield result
        return

    def filepath_from_devicepath(self, devicepath):
        if devicepath is None: return None
        devicepath_lower = devicepath.lower()
        for device, drive in self.DEVICE_DRIVES.items():
            if devicepath_lower.startswith(device):
                return drive + devicepath[len(device):]
        return devicepath

    def get_open_files(self, pids=None):
        if self._running:
            raise Exception("Can't multithread open_files")
        se_debug = win32security.LookupPrivilegeValue (u"", win32security.SE_DEBUG_NAME)
        hToken = win32security.OpenProcessToken (
            CURRENT_PROCESS,
            ntsecuritycon.MAXIMUM_ALLOWED
        )
        try:
            self._running = True
            for file in self.get_main_open_files(pids):
                path = self.filepath_from_devicepath(file[2])
                if path is None or not os.path.exists(path) or os.path.isdir(path):
                    continue
                yield (file[0], path, file[1], file[3])
        except x_file_handles, (context, errno):
            log.trace("Error in %r - with errno : %d", context, errno)
        finally:
            self._running = False
<commit_msg>NXDRIVE-568: Cosmit<commit_after>__author__ = 'loopingz'

# Based on https://code.google.com/p/winsys/source/browse/trunk/random/file_handles.py code

import os
from ctypes import *
from ctypes.wintypes import *
import struct
import threading

import ntsecuritycon
import win32api
import win32con
import win32file
import win32security
import winerror
from nxdrive.logging_config import get_logger
from time import sleep

UCHAR = c_ubyte
PVOID = c_void_p
ntdll = windll.ntdll

SystemHandleInformation = 16
STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
STATUS_BUFFER_OVERFLOW = 0x80000005L
STATUS_INVALID_HANDLE = 0xC0000008L
STATUS_BUFFER_TOO_SMALL = 0xC0000023L
STATUS_SUCCESS = 0

CURRENT_PROCESS = win32api.GetCurrentProcess ()

threads = 0
log = get_logger(__name__)

class x_file_handles(Exception):
    pass

def signed_to_unsigned (signed):
    unsigned, = struct.unpack ("L", struct.pack ("l", signed))
    return unsigned

class SYSTEM_HANDLE_TABLE_ENTRY_INFO(Structure):
    _fields_ = [
        ("UniqueProcessId", USHORT),
        ("CreatorBackTraceIndex", USHORT),
        ("ObjectTypeIndex", UCHAR),
        ("HandleAttributes", UCHAR),
        ("HandleValue", USHORT),
        ("Object", PVOID),
        ("GrantedAccess", ULONG),
    ]

class SYSTEM_HANDLE_INFORMATION(Structure):
    _fields_ = [
        ("NumberOfHandles", ULONG),
        ("Handles", SYSTEM_HANDLE_TABLE_ENTRY_INFO * 1),
    ]

class LSA_UNICODE_STRING(Structure):
    _fields_ = [
        ("Length", USHORT),
        ("MaximumLength", USHORT),
        ("Buffer", LPWSTR),
    ]

class PUBLIC_OBJECT_TYPE_INFORMATION(Structure):
    _fields_ = [
        ("Name", LSA_UNICODE_STRING),
        ("Reserved", ULONG * 22),
    ]

class OBJECT_NAME_INFORMATION(Structure):
    _fields_ = [
        ("Name", LSA_UNICODE_STRING),
    ]

class IO_STATUS_BLOCK_UNION(Union):
    _fields_ = [
        ("Status", LONG),
        ("Pointer", PVOID),
    ]

class IO_STATUS_BLOCK(Structure):
    _anonymous_ = ("u",)
    _fields_ = [
        ("u", IO_STATUS_BLOCK_UNION),
        ("Information", POINTER(ULONG)),
    ]

class FILE_NAME_INFORMATION(Structure):
    filename_size = 4096
    _fields_ = [
        ("FilenameLength", ULONG),
        ("FileName", WCHAR * filename_size),
    ]

class GetObjectInfoThread(threading.Thread):
    def __init__(self, pid, handles, filter_type):
        super(GetObjectInfoThread, self).__init__(target=self._get_object_info, args=(self, pid, handles, filter_type))
        self._pid = pid
        self._handles = handles
        self._filter_type = filter_type
        self._result = []
        self.lastGrantedAccess = None

    def get_pid(self):
        return self._pid

    @staticmethod
    def get_type_info(handle):
        public_object_type_information = PUBLIC_OBJECT_TYPE_INFORMATION()
        size = DWORD(sizeof(public_object_type_information))
        while True:
            result = signed_to_unsigned(
                ntdll.NtQueryObject(handle, 2, byref(public_object_type_information), size, None)
            )
            if result == STATUS_SUCCESS:
                return public_object_type_information.Name.Buffer
            elif result == STATUS_INFO_LENGTH_MISMATCH:
                size = DWORD(size.value * 4)
                resize(public_object_type_information, size.value)
            elif result == STATUS_INVALID_HANDLE:
                return None
            elif result == 0xc0000005:
                # Access denied
                # Windows 10 result
                return None
            else:
                raise x_file_handles ("NtQueryObject.2", hex (result))

    @staticmethod
    def get_name_info(handle):
        object_name_information = OBJECT_NAME_INFORMATION ()
        size = DWORD(sizeof(object_name_information))
        while True:
            info = ntdll.NtQueryObject (
                    handle, 1, byref (object_name_information), size, None
                )
            result = signed_to_unsigned (
                info
            )
            if result == STATUS_SUCCESS:
                return object_name_information.Name.Buffer
            elif result in (STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH):
                size = DWORD(size.value * 4)
                resize(object_name_information, size.value)
            else:
                return None

    @staticmethod
    def get_object_info(pid, process_handle, handle, filter_type):
        type = GetObjectInfoThread.get_type_info(handle)
        if filter_type is not None and filter_type != type and type is not None:
            return None
        name = GetObjectInfoThread.get_name_info(handle)
        if name is None:
            return None
        return (pid, type, name, handle)

    @staticmethod
    def _get_object_info(obj, pid, handles, filter_type):
        try:
            process_handler = win32api.OpenProcess (win32con.PROCESS_DUP_HANDLE, 0, pid)
        except win32api.error, (errno, errctx, errmsg):
            return
        for handle_obj in handles:
            obj.lastGrantedAccess = handle_obj[1]
            handle = handle_obj[0]
            hDuplicate = WindowsProcessFileHandlerSniffer.get_process_handle(process_handler, handle)
            if hDuplicate is None:
                continue
            handle = int(hDuplicate)
            resource = GetObjectInfoThread.get_object_info(pid, process_handler, handle, filter_type)
            win32api.CloseHandle(hDuplicate)
            if resource is not None:
                obj._result.append(resource)
        win32api.CloseHandle(process_handler)

    def get_results(self):
        return self._result

class WindowsProcessFileHandlerSniffer():

    def __init__(self):
        self.DEVICE_DRIVES = dict()
        self._running = False
        self._threads = []
        self._pid_blacklist = dict()
        self._pid_blacklist[os.getpid()] = True
        # Get Windows Drive
        for d in "abcdefghijklmnopqrstuvwxyz":
            try:
                device = win32file.QueryDosDevice(d + ":").strip("\x00").lower()
                self.DEVICE_DRIVES[device] = d.upper() + ":"
            except win32file.error, (errno, errctx, errmsg):
                if errno == 2:
                    pass
                else:
                    raise
        self._increase_privileges()

    @staticmethod
    def get_process_handle(hProcess, handle):
        try:
            return win32api.DuplicateHandle(hProcess, handle, CURRENT_PROCESS,
                                                   0, 0, win32con.DUPLICATE_SAME_ACCESS)
        except win32api.error, (errno, errctx, errmsg):
            if errno in (
                winerror.ERROR_ACCESS_DENIED,
                winerror.ERROR_INVALID_PARAMETER,
                winerror.ERROR_INVALID_HANDLE,
                winerror.ERROR_NOT_SUPPORTED
            ):
                return None
            else:
                raise

    def get_handles(self):
        system_handle_information = SYSTEM_HANDLE_INFORMATION ()
        size = DWORD(sizeof(system_handle_information))
        while True:
            result = signed_to_unsigned(ntdll.NtQuerySystemInformation(SystemHandleInformation,
                                byref(system_handle_information), size, byref(size)))
            if result == STATUS_SUCCESS:
                break
            elif result == STATUS_INFO_LENGTH_MISMATCH:
                size = DWORD (size.value * 4)
                resize(system_handle_information, size.value)
            else:
                raise x_file_handles("NtQuerySystemInformation", hex(result))

        result = dict()
        pHandles = cast(system_handle_information.Handles,
            POINTER(SYSTEM_HANDLE_TABLE_ENTRY_INFO * system_handle_information.NumberOfHandles))
        for handle in pHandles.contents:
            if handle.UniqueProcessId not in result:
                result[handle.UniqueProcessId] = dict()
                result[handle.UniqueProcessId]["handles"] = []
                result[handle.UniqueProcessId]["risky_handles"] = []

            # Useless for now but keep for later use in case
            if handle.GrantedAccess == 0x1a0089 or handle.GrantedAccess == 0x1a019f or handle.GrantedAccess == 0x12019f\
                     or handle.GrantedAccess == 0x120189 or handle.GrantedAccess == 0x01f01ff\
                     or handle.GrantedAccess == 0x100081:
                # Some process like ssh-agent or chrome make NtQueryObject hang when queried
                result[handle.UniqueProcessId]["risky_handles"].append((handle.HandleValue, handle.GrantedAccess))
                continue

            result[handle.UniqueProcessId]["risky_handles"].append((handle.HandleValue, handle.GrantedAccess))
        return result

    def get_main_open_files(self, pids=None, filter_type='File'):
        handles = self.get_handles()
        info_threads = []
        # Remove stopped pids from blacklist
        remove_pids = []
        for pid in self._pid_blacklist:
            if not pid in handles:
                remove_pids.append(pid)
        for pid in remove_pids:
            del self._pid_blacklist[pid]

        # Go through handles ( stored by pid )
        for pid in handles:
            if pid in self._pid_blacklist:
                continue
            try:
                process_handler = win32api.OpenProcess (win32con.PROCESS_DUP_HANDLE, 0, pid)
            except win32api.error, (errno, errctx, errmsg):
                continue
            for handle_obj in handles[pid]['handles']:
                handle = handle_obj[0]
                hDuplicate = WindowsProcessFileHandlerSniffer.get_process_handle(process_handler, handle)
                if hDuplicate is None:
                    continue
                handle = int(hDuplicate)
                resource = GetObjectInfoThread.get_object_info(pid, process_handler, handle, filter_type)
                win32api.CloseHandle(hDuplicate)
                if resource is not None:
                    yield resource
            win32api.CloseHandle(process_handler)
            if len(handles[pid]['risky_handles']) > 0:
                # As they can hang the kernel we need to launch another thread for those one
                thread = GetObjectInfoThread(pid, handles[pid]['risky_handles'], filter_type)
                # To avoid crash at the end
                thread.setDaemon(True)
                thread.start()
                info_threads.append(thread)
        # Wait 2s
        sleep(2)
        # Go through all the remaining thread
        for thread in info_threads:
            if thread.is_alive():
                # Should not be blacklist the pid
                self._pid_blacklist[thread.get_pid()] = True
                log.trace('Blacklisting process %d because of stuck thread (GrantedAccess: %d)',
                            thread.get_pid(), thread.lastGrantedAccess)
            else:
                for result in thread.get_results():
                    yield result
        return

    def filepath_from_devicepath(self, devicepath):
        if devicepath is None: return None
        devicepath_lower = devicepath.lower()
        for device, drive in self.DEVICE_DRIVES.items():
            if devicepath_lower.startswith(device):
                return drive + devicepath[len(device):]
        return devicepath

    def _increase_privileges(self):
        win32security.LookupPrivilegeValue(u"", win32security.SE_DEBUG_NAME)
        win32security.OpenProcessToken(CURRENT_PROCESS, ntsecuritycon.MAXIMUM_ALLOWED)

    def get_open_files(self, pids=None):
        if self._running:
            raise Exception("Can't multithread open_files")
        try:
            self._running = True
            for file in self.get_main_open_files(pids):
                path = self.filepath_from_devicepath(file[2])
                if path is None or not os.path.exists(path) or os.path.isdir(path):
                    continue
                yield (file[0], path, file[1], file[3])
        except x_file_handles, (context, errno):
            log.trace("Error in %r - with errno : %d", context, errno)
        finally:
            self._running = False
<|endoftext|>