Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ ignore = [
"test/sample-directive-special.py" = [
"N999", # invalid module name
]
"test/sample-inside-class.py" = [
"N999", # invalid module name
]

[format]
preview = true
Expand Down
40 changes: 24 additions & 16 deletions sphinxarg/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,28 @@ def _is_suppressed(item: str | None) -> bool:
item = str(item).replace('"', '').replace("'", '')
return item == '==SUPPRESS=='

def _get_parser(self, obj, path):
try:
for attr in path.split('.'):
if isinstance(obj, dict):
obj = obj[attr]
else:
obj = getattr(obj, attr)
except (KeyError, AttributeError) as exc:
msg = (
f'"{obj}" has no key/attribute "{attr} (path: {path})"\n'
f'Incorrect argparse :module: or :func: values?'
)
raise self.error(msg) from exc
if isinstance(obj, ArgumentParser):
parser = obj
elif 'passparser' in self.options:
parser = ArgumentParser()
obj(parser)
else:
parser = obj()
return parser

def run(self):
if 'module' in self.options and 'func' in self.options:
module_name = self.options['module']
Expand All @@ -734,7 +756,7 @@ def run(self):
exec(code, mod)
module_name = None
attr_name = self.options['func']
func = mod[attr_name]
parser = self._get_parser(mod, attr_name)
else:
msg = ':module: and :func: should be specified, or :ref:, or :filename: and :func:'
raise self.error(msg)
Expand All @@ -750,22 +772,8 @@ def run(self):
f'{sys.exc_info()[1]}'
)
raise self.error(msg) from exc
parser = self._get_parser(mod, attr_name)

if not hasattr(mod, attr_name):
msg = (
f'Module "{module_name}" has no attribute "{attr_name}"\n'
f'Incorrect argparse :module: or :func: values?'
)
raise self.error(msg)
func = getattr(mod, attr_name)

if isinstance(func, ArgumentParser):
parser = func
elif 'passparser' in self.options:
parser = ArgumentParser()
func(parser)
else:
parser = func()
if 'path' not in self.options:
self.options['path'] = ''
path = str(self.options['path'])
Expand Down
15 changes: 15 additions & 0 deletions test/roots/test-default-html/inside-class.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Argparse inside class Foo
#########################

.. argparse::
:filename: sample-inside-class.py
:prog: sample-inside-class-foo
:func: Foo.parser

Argparse inside class Foo.Bar
#############################

.. argparse::
:filename: sample-inside-class.py
:prog: sample-inside-class-foo-bar
:func: Foo.Bar.parser
14 changes: 14 additions & 0 deletions test/sample-inside-class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import argparse

desc = 'Test parsing of Argparse instances inside classes'


class Foo:
parser = argparse.ArgumentParser(prog=f'{__name__}-foo', description=desc)
parser.add_argument('--foo-arg1', help='foo-arg1 help')
parser.add_argument('--foo-arg2', help='foo-arg2 help')

class Bar:
parser = argparse.ArgumentParser(prog=f'{__name__}-foo-bar', description=desc)
parser.add_argument('--foo-bar-arg1', help='foo-bar-arg1 help')
parser.add_argument('--foo-bar-arg2', help='foo-bar-arg2 help')
45 changes: 45 additions & 0 deletions test/test_default_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,51 @@
('.//section/dl/dd/p', 'Default', False),
],
),
(
'inside-class.html',
[
(
".//section[@id='argparse-inside-class-foo']/h1",
'Argparse inside class Foo',
),
(
".//section[@id='argparse-inside-class-foo']//div[@class='highlight']//span",
'usage',
),
(
".//section[@id='sample-inside-class-foo-named-arguments']/h2",
'Named Arguments',
),
(
".//section[@id='sample-inside-class-foo-named-arguments']/dl/dt[1]/kbd",
'--foo-arg1',
),
(
".//section[@id='sample-inside-class-foo-named-arguments']/dl/dt[2]/kbd",
'--foo-arg2',
),
(
".//section[@id='argparse-inside-class-foo-bar']/h1",
'Argparse inside class Foo.Bar',
),
(
".//section[@id='argparse-inside-class-foo-bar']//div[@class='highlight']//span",
'usage',
),
(
".//section[@id='sample-inside-class-foo-bar-named-arguments']/h2",
'Named Arguments',
),
(
".//section[@id='sample-inside-class-foo-bar-named-arguments']/dl/dt[1]/kbd",
'--foo-bar-arg1',
),
(
".//section[@id='sample-inside-class-foo-bar-named-arguments']/dl/dt[2]/kbd",
'--foo-bar-arg2',
),
],
),
],
)
@pytest.mark.sphinx('html', testroot='default-html')
Expand Down
Loading