You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Preserve argparse.FileType by making it possible to context manage files as they're opened #150125
gh-58032 proposed, and eventually put a PendingDeprecationWarning on argparse.FileType, for the reasonable reason that it's, as written, impossible to properly close files when a failure occurs during parsing after a file has been opened (leading to ResourceWarnings), and even when parsing completes successfully, it's difficult for the caller to properly manage what it returns; with args.infile, args.outfile: works well enough up until the caller passes - as an argument, and now you're closing your own stdin/stdout unexpectedly.
My proposal is:
Add an argument (keyword-only to avoid interfering with potential changes to FileType to match open) to FileType, named stack or manager, that, if passed, will be treated as API compatible with contextlib.ExitStack (I can't imagine needing anything else, but any type providing a suitable .enter_context and .callback is adequate); when the file is opened, it will be registered on the ExitStack, via enter_context for the common case, and by registering a callback to flush it for the - case.
Only issue the PendingDeprecationWarning for FileTypes created without providing an ExitStack to manage them; FileType as a whole would no longer be deprecated, only using it in antipatterns
Document a basic recipe for using FileType so correct use would be obvious, along the lines of:
withcontextlib.ExitStack() asstack:
parser=argparse.ArgumentParser(description="FileType example")
parser.add_argument('infile', type=argparse.FileType(stack=stack), help="File to read")
parser.add_argument('outfile', type=argparse.FileType("w", stack=stack), help="File to write")
args=parser.parse_args()
# Do stuff with args.infile and args.outfile# Or use stack.pop_all() to transfer resource ownership elsewhere# Stack is closed here, all newly opened files are closed, any standard handles are flushed but not closed
The changes required are tiny, it continues to allow a race-free way to verify the legality of file arguments to the program (by opening them, the only truly race-free way) without requiring uglier patterns like:
try:
infile=open(args.infile)
exceptOSErrorase:
parser.print_usage(file=sys.stderr)
sys.exit(f"Couldn't open {args.infile!r}: {e}")
withinfile:
# Repeat for outfile
and keeping it relatively easy to write simple argparse-based programs.
For me, passing the stack is already an antipattern. Argparse should not be responsible for that. I am -1 on this one and would rather decouple responsibilities here
@picnixz: Argparse isn't directly responsible; the only part of it that ties to the ExitStack is argparse.FileType itself (the entirety of the rest of the argparse machinery is oblivious to it and remains unconnected; if FileType was on the io module instead as a sort of partial application of open, it would work equally well; it's part of argparse for namespacing reasons, there's no actual entanglement between it and the rest of the module).
I'd considered making ArgumentParser itself a context manager to manage any context managers it parses, but, discounted it due to it requiring the whole of argparse to become more context manager aware (needing to check each type-parsed argument for __enter__/__exit__ and/or requiring an argument to tell it to context manage things individually, requiring reimplementing concepts like handing off ownership, etc.). contextlib.ExitStack already does everything we want though, it's far more composable/reusable, and it's more explicit (you explicitly create and manage one with with, you explicitly pass it to FileType, or any other managed type a user invents following FileType as a model; it was created at least in part to be a model for user parsers).
I'm open to other approaches, but I think deprecating FileType, rather than fixing the problem, is a mistake; especially for short scripts, the incremental work to manually manage opening the files (especially if you're not aware of contextlib.ExitStack; a side-benefit of my approach is cross-linking to make it more discoverable) while keeping your try blocks minimal is verbose and ugly (see original post). Just this past weekend we had a talk at PyCon US 2026 by @drcraig, "How to Write a Great Command Line Application" that basically began with "I was dismayed to discover my entire presentation was recommending a deprecated tool", because any reasonable command line application taking files was using argparse.FileType to open them and benefit from all the automatic checks.
I personally teach a class that drills students on using argparse.FileType in their final project, because the final project is intended to be a "production grade tool" used by non-programmers, and we want them doing fine-grained error handling to report minimal, actionable information to the user when something goes wrong, rather than dumping stack traces to the screen. argparse.FileType means literally 80%+ of all the possible exceptions their program can raise (most of them boil down to "can't open a file", with the rest being "file with specific format isn't in correct format or otherwise contains the wrong stuff") can be handled seamlessly by argparse (without it, doing "the right thing", the terrible pattern I showed above, opening a file without with to narrow the scope for catching exceptions so they can output a more tailored message, rather than catching all OSErrors and guessing, bloats their code dramatically, and reduces the practice they get using with statements in idiomatic ways).
My point is, without FileType, the alternatives are ugly, verbose, and generally unidiomatic ("Manage your own files" ends up being "We reinvent wheels, badly"). With FileType as is, there are flaws, and while I don't think they were worth killing FileType given its utility for simple scripts, fixing them is a reasonable goal, and this is the least intrusive means of doing so.
I like FileType but the API, and having stack need to be passed seems error prone to me. Why not do something like type=pathlib.Path?
That makes it directly usable with open. Gives straightforward ways to resolve path arguments to absolute paths if needed (ex. tools which change cwd). A lot more attributes become easily accessible and there is not as much structural complexity.
In addition, other core devs already decided to deprecate the feature 2y ago. I do not like when we change our decisions on a whim either (hence the needs of a DPO thread to revert that decision)
Activity
picnixz commentedon May 19, 2026
For me, passing the stack is already an antipattern. Argparse should not be responsible for that. I am -1 on this one and would rather decouple responsibilities here
MojoVampire commentedon May 19, 2026
@picnixz: Argparse isn't directly responsible; the only part of it that ties to the
ExitStackisargparse.FileTypeitself (the entirety of the rest of theargparsemachinery is oblivious to it and remains unconnected; ifFileTypewas on theiomodule instead as a sort of partial application ofopen, it would work equally well; it's part ofargparsefor namespacing reasons, there's no actual entanglement between it and the rest of the module).I'd considered making
ArgumentParseritself a context manager to manage any context managers it parses, but, discounted it due to it requiring the whole ofargparseto become more context manager aware (needing to check eachtype-parsed argument for__enter__/__exit__and/or requiring an argument to tell it to context manage things individually, requiring reimplementing concepts like handing off ownership, etc.).contextlib.ExitStackalready does everything we want though, it's far more composable/reusable, and it's more explicit (you explicitly create and manage one withwith, you explicitly pass it toFileType, or any other managed type a user invents followingFileTypeas a model; it was created at least in part to be a model for user parsers).I'm open to other approaches, but I think deprecating
FileType, rather than fixing the problem, is a mistake; especially for short scripts, the incremental work to manually manage opening the files (especially if you're not aware ofcontextlib.ExitStack; a side-benefit of my approach is cross-linking to make it more discoverable) while keeping yourtryblocks minimal is verbose and ugly (see original post). Just this past weekend we had a talk at PyCon US 2026 by @drcraig, "How to Write a Great Command Line Application" that basically began with "I was dismayed to discover my entire presentation was recommending a deprecated tool", because any reasonable command line application taking files was usingargparse.FileTypeto open them and benefit from all the automatic checks.I personally teach a class that drills students on using
argparse.FileTypein their final project, because the final project is intended to be a "production grade tool" used by non-programmers, and we want them doing fine-grained error handling to report minimal, actionable information to the user when something goes wrong, rather than dumping stack traces to the screen.argparse.FileTypemeans literally 80%+ of all the possible exceptions their program can raise (most of them boil down to "can't open a file", with the rest being "file with specific format isn't in correct format or otherwise contains the wrong stuff") can be handled seamlessly byargparse(without it, doing "the right thing", the terrible pattern I showed above, opening a file withoutwithto narrow the scope for catching exceptions so they can output a more tailored message, rather than catching all OSErrors and guessing, bloats their code dramatically, and reduces the practice they get usingwithstatements in idiomatic ways).My point is, without
FileType, the alternatives are ugly, verbose, and generally unidiomatic ("Manage your own files" ends up being "We reinvent wheels, badly"). WithFileTypeas is, there are flaws, and while I don't think they were worth killingFileTypegiven its utility for simple scripts, fixing them is a reasonable goal, and this is the least intrusive means of doing so.cmaloney commentedon May 20, 2026
I like
FileTypebut the API, and having stack need to be passed seems error prone to me. Why not do something liketype=pathlib.Path?That makes it directly usable with open. Gives straightforward ways to resolve path arguments to absolute paths if needed (ex. tools which change
cwd). A lot more attributes become easily accessible and there is not as much structural complexity.picnixz commentedon May 20, 2026
Since we are in a strong disagreement, please open a discussion on discuss.python.org. This is the way to reach a consensus.
picnixz commentedon May 20, 2026
In addition, other core devs already decided to deprecate the feature 2y ago. I do not like when we change our decisions on a whim either (hence the needs of a DPO thread to revert that decision)