wizlib 2.0.10__py3-none-any.whl → 2.0.12__py3-none-any.whl
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.
- wizlib/app.py +8 -4
- wizlib/command.py +9 -0
- wizlib/handler.py +8 -0
- wizlib/input_handler.py +3 -1
- wizlib/ui/__init__.py +182 -0
- wizlib/ui/shell_ui.py +31 -0
- wizlib/ui_handler.py +32 -0
- {wizlib-2.0.10.dist-info → wizlib-2.0.12.dist-info}/METADATA +3 -1
- wizlib-2.0.12.dist-info/RECORD +17 -0
- wizlib-2.0.10.dist-info/RECORD +0 -14
- {wizlib-2.0.10.dist-info → wizlib-2.0.12.dist-info}/WHEEL +0 -0
wizlib/app.py
CHANGED
|
@@ -7,6 +7,8 @@ from wizlib.class_family import ClassFamily
|
|
|
7
7
|
from wizlib.command import WizHelpCommand
|
|
8
8
|
from wizlib.super_wrapper import SuperWrapper
|
|
9
9
|
from wizlib.parser import WizParser
|
|
10
|
+
from wizlib.ui import UI
|
|
11
|
+
from wizlib.ui.shell_ui import ShellUI
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
RED = '\033[91m'
|
|
@@ -31,7 +33,6 @@ class WizApp:
|
|
|
31
33
|
try:
|
|
32
34
|
cls.initialize()
|
|
33
35
|
app = cls(*args)
|
|
34
|
-
# if app.ready:
|
|
35
36
|
command = app.first_command
|
|
36
37
|
result = command.execute()
|
|
37
38
|
if result:
|
|
@@ -58,8 +59,8 @@ class WizApp:
|
|
|
58
59
|
cls.parser.add_argument(
|
|
59
60
|
f"--{handler.name}",
|
|
60
61
|
f"-{handler.name[0]}",
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
**handler.option_properties(cls))
|
|
63
|
+
|
|
63
64
|
subparsers = cls.parser.add_subparsers(dest='command')
|
|
64
65
|
for command in cls.base_command.family_members('name'):
|
|
65
66
|
key = command.get_member_attr('key')
|
|
@@ -68,12 +69,15 @@ class WizApp:
|
|
|
68
69
|
command.add_args(subparser)
|
|
69
70
|
|
|
70
71
|
def __init__(self, *args):
|
|
72
|
+
self.shell = ShellUI()
|
|
71
73
|
args = args if args else [self.base_command.default]
|
|
74
|
+
if not hasattr(self, 'parser'):
|
|
75
|
+
self.__class__.initialize()
|
|
72
76
|
self.vals = vars(self.parser.parse_args(args))
|
|
73
77
|
self.first_command = self.get_command(**self.vals)
|
|
74
78
|
|
|
75
79
|
def get_command(self, **vals):
|
|
76
|
-
"""
|
|
80
|
+
"""Returns a single command"""
|
|
77
81
|
if 'help' in vals:
|
|
78
82
|
return WizHelpCommand(**vals)
|
|
79
83
|
else:
|
wizlib/command.py
CHANGED
|
@@ -5,6 +5,7 @@ from wizlib.class_family import ClassFamily
|
|
|
5
5
|
from wizlib.config_handler import ConfigHandler
|
|
6
6
|
from wizlib.input_handler import InputHandler
|
|
7
7
|
from wizlib.super_wrapper import SuperWrapper
|
|
8
|
+
from wizlib.ui import UI
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class WizCommand(ClassFamily, SuperWrapper):
|
|
@@ -12,6 +13,7 @@ class WizCommand(ClassFamily, SuperWrapper):
|
|
|
12
13
|
|
|
13
14
|
status = ''
|
|
14
15
|
handlers = []
|
|
16
|
+
ui: UI = None
|
|
15
17
|
|
|
16
18
|
@classmethod
|
|
17
19
|
def add_args(self, parser):
|
|
@@ -31,6 +33,13 @@ class WizCommand(ClassFamily, SuperWrapper):
|
|
|
31
33
|
this and call super().handle_vals()."""
|
|
32
34
|
pass
|
|
33
35
|
|
|
36
|
+
def provided(self, argument):
|
|
37
|
+
"""Was an argument provided?"""
|
|
38
|
+
value = None
|
|
39
|
+
if hasattr(self, argument):
|
|
40
|
+
value = getattr(self, argument)
|
|
41
|
+
return True if (value is False) else bool(value)
|
|
42
|
+
|
|
34
43
|
def execute(self, method, *args, **kwargs):
|
|
35
44
|
"""Actually perform the command - override and wrap this via
|
|
36
45
|
SuperWrapper"""
|
wizlib/handler.py
CHANGED
|
@@ -6,6 +6,14 @@ class Handler:
|
|
|
6
6
|
|
|
7
7
|
appname = None
|
|
8
8
|
|
|
9
|
+
@classmethod
|
|
10
|
+
def option_properties(cls, app):
|
|
11
|
+
"""Argparse keyword arguments for this optional arg"""
|
|
12
|
+
return {
|
|
13
|
+
'type': cls.named(app.name),
|
|
14
|
+
'default': ''
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
@classmethod
|
|
10
18
|
def named(cls, name):
|
|
11
19
|
"""Subclass of the handler that holds the app name as a closure"""
|
wizlib/input_handler.py
CHANGED
|
@@ -3,6 +3,8 @@ import sys
|
|
|
3
3
|
|
|
4
4
|
from wizlib.handler import Handler
|
|
5
5
|
|
|
6
|
+
INTERACTIVE = sys.stdin.isatty()
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class InputHandler(Handler):
|
|
8
10
|
|
|
@@ -12,7 +14,7 @@ class InputHandler(Handler):
|
|
|
12
14
|
def __init__(self, file=None, stdin=True):
|
|
13
15
|
if file:
|
|
14
16
|
self.text = Path(file).read_text()
|
|
15
|
-
elif stdin and (not
|
|
17
|
+
elif stdin and (not INTERACTIVE):
|
|
16
18
|
self.text = sys.stdin.read()
|
|
17
19
|
|
|
18
20
|
def __str__(self):
|
wizlib/ui/__init__.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Abstract classes for UIs. The idea here is to allow many different user
|
|
2
|
+
# interfaces (shell, curses, Slack, etc) to drive the same data model without
|
|
3
|
+
# the data model needing specific UI knowledge.
|
|
4
|
+
#
|
|
5
|
+
# The UI keeps a reference to the Handler. The Handler will bring a Command
|
|
6
|
+
# with it, and this is the main root command. In the case of a Shell-type UI,
|
|
7
|
+
# that is the command to be executed. In the case of an interactive UI, it's
|
|
8
|
+
# really just a placeholder; the UI will instantiate other commands as it
|
|
9
|
+
# proceeds. But the Handler won't know about those.
|
|
10
|
+
#
|
|
11
|
+
# Commands might call back to the UI for confirmations or arguments that are
|
|
12
|
+
# previously omitted, using the get_ methods.
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# UI end classes must implement the following interface:
|
|
16
|
+
#
|
|
17
|
+
# __init__(handler): Takes the handler and hangs on to it. Implemented in the
|
|
18
|
+
# main base class.
|
|
19
|
+
#
|
|
20
|
+
# start(): No arguments. Actually performs the operation of the UI. It might be
|
|
21
|
+
# short running (in the case of a shell UI) or long-running (in the case of an
|
|
22
|
+
# interactive UI).
|
|
23
|
+
#
|
|
24
|
+
# output(intro=""): Output some multi-line text explaining an action, usually a
|
|
25
|
+
# list of items being acted upon.
|
|
26
|
+
#
|
|
27
|
+
# get_string(prompt, default=""): For arguments that are omitted, get a string
|
|
28
|
+
# from the user. The prompt is just a word (like "Description") telling the
|
|
29
|
+
# user what to input.
|
|
30
|
+
#
|
|
31
|
+
# get_confirmation(verb): For delete-oriented commands to confirm with the user
|
|
32
|
+
# before proceeding. Verb is what we're asking the user to confirm. Description
|
|
33
|
+
# can be multiple lines, as with get_string. Returns a boolean saying whether
|
|
34
|
+
# the action is confirmed.
|
|
35
|
+
|
|
36
|
+
import os
|
|
37
|
+
import re
|
|
38
|
+
import shutil
|
|
39
|
+
import subprocess
|
|
40
|
+
from dataclasses import dataclass, field
|
|
41
|
+
from enum import Enum
|
|
42
|
+
from io import StringIO
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
from tempfile import NamedTemporaryFile
|
|
45
|
+
|
|
46
|
+
from wizlib.class_family import ClassFamily
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class Choice:
|
|
51
|
+
"""One option of several"""
|
|
52
|
+
|
|
53
|
+
keys: list[str] = field(default_factory=list)
|
|
54
|
+
words: list[str] = field(default_factory=list)
|
|
55
|
+
action: object = None
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def key(self):
|
|
59
|
+
return self.keys[0]
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def word(self):
|
|
63
|
+
return self.words[0]
|
|
64
|
+
|
|
65
|
+
def hit_key(self, key):
|
|
66
|
+
return key in self.keys
|
|
67
|
+
|
|
68
|
+
def hit_word(self, word):
|
|
69
|
+
return word in self.words
|
|
70
|
+
|
|
71
|
+
def value(self):
|
|
72
|
+
return self.word if (self.action is None) else self.action
|
|
73
|
+
|
|
74
|
+
# def call(self, *args, **kwargs):
|
|
75
|
+
# if callable(self.action):
|
|
76
|
+
# return self.action(*args, **kwargs)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class Prompt:
|
|
81
|
+
"""Tell the user what kind of input to provide"""
|
|
82
|
+
|
|
83
|
+
intro: str = ""
|
|
84
|
+
default: str = ""
|
|
85
|
+
choices: list = field(default_factory=list)
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def prompt_string(self):
|
|
89
|
+
"""Simple prompt string for stdio, no colours"""
|
|
90
|
+
result = []
|
|
91
|
+
if self.intro:
|
|
92
|
+
result.append(self.intro)
|
|
93
|
+
if self.default:
|
|
94
|
+
result.append(f"[{self.default}]")
|
|
95
|
+
for choice in self.choices:
|
|
96
|
+
if choice.word == self.default:
|
|
97
|
+
continue
|
|
98
|
+
pre, it, post = choice.word.partition(choice.key)
|
|
99
|
+
result.append(f"{pre}({it}){post}")
|
|
100
|
+
return " ".join(result) + ": "
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class Chooser(Prompt):
|
|
105
|
+
"""Hold a set of choices and get the result"""
|
|
106
|
+
|
|
107
|
+
def add_choice(self, *args, **kwargs):
|
|
108
|
+
choice = Choice(*args, **kwargs)
|
|
109
|
+
self.choices.append(choice)
|
|
110
|
+
|
|
111
|
+
def choice_by_key(self, key):
|
|
112
|
+
chosenlist = [o.value for o in self.choices if o.hit_key(key)]
|
|
113
|
+
return self._choose(chosenlist)
|
|
114
|
+
|
|
115
|
+
def choice_by_word(self, word):
|
|
116
|
+
chosenlist = [o.value for o in self.choices if o.hit_word(word)]
|
|
117
|
+
return self._choose(chosenlist)
|
|
118
|
+
|
|
119
|
+
def _choose(self, chosenlist):
|
|
120
|
+
choice = next(iter(chosenlist), None)
|
|
121
|
+
if callable(choice):
|
|
122
|
+
return choice()
|
|
123
|
+
else:
|
|
124
|
+
return choice
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass
|
|
128
|
+
class UI(ClassFamily):
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
# handler: object
|
|
132
|
+
|
|
133
|
+
# # TODO: Make start an abstract method
|
|
134
|
+
# def start(self):
|
|
135
|
+
# pass
|
|
136
|
+
|
|
137
|
+
# # TODO: Make get_option an abstract method
|
|
138
|
+
# def get_option(self, chooser: Chooser):
|
|
139
|
+
# """Get an option from a list from the user"""
|
|
140
|
+
# pass
|
|
141
|
+
|
|
142
|
+
# # TODO: Make get_string an abstract method
|
|
143
|
+
# def get_string(self, intro, default=""):
|
|
144
|
+
# """Get a string value from the user"""
|
|
145
|
+
# pass
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# # Terminal UIs include Shell and Curses, since both will rely on a
|
|
149
|
+
# # terminal-based editor for the e.g. Manage command.
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# class TerminalUI(UI):
|
|
153
|
+
|
|
154
|
+
# # A convenience method to get a full textual prompt for a string input
|
|
155
|
+
|
|
156
|
+
# def full_prompt(self, prompt, default=None):
|
|
157
|
+
# if default:
|
|
158
|
+
# return f'{prompt} [{default}]: '
|
|
159
|
+
# else:
|
|
160
|
+
# return f'{prompt}: '
|
|
161
|
+
|
|
162
|
+
# # @staticmethod
|
|
163
|
+
# # def edit_text(text):
|
|
164
|
+
# # commands = [['sensible-editor'], ['open', '-W']]
|
|
165
|
+
# # with NamedTemporaryFile(mode="w+") as tempfile:
|
|
166
|
+
# # tempfile.write(text)
|
|
167
|
+
# # tempfile.seek(0)
|
|
168
|
+
# # command = [os.environ.get('EDITOR')]
|
|
169
|
+
# # if not command[0] or not shutil.which(command[0]):
|
|
170
|
+
# # iterator = (c for c in commands if shutil.which(c[0]))
|
|
171
|
+
# # command = next(filter(None, iterator), None)
|
|
172
|
+
# # if not command:
|
|
173
|
+
# # raise RuntimeError(
|
|
174
|
+
# # "A text editor at the $EDITOR " +
|
|
175
|
+
# # "environment variable is required")
|
|
176
|
+
# # subprocess.run(command + [tempfile.name])
|
|
177
|
+
# # tempfile.seek(0)
|
|
178
|
+
# # return tempfile.read()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# class UserCancelError(Exception):
|
|
182
|
+
# pass
|
wizlib/ui/shell_ui.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from readchar import readkey
|
|
4
|
+
from wizlib.rlinput import rlinput
|
|
5
|
+
from wizlib.ui import UI
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ShellUI(UI):
|
|
9
|
+
|
|
10
|
+
"""The UI to execute one command passed in through the shell. There will be
|
|
11
|
+
limited interactivity, if the user omits an argument on the command line,
|
|
12
|
+
but otherwise this is a run and done situation.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
name = "shell"
|
|
16
|
+
|
|
17
|
+
def send(self, value: str):
|
|
18
|
+
"""Output some text"""
|
|
19
|
+
if value:
|
|
20
|
+
print(value, file=sys.stderr)
|
|
21
|
+
|
|
22
|
+
def get_option(self, chooser):
|
|
23
|
+
"""Get a choice from the user with a single keystroke"""
|
|
24
|
+
# key = rlinput(chooser.prompt_string)
|
|
25
|
+
print(chooser.prompt_string, end='', file=sys.stderr, flush=True)
|
|
26
|
+
if sys.stdin.isatty():
|
|
27
|
+
key = readkey()
|
|
28
|
+
else:
|
|
29
|
+
key = input()
|
|
30
|
+
print(file=sys.stderr, flush=True)
|
|
31
|
+
return chooser.choice_by_key(key)
|
wizlib/ui_handler.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from wizlib.handler import Handler
|
|
5
|
+
from wizlib.ui import UI
|
|
6
|
+
|
|
7
|
+
# INTERACTIVE = sys.stdin.isatty()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UIHandler(Handler):
|
|
11
|
+
"""A sort of proxy-handler for the UI class family, which drives user
|
|
12
|
+
interactions (if any) during and between command execution. In this case,
|
|
13
|
+
the handler only contains class-level methods, because the action actually
|
|
14
|
+
returns the UI itself for the command to use."""
|
|
15
|
+
|
|
16
|
+
name = 'ui'
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def option_properties(cls, app):
|
|
20
|
+
"""Argparse keyword arguments for this optional arg"""
|
|
21
|
+
return {
|
|
22
|
+
# 'choices': ['shell'],
|
|
23
|
+
'default': 'shell',
|
|
24
|
+
'help': 'Only option currently is "shell" (the default)',
|
|
25
|
+
'type': cls.ui
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def ui(cls, uitype):
|
|
30
|
+
"""Instead of instantiating the handler, return the chosen UI
|
|
31
|
+
object."""
|
|
32
|
+
return UI.family_member('name', uitype)()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wizlib
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.12
|
|
4
4
|
Summary: Framework for flexible and powerful command-line applications
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Steampunk Wizard
|
|
@@ -11,7 +11,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Requires-Dist: PyYAML (>=6.0.1,<7.0.0)
|
|
13
13
|
Requires-Dist: gnureadline (>=8.1.2,<9.0.0) ; sys_platform == "darwin"
|
|
14
|
+
Requires-Dist: myst-parser (>=2.0.0,<3.0.0)
|
|
14
15
|
Requires-Dist: pyreadline3 (>=3.4.1,<4.0.0) ; sys_platform == "win32"
|
|
16
|
+
Requires-Dist: readchar (>=4.0.5,<5.0.0)
|
|
15
17
|
Description-Content-Type: text/markdown
|
|
16
18
|
|
|
17
19
|
# WizLib
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
wizlib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
wizlib/app.py,sha256=gox0cDfymoURC_-B-4uOGM1qrqpPXuGuGFXOXHngebM,2950
|
|
3
|
+
wizlib/class_family.py,sha256=EX1ZZmrmC2M-PV_iorHXCWqETE2thrwQE45RtHxojbs,4424
|
|
4
|
+
wizlib/command.py,sha256=kuAnAN9lc_o64bVFlzvw-imJp-RBW7UKwU3FBOcvvpc,1580
|
|
5
|
+
wizlib/config_handler.py,sha256=alHpJzFY39XXDv94W4fLwda6hjruySADAJD944rE3HU,2639
|
|
6
|
+
wizlib/error.py,sha256=ypwdMOYhtgKWd48ccfOX8idmCXmm-Skwx3gkPwqJB3c,46
|
|
7
|
+
wizlib/handler.py,sha256=BsJibs4gYHTtocvqseNiUHcM7s89EV_Nquzqb5wOaOo,518
|
|
8
|
+
wizlib/input_handler.py,sha256=QFiR9CC5bkW7HofNAKZEq_hL089hbWzyrFGAjAjvozg,624
|
|
9
|
+
wizlib/parser.py,sha256=O34azN4ttVfwwAsza0hujxGxDpzc4xUEVAf26DXJS5g,1505
|
|
10
|
+
wizlib/rlinput.py,sha256=l00Pa3rxNeY6LJgz8Aws_rTKoEchw33fuL8yqHF9_-o,1754
|
|
11
|
+
wizlib/super_wrapper.py,sha256=F834ytHqA7zegTD1ezk_uxlF9PLygh84wReuiqcI7BI,272
|
|
12
|
+
wizlib/ui/__init__.py,sha256=PDJP2_nCDYvEwagniFsSQ6i3bdpORx7ryHJHxGJo7cw,5621
|
|
13
|
+
wizlib/ui/shell_ui.py,sha256=8DwV0u0MtAYZjIFY7B1vKaZqnC0siTHdLgTx-H9h4Ao,891
|
|
14
|
+
wizlib/ui_handler.py,sha256=eTAUfXk1q8ZMv9H_upbSQEWlwb2G7wbAidiXdkaaVQY,954
|
|
15
|
+
wizlib-2.0.12.dist-info/METADATA,sha256=lUetOKxELDwR1441f70hQG5CTkr4BAfh5F4QtjnN3jc,6007
|
|
16
|
+
wizlib-2.0.12.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
17
|
+
wizlib-2.0.12.dist-info/RECORD,,
|
wizlib-2.0.10.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
wizlib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
wizlib/app.py,sha256=7emrVV077IXk7PGQeFz1jne0jpDCPyrJXrZfaf2Ya54,2822
|
|
3
|
-
wizlib/class_family.py,sha256=EX1ZZmrmC2M-PV_iorHXCWqETE2thrwQE45RtHxojbs,4424
|
|
4
|
-
wizlib/command.py,sha256=GQlXP8dOJHXUGDhjV3VYnOmHLtbEc4TMAGogLUqy2U8,1304
|
|
5
|
-
wizlib/config_handler.py,sha256=alHpJzFY39XXDv94W4fLwda6hjruySADAJD944rE3HU,2639
|
|
6
|
-
wizlib/error.py,sha256=ypwdMOYhtgKWd48ccfOX8idmCXmm-Skwx3gkPwqJB3c,46
|
|
7
|
-
wizlib/handler.py,sha256=vUrlMIER019Sz17yeg95Cs6u9Vo5u24qf0q7p9HEWec,306
|
|
8
|
-
wizlib/input_handler.py,sha256=RmoZA_FlQ_1qeDEV9YZSU8Zw933pMPEb4yOZLPUGnBA,597
|
|
9
|
-
wizlib/parser.py,sha256=O34azN4ttVfwwAsza0hujxGxDpzc4xUEVAf26DXJS5g,1505
|
|
10
|
-
wizlib/rlinput.py,sha256=l00Pa3rxNeY6LJgz8Aws_rTKoEchw33fuL8yqHF9_-o,1754
|
|
11
|
-
wizlib/super_wrapper.py,sha256=F834ytHqA7zegTD1ezk_uxlF9PLygh84wReuiqcI7BI,272
|
|
12
|
-
wizlib-2.0.10.dist-info/METADATA,sha256=84tG6lCjFJE3V1faec8bxqoLG6vZxXJ0A9Eqlkt2rhg,5922
|
|
13
|
-
wizlib-2.0.10.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
14
|
-
wizlib-2.0.10.dist-info/RECORD,,
|
|
File without changes
|