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 locale
|
||||
import logging
|
||||
import inspect
|
||||
import argparse
|
||||
import datetime
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ class stream:
|
|||
raise NotImplementedError
|
||||
|
||||
class stdin(Stream):
|
||||
"""Stream from the standard input."""
|
||||
def __call__(self):
|
||||
return sys.stdin
|
||||
|
||||
|
|
@ -33,10 +35,12 @@ class consume:
|
|||
raise NotImplementedError
|
||||
|
||||
class lines(Consume):
|
||||
"""Consume line by line."""
|
||||
def __call__(self, stream):
|
||||
return stream.readlines()
|
||||
|
||||
class paragraphs(Consume):
|
||||
"""Consume paragraph by paragraph (separated by an empty line)."""
|
||||
def __call__(self, stream):
|
||||
pars = []
|
||||
current = ""
|
||||
|
|
@ -50,9 +54,15 @@ class consume:
|
|||
return pars
|
||||
|
||||
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.skip = bool(skip)
|
||||
if skip == 'skip':
|
||||
self.skip = True
|
||||
elif skip == 'noskip':
|
||||
self.skip = False
|
||||
else:
|
||||
self.skip = bool(skip)
|
||||
|
||||
def __call__(self, stream):
|
||||
sec = []
|
||||
|
|
@ -70,6 +80,7 @@ class consume:
|
|||
return sec
|
||||
|
||||
class nlines(Consume):
|
||||
"""Consume by groups of `nb` lines."""
|
||||
def __init__(self, nb = "10"):
|
||||
self.nb = int(nb)
|
||||
|
||||
|
|
@ -89,17 +100,18 @@ class consume:
|
|||
return sec
|
||||
|
||||
|
||||
|
||||
class format:
|
||||
class Format:
|
||||
def __call__(self, items):
|
||||
raise NotImplementedError
|
||||
|
||||
class as_is(Format):
|
||||
class asis(Format):
|
||||
"""Do not format anything."""
|
||||
def __call__(self, items):
|
||||
return items
|
||||
|
||||
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.
|
||||
def __init__(self, max = "140"):
|
||||
self.max = int(max)
|
||||
|
|
@ -120,6 +132,7 @@ class format:
|
|||
return trimmed
|
||||
|
||||
class eol(Format):
|
||||
"""Add an end of line after the item."""
|
||||
def __call__(self, items):
|
||||
eoled = []
|
||||
for item in items:
|
||||
|
|
@ -127,6 +140,7 @@ class format:
|
|||
return eoled
|
||||
|
||||
class strip(Format):
|
||||
"""Remove any space character around the item."""
|
||||
def __call__(self, items):
|
||||
stripped = []
|
||||
for item in items:
|
||||
|
|
@ -134,6 +148,7 @@ class format:
|
|||
return stripped
|
||||
|
||||
class skip(Format):
|
||||
"""Skip items containing only spaces or being empty."""
|
||||
def __call__(self, items):
|
||||
res = []
|
||||
for item in items:
|
||||
|
|
@ -141,7 +156,8 @@ class format:
|
|||
res.append( item )
|
||||
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):
|
||||
glued = []
|
||||
current = ""
|
||||
|
|
@ -154,13 +170,15 @@ class format:
|
|||
return glued
|
||||
|
||||
class panel(Format):
|
||||
"""Surround each item by an ascii-art box. NOTE: only works when lifted on stdout."""
|
||||
def __call__(self, items):
|
||||
panel = []
|
||||
for item in items:
|
||||
panel.append( Panel.fit(item.strip()) )
|
||||
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'):
|
||||
self.sep = sep
|
||||
self.end = end
|
||||
|
|
@ -175,6 +193,7 @@ class format:
|
|||
return res
|
||||
|
||||
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'):
|
||||
self.content = content
|
||||
self.sep = sep
|
||||
|
|
@ -183,6 +202,7 @@ class format:
|
|||
return [i+self.sep+self.content for i in items]
|
||||
|
||||
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'):
|
||||
self.content = content
|
||||
self.sep = sep
|
||||
|
|
@ -197,6 +217,7 @@ class lift:
|
|||
raise NotImplementedError
|
||||
|
||||
class stdout(Lift):
|
||||
"""Print the items on the standard output."""
|
||||
def __call__(self, items):
|
||||
for item in items:
|
||||
if item:
|
||||
|
|
@ -212,7 +233,7 @@ class Forthlifter:
|
|||
def __init__(self,
|
||||
consumer = consume.lines(),
|
||||
streamers = [stream.stdin()],
|
||||
formatters = [format.as_is()],
|
||||
formatters = [format.asis()],
|
||||
lifters = [lift.stdout()],
|
||||
):
|
||||
if not isinstance(streamers, list):
|
||||
|
|
@ -277,6 +298,7 @@ def classes_of(namespace):
|
|||
subs = {cls.__name__:cls for cls in itf.__subclasses__()}
|
||||
return subs
|
||||
|
||||
|
||||
def operator(asked_op):
|
||||
logger.debug(f"├ Parsed operators:")
|
||||
for op in asked_op:
|
||||
|
|
@ -295,44 +317,86 @@ def operator(asked_op):
|
|||
yield name,args
|
||||
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():
|
||||
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."
|
||||
|
||||
parser = argparse.ArgumentParser( description=usage,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
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)
|
||||
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)
|
||||
parser.add_argument("-f", "--format",
|
||||
# choices = formaters.keys(),
|
||||
metavar = '{' + ','.join(formaters) + '}',
|
||||
default = [],
|
||||
action="append",
|
||||
help="How to format items.")
|
||||
|
||||
lifters = classes_of(lift)
|
||||
parser.add_argument("-l", "--lift",
|
||||
# choices = lifters.keys(),
|
||||
metavar = '{' + ','.join(lifters) + '}',
|
||||
|
||||
epilog = ""
|
||||
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 = [],
|
||||
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()
|
||||
|
|
@ -352,7 +416,7 @@ def main():
|
|||
asked.stream = ["stdin"]
|
||||
|
||||
if not asked.format:
|
||||
asked.format = ["as_is"]
|
||||
asked.format = ["asis"]
|
||||
|
||||
if not asked.lift:
|
||||
asked.lift = ["stdout"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue