feat(doc): autoextract operators' help
This commit is contained in:
parent
77bd035f72
commit
e0adccda32
1 changed files with 90 additions and 26 deletions
|
|
@ -4,6 +4,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import locale
|
import locale
|
||||||
import logging
|
import logging
|
||||||
|
import inspect
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
@ -24,6 +25,7 @@ class stream:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class stdin(Stream):
|
class stdin(Stream):
|
||||||
|
"""Stream from the standard input."""
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
return sys.stdin
|
return sys.stdin
|
||||||
|
|
||||||
|
|
@ -33,10 +35,12 @@ class consume:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class lines(Consume):
|
class lines(Consume):
|
||||||
|
"""Consume line by line."""
|
||||||
def __call__(self, stream):
|
def __call__(self, stream):
|
||||||
return stream.readlines()
|
return stream.readlines()
|
||||||
|
|
||||||
class paragraphs(Consume):
|
class paragraphs(Consume):
|
||||||
|
"""Consume paragraph by paragraph (separated by an empty line)."""
|
||||||
def __call__(self, stream):
|
def __call__(self, stream):
|
||||||
pars = []
|
pars = []
|
||||||
current = ""
|
current = ""
|
||||||
|
|
@ -50,9 +54,15 @@ class consume:
|
||||||
return pars
|
return pars
|
||||||
|
|
||||||
class sections(Consume):
|
class sections(Consume):
|
||||||
def __init__(self, mark = r"^#", skip = "False"):
|
"""Consume section by section. A new section starts when a line matches the `mark` regexp. If `skip` is set to 'skip', the marked line is not consumed."""
|
||||||
|
def __init__(self, mark = r"^#", skip = "noskip"):
|
||||||
self.mark = mark
|
self.mark = mark
|
||||||
self.skip = bool(skip)
|
if skip == 'skip':
|
||||||
|
self.skip = True
|
||||||
|
elif skip == 'noskip':
|
||||||
|
self.skip = False
|
||||||
|
else:
|
||||||
|
self.skip = bool(skip)
|
||||||
|
|
||||||
def __call__(self, stream):
|
def __call__(self, stream):
|
||||||
sec = []
|
sec = []
|
||||||
|
|
@ -70,6 +80,7 @@ class consume:
|
||||||
return sec
|
return sec
|
||||||
|
|
||||||
class nlines(Consume):
|
class nlines(Consume):
|
||||||
|
"""Consume by groups of `nb` lines."""
|
||||||
def __init__(self, nb = "10"):
|
def __init__(self, nb = "10"):
|
||||||
self.nb = int(nb)
|
self.nb = int(nb)
|
||||||
|
|
||||||
|
|
@ -89,17 +100,18 @@ class consume:
|
||||||
return sec
|
return sec
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class format:
|
class format:
|
||||||
class Format:
|
class Format:
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class as_is(Format):
|
class asis(Format):
|
||||||
|
"""Do not format anything."""
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
return items
|
return items
|
||||||
|
|
||||||
class trim(Format):
|
class trim(Format):
|
||||||
|
"""Split items if their length is longer than `max`, and create new items with the remaining parts."""
|
||||||
# Every argument is a string.
|
# Every argument is a string.
|
||||||
def __init__(self, max = "140"):
|
def __init__(self, max = "140"):
|
||||||
self.max = int(max)
|
self.max = int(max)
|
||||||
|
|
@ -120,6 +132,7 @@ class format:
|
||||||
return trimmed
|
return trimmed
|
||||||
|
|
||||||
class eol(Format):
|
class eol(Format):
|
||||||
|
"""Add an end of line after the item."""
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
eoled = []
|
eoled = []
|
||||||
for item in items:
|
for item in items:
|
||||||
|
|
@ -127,6 +140,7 @@ class format:
|
||||||
return eoled
|
return eoled
|
||||||
|
|
||||||
class strip(Format):
|
class strip(Format):
|
||||||
|
"""Remove any space character around the item."""
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
stripped = []
|
stripped = []
|
||||||
for item in items:
|
for item in items:
|
||||||
|
|
@ -134,6 +148,7 @@ class format:
|
||||||
return stripped
|
return stripped
|
||||||
|
|
||||||
class skip(Format):
|
class skip(Format):
|
||||||
|
"""Skip items containing only spaces or being empty."""
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
res = []
|
res = []
|
||||||
for item in items:
|
for item in items:
|
||||||
|
|
@ -141,7 +156,8 @@ class format:
|
||||||
res.append( item )
|
res.append( item )
|
||||||
return res
|
return res
|
||||||
|
|
||||||
class paragraph(Format):
|
class glue(Format):
|
||||||
|
"""Glue consecutive items together if they are not separated by an empty one."""
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
glued = []
|
glued = []
|
||||||
current = ""
|
current = ""
|
||||||
|
|
@ -154,13 +170,15 @@ class format:
|
||||||
return glued
|
return glued
|
||||||
|
|
||||||
class panel(Format):
|
class panel(Format):
|
||||||
|
"""Surround each item by an ascii-art box. NOTE: only works when lifted on stdout."""
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
panel = []
|
panel = []
|
||||||
for item in items:
|
for item in items:
|
||||||
panel.append( Panel.fit(item.strip()) )
|
panel.append( Panel.fit(item.strip()) )
|
||||||
return panel
|
return panel
|
||||||
|
|
||||||
class count(Format):
|
class counter(Format):
|
||||||
|
"""Add a counter at the end of each items, with the current index and the total. If `end` is given, it is added at the very last item. If `sep` is given, it is appended to the item before the counter itself."""
|
||||||
def __init__(self, end = ' ␄', sep = '\n'):
|
def __init__(self, end = ' ␄', sep = '\n'):
|
||||||
self.sep = sep
|
self.sep = sep
|
||||||
self.end = end
|
self.end = end
|
||||||
|
|
@ -175,6 +193,7 @@ class format:
|
||||||
return res
|
return res
|
||||||
|
|
||||||
class suffix(Format):
|
class suffix(Format):
|
||||||
|
"""Add the `content` string after each item. If `sep` is given, it is appended to the item before the content and after the item."""
|
||||||
def __init__(self, content = "", sep = '\n'):
|
def __init__(self, content = "", sep = '\n'):
|
||||||
self.content = content
|
self.content = content
|
||||||
self.sep = sep
|
self.sep = sep
|
||||||
|
|
@ -183,6 +202,7 @@ class format:
|
||||||
return [i+self.sep+self.content for i in items]
|
return [i+self.sep+self.content for i in items]
|
||||||
|
|
||||||
class prefix(Format):
|
class prefix(Format):
|
||||||
|
"""Add the `content` string before each item. If `sep` is given, it is prepended to the item after the content and before the item."""
|
||||||
def __init__(self, content = "", sep = '\n'):
|
def __init__(self, content = "", sep = '\n'):
|
||||||
self.content = content
|
self.content = content
|
||||||
self.sep = sep
|
self.sep = sep
|
||||||
|
|
@ -197,6 +217,7 @@ class lift:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class stdout(Lift):
|
class stdout(Lift):
|
||||||
|
"""Print the items on the standard output."""
|
||||||
def __call__(self, items):
|
def __call__(self, items):
|
||||||
for item in items:
|
for item in items:
|
||||||
if item:
|
if item:
|
||||||
|
|
@ -212,7 +233,7 @@ class Forthlifter:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
consumer = consume.lines(),
|
consumer = consume.lines(),
|
||||||
streamers = [stream.stdin()],
|
streamers = [stream.stdin()],
|
||||||
formatters = [format.as_is()],
|
formatters = [format.asis()],
|
||||||
lifters = [lift.stdout()],
|
lifters = [lift.stdout()],
|
||||||
):
|
):
|
||||||
if not isinstance(streamers, list):
|
if not isinstance(streamers, list):
|
||||||
|
|
@ -277,6 +298,7 @@ def classes_of(namespace):
|
||||||
subs = {cls.__name__:cls for cls in itf.__subclasses__()}
|
subs = {cls.__name__:cls for cls in itf.__subclasses__()}
|
||||||
return subs
|
return subs
|
||||||
|
|
||||||
|
|
||||||
def operator(asked_op):
|
def operator(asked_op):
|
||||||
logger.debug(f"├ Parsed operators:")
|
logger.debug(f"├ Parsed operators:")
|
||||||
for op in asked_op:
|
for op in asked_op:
|
||||||
|
|
@ -295,44 +317,86 @@ def operator(asked_op):
|
||||||
yield name,args
|
yield name,args
|
||||||
logger.debug("│ └OK")
|
logger.debug("│ └OK")
|
||||||
|
|
||||||
|
|
||||||
|
def help_op(ops):
|
||||||
|
h = ""
|
||||||
|
itf = list(ops.values())[0].__mro__[1].__name__.lower()
|
||||||
|
h += f"\nAVAILABLE OPERATORS FOR --{itf}:\n"
|
||||||
|
for name,cls in ops.items():
|
||||||
|
# Signature
|
||||||
|
hsig = f" -{itf[0]} {name.lower()}"
|
||||||
|
args = inspect.getfullargspec(cls)[0]
|
||||||
|
if args:
|
||||||
|
sign = inspect.signature(cls)
|
||||||
|
args = [sign.parameters[a].name for a in sign.parameters]
|
||||||
|
hsig += f"[:{','.join(args)}]"
|
||||||
|
h += hsig
|
||||||
|
|
||||||
|
# Example (using defaults)
|
||||||
|
hex = f"-{itf[0]} "
|
||||||
|
args = inspect.getfullargspec(cls)[0]
|
||||||
|
if args:
|
||||||
|
sep = ":"
|
||||||
|
sign = inspect.signature(cls)
|
||||||
|
defs = [sign.parameters[a].default for a in sign.parameters]
|
||||||
|
if not all(defs) or any(re.match(r'\s', d) for d in defs):
|
||||||
|
hdefs = [d.replace("\n", r"\n") for d in defs]
|
||||||
|
hex += f"'{name.lower()}{sep}{','.join(hdefs)}'"
|
||||||
|
else:
|
||||||
|
hex += f"{name.lower()}{sep}{','.join(defs)}"
|
||||||
|
h += f"\n\tDefault: {hex}"
|
||||||
|
else:
|
||||||
|
hex += name.lower()
|
||||||
|
|
||||||
|
if cls.__doc__:
|
||||||
|
h+= f"\n\t{cls.__doc__}\n"
|
||||||
|
else:
|
||||||
|
h += "\n"
|
||||||
|
return h
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
errors = {"NO_ERROR":0, "UNKNOWN_ERROR":1, "NO_SETUP_NEEDED":2, "NO_APP_KEY":10}
|
errors = {"NO_ERROR":0, "UNKNOWN_ERROR":1, "NO_SETUP_NEEDED":2, "NO_APP_KEY":10}
|
||||||
|
|
||||||
usage = "Post text read on the standard input to a website or an API."
|
usage = "Post text read on the standard input to a website or an API."
|
||||||
|
|
||||||
parser = argparse.ArgumentParser( description=usage,
|
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
||||||
|
|
||||||
streamers = classes_of(stream)
|
streamers = classes_of(stream)
|
||||||
parser.add_argument("-s", "--stream",
|
|
||||||
# choices = streamers.keys(),
|
|
||||||
metavar = '{' + ','.join(streamers) + '}',
|
|
||||||
default = [],
|
|
||||||
action="append",
|
|
||||||
help="Where to get items.")
|
|
||||||
|
|
||||||
consumers = classes_of(consume)
|
consumers = classes_of(consume)
|
||||||
parser.add_argument("-c", "--consume",
|
|
||||||
# choices = consumers.keys(),
|
|
||||||
metavar = '{' + ','.join(consumers) + '}',
|
|
||||||
default = "lines",
|
|
||||||
help="How to get the content form items.")
|
|
||||||
|
|
||||||
formaters = classes_of(format)
|
formaters = classes_of(format)
|
||||||
parser.add_argument("-f", "--format",
|
|
||||||
# choices = formaters.keys(),
|
|
||||||
metavar = '{' + ','.join(formaters) + '}',
|
|
||||||
default = [],
|
|
||||||
action="append",
|
|
||||||
help="How to format items.")
|
|
||||||
|
|
||||||
lifters = classes_of(lift)
|
lifters = classes_of(lift)
|
||||||
parser.add_argument("-l", "--lift",
|
|
||||||
# choices = lifters.keys(),
|
epilog = ""
|
||||||
metavar = '{' + ','.join(lifters) + '}',
|
epilog += help_op(streamers)
|
||||||
|
epilog += help_op(consumers)
|
||||||
|
epilog += help_op(formaters)
|
||||||
|
epilog += help_op(lifters)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=usage,
|
||||||
|
formatter_class = argparse.RawTextHelpFormatter,
|
||||||
|
epilog = epilog)
|
||||||
|
|
||||||
|
parser.add_argument("-s", "--stream",
|
||||||
|
metavar = "STREAM(S)",
|
||||||
default = [],
|
default = [],
|
||||||
action="append",
|
action="append",
|
||||||
help="How to lift items.")
|
help="Where to get items (several occurences possibles, order matters).")
|
||||||
|
|
||||||
|
parser.add_argument("-c", "--consume",
|
||||||
|
metavar = "CONSUME",
|
||||||
|
default = "lines",
|
||||||
|
help="How to extract the content from the stream (only one occurence).")
|
||||||
|
|
||||||
|
parser.add_argument("-f", "--format",
|
||||||
|
metavar = "FORMAT(S)",
|
||||||
|
default = [],
|
||||||
|
action="append",
|
||||||
|
help="How to format items (several occurences possibles, order matters).")
|
||||||
|
|
||||||
|
parser.add_argument("-l", "--lift",
|
||||||
|
metavar = "LIFT(S)",
|
||||||
|
default = [],
|
||||||
|
action="append",
|
||||||
|
help="How to send items somewhere (several occurences possibles, order matters).")
|
||||||
|
|
||||||
|
|
||||||
asked = parser.parse_args()
|
asked = parser.parse_args()
|
||||||
|
|
@ -352,7 +416,7 @@ def main():
|
||||||
asked.stream = ["stdin"]
|
asked.stream = ["stdin"]
|
||||||
|
|
||||||
if not asked.format:
|
if not asked.format:
|
||||||
asked.format = ["as_is"]
|
asked.format = ["asis"]
|
||||||
|
|
||||||
if not asked.lift:
|
if not asked.lift:
|
||||||
asked.lift = ["stdout"]
|
asked.lift = ["stdout"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue