Skip to content

Commit ee1de6a

Browse files
author
Nathan Lee
committed
Large additions to comments/docstrings. Reorganized location of opt parsing logic
1 parent 472595f commit ee1de6a

5 files changed

Lines changed: 187 additions & 60 deletions

File tree

pyrunner/cli.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def main():
3131
else:
3232
try:
3333
pyrunner = PyRunner()
34-
pyrunner.parse_args(sys.argv)
34+
pyrunner.parse_args()
3535
exit_status = pyrunner.execute()
3636
except ValueError as value_error:
3737
exit_status = 2
@@ -75,7 +75,7 @@ def main():
7575
def setup():
7676
print('\nINITIATING NEW PROJECT SETUP\n')
7777
app_name = input('Project Name (spaces will be removed): ')
78-
app_name = app_name.replace(' ', '_')
78+
app_name = app_name.replace(' ', '_').lower()
7979

8080
if not app_name.strip():
8181
raise ValueError('Please provide project name')
@@ -89,7 +89,7 @@ def setup():
8989
elif len(app_path) > 1 and app_path[-1] == '/':
9090
app_path = app_path[:-1]
9191

92-
app_root = '{}/{}'.format(app_path, app_name.lower())
92+
app_root = '{}/{}'.format(app_path, app_name)
9393

9494
if os.path.isdir(app_root) or os.path.exists(app_root):
9595
raise OSError('{} already exists!'.format(app_root))
@@ -101,8 +101,8 @@ def setup():
101101
print('Invalid Input')
102102

103103
print('\nSUMMARY:\n')
104-
print('Project Name: {}'.format(app_name.lower()))
105-
print('Project Path: {}/{}'.format(app_path, app_name.lower()))
104+
print('Project Name: {}'.format(app_name))
105+
print('Project Path: {}/{}'.format(app_path, app_name))
106106
print('Execution Mode: {}'.format(app_mode))
107107

108108
input('\nPress ENTER if this is correct or Ctrl + C to Abort...\n')
@@ -144,17 +144,16 @@ def setup():
144144
app_profile.write('if [ ! -e ${APP_TEMP_DIR} ]; then mkdir ${APP_TEMP_DIR}; fi\n')
145145
app_profile.write('if [ ! -e ${APP_DATA_DIR} ]; then mkdir ${APP_DATA_DIR}; fi\n')
146146

147-
print('Creating Blank Process List File: {}/config/{}.lst'.format(app_root, app_name.lower()))
148-
with open('{}/config/{}.lst'.format(app_root, app_name.lower()), 'w') as lst_file:
147+
print('Creating Blank Process List File: {}/config/{}.lst'.format(app_root, app_name))
148+
with open('{}/config/{}.lst'.format(app_root, app_name), 'w') as lst_file:
149149
if app_mode == 'SHELL': lst_file.write('{}\n\n'.format(constants.HEADER_SHELL))
150150
if app_mode == 'PYTHON': lst_file.write('{}\n\n'.format(constants.HEADER_PYTHON))
151151

152-
print('Creating Main Execution Script: {}/main.sh'.format(app_root))
153-
with open('{}/{}.sh'.format(app_root, app_name), 'w') as main_file:
154-
main_file.write('#!/bin/sh\n\n')
155-
main_file.write('pyrunner -c {}/config/app_profile -l {}/config/{}.lst "$@"\n'.format('$(dirname ${0})', '$(dirname ${0})', app_name.lower()))
152+
print('Creating Driver Program: {}/{}.py'.format(app_root, app_name))
153+
with open('{}/{}.py'.format(app_root, app_name), 'w') as main_file:
154+
main_file.write(constants.DRIVER_TEMPLATE)
156155

157-
os.chmod('{}/main.sh'.format(app_root), 0o744)
156+
os.chmod('{}/{}.py'.format(app_root, app_name), 0o744)
158157

159158
print('\nComplete!\n')
160159

pyrunner/core/config.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,21 @@ def __next__(self):
104104
return self._iter_keys.popleft()
105105

106106
def __getitem__(self, key):
107+
"""
108+
Emulates dictionry key get action.
109+
110+
Emulates the act of getting a key in a dictionary structure. Most importantly,
111+
keys in the Config object can receive it's value from one of multiple sources.
112+
113+
The currently supported sources are: manually assigned values, environment variable
114+
values, and hard-coded default values, with priority matching this order.
115+
116+
Args:
117+
key (str): The key name for which to return the value for.
118+
119+
Raises:
120+
KeyError: Given key is not valid for the Config object.
121+
"""
107122
detl = self._attr.get(key)
108123
if not detl:
109124
raise KeyError('Config object does not store key: {}'.format(key))
@@ -124,6 +139,20 @@ def __getitem__(self, key):
124139
return None
125140

