wizlib 3.0.1__tar.gz → 3.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wizlib might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wizlib
3
- Version: 3.0.1
3
+ Version: 3.1.1
4
4
  Summary: Framework for flexible and powerful command-line applications
5
5
  License: MIT
6
6
  Author: Steampunk Wizard
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "wizlib"
3
- version = "3.0.1"
3
+ version = "3.1.1"
4
4
  description = "Framework for flexible and powerful command-line applications"
5
5
  authors = ["Steampunk Wizard <wizlib@steampunkwizard.ca>"]
6
6
  license = "MIT"
@@ -0,0 +1,112 @@
1
+ from argparse import ArgumentError
2
+ import sys
3
+ from dataclasses import dataclass
4
+ import os
5
+ from pathlib import Path
6
+
7
+ from wizlib.class_family import ClassFamily
8
+ from wizlib.command import WizHelpCommand
9
+ from wizlib.super_wrapper import SuperWrapper
10
+ from wizlib.parser import WizArgumentError, WizParser
11
+ from wizlib.ui import UI
12
+
13
+
14
+ RED = '\033[91m'
15
+ RESET = '\033[0m'
16
+
17
+
18
+ class AppCancellation(BaseException):
19
+ pass
20
+
21
+
22
+ class WizApp:
23
+ """Root of all WizLib-based CLI applications. Subclass it. Can be
24
+ instantiated and then run multiple commands."""
25
+
26
+ # Name of this app, used in argparse (override)
27
+ name = ''
28
+
29
+ # Base command class, typically defined in command/__init__.py (override)
30
+ base = None
31
+
32
+ # List of Handler classes used by this app (override)
33
+ handlers = []
34
+
35
+ # Set some default types so linting works
36
+ ui: UI
37
+
38
+ @classmethod
39
+ def main(cls): # pragma: nocover
40
+ """Call this from a __main__ entrypoint"""
41
+ cls.start(*sys.argv[1:], debug=os.getenv('DEBUG'))
42
+
43
+ @classmethod
44
+ def start(cls, *args, debug=False):
45
+ """Call this from a Python entrypoint"""
46
+ try:
47
+ cls.initialize()
48
+ try:
49
+ ns = cls.parser.parse_args(args)
50
+ if (ns.command is None) and (not hasattr(ns, 'help')):
51
+ ns = cls.dparser.parse_args(args)
52
+ except ArgumentError as e:
53
+ ns = cls.dparser.parse_args(args)
54
+ app = cls(**vars(ns))
55
+ app.run(**vars(ns))
56
+ except AppCancellation as cancellation:
57
+ if str(cancellation):
58
+ print(str(cancellation), file=sys.stderr)
59
+ except BaseException as error:
60
+ if debug:
61
+ raise error
62
+ else:
63
+ name = type(error).__name__
64
+ print(f"\n{RED}{name}{': ' if str(error) else ''}" +
65
+ f"{error}{RESET}", file=sys.stderr)
66
+ sys.exit(1)
67
+
68
+ @classmethod
69
+ def initialize(cls):
70
+ """Set up the parser for the app class"""
71
+ cls.parser = WizParser(prog=cls.name)
72
+ subparsers = cls.parser.add_subparsers(dest='command')
73
+ for command in cls.base.family_members('name'):
74
+ key = command.get_member_attr('key')
75
+ aliases = [key] if key else []
76
+ subparser = subparsers.add_parser(command.name, aliases=aliases)
77
+ if command.name == cls.base.default:
78
+ cls.dparser = subparser
79
+ command.add_args(subparser)
80
+ for handler in cls.handlers:
81
+ handler.add_args(cls.parser)
82
+ handler.add_args(cls.dparser)
83
+
84
+ def __init__(self, **vals):
85
+ """Create the app. Only interested in the handlers from the parsed
86
+ values passed in"""
87
+ for hcls in self.handlers:
88
+ val = vals[hcls.name] if (hcls.name in vals) else hcls.default
89
+ handler = hcls.setup(val)
90
+ handler.app = self
91
+ setattr(self, hcls.name, handler)
92
+
93
+ def run(self, **vals):
94
+ """Perform a command. May be called more than once. Only interested in
95
+ the command itself and its specific arguments from the values passed
96
+ in."""
97
+ if 'help' in vals:
98
+ ccls = WizHelpCommand
99
+ else:
100
+ c = 'command'
101
+ cname = (vals.pop(c) if c in vals else None) or self.base.default
102
+ ccls = self.base.family_member('name', cname)
103
+ if not ccls:
104
+ raise Exception(f"Unknown command {cname}")
105
+ command = ccls(self, **vals)
106
+ result = command.execute()
107
+ if result:
108
+ print(result, end='')
109
+ if sys.stdout.isatty(): # pragma: nocover
110
+ print()
111
+ if command.status:
112
+ print(command.status, file=sys.stderr)
@@ -18,7 +18,6 @@ class WizCommand(ClassFamily, SuperWrapper):
18
18
  """Define all the args you want, but stdin always works."""
19
19
 
20
20
  status = ''
21
- handlers = [] # TODO: Move to app
22
21
 
23
22
  @classmethod
24
23
  def add_args(self, parser):
@@ -14,6 +14,8 @@ import sys
14
14
  import os
15
15
 
16
16
 
17
+ # We found the help process a little clumsy
18
+
17
19
  class WizHelpAction(Action):
18
20
 
