<commit_before>#!/usr/bin/env python
#Copyright (C) 2009-2010 :
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#
#This file is part of Shinken.
#
#Shinken is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#Shinken is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU Affero General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

import time

#Unix and windows do not have the same import
#if os.name == 'nt':
#    import subprocess, datetime, os, time, signal
#    import ctypes
#    TerminateProcess = ctypes.windll.kernel32.TerminateProcess
#else:
#    from pexpect import *

from action import Action
from brok import Brok
from shinken.property import UnusedProp, BoolProp, IntegerProp, FloatProp, CharProp, StringProp, ListProp

class Notification(Action):
    #id = 0 #Is in fact in the Action class to be common with Checks and
    #events handlers

    properties={
        'type' : StringProp(default=''),
        'notification_type': IntegerProp(
            default=0,
            fill_brok=['full_status']),
        'start_time': StringProp(
            default=0,
            fill_brok=['full_status']),
        'end_time': StringProp(
            default=0,
            fill_brok=['full_status']),
        'contact_name': StringProp(
            default='',
            fill_brok=['full_status']),
        'host_name': StringProp(
            default='',
            fill_brok=['full_status']),
        'service_description': StringProp(
            default='',
            fill_brok=['full_status']),
        'reason_type': StringProp(
            default=0,
            fill_brok=['full_status']),
        'state': StringProp(
            default=0,
            fill_brok=['full_status']),
        'output': StringProp(
            default='',
            fill_brok=['full_status']),
        'ack_author': StringProp(
            default='',
            fill_brok=['full_status']),
        'ack_data': StringProp(
            default='',
            fill_brok=['full_status']),
        'escalated': BoolProp(
            default=False,
            fill_brok=['full_status']),
        'contacts_notified': StringProp(
            default=0,
            fill_brok=['full_status']),
        'env': StringProp(
            default={}),
        'exit_status' : IntegerProp(default=3),
        'command_call' : StringProp(default=None),
        'contact' : StringProp(default=None),
        '_in_timeout' : BoolProp(default=False),
        'notif_nb' : IntegerProp(default=0),
        'status' : StringProp(default='scheduled'),
        't_to_go' : IntegerProp(default=0),
        'is_a' : StringProp(default=''),
        'command' : StringProp(default=''),
        'host_name' : StringProp(default=''),
        'sched_id' : IntegerProp(default=0),
        'timeout' : IntegerProp(default=10),
        'check_time' : IntegerProp(default=0),
        }

    macros = {
        'NOTIFICATIONTYPE' : 'type',
        'NOTIFICATIONRECIPIENTS' : 'recipients',
        'NOTIFICATIONISESCALATED' : 'escaladed',
        'NOTIFICATIONAUTHOR' : 'author',
        'NOTIFICATIONAUTHORNAME' : 'author_name',
        'NOTIFICATIONAUTHORALIAS' : 'author_alias',
        'NOTIFICATIONCOMMENT' : 'comment',
        'HOSTNOTIFICATIONNUMBER' : 'notif_nb',
        'HOSTNOTIFICATIONID' : 'id',
        'SERVICENOTIFICATIONNUMBER' : 'notif_nb',
        'SERVICENOTIFICATIONID' : 'id'
        }

    def __init__(self, type , status, command, command_call, ref, contact, t_to_go, \
                     contact_name='', host_name='', service_description='',
                     reason_type=1, state=0, ack_author='', ack_data='', \
                     escalated=False, contacts_notified=0, \
                     start_time=0, end_time=0, notification_type=0, id=None, \
                     notif_nb=1, timeout=10, env={}):
        self.is_a = 'notification'
        self.type = type
        if id == None: #id != None is for copy call only
            self.id = Action.id
            Action.id += 1

        self._in_timeout = False
        self.timeout = timeout
        self.status = status
        self.exit_status = 3
        self.command = command
        self.command_call = command_call
        self.output = None
        self.ref = ref
        self.env = env
        #self.ref_type = ref_type
        self.t_to_go = t_to_go
        self.notif_nb = notif_nb
        self.contact = contact

        #For brok part
        self.contact_name = contact_name
        self.host_name = host_name
        self.service_description = service_description
        self.reason_type = reason_type
        self.state = state
        self.ack_author = ack_author
        self.ack_data = ack_data
        self.escalated = escalated
        self.contacts_notified = contacts_notified
        self.start_time = start_time
        self.end_time = end_time
        self.notification_type = notification_type

        #DBG
        self.creation_time = time.time()

    #return a copy of the check but just what is important for execution
    #So we remove the ref and all
    def copy_shell(self):
        #We create a dummy check with nothing in it, jsut defaults values
        return self.copy_shell__( Notification('', '', '', '', '', '', '', id=self.id) )

    def is_launchable(self, t):
        return t > self.t_to_go

    def set_status(self, status):
        self.status = status

    def get_status(self):
        return self.status

    def get_output(self):
        return self.output

    def is_administrative(self):
        if self.type in ('PROBLEM', 'RECOVERY'):
            return False
        else:
            return True

    def __str__(self):
        return "Notification %d status:%s command:%s ref:%s t_to_go:%s" % (self.id, self.status, self.command, self.ref, time.asctime(time.localtime(self.t_to_go)))
        #return ''#str(self.__dict__)

    def get_id(self):
        return self.id

    def get_return_from(self, n):
        self.exit_status  = n.exit_status
        #self.output = c.output
        #self.check_time = c.check_time
        #self.execution_time = c.execution_time

    #Fill data with info of item by looking at brok_type
    #in props of properties or running_propterties
    def fill_data_brok_from(self, data, brok_type):
        cls = self.__class__
        #Now config properties
        for prop in cls.properties:
            if hasattr(prop, 'fill_brok'):
                if brok_type in cls.properties[prop]['fill_brok']:
                    data[prop] = getattr(self, prop)

    #Get a brok with initial status
    def get_initial_status_brok(self):
        data = {'id' : self.id}

        self.fill_data_brok_from(data, 'full_status')
        b = Brok('notification_raise', data)
        return b

    #Call by picle for dataify the coment
    #because we DO NOT WANT REF in this pickleisation!
    def __getstate__(self):
        cls = self.__class__
        # id is not in *_properties
        res = {'id' : self.id}
        for prop in cls.properties:
            if hasattr(self, prop):
                res[prop] = getattr(self, prop)

        return res

    # Inversed funtion of getstate
    def __setstate__(self, state):
        cls = self.__class__
        self.id = state['id']
        for prop in cls.properties:
            if prop in state:
                setattr(self, prop, state[prop])
<commit_msg>Fix: missing creation_time property for notif load for 0.4 daemons.<commit_after>#!/usr/bin/env python
#Copyright (C) 2009-2010 :
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#
#This file is part of Shinken.
#
#Shinken is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#Shinken is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU Affero General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

import time

#Unix and windows do not have the same import
#if os.name == 'nt':
#    import subprocess, datetime, os, time, signal
#    import ctypes
#    TerminateProcess = ctypes.windll.kernel32.TerminateProcess
#else:
#    from pexpect import *

from action import Action
from brok import Brok
from shinken.property import UnusedProp, BoolProp, IntegerProp, FloatProp, CharProp, StringProp, ListProp

class Notification(Action):
    #id = 0 #Is in fact in the Action class to be common with Checks and
    #events handlers

    properties={
        'type' : StringProp(default=''),
        'notification_type': IntegerProp(
            default=0,
            fill_brok=['full_status']),
        'start_time': StringProp(
            default=0,
            fill_brok=['full_status']),
        'end_time': StringProp(
            default=0,
            fill_brok=['full_status']),
        'contact_name': StringProp(
            default='',
            fill_brok=['full_status']),
        'host_name': StringProp(
            default='',
            fill_brok=['full_status']),
        'service_description': StringProp(
            default='',
            fill_brok=['full_status']),
        'reason_type': StringProp(
            default=0,
            fill_brok=['full_status']),
        'state': StringProp(
            default=0,
            fill_brok=['full_status']),
        'output': StringProp(
            default='',
            fill_brok=['full_status']),
        'ack_author': StringProp(
            default='',
            fill_brok=['full_status']),
        'ack_data': StringProp(
            default='',
            fill_brok=['full_status']),
        'escalated': BoolProp(
            default=False,
            fill_brok=['full_status']),
        'contacts_notified': StringProp(
            default=0,
            fill_brok=['full_status']),
        'env': StringProp(
            default={}),
        'exit_status' : IntegerProp(default=3),
        'command_call' : StringProp(default=None),
        'contact' : StringProp(default=None),
        '_in_timeout' : BoolProp(default=False),
        'notif_nb' : IntegerProp(default=0),
        'status' : StringProp(default='scheduled'),
        't_to_go' : IntegerProp(default=0),
        'is_a' : StringProp(default=''),
        'command' : StringProp(default=''),
        'host_name' : StringProp(default=''),
        'sched_id' : IntegerProp(default=0),
        'timeout' : IntegerProp(default=10),
        'check_time' : IntegerProp(default=0),
        }

    macros = {
        'NOTIFICATIONTYPE' : 'type',
        'NOTIFICATIONRECIPIENTS' : 'recipients',
        'NOTIFICATIONISESCALATED' : 'escaladed',
        'NOTIFICATIONAUTHOR' : 'author',
        'NOTIFICATIONAUTHORNAME' : 'author_name',
        'NOTIFICATIONAUTHORALIAS' : 'author_alias',
        'NOTIFICATIONCOMMENT' : 'comment',
        'HOSTNOTIFICATIONNUMBER' : 'notif_nb',
        'HOSTNOTIFICATIONID' : 'id',
        'SERVICENOTIFICATIONNUMBER' : 'notif_nb',
        'SERVICENOTIFICATIONID' : 'id'
        }

    def __init__(self, type , status, command, command_call, ref, contact, t_to_go, \
                     contact_name='', host_name='', service_description='',
                     reason_type=1, state=0, ack_author='', ack_data='', \
                     escalated=False, contacts_notified=0, \
                     start_time=0, end_time=0, notification_type=0, id=None, \
                     notif_nb=1, timeout=10, env={}):
        self.is_a = 'notification'
        self.type = type
        if id == None: #id != None is for copy call only
            self.id = Action.id
            Action.id += 1

        self._in_timeout = False
        self.timeout = timeout
        self.status = status
        self.exit_status = 3
        self.command = command
        self.command_call = command_call
        self.output = None
        self.ref = ref
        self.env = env
        #self.ref_type = ref_type
        self.t_to_go = t_to_go
        self.notif_nb = notif_nb
        self.contact = contact

        #For brok part
        self.contact_name = contact_name
        self.host_name = host_name
        self.service_description = service_description
        self.reason_type = reason_type
        self.state = state
        self.ack_author = ack_author
        self.ack_data = ack_data
        self.escalated = escalated
        self.contacts_notified = contacts_notified
        self.start_time = start_time
        self.end_time = end_time
        self.notification_type = notification_type

        self.creation_time = time.time()

    #return a copy of the check but just what is important for execution
    #So we remove the ref and all
    def copy_shell(self):
        #We create a dummy check with nothing in it, jsut defaults values
        return self.copy_shell__( Notification('', '', '', '', '', '', '', id=self.id) )

    def is_launchable(self, t):
        return t > self.t_to_go

    def set_status(self, status):
        self.status = status

    def get_status(self):
        return self.status

    def get_output(self):
        return self.output

    def is_administrative(self):
        if self.type in ('PROBLEM', 'RECOVERY'):
            return False
        else:
            return True

    def __str__(self):
        return "Notification %d status:%s command:%s ref:%s t_to_go:%s" % (self.id, self.status, self.command, self.ref, time.asctime(time.localtime(self.t_to_go)))
        #return ''#str(self.__dict__)

    def get_id(self):
        return self.id

    def get_return_from(self, n):
        self.exit_status  = n.exit_status
        #self.output = c.output
        #self.check_time = c.check_time
        #self.execution_time = c.execution_time

    #Fill data with info of item by looking at brok_type
    #in props of properties or running_propterties
    def fill_data_brok_from(self, data, brok_type):
        cls = self.__class__
        #Now config properties
        for prop in cls.properties:
            if hasattr(prop, 'fill_brok'):
                if brok_type in cls.properties[prop]['fill_brok']:
                    data[prop] = getattr(self, prop)

    #Get a brok with initial status
    def get_initial_status_brok(self):
        data = {'id' : self.id}

        self.fill_data_brok_from(data, 'full_status')
        b = Brok('notification_raise', data)
        return b

    #Call by picle for dataify the coment
    #because we DO NOT WANT REF in this pickleisation!
    def __getstate__(self):
        cls = self.__class__
        # id is not in *_properties
        res = {'id' : self.id}
        for prop in cls.properties:
            if hasattr(self, prop):
                res[prop] = getattr(self, prop)

        return res

    # Inversed funtion of getstate
    def __setstate__(self, state):
        cls = self.__class__
        self.id = state['id']
        for prop in cls.properties:
            if prop in state:
                setattr(self, prop, state[prop])
        # Hook for load of 0.4 notification : there were no
        # creation time, must put one
        if not hasattr(self, 'creation_time'):
            self.creation_time = time.time()
<|endoftext|>


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

#-------------------------------------------------------------------------
# drawElements Quality Program utilities
# --------------------------------------
#
# Copyright 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#-------------------------------------------------------------------------

import os
import re
import sys
import shlex
import subprocess
import multiprocessing
import string

try:
	import threading
except ImportError:
	import dummy_threading as threading

class NativeLib:
	def __init__ (self, apiVersion, abiVersion, prebuiltDir):
		self.apiVersion		= apiVersion
		self.abiVersion		= abiVersion
		self.prebuiltDir	= prebuiltDir

	def __str__ (self):
		return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)

	def __repr__ (self):
		return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)

def getPlatform ():
	if sys.platform.startswith('linux'):
		return 'linux'
	else:
		return sys.platform

def selectByOS (variants):
	platform = getPlatform()
	if platform in variants:
		return variants[platform]
	elif 'other' in variants:
		return variants['other']
	else:
		raise Exception("No configuration for '%s'" % platform)

def isExecutable (path):
	return os.path.isfile(path) and os.access(path, os.X_OK)

def which (binName):
	for path in os.environ['PATH'].split(os.pathsep):
		path = path.strip('"')
		fullPath = os.path.join(path, binName)
		if isExecutable(fullPath):
			return fullPath

	return None

def isBinaryInPath (binName):
	return which(binName) != None

def selectFirstExistingBinary (filenames):
	for filename in filenames:
		if filename != None and isExecutable(filename):
			return filename

	return None

def selectFirstExistingDir (paths):
	for path in paths:
		if path != None and os.path.isdir(path):
			return path

	return None

def die (msg):
	print msg
	exit(-1)

def shellquote(s):
	return '"%s"' % s.replace('\\', '\\\\').replace('"', '\"').replace('$', '\$').replace('`', '\`')

def execute (commandLine):
	args	= shlex.split(commandLine)
	retcode	= subprocess.call(args)
	if retcode != 0:
		raise Exception("Failed to execute '%s', got %d" % (commandLine, retcode))

def execArgs (args):
	# Make sure previous stdout prints have been written out.
	sys.stdout.flush()
	retcode	= subprocess.call(args)
	if retcode != 0:
		raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))

def execArgsInDirectory (args, cwd, linePrefix=""):

	def readApplyPrefixAndPrint (source, prefix, sink):
		while True:
			line = source.readline()
			if len(line) == 0: # EOF
				break;
			sink.write(prefix + line)

	process = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	stdoutJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stdout, linePrefix, sys.stdout))
	stderrJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stderr, linePrefix, sys.stderr))
	stdoutJob.start()
	stderrJob.start()
	retcode = process.wait()
	if retcode != 0:
		raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))

def serialApply(f, argsList):
	for args in argsList:
		f(*args)

def parallelApply(f, argsList):
	class ErrorCode:
		def __init__ (self):
			self.error = None;

	def applyAndCaptureError (func, args, errorCode):
		try:
			func(*args)
		except:
			errorCode.error = sys.exc_info()

	errorCode = ErrorCode()
	jobs = []
	for args in argsList:
		job = threading.Thread(target=applyAndCaptureError, args=(f, args, errorCode))
		job.start()
		jobs.append(job)

	for job in jobs:
		job.join()

	if errorCode.error:
		raise errorCode.error[0], errorCode.error[1], errorCode.error[2]

class Device:
	def __init__(self, serial, product, model, device):
		self.serial		= serial
		self.product	= product
		self.model		= model
		self.device		= device

	def __str__ (self):
		return "%s: {product: %s, model: %s, device: %s}" % (self.serial, self.product, self.model, self.device)

def getDevices (adb):
	proc = subprocess.Popen([adb, 'devices', '-l'], stdout=subprocess.PIPE)
	(stdout, stderr) = proc.communicate()

	if proc.returncode != 0:
		raise Exception("adb devices -l failed, got %d" % proc.returncode)

	ptrn = re.compile(r'^([a-zA-Z0-9:]+)\s+.*product:([^\s]+)\s+model:([^\s]+)\s+device:([^\s]+)')
	devices = []
	for line in stdout.splitlines()[1:]:
		if len(line.strip()) == 0:
			continue

		m = ptrn.match(line)
		if m == None:
			print "WARNING: Failed to parse device info '%s'" % line
			continue

		devices.append(Device(m.group(1), m.group(2), m.group(3), m.group(4)))

	return devices

def getWin32Generator ():
	if which("jom.exe") != None:
		return "NMake Makefiles JOM"
	else:
		return "NMake Makefiles"

def isNinjaSupported ():
	return which("ninja") != None

def getUnixGenerator ():
	if isNinjaSupported():
		return "Ninja"
	else:
		return "Unix Makefiles"

def getExtraBuildArgs (generator):
	if generator == "Unix Makefiles":
		return ["--", "-j%d" % multiprocessing.cpu_count()]
	else:
		return []

NDK_HOST_OS_NAMES = [
	"windows",
	"windows-x86_64",
	"darwin-x86",
	"darwin-x86_64",
	"linux-x86",
	"linux-x86_64"
]

def getNDKHostOsName (ndkPath):
	for name in NDK_HOST_OS_NAMES:
		if os.path.exists(os.path.join(ndkPath, "prebuilt", name)):
			return name

	raise Exception("Couldn't determine NDK host OS")

# deqp/android path
ANDROID_DIR				= os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))

# Build configuration
NATIVE_LIBS				= [
		#		  API		ABI				prebuiltsDir
		NativeLib(13,		"armeabi-v7a",	'android-arm'),		# ARM v7a ABI
		NativeLib(13,		"x86",			'android-x86'),		# x86
		NativeLib(21,		"arm64-v8a",	'android-arm64'),	# ARM64 v8a ABI
	]

ANDROID_JAVA_API		= "android-22"
NATIVE_LIB_NAME			= "libdeqp.so"

def selectNDKPath ():
	candidates =  [
		os.path.expanduser("~/android-ndk-r11"),
		"C:/android/android-ndk-r11",
		os.environ.get("ANDROID_NDK_PATH", None), # If not defined, return None
	]

	ndkPath = selectFirstExistingDir(candidates)

	if ndkPath == None:
		raise Exception("None of NDK directory candidates exist: %s. Check ANDROID_NDK_PATH in common.py" % candidates)

	return ndkPath

