I want to subclass/wrap subprocess.Popen. However, it has a lot of arguments. The usual ways to solve this, as far as I'm aware, are 1. "biting the bullet":
class MyPopen1(subprocess.Popen):
def __init__(self, myarg1, myarg2, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True,
shell=False, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, user=None, group=None, extra_groups=None,
encoding=None, errors=None, text=None, umask=-1, pipesize=-1,
process_group=None):
arguments = self.handle_custom_arguments(myarg1, myarg2)
super().__init__(arguments, bufsize, executable,
stdin, stdout, stderr,
preexec_fn, close_fds,
shell, cwd, env, universal_newlines,
startupinfo, creationflags,
restore_signals, start_new_session,
pass_fds, user=user, group=group, extra_groups=extra_groups,
encoding=encoding, errors=errors, text=text, umask=umask, pipesize=pipesize,
process_group=process_group)
or 2. using *args, **kwargs.
class MyPopen2(subprocess.Popen):
def __init__(self, myarg1, myarg2, *args, **kwargs):
arguments = self.handle_custom_arguments(myarg1, myarg2)
super().__init__(arguments, *args, **kwargs)
The second is a lot easier to write; it requires no maintenance if a new argument is added to subprocess.Popen in 3.1x. The downside is that it has no types, which means no static typechecking and no function signature hinting in a text editor.
We can get a little closer with @functools.wraps(...), but at the cost of the signature needing to be the exact same as subprocess.Popen, and it overriding doc and etc. I don't think it's what wraps is intended for.
# Not good solution.
class MyPopen3(subprocess.Popen):
@functools.wraps(subprocess.Popen.__init__)
def __init__(self, myarg1: int, myarg2: str, *args, **kwargs):
"""
:param myarg1: does foo
:param myarg2: does bar
"""
arguments = self.handle_custom_arguments(myarg1, myarg2)
super().__init__(arguments, *args, **kwargs)
Ideally, I'd want a type signature like...
def __init__(self,
myarg1: int, myarg2: str,
*args: ArgsOf[subprocess.Popen.__init__][1:],
**kwargs: KwargsOf[subprocess.Popen.__init__]):
Is there any way to get this sort of effect? If not, how can I preserve typechecking when doing something like this?
You can use Unpack[] from the typing library to grab type hints from a TypedDict:
from typing import TypedDict, Unpack
class Movie(TypedDict):
name: str
year: int
def foo(**kwargs: Unpack[Movie]) -> None: ..
https://typing.readthedocs.io/en/latest/spec/callables.html#unpack-kwargs
But given that this still makes you explicitly lay out all params in a new object, I'm not sure how much lift this is going to give you - the first method might be more expedient.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With