19
21
  def __init__(self,
@@ -3,7 +3,6 @@ import sys
3
3
 
4
4
  from readchar import readkey
5
5
 
6
- from wizlib.rlinput import rlinput
7
6
  from wizlib.ui import UI, Chooser, Emphasis
8
7
  from wizlib.ui.shell.line_editor import ShellLineEditor
9
8
  from wizlib.ui.shell import S
@@ -1,98 +0,0 @@
1
- import sys
2
- from dataclasses import dataclass
3
- import os
4
- from pathlib import Path
5
-
6
- from wizlib.class_family import ClassFamily
7
- from wizlib.command import WizHelpCommand
8
- from wizlib.super_wrapper import SuperWrapper
9
- from wizlib.parser import WizParser
10
- from wizlib.ui import UI
11
-
12
-
13
- RED = '\033[91m'
14
- RESET = '\033[0m'
15
-
16
-
17
- class AppCancellation(BaseException):
18
- pass
19
-
20
-
21
- class WizApp:
22
- """Root of all WizLib-based CLI applications. Subclass it. Can be
23
- instantiated and then run multiple commands."""
24
-
25
- base_command = None
26
- name = ''
27
-
28
- # Set some default types so linting works
29
- ui: UI
30
-
31
- @classmethod
32
- def main(cls): # pragma: nocover
33
- """Call this from a __main__ entrypoint"""
34
- cls.start(*sys.argv[1:], debug=os.getenv('DEBUG'))
35
-
36
- @classmethod
37
- def start(cls, *args, debug=False):
38
- """Call this from a Python entrypoint"""
39
- try:
40
- parser = WizParser(prog=cls.name)
41
- for handler in cls.base_command.handlers:
42
- handler.add_args(parser)
43
- ns, more = parser.parse_known_args(args)
44
- app = cls.initialize(**vars(ns))
45
- more = more if more else [cls.base_command.default]
46
- app.run(*more)
47
- except AppCancellation as cancellation:
48
- if str(cancellation):
49
- print(str(cancellation), file=sys.stderr)
50
- except BaseException as error:
51
- if debug:
52
- raise error
53
- else:
54
- name = type(error).__name__
55
- print(f"\n{RED}{name}{': ' if str(error) else ''}" +
56
- f"{error}{RESET}", file=sys.stderr)
57
- sys.exit(1)
58
-
59
- @classmethod
60
- def initialize(cls, **vals):
61
- """Converts argparse values (strings) into actual handlers and
62
- instantiates the app"""
63
- handlers = {}
64
- for handler in cls.base_command.handlers:
65
- val = vals[handler.name] if (
66
- handler.name in vals) else handler.default
67
- handlers[handler.name] = handler.setup(val)
68
- return cls(**handlers)
69
-
70
- def __init__(self, **handlers):
71
- for name, handler in handlers.items():
72
- handler.app = self
73
- setattr(self, name, handler)
74
- self.parser = WizParser()
75
- subparsers = self.parser.add_subparsers(dest='command')
76
- for command in self.base_command.family_members('name'):
77
- key = command.get_member_attr('key')
78
- aliases = [key] if key else []
79
- subparser = subparsers.add_parser(command.name, aliases=aliases)
80
- command.add_args(subparser)
81
-
82
- def run(self, *args):
83
- vals = vars(self.parser.parse_args(args))
84
- if 'help' in vals:
85
- return WizHelpCommand(**vals)
86
- command_name = vals.pop('command')
87
- command_class = self.base_command.family_member(
88
- 'name', command_name)
89
- if not command_class:
90
- raise Exception(f"Unknown command {command_name}")
91
- command = command_class(self, **vals)
92
- result = command.execute()
93
- if result:
94
- print(result, end='')
95
- if sys.stdout.isatty(): # pragma: nocover
96
- print()
97
- if command.status:
98
- print(command.status, file=sys.stderr)
@@ -1,71 +0,0 @@
1
- try: # pragma: nocover
2
- import gnureadline as readline
3
- except ImportError: # pragma: nocover
4
- import readline
5
- import sys
6
-
7
-
8
- # Use tab for completion
9
- readline.parse_and_bind('tab: complete')
10
-
11
- # Only a space separates words
12
- readline.set_completer_delims(' ')
13
-
14
-
15
- # Readline defaults to complete with local filenames. Definitely not what we
16
- # want.
17
-
18
- def null_complete(text, start): # pragma: nocover
19
- return None
20
-
21
-
22
- readline.set_completer(null_complete)
23
-
24
-
25
- def rlinput(prompt: str = "", default: str = "",
26
- options: list = None): # pragma: nocover
27
- """
28
- Get input with preset default and/or tab completion of options
29
-
30
- Parameters:
31
-
32
- prompt:str - string to output before requesting input, same as in the
33
- input() function
34
-
35
- default:str - value with which to prepopulate the response, can be cleared
36
- with ctrl-a, ctrl-k
37
-
38
- options:list - list of choices for tab completion
39
- """
40
-
41
- # Clean out the options
42
- options = [o.strip() + " " for o in options] if options else []
43
-
44
- # Create the completer using the options
45
- def complete(text, state):
46
- results = [x for x in options if x.startswith(text)] + [None]
47
- return results[state]
48
- readline.set_completer(complete)
49
-
50
- # Insert the default when we launch
51
- def start():
52
- readline.insert_text(default)
53
- readline.set_startup_hook(start)
54
-
55
- # Actually perform the input
56
- try:
57
- value = input(prompt)
58
- finally:
59
- readline.set_startup_hook()
60
- readline.set_completer(null_complete)
61
- return value.strip()
62
-
63
-
64
- def tryit(): # pragma: nocover
65
- """Quick and dirty tester function"""
66
- return rlinput(prompt='>',
67
- options=['a-b', 'a-c', 'b-a'])
68
-
69
-
70
- if __name__ == '__main__':
71
- tryit()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes