Passed
Push — master ( e96c3d...44474a )
by Ian
06:20
created

build.rsudp.c_custom   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 116
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 56
dl 0
loc 116
rs 10
c 0
b 0
f 0
wmc 14

3 Methods

Rating   Name   Duplication   Size   Complexity  
B Custom.__init__() 0 42 7
A Custom.exec_code() 0 12 3
A Custom.run() 0 17 4
1
import sys, os
2
from rsudp import printM, printW, printE
3
from rsudp.raspberryshake import ConsumerThread
4
5
class Custom(ConsumerThread):
6
	"""
7
	.. versionadded:: 0.4.3
8
9
	.. |lineendings_howto| raw:: html
10
11
		<a href="https://stackoverflow.com/questions/17579553/windows-command-to-convert-unix-line-endings" target="_blank">this stackoverflow question</a>
12
13
	.. |lineendings_wiki| raw:: html
14
15
		<a href="https://en.wikipedia.org/wiki/Newline" target="_blank">here</a>
16
17
	.. role:: json(code)
18
		:language: json
19
20
21
	A consumer class that runs custom code from a python file passed to it.
22
	Please read the disclaimers and warnings at :ref:`customcode` prior to using this module.
23
24
	.. warning::
25
26
		If you are running Windows and have code you want to pass to the :py:func:`exec` function,
27
		Python requires that your newline characters are in the UNIX style (:code:`\\n`),
28
		not the standard Windows style (:code:`\\r\\n`).
29
		To convert, follow the instructions in one of the answers to |lineendings_howto|.
30
		If you're not sure what this means, please read about newline/line ending characters |lineendings_wiki|.
31
		If you are certain that your code file has no Windows newlines, you can set :json:`"win_override"` to true.
32
33
		Read more warnings about this module at :ref:`customcode`.
34
35
	:param queue.Queue q: queue of data and messages sent by :class:`rsudp.c_consumer.Consumer`.
36
	:param codefile: string of the python (.py) file to run, or False if none.
37
	:type codefile: str or bool
38
	:param bool win_ovr: user check to make sure that line ending format is correct (see warning above)
39
40
	"""
41
42
	def __init__(self, q=False, codefile=False, win_ovr=False):
43
		"""
44
		Initializes the custom code execution thread.
45
		"""
46
		super().__init__()
47
		self.sender = 'Custom'
48
		self.alive = True
49
		self.codefile = False
50
		self.win_ovr = win_ovr
51
		if codefile:
52
			if (os.path.exists(os.path.expanduser(codefile))) and (os.path.splitext(codefile)[1]):
53
				self.codefile = os.path.expanduser(codefile)
54
				printM('Custom code file to run: %s' % self.codefile, sender=self.sender)
55
			else:
56
				printW('No python file exists at %s. No custom code will be run during alarms.' % codefile, sender=self.sender)
57
		else:
58
			printW('No custom code file set. No custom code will be run during alarms.', sender=self.sender)
59
60
		if (os.name in 'nt') and (not self.win_ovr):
61
			printE('Using Windows with custom alert code! Your code MUST have UNIX/Mac newline characters!')
62
			printE('Please use a conversion tool like dos2unix to convert line endings', spaces=True)
63
			printE('(https://en.wikipedia.org/wiki/Unix2dos) to make your code file', spaces=True)
64
			printE('readable to the Python interpreter.', spaces=True)
65
			printE('Once you have done that, please set "win_override" to true', spaces=True)
66
			printE('in the settings file.', spaces=True)
67
			printE('(see also footnote [1] on this page: https://docs.python.org/3/library/functions.html#id2)', spaces=True)
68
			printE('THREAD EXITING, please correct and restart!', self.sender, spaces=True)
69
			sys.exit(2)
70
		else:
71
			pass
72
73
74
		if q:
75
			self.queue = q
76
		else:
77
			printE('no queue passed to the consumer thread! We will exit now!',
78
				   self.sender)
79
			sys.stdout.flush()
80
			self.alive = False
81
			sys.exit()
82
83
		printM('Starting.', self.sender)
84
85
	def exec_code(self):
86
		if self.codefile:
87
			# if the user has set a code file
88
			printM('Executing code from file: %s' % self.codefile, sender=self.sender)
89
			try:
90
				# try to execute some code
91
				exec(self.codefile)
92
			except Exception as e:
93
				# do something if it fails
94
				printE('Code execution failed. Error: %s' % e, sender=self.sender, announce=False)
95
		else:
96
			printW('No code to run, codefile variable not set correctly.', sender=self.sender)
97
98
99
	def run(self):
100
		"""
101
		Reads data from the queue and executes self.codefile if it sees an ``ALARM`` message.
102
		Quits if it sees a ``TERM`` message.
103
		"""
104
		while True:
105
			d = self.queue.get()
106
			self.queue.task_done()
107
			if 'TERM' in str(d):
108
				self.alive = False
109
				printM('Exiting.', self.sender)
110
				sys.exit()
111
			elif 'ALARM' in str(d):
112
				printM('Got ALARM message...', sender=self.sender)
113
				self.exec_code()
114
115
		self.alive = False
116