Skip to content

Commit 9bf9386

Browse files
committed
Merge pull request #520 from cchampet/fix_samDoExistIfError
* sam ls: fixed browses with filename and folders as inputs. * sam do: * exit the process if there is an error in the command line * added 'default' constant to indicate the default value of Choice parameter * no ambiguity when calling a plugin with the exact id * fix print of the command line help even if there are arguments to process * Fist tests of sam command line.
2 parents 29bddae + 6f72afd commit 9bf9386

File tree

10 files changed

+206
-48
lines changed

10 files changed

+206
-48
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,11 @@ addons:
8080
- swig
8181
- libboost1.55-all-dev
8282
- python-dev
83+
- python-imaging
8384
- python-numpy
85+
- python-nose
8486
- python3-dev
87+
- python3-setuptools
8588
- python3-nose
8689
- libfreetype6-dev
8790
- libbz2-dev
@@ -97,8 +100,6 @@ addons:
97100
- libglew-dev
98101
- libgraphviz-dev
99102
- graphviz-dev
100-
- python-nose
101-
- python-imaging
102103
- libopenjpeg-dev
103104
- libturbojpeg
104105
- libxmu-dev

applications/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,16 @@ add_subdirectory(sam)
33

44
# scripts
55
add_subdirectory(script)
6+
7+
# Edit python scripts if the host is built with python3
8+
if(${TUTTLE_PYTHON_VERSION} VERSION_GREATER "3")
9+
set(BASH_PYTHON2 "#!/usr/bin/env python")
10+
set(BASH_PYTHON3 "#!/usr/bin/env python3")
11+
12+
file(GLOB_RECURSE PYTHON_APPS sam/*.py script/*.py)
13+
foreach(PYTHON_APP ${PYTHON_APPS})
14+
file(READ "${PYTHON_APP}" APP_CONTENT_PYTHON2)
15+
string(REGEX REPLACE ${BASH_PYTHON2} ${BASH_PYTHON3} APP_CONTENT_PYTHON3 "${APP_CONTENT_PYTHON2}" )
16+
file(WRITE "${PYTHON_APP}" "${APP_CONTENT_PYTHON3}")
17+
endforeach()
18+
endif()

applications/sam/common/samDoUtils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env python
21
# coding: utf-8
32

43
import os
@@ -232,7 +231,12 @@ def getPluginName(self, logger):
232231
# get plugins name which match with the given id
233232
pluginNames = []
234233
for pluginName in pluginsMap:
235-
if self._pluginId in pluginName:
234+
# if the given id exactly matches the plugin id
235+
# plugin id = plugin name without its group
236+
if self._pluginId == pluginName[pluginName.rfind('.') + 1:]:
237+
return pluginName
238+
# else if the given id is contains in the plugin name
239+
elif self._pluginId in pluginName:
236240
pluginNames.append(pluginName)
237241
# one plugin matches
238242
if len(pluginNames) == 1:

applications/sam/common/samUtils.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env python
21
# coding: utf-8
32

43
import os
@@ -8,7 +7,7 @@
87
# python module to get completions
98
import argcomplete
109
# python module to get colors
11-
from clint.textui import colored
10+
from clint.textui import colored, puts
1211

1312
# parser of sequence
1413
from pySequenceParser import sequenceParser
@@ -81,6 +80,20 @@ def setLogLevel(self, tuttleVerboseLevel):
8180
else:
8281
handler.setLevel(logging.WARNING)
8382

83+
def _displayCommandLineHelp(self, parser):
84+
"""
85+
Display sam command line help.
86+
"""
87+
if not self.command:
88+
raise NotImplementedError
89+
90+
subparser = getSubParser(parser, self.command)
91+
# if sam command is called from sam main command line
92+
if subparser is not None:
93+
puts(subparser.format_help())
94+
else:
95+
parser.print_help()
96+
8497

8598
class SamFormatter(logging.Formatter):
8699
"""

applications/sam/sam_do.py

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# coding: utf-8
33
# PYTHON_ARGCOMPLETE_OK
44

5+
import sys
56
import argparse
67
import itertools
78

@@ -130,16 +131,29 @@ def _isCommandLineInvalid(self, inputsToProcess, inputsUnknown):
130131
@param inputsToProcess the 'inputs' arguments of the command line.
131132
@param inputsUnknown the 'inputs' arguments of the command line which are not recognized by argparse.
132133
"""
133-
# check if there is no arguments (or only the help option)
134+
# check if there is no arguments
134135
if len(inputsToProcess) == 0:
135-
if len(inputsUnknown) == 0 or '-h' in inputsUnknown or '--help' in inputsUnknown:
136-
return True
137-
# check if last input is the separator
138-
if inputsToProcess[-1] == '//':
136+
return True
137+
# else check if last input is the separator
138+
elif inputsToProcess[-1] == '//':
139139
self.logger.info('The given inputs to process are invalid: ' + str(inputsToProcess))
140140
return True
141141
return False
142142

143+
def _isCommandLineAskForHelp(self, inputsToProcess, inputsUnknown):
144+
"""
145+
Returns if the given sam do command expects to print its help.
146+
"""
147+
# check if there is no arguments
148+
if len(inputsToProcess) == 0:
149+
if len(inputsUnknown) == 0 or '-h' in inputsUnknown or '--help' in inputsUnknown:
150+
return True
151+
# else check unknow arguments
152+
else:
153+
if len(inputsUnknown) == 1 and ('-h' in inputsUnknown or '--help' in inputsUnknown):
154+
return True
155+
return False
156+
143157
def _setTimeRanges(self, computeOptions, ranges):
144158
"""
145159
Set time ranges of the given compute options.
@@ -300,7 +314,7 @@ def _displayParamHelp(self, param):
300314
with indent(40):
301315
for choiceValue in paramChoiceValues:
302316
puts('{choiceValue!s:50} {label}'.format(
303-
choiceValue=(colored.yellow(choiceValue) if paramChoiceValues.index(choiceValue) == paramDefaultValue else colored.red(choiceValue)),
317+
choiceValue=(colored.yellow(choiceValue + ' (default)') if paramChoiceValues.index(choiceValue) == paramDefaultValue else colored.red(choiceValue)),
304318
label=(paramChoiceLabel[paramChoiceValues.index(choiceValue)] if hasLabel else '')))
305319
else:
306320
puts('{defaultValue!s:9}'.format(
@@ -343,6 +357,9 @@ def _displayNodeHelp(self, nodeFullName, node):
343357
"""
344358
Display help of a specific node in the command line.
345359
"""
360+
if not node:
361+
self.logger.error('Cannot print help of unknown plugin "' + nodeFullName + '".')
362+
exit(1)
346363
# NODE
347364
self._displayTitle('NODE')
348365
with indent(4):
@@ -391,24 +408,14 @@ def _displayNodeHelp(self, nodeFullName, node):
391408
name=colored.green('Output'),
392409
bitdepth=', '.join(bitDepthOutputStr)))
393410

394-
def _displayCommandLineHelp(self, parser):
395-
"""
396-
Display sam-do command line help.
397-
"""
398-
subparser = samUtils.getSubParser(parser, 'do')
399-
# if sam-do is called from sam main command line
400-
if subparser is not None:
401-
puts(subparser.format_help())
402-
else:
403-
parser.print_help()
404-
405411
def _getTuttleGraph(self, splitCmdGraph):
406412
"""
407413
Return the tuple (tuttle graph, its nodes) which corresponds to the given split command.
408414
"""
409415
graph = tuttle.Graph()
410416
nodes = []
411417
connections = []
418+
error = 0
412419

413420
for splitCmdNode in splitCmdGraph.getNodes():
414421
# Create a tuttle node
@@ -421,9 +428,9 @@ def _getTuttleGraph(self, splitCmdGraph):
421428
# Plugin not found
422429
if not nodeFullName:
423430
nodeFullName = splitCmdNode._pluginId
424-
self.logger.error('Cannot create node "' + nodeFullName + '": the node will be skipped from the command line.')
431+
self.logger.error('Cannot create node "' + nodeFullName + '".')
425432
self.logger.debug(e)
426-
continue
433+
error = 1
427434
# sam-do node --help
428435
if splitCmdNode.hasHelp():
429436
self._displayNodeHelp(nodeFullName, node)
@@ -447,22 +454,26 @@ def _getTuttleGraph(self, splitCmdGraph):
447454
graph.connect(nodes[connections.index(argValue)], node.getAttribute(argName))
448455
except Exception as e:
449456
# Cannot connect attribute of node
450-
self.logger.warning('Cannot connect attribute "'
451-
+ argName + '" of node "' + nodeFullName
452-
+ '" to id "' + argValue
453-
+ '": the connection will be skipped from the command line.')
457+
self.logger.warning('Cannot connect attribute "'
458+
+ argName + '" of node "' + nodeFullName
459+
+ '" to id "' + argValue)
454460
self.logger.debug(e)
461+
error = 1
455462
else:
456463
# Cannot set param of node
457464
self.logger.warning('Cannot set '
458465
+ (('parameter "' + argName + '" of ') if argName else '')
459-
+ 'node "' + nodeFullName + '" '
460-
+ (('to value "' + argValue + '"') if argValue else '')
461-
+ ': the parameter will be skipped from the command line.')
466+
+ 'node "' + nodeFullName + '" '
467+
+ (('to value "' + argValue + '"') if argValue else ''))
462468
self.logger.debug(e)
469+
error = 1
463470
nodes.append(node)
464471
connections.append(splitCmdNode.getArgument('id')[1])
465472

473+
# exit the process if there is an error in the command line
474+
if error:
475+
exit(error)
476+
466477
# Create in line connections
467478
for i in range(0, len(connections)):
468479
if connections[i] is None and i+1 < len(connections):
@@ -478,6 +489,11 @@ def run(self, parser):
478489
# Parse command-line
479490
args, unknown = parser.parse_known_args()
480491

492+
# sam-do --help
493+
if self._isCommandLineAskForHelp(args.inputs, unknown):
494+
self._displayCommandLineHelp(parser)
495+
exit(0)
496+
481497
# Set sam log level
482498
self.setLogLevel(args.verbose)
483499
# set tuttle host log level
@@ -503,10 +519,10 @@ def run(self, parser):
503519
self._displayFileFormats()
504520
exit(0)
505521

506-
# sam-do --help
522+
# Check sam-do command line
507523
if self._isCommandLineInvalid(args.inputs, unknown):
508524
self._displayCommandLineHelp(parser)
509-
exit(0)
525+
exit(1)
510526

511527
# Add unknown options to the command line to process
512528
args.inputs.extend(unknown)

applications/sam/sam_ls.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ def __init__(self):
3232
self._sequenceExploded = [] # name of Sequences already exploded
3333

3434
def fillParser(self, parser):
35+
def level_type(level):
36+
"""
37+
Check constrains of 'level' argument.
38+
"""
39+
level = int(level)
40+
if level < 0:
41+
raise argparse.ArgumentTypeError("Minimum level is 0")
42+
return level
43+
3544
# Arguments
3645
parser.add_argument('inputs', nargs='*', action='store', help='list of files/sequences/directories to analyse').completer = samUtils.sequenceParserCompleter
3746

@@ -45,7 +54,7 @@ def fillParser(self, parser):
4554
parser.add_argument('-l', '--long-listing', dest='longListing', action='store_true', help='use a long listing format (display in this order: type | permissions | owner | group | last update | minSize | maxSize | totalSize | name)')
4655
parser.add_argument('--format', dest='format', choices=['default', 'nuke', 'rv'], default='default', help='specify formatting of the sequence padding')
4756
parser.add_argument('-R', '--recursive', dest='recursive', action='store_true', help='handle directories and their content recursively')
48-
parser.add_argument('-L', '--level', dest='level', type=int, help='max display depth of the directory tree (without formatting if 0)')
57+
parser.add_argument('-L', '--level', dest='level', type=level_type, help='max display depth of the directory tree (without formatting if 0)')
4958
parser.add_argument('--absolute-path', dest='absolutePath', action='store_true', help='display the absolute path of each object')
5059
parser.add_argument('--relative-path', dest='relativePath', action='store_true', help='display the relative path of each object')
5160
parser.add_argument('--no-color', dest='noColor', action='store_true', default=False, help='display the output without colors (with colors by default)')
@@ -246,27 +255,44 @@ def run(self, parser):
246255
# Parse command-line
247256
args = parser.parse_args()
248257

258+
# sam-ls --absolute-path --relative-path
259+
if args.absolutePath and args.relativePath:
260+
self._displayCommandLineHelp(parser)
261+
exit(1)
262+
249263
# Set sam log level
250264
self.setLogLevel(args.verbose)
251265

252-
inputs = []
253-
# for each input to scan
266+
class InputToBrowse(object):
267+
"""
268+
Represents an input to browse: a path and a list of filters.
269+
"""
270+
def __init__(self, inputPath):
271+
self.inputPath = inputPath
272+
self.filters = []
273+
274+
def addFilter(self, filterToAdd):
275+
self.filters.append(filterToAdd)
276+
277+
# translate user inputs to a list of InputToBrowse
278+
inputsToBrowse = []
254279
for inputPath in args.inputs:
255280
# if the input is a directory, add it and continue
256281
if os.path.isdir(inputPath):
257-
inputs.append(inputPath)
282+
inputsToBrowse.append(InputToBrowse(inputPath))
258283
continue
259284
# else split the input to a path and a filename
260285
subPath = os.path.dirname(inputPath)
261286
if not subPath:
262287
subPath = '.'
263288
filename = os.path.basename(inputPath)
264289
# add the path and the filename as an expression
265-
inputs.append(subPath)
266-
if filename:
267-
args.expression.append(filename)
268-
if not inputs:
269-
inputs.append(os.getcwd())
290+
inputToBrowse = InputToBrowse(subPath)
291+
inputToBrowse.addFilter(filename)
292+
inputsToBrowse.append(inputToBrowse)
293+
# if no user input, will browse in the current working directory
294+
if not inputsToBrowse:
295+
inputsToBrowse.append(InputToBrowse(os.getcwd()))
270296

271297
# sam-ls -a
272298
detectionMethod = sequenceParser.eDetectionDefault
@@ -282,13 +308,15 @@ def run(self, parser):
282308
detectionMethod = detectionMethod | sequenceParser.eDetectionSequenceWithoutHoles
283309

284310
# sam-ls -e
285-
filters = []
286311
for expression in args.expression:
287-
filters.append(expression)
312+
for inputToBrowse in inputsToBrowse:
313+
inputToBrowse.addFilter(expression)
288314

289-
# get list of items for each inputs
290-
for inputPath in inputs:
315+
# for each input to browse, print the finding items
316+
for inputToBrowse in inputsToBrowse:
291317
items = []
318+
inputPath = inputToBrowse.inputPath
319+
filters = inputToBrowse.filters
292320
try:
293321
self.logger.debug('Browse in "' + inputPath + '" with the following filters: ' + str(filters))
294322
items = sequenceParser.browse(inputPath, detectionMethod, filters)
@@ -325,7 +353,7 @@ def run(self, parser):
325353
items += sequenceParser.browse(newBrowsePath, detectionMethod, newFilter)
326354

327355
if not len(items):
328-
self.logger.warning('No items found for input "' + inputPath + '".')
356+
self.logger.warning('No items found for input "' + inputPath + '" with the following filters: ' + str(filters))
329357
else:
330358
self.printItems(items, args, detectionMethod, filters)
331359

0 commit comments

Comments
 (0)