diff --git a/py3plex/cli.py b/py3plex/cli.py index f5b00b45..b7d7adb4 100644 --- a/py3plex/cli.py +++ b/py3plex/cli.py @@ -399,7 +399,12 @@ def create_parser() -> argparse.ArgumentParser: parser.add_argument("--version", action="version", version=f"py3plex {__version__}") - subparsers = parser.add_subparsers(dest="command", help="Available commands") + subparsers = parser.add_subparsers( + dest="command", + title="commands", + metavar="COMMAND", + help="Available commands (run 'py3plex COMMAND --help' for details)", + ) # HELP command subparsers.add_parser("help", help="Show detailed help information about py3plex") @@ -438,7 +443,7 @@ def create_parser() -> argparse.ArgumentParser: "--type", choices=["random", "er", "ba", "ws"], default="random", - help="Network type - Possible values: 'random' (random network, default), 'er' (Erdős-Rényi), 'ba' (Barabási-Albert preferential attachment), 'ws' (Watts-Strogatz small-world)", + help="Network model (default: random): random (random multilayer), er (Erdős-Rényi), ba (Barabási-Albert), ws (Watts-Strogatz)", ) create_parser.add_argument( "--probability", @@ -460,7 +465,7 @@ def create_parser() -> argparse.ArgumentParser: load_parser = subparsers.add_parser( "load", help="Load and inspect a multilayer network (use '-' for stdin)" ) - load_parser.add_argument("input", help="Input network file (use '-' to read from stdin)") + load_parser.add_argument("input", help="Input network file (use '-' for stdin)") load_parser.add_argument( "--info", action="store_true", help="Display network information" ) @@ -470,6 +475,7 @@ def create_parser() -> argparse.ArgumentParser: load_parser.add_argument("--output", "-o", help="Save output to file (JSON format)") load_parser.add_argument( "--input-format", + dest="input_format", choices=["auto", "multiedgelist", "edgelist", "json"], default="auto", help="Input format for stdin data (default: auto-detect)", @@ -479,13 +485,13 @@ def create_parser() -> argparse.ArgumentParser: community_parser = subparsers.add_parser( "community", help="Detect communities in the network" ) - community_parser.add_argument("input", help="Input network file") + community_parser.add_argument("input", help="Input network file (use '-' for stdin)") community_parser.add_argument( "--algorithm", "-a", choices=["louvain", "infomap", "label_prop"], default="louvain", - help="Community detection algorithm - Possible values: 'louvain' (Louvain method, default), 'infomap' (Infomap algorithm), 'label_prop' (Label propagation)", + help="Community detection algorithm (default: louvain): louvain, infomap, or label_prop (label propagation)", ) community_parser.add_argument( "--output", "-o", help="Output file for community assignments (JSON)" @@ -501,13 +507,13 @@ def create_parser() -> argparse.ArgumentParser: centrality_parser = subparsers.add_parser( "centrality", help="Compute node centrality measures" ) - centrality_parser.add_argument("input", help="Input network file") + centrality_parser.add_argument("input", help="Input network file (use '-' for stdin)") centrality_parser.add_argument( "--measure", "-m", choices=["degree", "betweenness", "closeness", "eigenvector", "pagerank"], default="degree", - help="Centrality measure - Possible values: 'degree' (degree centrality, default), 'betweenness' (betweenness centrality), 'closeness' (closeness centrality), 'eigenvector' (eigenvector centrality), 'pagerank' (PageRank)", + help="Centrality measure (default: degree): degree, betweenness, closeness, eigenvector, or pagerank", ) centrality_parser.add_argument( "--output", "-o", help="Output file for centrality scores (JSON)" @@ -518,7 +524,7 @@ def create_parser() -> argparse.ArgumentParser: stats_parser = subparsers.add_parser( "stats", help="Compute multilayer network statistics" ) - stats_parser.add_argument("input", help="Input network file") + stats_parser.add_argument("input", help="Input network file (use '-' for stdin)") stats_parser.add_argument( "--measure", "-m", @@ -532,7 +538,7 @@ def create_parser() -> argparse.ArgumentParser: "edge_overlap", ], default="all", - help="Statistic to compute - Possible values: 'all' (compute all statistics, default), 'density' (network density), 'clustering' (clustering coefficient), 'layer_density' (density per layer), 'node_activity' (node activity across layers), 'versatility' (versatility centrality), 'edge_overlap' (edge overlap between layers)", + help="Statistic (default: all): all, density, clustering, layer_density, node_activity, versatility, or edge_overlap", ) stats_parser.add_argument( "--layer", help="Specific layer for layer-specific statistics" @@ -545,7 +551,7 @@ def create_parser() -> argparse.ArgumentParser: viz_parser = subparsers.add_parser( "visualize", help="Visualize the multilayer network" ) - viz_parser.add_argument("input", help="Input network file") + viz_parser.add_argument("input", help="Input network file (use '-' for stdin)") viz_parser.add_argument( "--output", "-o", @@ -556,7 +562,7 @@ def create_parser() -> argparse.ArgumentParser: "--layout", choices=["spring", "circular", "kamada_kawai", "multilayer"], default="multilayer", - help="Layout algorithm - Possible values: 'spring' (force-directed spring layout), 'circular' (circular layout), 'kamada_kawai' (Kamada-Kawai force layout), 'multilayer' (specialized multilayer layout, default)", + help="Layout algorithm (default: multilayer): spring, circular, kamada_kawai, or multilayer", ) viz_parser.add_argument( "--width", type=int, default=12, help="Figure width in inches (default: 12)" @@ -569,12 +575,12 @@ def create_parser() -> argparse.ArgumentParser: aggregate_parser = subparsers.add_parser( "aggregate", help="Aggregate multilayer network into single layer" ) - aggregate_parser.add_argument("input", help="Input network file") + aggregate_parser.add_argument("input", help="Input network file (use '-' for stdin)") aggregate_parser.add_argument( "--method", choices=["sum", "mean", "max", "min"], default="sum", - help="Aggregation method for edge weights - Possible values: 'sum' (sum weights, default), 'mean' (average weights), 'max' (maximum weight), 'min' (minimum weight)", + help="Aggregation method (default: sum): sum, mean, max, or min", ) aggregate_parser.add_argument( "--output", "-o", required=True, help="Output file for aggregated network" @@ -584,7 +590,7 @@ def create_parser() -> argparse.ArgumentParser: convert_parser = subparsers.add_parser( "convert", help="Convert network between different formats" ) - convert_parser.add_argument("input", help="Input network file") + convert_parser.add_argument("input", help="Input network file (use '-' for stdin)") convert_parser.add_argument( "--output", "-o", @@ -629,7 +635,7 @@ def create_parser() -> argparse.ArgumentParser: ) query_parser.add_argument( "input", - help="Input network file (use '-' to read from stdin)", + help="Input network file (use '-' for stdin)", ) query_parser.add_argument( "query", @@ -652,6 +658,7 @@ def create_parser() -> argparse.ArgumentParser: ) query_parser.add_argument( "--input-format", + dest="input_format", choices=["auto", "multiedgelist", "edgelist", "json"], default="auto", help="Input format for stdin data (default: auto-detect)", diff --git a/tests/test_cli_ergonomics.py b/tests/test_cli_ergonomics.py index f8f8951c..bc898b38 100644 --- a/tests/test_cli_ergonomics.py +++ b/tests/test_cli_ergonomics.py @@ -255,3 +255,22 @@ def test_create_help_mentions_edgelist(self, capsys): # Check that edgelist is mentioned as a recommended format assert "edgelist" in help_text.lower() or ".txt" in help_text.lower() + + def test_help_text_without_redundant_possible_values(self, capsys): + """Test that help text avoids redundant 'Possible values' phrasing.""" + with pytest.raises(SystemExit): + cli.main(["create", "--help"]) + + captured = capsys.readouterr() + help_text = captured.out + assert "Possible values:" not in help_text + + def test_file_input_commands_document_stdin(self, capsys): + """Test that file-input commands consistently document stdin support.""" + commands = ["load", "community", "centrality", "stats", "visualize", "aggregate", "convert", "query"] + for command in commands: + with pytest.raises(SystemExit): + cli.main([command, "--help"]) + captured = capsys.readouterr() + help_text = captured.out + assert "Input network file (use '-' for stdin)" in help_text