Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

#!/usr/bin/env python3 

 

"""Command-line interface.""" 

 

import sys 

import argparse 

import logging 

 

from . import CLI, VERSION, DESCRIPTION 

from . import common, exceptions, commands 

 

log = logging.getLogger(__name__) 

 

 

def main(args=None, function=None): # pylint: disable=too-many-statements 

"""Process command-line arguments and run the program.""" 

 

# Shared options 

debug = argparse.ArgumentParser(add_help=False) 

debug.add_argument('-V', '--version', action='version', version=VERSION) 

group = debug.add_mutually_exclusive_group() 

group.add_argument('-v', '--verbose', action='count', default=0, 

help="enable verbose logging") 

group.add_argument('-q', '--quiet', action='store_const', const=-1, 

dest='verbose', help="only display errors and prompts") 

project = argparse.ArgumentParser(add_help=False) 

project.add_argument('-r', '--root', metavar='PATH', 

help="root directory of the project") 

depth = argparse.ArgumentParser(add_help=False) 

depth.add_argument('-d', '--depth', type=common.positive_int, 

default=5, metavar="NUM", 

help="limit the number of dependency levels") 

options = argparse.ArgumentParser(add_help=False) 

options.add_argument('-f', '--force', action='store_true', 

help="overwrite uncommitted changes in dependencies") 

options.add_argument('-c', '--clean', action='store_true', 

help="delete ignored files in dependencies") 

shared = {'formatter_class': common.WideHelpFormatter} 

 

# Main parser 

parser = argparse.ArgumentParser(prog=CLI, description=DESCRIPTION, 

parents=[debug], **shared) 

subs = parser.add_subparsers(help="", dest='command', metavar="<command>") 

 

# Init parser 

info = "create a new configuration file for the project" 

sub = subs.add_parser('init', description=info.capitalize() + '.', 

help=info, parents=[debug], **shared) 

 

# Install parser 

info = "get the specified versions of all dependencies" 

sub = subs.add_parser('install', description=info.capitalize() + '.', 

help=info, parents=[debug, project, depth, options], 

**shared) 

sub.add_argument('name', nargs='*', 

help="list of dependencies names to install") 

sub.add_argument('-e', '--fetch', action='store_true', 

help="always fetch the latest branches") 

 

# Update parser 

info = "update dependencies to the latest versions" 

sub = subs.add_parser('update', description=info.capitalize() + '.', 

help=info, parents=[debug, project, depth, options], 

**shared) 

sub.add_argument('name', nargs='*', 

help="list of dependencies names to update") 

sub.add_argument('-a', '--all', action='store_true', dest='recurse', 

help="update all nested dependencies, recursively") 

group = sub.add_mutually_exclusive_group() 

group.add_argument('-l', '--lock', 

action='store_true', dest='lock', default=None, 

help="enable recording of versions for later reinstall") 

group.add_argument('-L', '--no-lock', 

action='store_false', dest='lock', default=None, 

help="disable recording of versions for later reinstall") 

 

# List parser 

info = "display the current version of each dependency" 

sub = subs.add_parser('list', description=info.capitalize() + '.', 

help=info, parents=[debug, project, depth], **shared) 

sub.add_argument('-D', '--no-dirty', action='store_false', 

dest='allow_dirty', 

help="fail if a source has uncommitted changes") 

 

# Lock parser 

info = "lock the current version of each dependency" 

sub = subs.add_parser('lock', description=info.capitalize() + '.', 

help=info, parents=[debug, project], **shared) 

sub.add_argument('name', nargs='*', 

help="list of dependency names to lock") 

 

# Uninstall parser 

info = "delete all installed dependencies" 

sub = subs.add_parser('uninstall', description=info.capitalize() + '.', 

help=info, parents=[debug, project], **shared) 

sub.add_argument('-f', '--force', action='store_true', 

help="delete uncommitted changes in dependencies") 

 

# Show parser 

info = "display the path of a dependency or internal file" 

sub = subs.add_parser('show', description=info.capitalize() + '.', 

help=info, parents=[debug, project], **shared) 

sub.add_argument('name', nargs='*', 

help="display the path of this dependency") 

sub.add_argument('-c', '--config', action='store_true', 

help="display the path of the config file") 

sub.add_argument('-l', '--log', action='store_true', 

help="display the path of the log file") 

 

# Edit parser 

info = "open the configuration file in the default editor" 

sub = subs.add_parser('edit', description=info.capitalize() + '.', 

help=info, parents=[debug, project], **shared) 

 

# Parse arguments 

namespace = parser.parse_args(args=args) 

 

# Configure logging 

common.configure_logging(namespace.verbose) 

 

# Run the program 

function, args, kwargs = _get_command(function, namespace) 

123 ↛ 126line 123 didn't jump to line 126, because the condition on line 123 was never false if function: 

_run_command(function, args, kwargs) 

else: 

parser.print_help() 

sys.exit(1) 

 

 

def _get_command(function, namespace): # pylint: disable=too-many-statements 

args = [] 

kwargs = {} 

 

134 ↛ 135line 134 didn't jump to line 135, because the condition on line 134 was never true if namespace.command == 'init': 

function = commands.init 

 

137 ↛ 138line 137 didn't jump to line 138, because the condition on line 137 was never true elif namespace.command in ['install', 'update']: 

function = getattr(commands, namespace.command) 

args = namespace.name 

kwargs.update(root=namespace.root, 

depth=namespace.depth, 

force=namespace.force, 

clean=namespace.clean) 

if namespace.command == 'install': 

kwargs.update(fetch=namespace.fetch) 

if namespace.command == 'update': 

kwargs.update(recurse=namespace.recurse, 

lock=namespace.lock) 

 

elif namespace.command == 'list': 

function = commands.display 

kwargs.update(root=namespace.root, 

depth=namespace.depth, 

allow_dirty=namespace.allow_dirty) 

 

156 ↛ 157line 156 didn't jump to line 157, because the condition on line 156 was never true elif namespace.command == 'lock': 

function = getattr(commands, namespace.command) 

args = namespace.name 

kwargs.update(root=namespace.root) 

 

161 ↛ 166line 161 didn't jump to line 166, because the condition on line 161 was never false elif namespace.command == 'uninstall': 

function = commands.delete 

kwargs.update(root=namespace.root, 

force=namespace.force) 

 

elif namespace.command == 'show': 

function = commands.show 

args = namespace.name 

kwargs.update(root=namespace.root) 

if namespace.config: 

args.append('__config__') 

if namespace.log: 

args.append('__log__') 

 

elif namespace.command == 'edit': 

function = commands.edit 

kwargs.update(root=namespace.root) 

 

return function, args, kwargs 

 

 

def _run_command(function, args, kwargs): 

success = False 

exit_message = None 

try: 

log.debug("Running %s command...", getattr(function, '__name__', 'a')) 

success = function(*args, **kwargs) 

except KeyboardInterrupt: 

log.debug("Command canceled") 

except exceptions.UncommittedChanges as exception: 

_show_error(exception) 

exit_message = "Run again with '--force' to discard changes" 

except exceptions.ScriptFailure as exception: 

_show_error(exception) 

exit_message = "Run again with '--force' to ignore script errors" 

finally: 

197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true if exit_message: 

common.show(exit_message, color='message') 

common.newline() 

 

201 ↛ 204line 201 didn't jump to line 204, because the condition on line 201 was never false if success: 

log.debug("Command succeeded") 

else: 

log.debug("Command failed") 

sys.exit(1) 

 

 

def _show_error(exception): 

# TODO: require level=, evaluate all calls to dedent() 

common.dedent(0) 

common.newline() 

common.show(str(exception), color='error') 

common.newline() 

 

 

if __name__ == '__main__': # pragma: no cover (manual test) 

main()