# Copyright 2022-2025 The Ramble Authors
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.
import argparse
import fnmatch
import math
import os
import re
import sys
from html import escape # novm
from llnl.util.tty.colify import colify
import ramble.cmd.common.arguments as arguments
import ramble.repository
from ramble.util.logger import logger
formatters = {}
[docs]
def filter_by_name(objs, args, object_type):
"""
Filters the sequence of objects according to user prescriptions
Args:
objs: sequence of objects
args: parsed command line arguments
Returns:
filtered and sorted list of objects
"""
if args.filter:
res = []
for f in args.filter:
if "*" not in f and "?" not in f:
r = fnmatch.translate("*" + f + "*")
else:
r = fnmatch.translate(f)
rc = re.compile(r, flags=re.IGNORECASE)
res.append(rc)
if args.search_description:
def match(p, f):
if f.match(p):
return True
obj = ramble.repository.get(p, object_type=object_type)
if obj.__doc__:
return f.match(obj.__doc__)
return False
else:
def match(p, f):
return f.match(p)
objs = [obj for obj in objs if any(match(obj, f) for f in res)]
return sorted(objs, key=lambda s: s.lower())
[docs]
@formatter
def name_only(objs, out, object_type):
obj_def = ramble.repository.type_definitions[object_type]
indent = 0
if out.isatty():
logger.msg(f'{len(objs)} {obj_def["dir_name"]}')
colify(objs, indent=indent, output=out)
[docs]
def github_url(objs, object_type):
"""Link to an object file on github."""
obj_def = ramble.repository.type_definitions[object_type]
url = (
"https://github.com/ramble/ramble/blob/develop/var/ramble/repos/builtina/"
+ f'{obj_def["dir_name"]}/'
+ "{0}"
+ f'/{obj_def["file_name"]}'
)
return url.format(objs.name)
[docs]
def rows_for_ncols(elts, ncols):
"""Print out rows in a table with ncols of elts laid out vertically."""
clen = int(math.ceil(len(elts) / ncols))
for r in range(clen):
row = []
for c in range(ncols):
i = c * clen + r
row.append(elts[i] if i < len(elts) else None)
yield row
[docs]
@formatter
def version_json(obj_names, out, object_type):
"""Print all objects with their latest versions."""
objs = [ramble.repository.get(name, object_type=object_type) for name in obj_names]
out.write("[\n")
# output name and latest version for each object
obj_latest = ",\n".join(
[
' {{"name": "{0}"\n'
"}}".format(
obj.name,
)
for obj in objs
]
)
out.write(obj_latest)
# important: no trailing comma in JSON arrays
out.write("\n]\n")
[docs]
@formatter
def html(obj_names, out, object_type):
"""Print out information on all objects in Sphinx HTML.
This is intended to be inlined directly into Sphinx documentation.
We write HTML instead of RST for speed; generating RST from *all*
objects causes the Sphinx build to take forever. Including this as
raw HTML is much faster.
"""
obj_def = ramble.repository.type_definitions[object_type]
# Read in all objects
objs = [ramble.repository.get(name, object_type=object_type) for name in obj_names]
# Start at 2 because the title of the page from Sphinx is id1.
span_id = 2
# HTML header with an increasing id span
def head(n, span_id, title, anchor=None):
if anchor is None:
anchor = title
out.write(
(
'<span id="id%d"></span>'
'<h1>%s<a class="headerlink" href="#%s" '
'title="Permalink to this headline">¶</a>'
"</h1>\n"
)
% (span_id, title, anchor)
)
# Start with the number of objects, skipping the title and intro
# blurb, which we maintain in the RST file.
out.write("<p>\n")
out.write(f'Ramble currently has {len(objs)} mainline {obj_def["dir_name"]}:\n')
out.write("</p>\n")
# Table of links to all objects
out.write('<table border="1" class="docutils">\n')
out.write('<tbody valign="top">\n')
for i, row in enumerate(rows_for_ncols(obj_names, 3)):
out.write('<tr class="row-odd">\n' if i % 2 == 0 else '<tr class="row-even">\n')
for name in row:
out.write("<td>\n")
out.write(f'<a class="reference internal" href="#{name}">{name}</a></td>\n')
out.write("</td>\n")
out.write("</tr>\n")
out.write("</tbody>\n")
out.write("</table>\n")
out.write('<hr class="docutils"/>\n')
# Output some text for each objects.
for obj in objs:
out.write('<div class="section" id="%s">\n' % obj.name)
head(2, span_id, obj.name)
span_id += 1
out.write('<dl class="docutils">\n')
# out.write('<dt>Homepage:</dt>\n')
# out.write('<dd><ul class="first last simple">\n')
# out.write(('<li>'
# '<a class="reference external" href="%s">%s</a>'
# '</li>\n') % (obj.homepage, escape(obj.homepage, True)))
# out.write('</ul></dd>\n')
out.write(f'<dt>Ramble {obj_def["dir_name"]}:</dt>\n')
out.write('<dd><ul class="first last simple">\n')
out.write(
"<li>"
'<a class="reference external" '
f'href="{github_url(obj, object_type)}">'
f'{obj.name}/{obj_def["file_name"]}</a>' # noqa: E501
"</li>\n"
)
out.write("</ul></dd>\n")
# if obj.versions:
# out.write('<dt>Versions:</dt>\n')
# out.write('<dd>\n')
# out.write(', '.join(
# str(v) for v in reversed(sorted(obj.versions))))
# out.write('\n')
# out.write('</dd>\n')
out.write("<dt>Description:</dt>\n")
out.write("<dd>\n")
out.write(escape(obj.format_doc(indent=2), True))
out.write("\n")
out.write("</dd>\n")
out.write("</dl>\n")
out.write('<hr class="docutils"/>\n')
out.write("</div>\n")
[docs]
def setup_list_parser(subparser):
subparser.add_argument(
"filter",
nargs=argparse.REMAINDER,
help="optional case-insensitive glob patterns to filter results",
)
subparser.add_argument(
"-d",
"--search-description",
action="store_true",
default=False,
help="filtering will also search the description for a match",
)
subparser.add_argument(
"--format",
default="name_only",
choices=formatters,
help="format to be used to print the output [default: name_only]",
)
subparser.add_argument(
"--update",
metavar="FILE",
default=None,
action="store",
help="write output to the specified file, if any object is newer",
)
arguments.add_common_arguments(subparser, ["tags", "obj_type"])