<commit_before># ----------------------------------------------------------------------
# Copyright (c) 2014 <NAME>.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# ----------------------------------------------------------------------

# ========================== DESIGN NOTES ==============================
#
# The EMA hardware is quite smart. It does many things on its own:
# if any of its various thresholds are reached it opens or closes relays
# Threshold caluclations are done inside the EMA.
# But there may be other actions that could be triggered
# when the roof or aux relay or change state, like sending an SMS.
#
# Also, in my current setup, the battery voltage should be carefully 
# controled. If volage falls below a threshold, we should start 
# switching off all devices except EMAitself.
#
# This module is the real added value of this EMA Python server 
# to this excellent hardware.
#
# It allows you to trigger scripts that do interesting things like
# sendimg SMS or switching off its own computer (a Raspberry Pi)
#
# Scripts can be written in any language you like, of course. This
# project includes scripts to send SMS using the python-gammu 
# binding to gammu project.
#
# I have preferred to trigger arbitrary scripts rather than a tight
# integration to this EMA server. Loosely couple modules evolve better 
# over time.
#
# Scripts are forked in background and can be triggered each time 
# the event takes places or just once. There will be only one script 
# process running. If a recurring event takes place and the script is 
# still active, the new script is not launched.
#
# Notifier Class responsibilities:
# 1)  capture Voltage Low , Roff Relay on/off  
# and Aux Relay off/on events
# 2) Hold Script objects to run
# 3) Run them when the event comes.
#
# Script Class responsibilities
# 1) Hold a path to the external script file
# 2) Knows what its execution mode is (run once, run many times)
# 3) Forks the script in background and does not wait for its completion
#
# We use an exception to signal notifier about an porcess already being
# executed. I think this is cleaner than carrying return information 
# across two levels.
#
# ======================================================================


import logging
import subprocess
import os

log = logging.getLogger('notifier')

def setLogLevel(level):
	log.setLevel(level)



class ExecutedScript(Exception):
	'''Signals a script has executed'''
	def __init__(self, name, *args):
		self.name = name
		self.args = args

	def  __str__(self):
		'''Prints useful information'''
		tmp = ''
		for arg in self.args:
			tmp += ' ' + arg
		return self.name + ' ' + tmp



class Script(object):
	'''Notifier creates Script wrapper objects, representing
	scripts to be launched'''

	# modes as constants
	NEVER = 0
	ONCE  = 1
	MANY  = 2

	# mappping from strings to numbers
	MODES = { 'Never' : NEVER, 'Once' : ONCE, 'Many' : MANY }

	def __init__(self,  cfg):
		self.mode  = Script.MODES[cfg[1]]
		self.path  = cfg[0]
		self.name  = os.path.basename(self.path)
		self.child = None
		self.executed  = False


	def runOnce(self, *args):
		'''run only once in the whole server lifetime'''
		# skip if already run
		# otherwise, spawn it
		if self.executed:
			return False
		# If not running, spawn it
		try:
			self.child = subprocess.Popen((self.path,) + args)
		except (OSError, ValueError) as e:
			log.error(e)
		else:
			self.executed = True
			raise ExecutedScript(self.name, *args)


	def runMany(self, *args):
		'''Run one more time, if previous run completed'''
		# Check existing script already running
		# If running we don't go any further and return.
		# otherwise, spawn it.
		if self.child:
			self.child.poll()
			if self.child.returncode is None:
				log.warning("script %s has not finished. Can't launch it again", self.name)
				return
		try:
			self.child = subprocess.Popen((self.path,) + args)
		except (OSError, ValueError) as e:
			log.error(e)<commit_msg>added more output to notifier runOnce/runMany OSExceptions<commit_after># ----------------------------------------------------------------------
# Copyright (c) 2014 <NAME>.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# ----------------------------------------------------------------------

# ========================== DESIGN NOTES ==============================
#
# The EMA hardware is quite smart. It does many things on its own:
# if any of its various thresholds are reached it opens or closes relays
# Threshold caluclations are done inside the EMA.
# But there may be other actions that could be triggered
# when the roof or aux relay or change state, like sending an SMS.
#
# Also, in my current setup, the battery voltage should be carefully 
# controled. If volage falls below a threshold, we should start 
# switching off all devices except EMAitself.
#
# This module is the real added value of this EMA Python server 
# to this excellent hardware.
#
# It allows you to trigger scripts that do interesting things like
# sendimg SMS or switching off its own computer (a Raspberry Pi)
#
# Scripts can be written in any language you like, of course. This
# project includes scripts to send SMS using the python-gammu 
# binding to gammu project.
#
# I have preferred to trigger arbitrary scripts rather than a tight
# integration to this EMA server. Loosely couple modules evolve better 
# over time.
#
# Scripts are forked in background and can be triggered each time 
# the event takes places or just once. There will be only one script 
# process running. If a recurring event takes place and the script is 
# still active, the new script is not launched.
#
# Notifier Class responsibilities:
# 1)  capture Voltage Low , Roff Relay on/off  
# and Aux Relay off/on events
# 2) Hold Script objects to run
# 3) Run them when the event comes.
#
# Script Class responsibilities
# 1) Hold a path to the external script file
# 2) Knows what its execution mode is (run once, run many times)
# 3) Forks the script in background and does not wait for its completion
#
# We use an exception to signal notifier about an porcess already being
# executed. I think this is cleaner than carrying return information 
# across two levels.
#
# ======================================================================


import logging
import subprocess
import os

log = logging.getLogger('notifier')

def setLogLevel(level):
	log.setLevel(level)



class ExecutedScript(Exception):
	'''Signals a script has executed'''
	def __init__(self, name, *args):
		self.name = name
		self.args = args

	def  __str__(self):
		'''Prints useful information'''
		tmp = ''
		for arg in self.args:
			tmp += ' ' + arg
		return self.name + ' ' + tmp



class Script(object):
	'''Notifier creates Script wrapper objects, representing
	scripts to be launched'''

	# modes as constants
	NEVER = 0
	ONCE  = 1
	MANY  = 2

	# mappping from strings to numbers
	MODES = { 'Never' : NEVER, 'Once' : ONCE, 'Many' : MANY }

	def __init__(self,  cfg):
		self.mode  = Script.MODES[cfg[1]]
		self.path  = cfg[0]
		self.name  = os.path.basename(self.path)
		self.child = None
		self.executed  = False


	def runOnce(self, *args):
		'''run only once in the whole server lifetime'''
		# skip if already run
		# otherwise, spawn it
		if self.executed:
			return False
		# If not running, spawn it
		try:
			self.child = subprocess.Popen((self.path,) + args)
		except (OSError, ValueError) as e:
			log.error("runOnce(%s): %s", self.path, e)
		else:
			self.executed = True
			raise ExecutedScript(self.name, *args)


	def runMany(self, *args):
		'''Run one more time, if previous run completed'''
		# Check existing script already running
		# If running we don't go any further and return.
		# otherwise, spawn it.
		if self.child:
			self.child.poll()
			if self.child.returncode is None:
				log.warning("script %s has not finished. Can't launch it again", self.name)
				return
		try:
			self.child = subprocess.Popen((self.path,) + args)
		except (OSError, ValueError) as e:
			log.error("runMany(%s): %s", self.path, e)
{'fraction_non_alphanumeric': 0.12600376722514128, 'fraction_numerical': 0.0029741251115296915, 'mean_word_length': 3.4835555555555557, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7412084', 'n_tokens_mistral': 2952, 'n_tokens_neox': 2704, 'n_words': 1327}