def noneSafePathJoin (*components):
	if None in components:
		return None
	return os.path.join(*components)

# NDK paths
ANDROID_NDK_PATH				= selectNDKPath()
ANDROID_NDK_HOST_OS				= getNDKHostOsName(ANDROID_NDK_PATH)
ANDROID_NDK_TOOLCHAIN_VERSION	= "r11" # Toolchain file is selected based on this

# Native code build settings
CMAKE_GENERATOR			= selectByOS({
		'win32':	getWin32Generator(),
		'other':	getUnixGenerator()
	})
EXTRA_BUILD_ARGS		= getExtraBuildArgs(CMAKE_GENERATOR)

# SDK paths
ANDROID_SDK_PATH		= selectFirstExistingDir([
		os.environ.get("ANDROID_SDK_PATH", None),
		os.path.expanduser("~/android-sdk-linux"),
		os.path.expanduser("~/android-sdk-mac_x86"),
		"C:/android/android-sdk-windows",
	])
ANDROID_BIN				= selectFirstExistingBinary([
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android"),
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android.bat"),
		which('android'),
	])
ADB_BIN					= selectFirstExistingBinary([
		which('adb'), # \note Prefer adb in path to avoid version issues on dev machines
		noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb"),
		noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb.exe"),
	])
ZIPALIGN_BIN			= selectFirstExistingBinary([
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign"),
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign.exe"),
		which('zipalign'),
	])
JARSIGNER_BIN			= which('jarsigner')

# Apache ant
ANT_BIN					= selectFirstExistingBinary([
		which('ant'),
		"C:/android/apache-ant-1.8.4/bin/ant.bat",
		"C:/android/apache-ant-1.9.2/bin/ant.bat",
		"C:/android/apache-ant-1.9.3/bin/ant.bat",
		"C:/android/apache-ant-1.9.4/bin/ant.bat",
	])

def makeNameValueTuple (name):
	return (name, str(eval(name)))

CONFIG_VAR_NAMES = [
		"ANDROID_DIR",
		"NATIVE_LIBS",
		"ANDROID_JAVA_API",
		"NATIVE_LIB_NAME",
		"ANDROID_NDK_PATH",
		"ANDROID_NDK_HOST_OS",
		"ANDROID_NDK_TOOLCHAIN_VERSION",
		"CMAKE_GENERATOR",
		"EXTRA_BUILD_ARGS",
		"ANDROID_SDK_PATH",
		"ANDROID_BIN",
		"ADB_BIN",
		"ZIPALIGN_BIN",
		"JARSIGNER_BIN",
		"ANT_BIN",
	]
CONFIG_STRINGS = [makeNameValueTuple(x) for x in CONFIG_VAR_NAMES]
<commit_msg>GetDevices: Also allow `\.` to be part of the host name am: dbf9715509 am: 133c610be6<commit_after># -*- coding: utf-8 -*-

#-------------------------------------------------------------------------
# drawElements Quality Program utilities
# --------------------------------------
#
# Copyright 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#-------------------------------------------------------------------------

import os
import re
import sys
import shlex
import subprocess
import multiprocessing
import string

try:
	import threading
except ImportError:
	import dummy_threading as threading

class NativeLib:
	def __init__ (self, apiVersion, abiVersion, prebuiltDir):
		self.apiVersion		= apiVersion
		self.abiVersion		= abiVersion
		self.prebuiltDir	= prebuiltDir

	def __str__ (self):
		return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)

	def __repr__ (self):
		return "(API: %s, ABI: %s)" % (self.apiVersion, self.abiVersion)

def getPlatform ():
	if sys.platform.startswith('linux'):
		return 'linux'
	else:
		return sys.platform

def selectByOS (variants):
	platform = getPlatform()
	if platform in variants:
		return variants[platform]
	elif 'other' in variants:
		return variants['other']
	else:
		raise Exception("No configuration for '%s'" % platform)

def isExecutable (path):
	return os.path.isfile(path) and os.access(path, os.X_OK)

def which (binName):
	for path in os.environ['PATH'].split(os.pathsep):
		path = path.strip('"')
		fullPath = os.path.join(path, binName)
		if isExecutable(fullPath):
			return fullPath

	return None

def isBinaryInPath (binName):
	return which(binName) != None

def selectFirstExistingBinary (filenames):
	for filename in filenames:
		if filename != None and isExecutable(filename):
			return filename

	return None

def selectFirstExistingDir (paths):
	for path in paths:
		if path != None and os.path.isdir(path):
			return path

	return None

def die (msg):
	print msg
	exit(-1)

def shellquote(s):
	return '"%s"' % s.replace('\\', '\\\\').replace('"', '\"').replace('$', '\$').replace('`', '\`')

def execute (commandLine):
	args	= shlex.split(commandLine)
	retcode	= subprocess.call(args)
	if retcode != 0:
		raise Exception("Failed to execute '%s', got %d" % (commandLine, retcode))

def execArgs (args):
	# Make sure previous stdout prints have been written out.
	sys.stdout.flush()
	retcode	= subprocess.call(args)
	if retcode != 0:
		raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))

def execArgsInDirectory (args, cwd, linePrefix=""):

	def readApplyPrefixAndPrint (source, prefix, sink):
		while True:
			line = source.readline()
			if len(line) == 0: # EOF
				break;
			sink.write(prefix + line)

	process = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	stdoutJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stdout, linePrefix, sys.stdout))
	stderrJob = threading.Thread(target=readApplyPrefixAndPrint, args=(process.stderr, linePrefix, sys.stderr))
	stdoutJob.start()
	stderrJob.start()
	retcode = process.wait()
	if retcode != 0:
		raise Exception("Failed to execute '%s', got %d" % (str(args), retcode))

def serialApply(f, argsList):
	for args in argsList:
		f(*args)

def parallelApply(f, argsList):
	class ErrorCode:
		def __init__ (self):
			self.error = None;

	def applyAndCaptureError (func, args, errorCode):
		try:
			func(*args)
		except:
			errorCode.error = sys.exc_info()

	errorCode = ErrorCode()
	jobs = []
	for args in argsList:
		job = threading.Thread(target=applyAndCaptureError, args=(f, args, errorCode))
		job.start()
		jobs.append(job)

	for job in jobs:
		job.join()

	if errorCode.error:
		raise errorCode.error[0], errorCode.error[1], errorCode.error[2]

class Device:
	def __init__(self, serial, product, model, device):
		self.serial		= serial
		self.product	= product
		self.model		= model
		self.device		= device

	def __str__ (self):
		return "%s: {product: %s, model: %s, device: %s}" % (self.serial, self.product, self.model, self.device)

def getDevices (adb):
	proc = subprocess.Popen([adb, 'devices', '-l'], stdout=subprocess.PIPE)
	(stdout, stderr) = proc.communicate()

	if proc.returncode != 0:
		raise Exception("adb devices -l failed, got %d" % proc.returncode)

	ptrn = re.compile(r'^([a-zA-Z0-9\.:]+)\s+.*product:([^\s]+)\s+model:([^\s]+)\s+device:([^\s]+)')
	devices = []
	for line in stdout.splitlines()[1:]:
		if len(line.strip()) == 0:
			continue

		m = ptrn.match(line)
		if m == None:
			print "WARNING: Failed to parse device info '%s'" % line
			continue

		devices.append(Device(m.group(1), m.group(2), m.group(3), m.group(4)))

	return devices

def getWin32Generator ():
	if which("jom.exe") != None:
		return "NMake Makefiles JOM"
	else:
		return "NMake Makefiles"

def isNinjaSupported ():
	return which("ninja") != None

def getUnixGenerator ():
	if isNinjaSupported():
		return "Ninja"
	else:
		return "Unix Makefiles"

def getExtraBuildArgs (generator):
	if generator == "Unix Makefiles":
		return ["--", "-j%d" % multiprocessing.cpu_count()]
	else:
		return []

NDK_HOST_OS_NAMES = [
	"windows",
	"windows-x86_64",
	"darwin-x86",
	"darwin-x86_64",
	"linux-x86",
	"linux-x86_64"
]

def getNDKHostOsName (ndkPath):
	for name in NDK_HOST_OS_NAMES:
		if os.path.exists(os.path.join(ndkPath, "prebuilt", name)):
			return name

	raise Exception("Couldn't determine NDK host OS")

# deqp/android path
ANDROID_DIR				= os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))

# Build configuration
NATIVE_LIBS				= [
		#		  API		ABI				prebuiltsDir
		NativeLib(13,		"armeabi-v7a",	'android-arm'),		# ARM v7a ABI
		NativeLib(13,		"x86",			'android-x86'),		# x86
		NativeLib(21,		"arm64-v8a",	'android-arm64'),	# ARM64 v8a ABI
	]

ANDROID_JAVA_API		= "android-22"
NATIVE_LIB_NAME			= "libdeqp.so"

def selectNDKPath ():
	candidates =  [
		os.path.expanduser("~/android-ndk-r11"),
		"C:/android/android-ndk-r11",
		os.environ.get("ANDROID_NDK_PATH", None), # If not defined, return None
	]

	ndkPath = selectFirstExistingDir(candidates)

	if ndkPath == None:
		raise Exception("None of NDK directory candidates exist: %s. Check ANDROID_NDK_PATH in common.py" % candidates)

	return ndkPath

def noneSafePathJoin (*components):
	if None in components:
		return None
	return os.path.join(*components)

# NDK paths
ANDROID_NDK_PATH				= selectNDKPath()
ANDROID_NDK_HOST_OS				= getNDKHostOsName(ANDROID_NDK_PATH)
ANDROID_NDK_TOOLCHAIN_VERSION	= "r11" # Toolchain file is selected based on this

# Native code build settings
CMAKE_GENERATOR			= selectByOS({
		'win32':	getWin32Generator(),
		'other':	getUnixGenerator()
	})
EXTRA_BUILD_ARGS		= getExtraBuildArgs(CMAKE_GENERATOR)

# SDK paths
ANDROID_SDK_PATH		= selectFirstExistingDir([
		os.environ.get("ANDROID_SDK_PATH", None),
		os.path.expanduser("~/android-sdk-linux"),
		os.path.expanduser("~/android-sdk-mac_x86"),
		"C:/android/android-sdk-windows",
	])
ANDROID_BIN				= selectFirstExistingBinary([
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android"),
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "android.bat"),
		which('android'),
	])
ADB_BIN					= selectFirstExistingBinary([
		which('adb'), # \note Prefer adb in path to avoid version issues on dev machines
		noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb"),
		noneSafePathJoin(ANDROID_SDK_PATH, "platform-tools", "adb.exe"),
	])
ZIPALIGN_BIN			= selectFirstExistingBinary([
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign"),
		noneSafePathJoin(ANDROID_SDK_PATH, "tools", "zipalign.exe"),
		which('zipalign'),
	])
JARSIGNER_BIN			= which('jarsigner')

# Apache ant
ANT_BIN					= selectFirstExistingBinary([
		which('ant'),
		"C:/android/apache-ant-1.8.4/bin/ant.bat",
		"C:/android/apache-ant-1.9.2/bin/ant.bat",
		"C:/android/apache-ant-1.9.3/bin/ant.bat",
		"C:/android/apache-ant-1.9.4/bin/ant.bat",
	])

def makeNameValueTuple (name):
	return (name, str(eval(name)))

CONFIG_VAR_NAMES = [
		"ANDROID_DIR",
		"NATIVE_LIBS",
		"ANDROID_JAVA_API",
		"NATIVE_LIB_NAME",
		"ANDROID_NDK_PATH",
		"ANDROID_NDK_HOST_OS",
		"ANDROID_NDK_TOOLCHAIN_VERSION",
		"CMAKE_GENERATOR",
		"EXTRA_BUILD_ARGS",
		"ANDROID_SDK_PATH",
		"ANDROID_BIN",
		"ADB_BIN",
		"ZIPALIGN_BIN",
		"JARSIGNER_BIN",
		"ANT_BIN",
	]
CONFIG_STRINGS = [makeNameValueTuple(x) for x in CONFIG_VAR_NAMES]
<|endoftext|>


<commit_before>import functools
import sys
import contextlib
from collections import defaultdict
from inspect import Signature
from typing import TYPE_CHECKING, TypeVar, Type, Generic, Callable, Optional

import progressbar
import logging
import time

from ..misc.plugins import PluginVendor, VendorPreset
from ..misc.ux import deprecated

if TYPE_CHECKING:
    from ..knowledge_base import KnowledgeBase
    import angr
    from ..project import Project
    from typing_extensions import ParamSpec
    AnalysisParams = ParamSpec("AnalysisParams")

l = logging.getLogger(name=__name__)

class AnalysisLogEntry:
    def __init__(self, message, exc_info=False):
        if exc_info:
            (e_type, value, traceback) = sys.exc_info()
            self.exc_type = e_type
            self.exc_value = value
            self.exc_traceback = traceback
        else:
            self.exc_type = None
            self.exc_value = None
            self.exc_traceback = None

        self.message = message

    def __getstate__(self):
        return str(self.__dict__.get("exc_type")), \
               str(self.__dict__.get("exc_value")), \
               str(self.__dict__.get("exc_traceback")), \
               self.message

    def __setstate__(self, s):
        self.exc_type, self.exc_value, self.exc_traceback, self.message = s

    def __repr__(self):
        if self.exc_type is None:
            msg_str = repr(self.message)
            if len(msg_str) > 70:
                msg_str = msg_str[:66] + '...'
                if msg_str[0] in ('"', "'"):
                    msg_str += msg_str[0]
            return '<AnalysisLogEntry %s>' % msg_str
        else:
            msg_str = repr(self.message)
            if len(msg_str) > 40:
                msg_str = msg_str[:36] + '...'
                if msg_str[0] in ('"', "'"):
                    msg_str += msg_str[0]
            return '<AnalysisLogEntry %s with %s: %s>' % (msg_str, self.exc_type.__name__, self.exc_value)

A = TypeVar("A", bound="Analysis")
class AnalysesHub(PluginVendor):
    """
    This class contains functions for all the registered and runnable analyses,
    """
    def __init__(self, project):
        super(AnalysesHub, self).__init__()
        self.project = project

    @deprecated()
    def reload_analyses(self): # pylint: disable=no-self-use
        return

    def _init_plugin(self, plugin_cls: Type[A]) -> "AnalysisFactory[A]":
        return AnalysisFactory(self.project, plugin_cls)

    def __getstate__(self):
        s = super(AnalysesHub, self).__getstate__()
        return (s, self.project)

    def __setstate__(self, sd):
        s, self.project = sd
        super(AnalysesHub, self).__setstate__(s)

    def __getitem__(self, plugin_cls: "Type[A]") -> "AnalysisFactory[A]":
        return AnalysisFactory(self.project, plugin_cls)

class AnalysisFactory(Generic[A]):
    def __init__(self, project: "Project", analysis_cls: Type[A]):
        self._project = project
        self._analysis_cls = analysis_cls
        self.__doc__ = ''
        self.__doc__ += analysis_cls.__doc__ or ''
        self.__doc__ += analysis_cls.__init__.__doc__ or ''
        self.__call__.__func__.__signature__ = Signature.from_callable(analysis_cls.__init__)

    def prep(self,
              fail_fast=False,
              kb: Optional["KnowledgeBase"] = None,
              progress_callback: Optional[Callable] = None,
              show_progressbar: bool = False) -> Type[A]:

        @functools.wraps(self._analysis_cls.__init__)
        def wrapper(*args, **kwargs):
            oself = object.__new__(self._analysis_cls)
            oself.named_errors = defaultdict(list)
            oself.errors = []
            oself.log = []

            oself._fail_fast = fail_fast
            oself._name = self._analysis_cls.__name__
            oself.project = self._project
            oself.kb = kb or self._project.kb
            oself._progress_callback = progress_callback

            oself._show_progressbar = show_progressbar
            oself.__init__(*args, **kwargs)
            return oself

        return wrapper # type: ignore

    def __call__(self, *args, **kwargs):
        fail_fast = kwargs.pop('fail_fast', False)
        kb = kwargs.pop('kb', self._project.kb)
        progress_callback = kwargs.pop('progress_callback', None)
        show_progressbar = kwargs.pop('show_progressbar', False)

        w = self.prep(fail_fast=fail_fast,
                  kb=kb,
                  progress_callback=progress_callback,
                  show_progressbar=show_progressbar)

        r = w(*args, **kwargs)
        # clean up so that it's always pickleable
        r._progressbar = None
        return r

class StatusBar(progressbar.widgets.WidgetBase):
    """
    Implements a progressbar component for displaying raw text.
    """
    def __init__(self, width: Optional[int]=40):
        super().__init__()
        self.status: str = ""
        self.width = width

    def __call__(self, progress, data, **kwargs):  # pylint:disable=unused-argument
        if self.width is None:
            return self.status
        if len(self.status) < self.width:
            return self.status.ljust(self.width, " ")
        else:
            return self.status[:self.width]

class Analysis:
    """
    This class represents an analysis on the program.

    :ivar project:  The project for this analysis.
    :type project:  angr.Project
    :ivar KnowledgeBase kb: The knowledgebase object.
    :ivar _progress_callback: A callback function for receiving the progress of this analysis. It only takes
                                        one argument, which is a float number from 0.0 to 100.0 indicating the current
                                        progress.
    :ivar bool _show_progressbar: If a progressbar should be shown during the analysis. It's independent from
                                    _progress_callback.
    :ivar progressbar.ProgressBar _progressbar: The progress bar object.
    """

    project: "Project" = None
    kb: 'KnowledgeBase' = None
    _fail_fast = None
    _name = None
    errors = []
    named_errors = defaultdict(list)
    _progress_callback = None
    _show_progressbar = False
    _progressbar = None
    _statusbar: Optional[StatusBar] = None

    _PROGRESS_WIDGETS = [
        progressbar.Percentage(),
        ' ',
        progressbar.Bar(),
        ' ',
        progressbar.Timer(),
        ' ',
        progressbar.ETA(),
        ' ',
        StatusBar(),
    ]

    @contextlib.contextmanager
    def _resilience(self, name=None, exception=Exception):
        try:
            yield
        except exception:  # pylint:disable=broad-except
            if self._fail_fast:
                raise
            else:
                error = AnalysisLogEntry("exception occurred", exc_info=True)
                l.error("Caught and logged %s with resilience: %s", error.exc_type.__name__, error.exc_value)
                if name is None:
                    self.errors.append(error)
                else:
                    self.named_errors[name].append(error)

    def _initialize_progressbar(self):
        """
        Initialize the progressbar.
        :return: None
        """

        self._progressbar = progressbar.ProgressBar(widgets=Analysis._PROGRESS_WIDGETS, max_value=10000 * 100).start()
        self._statusbar = self._progressbar.widgets[-1]

    def _update_progress(self, percentage, text=None, **kwargs):
        """
        Update the progress with a percentage, including updating the progressbar as well as calling the progress
        callback.

        :param float percentage:    Percentage of the progressbar. from 0.0 to 100.0.
        :param kwargs:              Other parameters that will be passed to the progress_callback handler.
        :return: None
        """

        if self._show_progressbar:
            if self._progressbar is None:
                self._initialize_progressbar()

            self._progressbar.update(percentage * 10000)

        if text is not None and self._statusbar is not None:
            self._statusbar.status = text

        if self._progress_callback is not None:
            self._progress_callback(percentage, text=text, **kwargs)  # pylint:disable=not-callable

    def _finish_progress(self):
        """
        Mark the progressbar as finished.
        :return: None
        """

        if self._show_progressbar:
            if self._progressbar is None:
                self._initialize_progressbar()
            if self._progressbar is not None:
                self._progressbar.finish()
                # Remove the progressbar object so it will not be pickled
                self._progressbar = None

        if self._progress_callback is not None:
            self._progress_callback(100.0)  # pylint:disable=not-callable

    @staticmethod
    def _release_gil(ctr, freq, sleep_time=0.001):
        """
        Periodically calls time.sleep() and releases the GIL so other threads (like, GUI threads) have a much better
        chance to be scheduled, and other critical components (like the GUI) can be kept responsiveness.

        This is, of course, a hack before we move all computational intensive tasks to pure C++ implementations.

        :param int ctr:     A number provided by the caller.
        :param int freq:    How frequently time.sleep() should be called. time.sleep() is called when ctr % freq == 0.
        :param sleep_time:  Number (or fraction) of seconds to sleep.
        :return:            None
        """

        if ctr != 0 and ctr % freq == 0:
            time.sleep(sleep_time)

    def __getstate__(self):
        d = dict(self.__dict__)
        if "_progressbar" in d:
            del d['_progressbar']
        if '_progress_callback' in d:
            del d['_progress_callback']
        if '_statusbar' in d:
            del d['_statusbar']
        return d

    def __setstate__(self, state):
        self.__dict__.update(state)

    def __repr__(self):
        return '<%s Analysis Result at %#x>' % (self._name, id(self))

default_analyses = VendorPreset()
AnalysesHub.register_preset('default', default_analyses)
<commit_msg>Add a very convenient type annotation (#3577)<commit_after>import functools
import sys
import contextlib
from collections import defaultdict
from inspect import Signature
from typing import TYPE_CHECKING, TypeVar, Type, Generic, Callable, Optional

import progressbar
import logging
import time

from ..misc.plugins import PluginVendor, VendorPreset
from ..misc.ux import deprecated

if TYPE_CHECKING:
    from ..knowledge_base import KnowledgeBase
    import angr
    from ..project import Project
    from typing_extensions import ParamSpec
    AnalysisParams = ParamSpec("AnalysisParams")

l = logging.getLogger(name=__name__)

class AnalysisLogEntry:
    def __init__(self, message, exc_info=False):
        if exc_info:
            (e_type, value, traceback) = sys.exc_info()
            self.exc_type = e_type
            self.exc_value = value
            self.exc_traceback = traceback
        else:
            self.exc_type = None
            self.exc_value = None
            self.exc_traceback = None

        self.message = message

    def __getstate__(self):
        return str(self.__dict__.get("exc_type")), \
               str(self.__dict__.get("exc_value")), \
               str(self.__dict__.get("exc_traceback")), \
               self.message

    def __setstate__(self, s):
        self.exc_type, self.exc_value, self.exc_traceback, self.message = s

    def __repr__(self):
        if self.exc_type is None:
            msg_str = repr(self.message)
            if len(msg_str) > 70:
                msg_str = msg_str[:66] + '...'
                if msg_str[0] in ('"', "'"):
                    msg_str += msg_str[0]
            return '<AnalysisLogEntry %s>' % msg_str
        else:
            msg_str = repr(self.message)
            if len(msg_str) > 40:
                msg_str = msg_str[:36] + '...'
                if msg_str[0] in ('"', "'"):
                    msg_str += msg_str[0]
            return '<AnalysisLogEntry %s with %s: %s>' % (msg_str, self.exc_type.__name__, self.exc_value)

A = TypeVar("A", bound="Analysis")
class AnalysesHub(PluginVendor):
    """
    This class contains functions for all the registered and runnable analyses,
    """
    def __init__(self, project):
        super(AnalysesHub, self).__init__()
        self.project = project

    @deprecated()
    def reload_analyses(self): # pylint: disable=no-self-use
        return

    def _init_plugin(self, plugin_cls: Type[A]) -> "AnalysisFactory[A]":
        return AnalysisFactory(self.project, plugin_cls)

    def __getstate__(self):
        s = super(AnalysesHub, self).__getstate__()
        return (s, self.project)

    def __setstate__(self, sd):
        s, self.project = sd
        super(AnalysesHub, self).__setstate__(s)

    def __getitem__(self, plugin_cls: "Type[A]") -> "AnalysisFactory[A]":
        return AnalysisFactory(self.project, plugin_cls)

class AnalysisFactory(Generic[A]):
    def __init__(self, project: "Project", analysis_cls: Type[A]):
        self._project = project
        self._analysis_cls = analysis_cls
        self.__doc__ = ''
        self.__doc__ += analysis_cls.__doc__ or ''
        self.__doc__ += analysis_cls.__init__.__doc__ or ''
        self.__call__.__func__.__signature__ = Signature.from_callable(analysis_cls.__init__)

    def prep(self,
              fail_fast=False,
              kb: Optional["KnowledgeBase"] = None,
              progress_callback: Optional[Callable] = None,
              show_progressbar: bool = False) -> Type[A]:

        @functools.wraps(self._analysis_cls.__init__)
        def wrapper(*args, **kwargs):
            oself = object.__new__(self._analysis_cls)
            oself.named_errors = defaultdict(list)
            oself.errors = []
            oself.log = []

            oself._fail_fast = fail_fast
            oself._name = self._analysis_cls.__name__
            oself.project = self._project
            oself.kb = kb or self._project.kb
            oself._progress_callback = progress_callback

            oself._show_progressbar = show_progressbar
            oself.__init__(*args, **kwargs)
            return oself

        return wrapper # type: ignore

    def __call__(self, *args, **kwargs) -> A:
        fail_fast = kwargs.pop('fail_fast', False)
        kb = kwargs.pop('kb', self._project.kb)
        progress_callback = kwargs.pop('progress_callback', None)
        show_progressbar = kwargs.pop('show_progressbar', False)

        w = self.prep(fail_fast=fail_fast,
                  kb=kb,
                  progress_callback=progress_callback,
                  show_progressbar=show_progressbar)

        r = w(*args, **kwargs)
        # clean up so that it's always pickleable
        r._progressbar = None
        return r

class StatusBar(progressbar.widgets.WidgetBase):
    """
    Implements a progressbar component for displaying raw text.
    """
    def __init__(self, width: Optional[int]=40):
        super().__init__()
        self.status: str = ""
        self.width = width

    def __call__(self, progress, data, **kwargs):  # pylint:disable=unused-argument
        if self.width is None:
            return self.status
        if len(self.status) < self.width:
            return self.status.ljust(self.width, " ")
        else:
            return self.status[:self.width]

class Analysis:
    """
    This class represents an analysis on the program.

    :ivar project:  The project for this analysis.
    :type project:  angr.Project
    :ivar KnowledgeBase kb: The knowledgebase object.
    :ivar _progress_callback: A callback function for receiving the progress of this analysis. It only takes
                                        one argument, which is a float number from 0.0 to 100.0 indicating the current
                                        progress.
    :ivar bool _show_progressbar: If a progressbar should be shown during the analysis. It's independent from
                                    _progress_callback.
    :ivar progressbar.ProgressBar _progressbar: The progress bar object.
    """

    project: "Project" = None
    kb: 'KnowledgeBase' = None
    _fail_fast = None
    _name = None
    errors = []
    named_errors = defaultdict(list)
    _progress_callback = None
    _show_progressbar = False
    _progressbar = None
    _statusbar: Optional[StatusBar] = None

    _PROGRESS_WIDGETS = [
        progressbar.Percentage(),
        ' ',
        progressbar.Bar(),
        ' ',
        progressbar.Timer(),
        ' ',
        progressbar.ETA(),
        ' ',
        StatusBar(),
    ]

    @contextlib.contextmanager
    def _resilience(self, name=None, exception=Exception):
        try:
            yield
        except exception:  # pylint:disable=broad-except
            if self._fail_fast:
                raise
            else:
                error = AnalysisLogEntry("exception occurred", exc_info=True)
                l.error("Caught and logged %s with resilience: %s", error.exc_type.__name__, error.exc_value)
                if name is None:
                    self.errors.append(error)
                else:
                    self.named_errors[name].append(error)

    def _initialize_progressbar(self):
        """
        Initialize the progressbar.
        :return: None
        """

        self._progressbar = progressbar.ProgressBar(widgets=Analysis._PROGRESS_WIDGETS, max_value=10000 * 100).start()
        self._statusbar = self._progressbar.widgets[-1]

    def _update_progress(self, percentage, text=None, **kwargs):
        """
        Update the progress with a percentage, including updating the progressbar as well as calling the progress
        callback.

        :param float percentage:    Percentage of the progressbar. from 0.0 to 100.0.
        :param kwargs:              Other parameters that will be passed to the progress_callback handler.
        :return: None
        """

        if self._show_progressbar:
            if self._progressbar is None:
                self._initialize_progressbar()

            self._progressbar.update(percentage * 10000)

        if text is not None and self._statusbar is not None:
            self._statusbar.status = text

        if self._progress_callback is not None:
            self._progress_callback(percentage, text=text, **kwargs)  # pylint:disable=not-callable

    def _finish_progress(self):
        """
        Mark the progressbar as finished.
        :return: None
        """

        if self._show_progressbar:
            if self._progressbar is None:
                self._initialize_progressbar()
            if self._progressbar is not None:
                self._progressbar.finish()
                # Remove the progressbar object so it will not be pickled
                self._progressbar = None

        if self._progress_callback is not None:
            self._progress_callback(100.0)  # pylint:disable=not-callable

    @staticmethod
    def _release_gil(ctr, freq, sleep_time=0.001):
        """
        Periodically calls time.sleep() and releases the GIL so other threads (like, GUI threads) have a much better
        chance to be scheduled, and other critical components (like the GUI) can be kept responsiveness.

        This is, of course, a hack before we move all computational intensive tasks to pure C++ implementations.

        :param int ctr:     A number provided by the caller.
        :param int freq:    How frequently time.sleep() should be called. time.sleep() is called when ctr % freq == 0.
        :param sleep_time:  Number (or fraction) of seconds to sleep.
        :return:            None
        """

        if ctr != 0 and ctr % freq == 0:
            time.sleep(sleep_time)

    def __getstate__(self):
        d = dict(self.__dict__)
        if "_progressbar" in d:
            del d['_progressbar']
        if '_progress_callback' in d:
            del d['_progress_callback']
        if '_statusbar' in d:
            del d['_statusbar']
        return d

    def __setstate__(self, state):
        self.__dict__.update(state)

    def __repr__(self):
        return '<%s Analysis Result at %#x>' % (self._name, id(self))

default_analyses = VendorPreset()
AnalysesHub.register_preset('default', default_analyses)
<|endoftext|>


<commit_before>import threading, thread
import socket, subprocess,sys
import time
from datetime import datetime
import shelve, collections

class myThread (threading.Thread):
  def __init__(self, threadName,host,startPort,middlePort,timeout):
    threading.Thread.__init__(self)
    self.threadName = threadName
    self.host = host
    self.startPort = startPort
    self.middlePort = middlePort
    self.timeout =timeout
  def run(self):
    scantcp(self.threadName,self.host,self.startPort,self.middlePort,self.timeout)

def scantcp(threadName,host,startPort,middlePort,timeout):
  try:
    for port in range(startPort,middlePort):
      sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM) # TCP
      #sock= socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # UDP
      socket.setdefaulttimeout(timeout)
      result = sock.connect_ex((host,port))
      if result==0:
        dic[port] = "Port: %s is opened; Service %s" % (port, data.get(port, "Not in Database"))
        sock.close()
  except KeyboardInterrupt:
    print "You stop this "
    sys.exit()
  except socket.gaierror:
    print "Hostname could not be resolved"
    sys.exit()
  except socket.error:
    print "could not connect to server"
    sys.exit()
  shelf.close()

def getHostPorts():
  host=''
  while(host==''):
    d=raw_input("\ t Press D for Domain Name or Press I for IP Address\t")
    if (d=='D' or d=='d'):
      rmserver = raw_input("\t Enter the Domain Name to scan:\t")
      host = socket.gethostbyname(rmserver)
    elif(d=='I' or d=='i'):
      host = raw_input("\t Enter the IP Address to scan: ")
    else:
      print "Wrong input"
  startPort = int(raw_input("\t Enter the start port number\t"))
  endPort = int (raw_input("\t Enter the last port number\t"))
  return host, startPort, endPort

def getTimeout():
  conect=raw_input("For low connectivity press L and High connectivity Press H\t")
  if (conect=='L' or conect=='l'):
    timeout =1.5
  elif(conect =='H' or conect=='h'):
    timeout=0.5
  else:
    timeout = 1
    print "\t wrong Input, time connectivity is 1"
  return timeout

def dividePorts (endPort,startPort):
  stepPort=endPort-startPort
  tnumber =30   # tnumber number of port handled by one thread
  tnum=stepPort/tnumber # tnum number of threads
  if (stepPort%tnumber != 0):
    tnum= tnum+1
  if (tnum > 300):
    tnumber = stepPort/300
    tnumber= tnumber+1
    tnum=stepPort/tnumber
    if (stepPort%tnumber != 0):
      tnum= tnum+1
  print "\tNumber of threads: %s" % tnum
  return tnum, tnumber

def multiThreads (tnum, startPort, tnumber, timeout, host):
  threads= []
  try:
    for i in range(tnum):
      middlePort=startPort+tnumber
      thread = myThread("T%s" %i,host,startPort,middlePort,timeout)
      thread.start()
      threads.append(thread)
      startPort=middlePort
  except:
    print "Error: unable to start thread"
  print "\t Number of Threads active:", threading.activeCount()
  for t in threads:
    t.join()
  print "Exiting Main Thread"

if __name__ == '__main__':
  subprocess.call('clear',shell=True)
  shelf = shelve.open("Scan_ports.raj")
  data=(shelf['desc'])
  dic = collections.OrderedDict()
  print "-"*75
  print " \tPort scanner\n "
  host, startPort, endPort = getHostPorts()
  timeout = getTimeout()
  print "\n Scanner is working on ",host
  print "-"*75
  startTime= datetime.now()
  tnum,tnumber = dividePorts(endPort,startPort)
  multiThreads (tnum, startPort, tnumber, timeout, host)
  sortDict = collections.OrderedDict(sorted(dic.items()))   # sort ports
  for key in sortDict:
    print sortDict[key],":-)"
  endTime= datetime.now()
  totalTime =endTime-startTime
  print "Scanning complete in: " , totalTime

<commit_msg>Scan ports add Assert and Value Error<commit_after>import threading, thread
import socket, subprocess,sys
import time
from datetime import datetime
import shelve, collections

class myThread (threading.Thread):
  def __init__(self, threadName,host,startPort,middlePort,timeout):
    threading.Thread.__init__(self)
    self.threadName = threadName
    self.host = host
    self.startPort = startPort
    self.middlePort = middlePort
    self.timeout =timeout
  def run(self):
    scantcp(self.threadName,self.host,self.startPort,self.middlePort,self.timeout)

def scantcp(threadName,host,startPort,middlePort,timeout):
  try:
    for port in range(startPort,middlePort):
      sock= socket.socket(socket.AF_INET,socket.SOCK_STREAM) # TCP
      #sock= socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # UDP
      socket.setdefaulttimeout(timeout)
      result = sock.connect_ex((host,port))
      if result==0:
        dic[port] = "Port: %s is opened; Service %s" % (port, data.get(port, "Not in Database"))
        sock.close()
  except KeyboardInterrupt:
    print "You stop this "
    sys.exit()
  except socket.gaierror:
    print "Hostname could not be resolved"
    sys.exit()
  except socket.error:
    print "could not connect to server"
    sys.exit()
  shelf.close()

def getHostPorts():
  host=''
  while(host==''):
    d=raw_input("\ t Press D for Domain Name or Press I for IP Address\t")
    if (d=='D' or d=='d'):
      rmserver = raw_input("\t Enter the Domain Name to scan:\t")
      host = socket.gethostbyname(rmserver)
    elif(d=='I' or d=='i'):
      host = raw_input("\t Enter the IP Address to scan: ")
    else:
      print "Wrong input"
  while True:
    try:
      startPort = int(raw_input("\t Enter the start port number\t"))
      assert 0 <= startPort <= 255, "must be between 0 and 255 (inclusive)"
    except ValueError:
      print("Please write the number")
    except AssertionError, e:
      print e

  while True:
    try:
      endPort = int (raw_input("\t Enter the last port number\t"))
      assert 0 <= endPort <= 255, "must be between 0 and 255 (inclusive)"
    except ValueError:
      print("Please write the number")
    except AssertionError, e:
      print e
  return host, startPort, endPort

def getTimeout():
  conect=raw_input("For low connectivity press L and High connectivity Press H\t")
  if (conect=='L' or conect=='l'):
    timeout =1.5
  elif(conect =='H' or conect=='h'):
    timeout=0.5
  else:
    timeout = 1
    print "\t wrong Input, time connectivity is 1"
  return timeout

def dividePorts (endPort,startPort):
  stepPort=endPort-startPort
  tnumber =30   # tnumber number of port handled by one thread
  tnum=stepPort/tnumber # tnum number of threads
  if (stepPort%tnumber != 0):
    tnum= tnum+1
  if (tnum > 300):
    tnumber = stepPort/300
    tnumber= tnumber+1
    tnum=stepPort/tnumber
    if (stepPort%tnumber != 0):
      tnum= tnum+1
  print "\tNumber of threads: %s" % tnum
  return tnum, tnumber

def multiThreads (tnum, startPort, tnumber, timeout, host):
  threads= []
  try:
    for i in range(tnum):
      middlePort=startPort+tnumber
      thread = myThread("T%s" %i,host,startPort,middlePort,timeout)
      thread.start()
      threads.append(thread)
      startPort=middlePort
  except:
    print "Error: unable to start thread"
  print "\t Number of Threads active:", threading.activeCount()
  for t in threads:
    t.join()
  print "Exiting Main Thread"

if __name__ == '__main__':
  subprocess.call('clear',shell=True)
  shelf = shelve.open("Scan_ports.raj")
  data=(shelf['desc'])
  dic = collections.OrderedDict()
  print "-"*75
  print " \tPort scanner\n "
  host, startPort, endPort = getHostPorts()
  timeout = getTimeout()
  print "\n Scanner is working on ",host
  print "-"*75
  startTime= datetime.now()
  tnum,tnumber = dividePorts(endPort,startPort)
  multiThreads (tnum, startPort, tnumber, timeout, host)
  sortDict = collections.OrderedDict(sorted(dic.items()))   # sort ports
  for key in sortDict:
    print sortDict[key],":-)"
  endTime= datetime.now()
  totalTime =endTime-startTime
  print "Scanning complete in: " , totalTime

<|endoftext|>


<commit_before>
from .errors import SimStateOptionsError

_NO_DEFAULT_VALUE = "_NO_DEFAULT_VALUE"  # please god don't use this value as the default value of your state option

class StateOption(object):
    """
    Describes a state option.
    """

    __slots__ = [ 'name', 'types', 'default', 'description' ]

    def __init__(self, name, types, default=_NO_DEFAULT_VALUE, description=None):
        self.name = name
        self.types = tuple(types)
        self.default = default
        self.description = description

        # Sanity check
        if not isinstance(self.default, tuple(self.types)):
            raise SimStateOptionsError("The type of the default value does not match the expected types of this state "
                                       "option.")

    @property
    def has_default_value(self):
        return self.default != _NO_DEFAULT_VALUE

    def one_type(self):
        if len(self.types) == 1:
            return next(iter(self.types))

        return None

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return isinstance(other, StateOption) \
               and self.name == other.name \
               and self.types == other.types

    def __repr__(self):
        if self.description is not None:
            desc = ": %s" % self.description
        else:
            desc = ""
        if self.one_type() is not None:
            types = self.one_type().__name__
        else:
            types = ",".join(t.__name__ for t in self.types)

        return "<O %s[%s]%s>" % (self.name, types, desc)

    def __getstate__(self):
        return {
            "name": self.name,
            "types": self.types,
            "default": self.default,
            "description": self.description,
        }

    def __setstate__(self, state):
        self.name = state["name"]
        self.types = state["types"]
        self.default = state["default"]
        self.description = state["description"]

class SimStateOptions(object):
    """
    A per-state manager of state options. An option can be either a key-valued entry or a Boolean switch (which can be
    seen as a key-valued entry whose value can only be either True or False).
    """

    OPTIONS = { }

    def __init__(self, thing):
        """
        :param thing:    Either a set of Boolean switches to enable, or an existing SimStateOptions instance.
        """

        self._options = { }
        if thing is None:
            pass
        elif isinstance(thing, (set, list)):
            boolean_switches = thing
            for name in boolean_switches:
                self[name] = True
        elif isinstance(thing, SimStateOptions):
            ops = thing
            self._options = ops._options.copy()
        else:
            raise SimStateOptionsError("Unsupported constructor argument type '%s'." % type(thing))

    def _get_option_desc(self, key):
        """
        Get the option descriptor from self.OPTIONS.

        :param str key: Name of the state option.
        :return:        The option descriptor.
        :rtype:         StateOption
        """

        if key not in self.OPTIONS:
            raise SimStateOptionsError("The state option '%s' does not exist." % key)

        return self.OPTIONS[key]

    def __repr__(self):
        s = "<SimStateOptions>"
        return s

    def __contains__(self, key):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, __contains__() only supports testing the value of a Boolean
        switch.

        E.g., in the old days:
        >>> sim_options.SYMBOLIC in state.options
        False

        nowadays:
        >>> sim_options.SYMBOLIC in state.options
        False

        But you cannot use it to test the value of a non-existent option, or the value of a key-valued entry that is not
        linked to a Boolean value.

        >>> "symbolic_ip_max_targets" in state.options
        SimStateOptionsError('"symbolic_ip_max_targets" is not a Boolean switch.')

        :param str key: Name of the Boolean switch.
        :return:        True if the switch is on (the option is switched on), False otherwise.
        :rtype:         bool
        """

        o = self._get_option_desc(key)

        if o.one_type() is not bool:
            raise SimStateOptionsError("The state option '%s' is not a Boolean switch." % key)

        return key in self._options and self._options[key] is True

    def __setitem__(self, key, value):
        """
        Set the value of a state option.

        :param str key:     Name of the state option.
        :param str value:   The value of the state option. Must be of the same type as registered.
        :return:            None
        """

        o = self._get_option_desc(key)

        if type(value) not in o.types:
            raise SimStateOptionsError("The value '%s' does not have an acceptable type for state option '%s'. "
                                       "Accepted types are: %s." %
                                       (value, key, str(o.types))
                                       )

        self._options[o.name] = value

    def __getitem__(self, key):
        """
        Get the value of a state option.

        :param str key: Name of the state option.
        :return:        Value of the state option.
        """

        o = self._get_option_desc(key)

        if o.name not in self._options:
            # Special handling for Boolean switches
            if o.one_type() is bool:
                return o.default

            # Special handling for options with default values
            if o.has_default_value:
                return o.default

        return self._options[o.name]

    def __ior__(self, boolean_switches):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, you can enable a collection of Boolean switches at the same
        time by doing the following:

        >>> state.options |= {sim_options.SYMBOLIC, sim_options.ABSTRACT_MEMORY}

        :param set boolean_switches:    A collection of Boolean switches to enable.
        :return:                        self
        """

        for name in boolean_switches:
            self[name] = True
        return self

    def __isub__(self, boolean_switches):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, you can disable a collection of Boolean switches at the same
        time by doing the following:

        >>> state.options -= {sim_options.SYMBOLIC, sim_options.ABSTRACT_MEMORY}

        :param set boolean_switches:    A collection of Boolean switches to disable.
        :return:                        self
        """

        for name in boolean_switches:
            self[name] = False
        return self

    def __sub__(self, boolean_switches):
        """
        [COMPATIBILITY]
        You may disable a collection of Boolean switches by doing:

        >>> state.options = state.options - {sim_options.SYMBOLIC}

        :param set boolean_switches:    A collection of Boolean switches to disable.
        :return:                        A new SimStateOptions instance.
        :rtype:                         SimStateOptions
        """

        ops = SimStateOptions(self)
        for name in boolean_switches:
            ops[name] = False
        return ops

    def __getattr__(self, key):
        if key in { 'OPTIONS', '_options' }:
            return self.__getattribute__(key)
        return self[key]

    def __setattr__(self, key, value):
        if key in { 'OPTIONS', '_options' }:
            super(SimStateOptions, self).__setattr__(key, value)
            return
        self[key] = value

    def __getstate__(self):
        return {
            "_options": self._options,
        }

    def __setstate__(self, state):
        self._options = state["_options"]

    def add(self, boolean_switch):
        """
        [COMPATIBILITY]
        Enable a Boolean switch.

        :param str boolean_switch:  Name of the Boolean switch.
        :return:                    None
        """

        self[boolean_switch] = True

    def update(self, boolean_switches):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, you can enable a collection of Boolean switches at the same
        time by doing the following:

        >>> state.options.update({sim_options.SYMBOLIC, sim_options.ABSTRACT_MEMORY})

        or

        >>> state.options.update(sim_options.unicorn)

        :param set boolean_switches:    A collection of Boolean switches to enable.
        :return:                        None
        """

        for name in boolean_switches:
            self[name] = True

    def remove(self, name):
        """
        Drop a state option if it exists, or raise a KeyError if the state option is not set.

        [COMPATIBILITY]
        Remove a Boolean switch.

        :param str name:    Name of the state option.
        :return:            NNone
        """

        del self._options[name]

    def discard(self, name):
        """
        Drop a state option if it exists, or silently return if the state option is not set.

        [COMPATIBILITY]
        Disable a Boolean switch.

        :param str name:  Name of the Boolean switch.
        :return:          None
        """

        if name in self._options:
            del self._options[name]

    def difference(self, boolean_switches):
        """
        [COMPATIBILITY]
        Make a copy of the current instance, and then discard all options that are in boolean_switches.

        :param set boolean_switches:    A collection of Boolean switches to disable.
        :return:                        A new SimStateOptions instance.
        """

        ops = SimStateOptions(self)
        for key in boolean_switches:
            ops.discard(key)
        return ops

    def copy(self):
        """
        Get a copy of the current SimStateOptions instance.

        :return:    A new SimStateOptions instance.
        :rtype:     SimStateOptions
        """

        return SimStateOptions(self)

    def tally(self, exclude_false=True, description=False):
        """
        Return a string representation of all state options.

        :param bool exclude_false:  Whether to exclude Boolean switches that are disabled.
        :param bool description:    Whether to display the description of each option.
        :return:                    A string representation.
        :rtype:                     str
        """

        total = [ ]

        for o in sorted(self.OPTIONS.values(), key=lambda x: x.name):
            try:
                value = self[o.name]
            except SimStateOptionsError:
                value = "<Unset>"

            if exclude_false and o.one_type() is bool and value is False:
                # Skip Boolean switches that are False
                continue

            s = "{option}: {value}".format(option=o.name, value=value)
            if description:
                s += " | {description}".format(description=o.description)

            total.append(s)

        return "\n".join(total)

    @classmethod
    def register_option(cls, name, types, default=None, description=None):
        """
        Register a state option.

        :param str name:        Name of the state option.
        :param types:           A collection of allowed types of this state option.
        :param default:         The default value of this state option.
        :param str description: The description of this state option.
        :return:                None
        """

        if name in cls.OPTIONS:
            raise SimStateOptionsError("A state option with the same name has been registered.")

        if isinstance(types, type):
            types = { types }

        o = StateOption(name, types, default=default, description=description)
        cls.OPTIONS[name] = o

    @classmethod
    def register_bool_option(cls, name, description=None):
        """
        Register a Boolean switch as state option.
        This is equivalent to cls.register_option(name, set([bool]), description=description)

        :param str name:        Name of the state option.
        :param str description: The description of this state option.
        :return:                None
        """

        cls.register_option(name, { bool }, default=False, description=description)
<commit_msg>SimStateOptions: Do not raise exceptions upon calling dir(). (#984)<commit_after>
from .errors import SimStateOptionsError

_NO_DEFAULT_VALUE = "_NO_DEFAULT_VALUE"  # please god don't use this value as the default value of your state option

class StateOption(object):
    """
    Describes a state option.
    """

    __slots__ = [ 'name', 'types', 'default', 'description' ]

    def __init__(self, name, types, default=_NO_DEFAULT_VALUE, description=None):
        self.name = name
        self.types = tuple(types)
        self.default = default
        self.description = description

        # Sanity check
        if not isinstance(self.default, tuple(self.types)):
            raise SimStateOptionsError("The type of the default value does not match the expected types of this state "
                                       "option.")

    @property
    def has_default_value(self):
        return self.default != _NO_DEFAULT_VALUE

    def one_type(self):
        if len(self.types) == 1:
            return next(iter(self.types))

        return None

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return isinstance(other, StateOption) \
               and self.name == other.name \
               and self.types == other.types

    def __repr__(self):
        if self.description is not None:
            desc = ": %s" % self.description
        else:
            desc = ""
        if self.one_type() is not None:
            types = self.one_type().__name__
        else:
            types = ",".join(t.__name__ for t in self.types)

        return "<O %s[%s]%s>" % (self.name, types, desc)

    def __getstate__(self):
        return {
            "name": self.name,
            "types": self.types,
            "default": self.default,
            "description": self.description,
        }

    def __setstate__(self, state):
        self.name = state["name"]
        self.types = state["types"]
        self.default = state["default"]
        self.description = state["description"]

class SimStateOptions(object):
    """
    A per-state manager of state options. An option can be either a key-valued entry or a Boolean switch (which can be
    seen as a key-valued entry whose value can only be either True or False).
    """

    OPTIONS = { }

    def __init__(self, thing):
        """
        :param thing:    Either a set of Boolean switches to enable, or an existing SimStateOptions instance.
        """

        self._options = { }
        if thing is None:
            pass
        elif isinstance(thing, (set, list)):
            boolean_switches = thing
            for name in boolean_switches:
                self[name] = True
        elif isinstance(thing, SimStateOptions):
            ops = thing
            self._options = ops._options.copy()
        else:
            raise SimStateOptionsError("Unsupported constructor argument type '%s'." % type(thing))

    def _get_option_desc(self, key):
        """
        Get the option descriptor from self.OPTIONS.

        :param str key: Name of the state option.
        :return:        The option descriptor.
        :rtype:         StateOption
        """

        if key not in self.OPTIONS:
            raise SimStateOptionsError("The state option '%s' does not exist." % key)

        return self.OPTIONS[key]

    def __repr__(self):
        s = "<SimStateOptions>"
        return s

    def __contains__(self, key):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, __contains__() only supports testing the value of a Boolean
        switch.

        E.g., in the old days:
        >>> sim_options.SYMBOLIC in state.options
        False

        nowadays:
        >>> sim_options.SYMBOLIC in state.options
        False

        But you cannot use it to test the value of a non-existent option, or the value of a key-valued entry that is not
        linked to a Boolean value.

        >>> "symbolic_ip_max_targets" in state.options
        SimStateOptionsError('"symbolic_ip_max_targets" is not a Boolean switch.')

        :param str key: Name of the Boolean switch.
        :return:        True if the switch is on (the option is switched on), False otherwise.
        :rtype:         bool
        """

        o = self._get_option_desc(key)

        if o.one_type() is not bool:
            raise SimStateOptionsError("The state option '%s' is not a Boolean switch." % key)

        return key in self._options and self._options[key] is True

    def __setitem__(self, key, value):
        """
        Set the value of a state option.

        :param str key:     Name of the state option.
        :param str value:   The value of the state option. Must be of the same type as registered.
        :return:            None
        """

        o = self._get_option_desc(key)

        if type(value) not in o.types:
            raise SimStateOptionsError("The value '%s' does not have an acceptable type for state option '%s'. "
                                       "Accepted types are: %s." %
                                       (value, key, str(o.types))
                                       )

        self._options[o.name] = value

    def __getitem__(self, key):
        """
        Get the value of a state option.

        :param str key: Name of the state option.
        :return:        Value of the state option.
        """

        o = self._get_option_desc(key)

        if o.name not in self._options:
            # Special handling for Boolean switches
            if o.one_type() is bool:
                return o.default

            # Special handling for options with default values
            if o.has_default_value:
                return o.default

        return self._options[o.name]

    def __ior__(self, boolean_switches):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, you can enable a collection of Boolean switches at the same
        time by doing the following:

        >>> state.options |= {sim_options.SYMBOLIC, sim_options.ABSTRACT_MEMORY}

        :param set boolean_switches:    A collection of Boolean switches to enable.
        :return:                        self
        """

        for name in boolean_switches:
            self[name] = True
        return self

    def __isub__(self, boolean_switches):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, you can disable a collection of Boolean switches at the same
        time by doing the following:

        >>> state.options -= {sim_options.SYMBOLIC, sim_options.ABSTRACT_MEMORY}

        :param set boolean_switches:    A collection of Boolean switches to disable.
        :return:                        self
        """

        for name in boolean_switches:
            self[name] = False
        return self

    def __sub__(self, boolean_switches):
        """
        [COMPATIBILITY]
        You may disable a collection of Boolean switches by doing:

        >>> state.options = state.options - {sim_options.SYMBOLIC}

        :param set boolean_switches:    A collection of Boolean switches to disable.
        :return:                        A new SimStateOptions instance.
        :rtype:                         SimStateOptions
        """

        ops = SimStateOptions(self)
        for name in boolean_switches:
            ops[name] = False
        return ops

    def __getattr__(self, key):
        if key in { 'OPTIONS', '_options' }:
            return self.__getattribute__(key)
        if key.startswith("__") and key.endswith("__"):
            return self.__getattribute__(key)
        return self[key]

    def __setattr__(self, key, value):
        if key in { 'OPTIONS', '_options' }:
            super(SimStateOptions, self).__setattr__(key, value)
            return
        self[key] = value

    def __getstate__(self):
        return {
            "_options": self._options,
        }

    def __setstate__(self, state):
        self._options = state["_options"]

    def add(self, boolean_switch):
        """
        [COMPATIBILITY]
        Enable a Boolean switch.

        :param str boolean_switch:  Name of the Boolean switch.
        :return:                    None
        """

        self[boolean_switch] = True

    def update(self, boolean_switches):
        """
        [COMPATIBILITY]
        In order to be compatible with the old interface, you can enable a collection of Boolean switches at the same
        time by doing the following:

        >>> state.options.update({sim_options.SYMBOLIC, sim_options.ABSTRACT_MEMORY})

        or

        >>> state.options.update(sim_options.unicorn)

        :param set boolean_switches:    A collection of Boolean switches to enable.
        :return:                        None
        """

        for name in boolean_switches:
            self[name] = True

    def remove(self, name):
        """
        Drop a state option if it exists, or raise a KeyError if the state option is not set.

        [COMPATIBILITY]
        Remove a Boolean switch.

        :param str name:    Name of the state option.
        :return:            NNone
        """

        del self._options[name]

    def discard(self, name):
        """
        Drop a state option if it exists, or silently return if the state option is not set.

        [COMPATIBILITY]
        Disable a Boolean switch.

        :param str name:  Name of the Boolean switch.
        :return:          None
        """

        if name in self._options:
            del self._options[name]

    def difference(self, boolean_switches):
        """
        [COMPATIBILITY]
        Make a copy of the current instance, and then discard all options that are in boolean_switches.

        :param set boolean_switches:    A collection of Boolean switches to disable.
        :return:                        A new SimStateOptions instance.
        """

        ops = SimStateOptions(self)
        for key in boolean_switches:
            ops.discard(key)
        return ops

    def copy(self):
        """
        Get a copy of the current SimStateOptions instance.

        :return:    A new SimStateOptions instance.
        :rtype:     SimStateOptions
        """

        return SimStateOptions(self)

    def tally(self, exclude_false=True, description=False):
        """
        Return a string representation of all state options.

        :param bool exclude_false:  Whether to exclude Boolean switches that are disabled.
        :param bool description:    Whether to display the description of each option.
        :return:                    A string representation.
        :rtype:                     str
        """

        total = [ ]

        for o in sorted(self.OPTIONS.values(), key=lambda x: x.name):
            try:
                value = self[o.name]
            except SimStateOptionsError:
                value = "<Unset>"

            if exclude_false and o.one_type() is bool and value is False:
                # Skip Boolean switches that are False
                continue

            s = "{option}: {value}".format(option=o.name, value=value)
            if description:
                s += " | {description}".format(description=o.description)

            total.append(s)

        return "\n".join(total)

    @classmethod
    def register_option(cls, name, types, default=None, description=None):
        """
        Register a state option.

        :param str name:        Name of the state option.
        :param types:           A collection of allowed types of this state option.
        :param default:         The default value of this state option.
        :param str description: The description of this state option.
        :return:                None
        """

        if name in cls.OPTIONS:
            raise SimStateOptionsError("A state option with the same name has been registered.")

        if isinstance(types, type):
            types = { types }

        o = StateOption(name, types, default=default, description=description)
        cls.OPTIONS[name] = o

    @classmethod
    def register_bool_option(cls, name, description=None):
        """
        Register a Boolean switch as state option.
        This is equivalent to cls.register_option(name, set([bool]), description=description)

        :param str name:        Name of the state option.
        :param str description: The description of this state option.
        :return:                None
        """

        cls.register_option(name, { bool }, default=False, description=description)
<|endoftext|>