126141
def __setitem__(self, key, value):
142+
"""
143+
Emulates dictionry key set action.
144+
145+
Emulates the act of setting a key in a dictionary structure. Additionally,
146+
key names and variable values/types are validated upon set. If invalid key
147+
name or invalid value type, an exception will be thrown.
148+
149+
Args:
150+
key (str): The key name for which to assign the incoming value to.
151+
value (mixed): The value to assign key with.
152+
153+
Raises:
154+
KeyError: Given key is not valid for the Config object.
155+
"""
127156
if key not in self._attr:
128157
raise KeyError('Config object does not store key: {}'.format(key))
129158
else:
@@ -140,26 +169,70 @@ def __setitem__(self, key, value):
140169
self._attr[key]['value'] = self._attr[key]['type'](value)
141170

142171
def __delitem__(self, key):
172+
"""
173+
Emulates dictionry key delete action.
174+
175+
Emulates the act of deleting a key in a dictionary structure.
176+
177+
Args:
178+
key (str): The key name for which to delete the current.
179+
180+
Raises:
181+
KeyError: Given key is not valid for the Config object.
182+
"""
143183
if key not in self._attr:
144184
raise KeyError('Config object does not store key: {}'.format(key))
145185
else:
146186
self._attr[key]['value'] = None
147187

148188
def __contains__(self, key):
189+
"""
190+
Determines if key is stored in the Config object.
191+
192+
Args:
193+
key (str): The key name for which to check for existence.
194+
195+
Returns:
196+
Boolean True/False indicating if key exists in Config.
197+
"""
149198
return key in self._attr
150199

151200
def items(self, only_preserve=True):
201+
"""
202+
Converts the Config object into a simple dictionary containing only simple key:value pairs.
203+
204+
Converts the internal representation of Config object and returns a simple dictionary
205+
containing each key and it's highest priority value currently assigned. Optionally allows
206+
for selection of only key:value pairs marked for preservation, rather than returning
207+
all pairs. By default, only returns preserved pairs.
208+
209+
Preserved pairs aare those designated for persisting into longer term storage in case
210+
of job failure.
211+
212+
Args:
213+
only_preserve (bool, optional): Indicates if only keys marked for preservation
214+
should be returned. Default: True
215+
216+
Returns:
217+
Dictionary containing keys assigned with their highest priority value in Config object.
218+
"""
152219
return { k:v['value'] for k,v in self._attr.items() if v['preserve'] }
153220

154221
@property
155222
def ctllog_file(self):
223+
"""
224+
Path/filename of job's .ctllog file.
225+
"""
156226
if not self['temp_dir'] or not self['app_name']:
157227
return None
158228
else:
159229
return '{}/{}.ctllog'.format(self['temp_dir'], self['app_name'])
160230

161231
@property
162232
def ctx_file(self):
233+
"""
234+
Path/filename of job's .ctx file.
235+
"""
163236
if not self['temp_dir'] or not self['app_name']:
164237
return None
165238
else:
@@ -173,6 +246,9 @@ def source_config_file(self, config_file):
173246
prior to executing app/job instance. Only variables beginning with
174247
'APP_' will be preserved/exported, while other vars will be lost.
175248
249+
Args:
250+
config_file (str): The path to the application profile/config to source.
251+
176252
Raises:
177253
FileNotFoundError: Could not find specified file or is not a file.
178254
"""
@@ -192,6 +268,9 @@ def source_config_file(self, config_file):
192268
proc.communicate()
193269

194270
def print_attributes(self):
271+
"""
272+
Prints out the Contig object's key:value pairs, using the highest
273+
priority source as value.
274+
"""
195275
for k in self._attr:
196-
print('{} : {}'.format(k, self._attr[k]))
197-
return
276+
print('{} : {}'.format(k, self._attr[k]))

pyrunner/core/constants.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,31 @@
3434
HEADER_SHELL = '#{}\n#ID|PARENT_IDS|MAX_ATTEMPTS|RETRY_WAIT_TIME|PROCESS_NAME|SHELL_COMMAND|LOGFILE'.format(MODE_SHELL)
3535
HEADER_PYTHON = '#{}\n#ID|PARENT_IDS|MAX_ATTEMPTS|RETRY_WAIT_TIME|PROCESS_NAME|MODULE_NAME|WORKER_NAME|ARGUMENTS|LOGFILE'.format(MODE_PYTHON)
3636

37-
ROOT_NODE_NAME = 'PyRunnerRootNode'
37+
ROOT_NODE_NAME = 'PyRunnerRootNode'
38+
39+
DRIVER_TEMPLATE = """
40+
#!/usr/bin/env python3
41+
42+
import sys
43+
from pyrunner import PyRunner
44+
from pathlib import Path
45+
46+
def main():
47+
pr = PyRunner()
48+
49+
# Assign default config and .lst file
50+
pr.source_config_file(Path('{}/app_profile'.format(pr.config['config_dir'])))
51+
pr.load_from_file(Path('{}/{}.lst'.format(pr.config['config_dir'], pr.config['app_name'])))
52+
53+
# Parse command line args
54+
pr.parse_args()
55+
56+
# Initiate job
57+
exit_status = pr.execute()
58+
59+
# Ensure job exit status is passed to the caller
60+
sys.exit(exit_status)
61+
62+
if __name__ == '__main__':
63+
main()
64+
"""

0 commit comments

Comments
 (0)