Skip to content

Multiple parser creation fails in v2.0.1 #10

@jaelliot

Description

@jaelliot

multicommand Bug Report: Multiple parser creation fails in v2.0.1

Summary

multicommand.create_parser() can only be called once per Python process in v2.0.1. Subsequent calls raise ValueError: conflicting subparser even when using the same command module.

Versions

  • multicommand: 2.0.1
  • Python: 3.14.2 (also reproduced on 3.10.12)
  • OS: Linux (Ubuntu 22.04)

Expected Behavior

create_parser() should be idempotent and allow multiple invocations in the same process, as it did in v1.0.0.

Actual Behavior

  • First call: ✅ Succeeds
  • Second call: ❌ Raises ValueError: conflicting subparser: <name>
  • Subsequent calls: ❌ Continue to fail

Minimal Reproduction

Setup

pip install multicommand==2.0.1

Test Code

import multicommand

# Create a simple command module structure
import types
commands = types.ModuleType('commands')
commands.__file__ = '/tmp/commands/__init__.py'
commands.__path__ = ['/tmp/commands']

# Create a dummy command
def hello(args):
    print("Hello!")
hello.command = "hello"

setattr(commands, 'hello', hello)

# Test multiple parser creation
print("Test 1: First parser creation")
parser1 = multicommand.create_parser(commands)
print("  ✅ SUCCESS")

print("\nTest 2: Second parser creation")
try:
    parser2 = multicommand.create_parser(commands)
    print("  ✅ SUCCESS")
except ValueError as e:
    print(f"  ❌ FAILED: {e}")

Output

Test 1: First parser creation
  ✅ SUCCESS

Test 2: Second parser creation
  ❌ FAILED: conflicting subparser: hello

Real-World Impact

This breaks pytest test suites where:

  • conftest.py creates a parser in fixtures
  • Individual tests also create parsers

Example Failing Test

# conftest.py
import multicommand
from myapp import commands

@pytest.fixture
def parser():
    return multicommand.create_parser(commands)  # First call

# test_commands.py
def test_help():
    parser = multicommand.create_parser(commands)  # Second call - FAILS!
    # ...

Comparison with v1.0.0

v1.0.0 Behavior: ✅ Multiple create_parser() calls work correctly

$ pip install multicommand==1.0.0
$ python test_script.py
Test 1: First parser creation
  ✅ SUCCESS
Test 2: Second parser creation
  ✅ SUCCESS

Stack Trace

Traceback (most recent call last):
  File "test.py", line 18, in <module>
    parser2 = multicommand.create_parser(commands)
  File ".venv/lib/python3.14/site-packages/multicommand.py", line 33, in create_parser
    _link_parsers(root)
  File ".venv/lib/python3.14/site-packages/multicommand.py", line 145, in _link_parsers
    dir_node.subparsers_action.add_parser(
  File "/usr/lib/python3.14/argparse.py", line 1257, in add_parser
    raise ValueError(f'conflicting subparser: {name}')
ValueError: conflicting subparser: hello

Hypothesis

multicommand v2.0.1 appears to maintain global state or cache subparser registrations across create_parser() invocations, causing the second call to detect previously registered parsers as conflicts.

Suggested Fix

Either:

  1. Clear any cached state at the start of create_parser()
  2. Make parser creation truly idempotent
  3. Document that create_parser() can only be called once per process (breaking change)

Workaround

For projects affected by this issue, cache the parser:

# Cache parser globally
_cached_parser = None

def get_parser():
    global _cached_parser
    if _cached_parser is None:
        _cached_parser = multicommand.create_parser(commands)
    return _cached_parser

Environment

Python 3.14.2
multicommand 2.0.1
argparse (stdlib)

Related

  • This issue was discovered while attempting to upgrade from v1.0.0 to v2.0.1 to fix Python 3.13+ argparse compatibility
  • v2.0.1 fixes the original argparse issue but introduces this regression

Thank you for maintaining multicommand! This is a great library, and we'd love to help test a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions