Skip to content

Make _SubParsersAction public #85758

Closed
Closed

Description

@sourcedelica
BPO 41592
Nosy @rhettinger, @sourcedelica, @Fidget-Spinner

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2021-10-22.12:28:22.102>
created_at = <Date 2020-08-19.19:21:34.462>
labels = ['type-feature', '3.8', '3.9', 'invalid', '3.7', 'library']
title = 'Make _SubParsersAction public'
updated_at = <Date 2021-10-25.23:51:03.692>
user = 'https://github.com/sourcedelica'

bugs.python.org fields:

activity = <Date 2021-10-25.23:51:03.692>
actor = 'paul.j3'
assignee = 'none'
closed = True
closed_date = <Date 2021-10-22.12:28:22.102>
closer = 'kj'
components = ['Library (Lib)']
creation = <Date 2020-08-19.19:21:34.462>
creator = 'sourcedelica'
dependencies = []
files = []
hgrepos = []
issue_num = 41592
keywords = []
message_count = 8.0
messages = ['375670', '404724', '404759', '404769', '404774', '404979', '404982', '405009']
nosy_count = 4.0
nosy_names = ['rhettinger', 'paul.j3', 'sourcedelica', 'kj']
pr_nums = []
priority = 'normal'
resolution = 'not a bug'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue41592'
versions = ['Python 3.5', 'Python 3.6', 'Python 3.7', 'Python 3.8', 'Python 3.9']

Activity

  1. sourcedelica commented on Aug 19, 2020

    sourcedelicamannequin
    MannequinAuthor

    ArgumentParser.add_subparsers() returns a _SubParsersAction. This requires user code using type annotations to use a protected type which causes type checkers like PyCharm to complain. For example:

    def add_subparser(name: str, subparser: _SubParsersAction, subparsers: dict) -> ArgumentParser:
        parser = subparser.add_parser(name)
        parser.add_argument('-v', '--verbose', action='store_true')
        subparsers[name] = parser
        return parser

    You can't use plain Action because Action doesn't have the add_parser() method.

  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    type-featureA feature request or enhancement
    on Aug 19, 2020
  3. Fidget-Spinner commented on Oct 22, 2021

    @Fidget-Spinner
    Member

    Turning a class public or not requires a lot more discussion by the argparse maintainers (I'm definitely not one of them). Have you considered this workaround for PyCharm?

    from argparse import (
    _SubParsersAction, # noqa
    ArgumentParser,
    )

  4. Fidget-Spinner commented on Oct 22, 2021

    @Fidget-Spinner
    Member

    Closing this because this is a linter problem (inside PyCharm), not a Python problem. In the earlier message I provided a solution for PyCharm.

    PS, mypy --strict doesn't complain when I import _SubParsersAction, meaning most type checkers likely won't complain either.

    Thanks for your time!

  5. 1 remaining item

  6. sourcedelica commented on Oct 22, 2021

    sourcedelicamannequin
    MannequinAuthor

    Yes - that was the first thing I tried and why I logged the bug. I will log a bug with Pycharm but it still seems like the long term solution is to make _SubParsersAction public since it is used like a public object. I thought that logging the bug here would be the start of the discussion by the argparse maintainers. Where can I get their attention?

  7. Fidget-Spinner commented on Oct 22, 2021

    @Fidget-Spinner
    Member

    Where can I get their attention?

    I've nosied Raymond on this bug, he should be receiving our messages.

  8. paulj3 commented on Oct 25, 2021

    paulj3mannequin
    Mannequin

    add_argument also returns a Action subclass object. All of those subclasses are "private". While that return reference is usually ignored, sometimes it is useful to it, assigning it to a variable or list. The documentation could be clearer about access to Action objects.

  9. sourcedelica commented on Oct 25, 2021

    sourcedelicamannequin
    MannequinAuthor

    But is _SubParsersAction really private if there are methods on it that are required to use (add_parser())?

  10. paulj3 commented on Oct 25, 2021

    paulj3mannequin
    Mannequin

    A _SubParsersAction is private to the extent that users should not attempt to create it directly, and thus don't need to know the details - beyond what's documented:

    "The add_subparsers() method is normally called with no arguments and returns a special action object. This object has a single method, add_parser(), which takes a command name and any ArgumentParser constructor arguments, and returns an ArgumentParser object that can be modified as usual."

    All action objects have methods like __call__ and format_usage. The subparsers Action has other methods, but add_parser is the only new "public" method.

    There's a tension in writing documentation between getting all details just right, and providing just enough for most users. As it is, many new users are over whelmed by the documentation.

    My understanding is that the "private/public" designation is a convenience for users, and not enforced by Python developers.

    I gather though that some corporate users have policies that prohibit modification of "private" objects, supposedly due to a fear that Python could modify or eliminate those objects without proper notification. Somehow the "public" documentation sets that part of the code in stone. My experience here is that it's easier modify the documentation to fit the code than the other way around :)

  11. transferred this issue fromon Apr 10, 2022
  12. ms-lolo commented on Nov 18, 2022

    @ms-lolo

    I'd love to resurface this issue or, at least, voice my disappointment with the resolution. The _SubParsersAction simply has to be a public class if we are expected to handle instances of it and be able to pass them around. The documentation is meant to document the public API but the code is still in charge of what that public API is, and the fact that there is a class with an add_parser() method that we should call means it's public.

    If the developers don't want us to initialize it, then an abstract class or protocol should be defined and made public so we can import it and use it as a type hint without having access to the concrete class for us to instantiate.

    This is not in any way related to pycharm.

    Additionally, the __all__ variable in arparse.py doesn't even expose _SubParsersAction so there are now two things telling us that we should never import this class. The fact that it's not exposed by __all__, and the fact that the class name starts with an underscore. The fact that the documentation is promising to keep things stable does not negate the fact that the code is warning us, in two different ways, that what we are doing is very much not stable. It's not acceptable to think that when we run into private values in the code, we should go make sure things are actually private in the documentation.

    Here's an example of what could be exposed by the library (ignoring any versioning differences):

    class SubParser(Protocol):
        @abstractmethod
        def add_parser(self, name, **kwargs):
            pass

    @paulj3, @Fidget-Spinner: I would really like you to revisit this issue since it causes a large amount of confusion on large projects and teams where this constantly pops up as unstable tech debt because we import private classes and have to explain to folks that the library maintainers are telling us to just not worry about this one case.

  13. Fidget-Spinner commented on Nov 19, 2022

    @Fidget-Spinner
    Member

    @ms-lolo reading again, I see what you mean. Thank you for explaining further. I will reopen the issue. This is the most I can do, as I do not maintain the argparse module and I am not comfortable changing anything in there.

    I don't know if argparse even has a maintainer, considering it has no maintainers listed in the experts index https://devguide.python.org/core-developers/experts/index.html.

  14. Fidget-Spinner commented on Nov 19, 2022

    @Fidget-Spinner
    Member

    Sorry if this isn't the answer you want, and about me closing this issue too hastily in the first place.

  15. ms-lolo commented on Nov 21, 2022

    @ms-lolo

    oh no, this was a great answer thank you @Fidget-Spinner! do you have any idea how we might be able to find someone that could help work on this? otherwise just having this issue open will already provide some benefits for us in internal conversations.

  16. gvanrossum commented on Nov 22, 2022

    @gvanrossum
    Member

    @ms-lolo If you are at MS can you contact me on Teams chat? That might be quicker than going back and forth on the issue.

  17. ms-lolo commented on Nov 22, 2022

    @ms-lolo

    @Fidget-Spinner, had a chat with @gvanrossum and it sounds like we have a philosophical disagreement. This is unlikely to be changed so I think you can go ahead and close the issue after all.

    For folks running into this issue, creating a protocol for the class and using that in your type hints will at least prevent you from needing to import and ignore warnings for protected classes. The one I've created on my side looks like the one I showed in the comments above. Linters will properly allow you to pass in an instance of argparse._SubParsersAction as long as your protocol matches the signature of argparse._SubParsersAction. I'm a PyCharm user and can confirm this works fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.7 (EOL)end of life3.8 (EOL)end of life3.9 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions