You should decouple your cli from your main function
Many of my colleagues are working on writing better code, so I’m sharing a little tip that I find very useful.
Your main()
function should be independent of the argparser
.
You should be able to drive your application from both the cli/terminal (using the CliAdapter
) and for example your tests (using the TestAdapter
).
I have written about this before here in Your argparse is not the interface of your application.
Let me illustrate:
"""
Example script to show how to decouple your application from the CLI.
Your CLI shouldn't be the interface to your application, it should just be one
way of driving the application.
Notice how main() is decoupled from the CLI. It knows nothing about how to parse
arguments.
"""
import argparse
from abc import abstractmethod, ABC
from pydantic import BaseModel
class InputModel(BaseModel):
value: int
class InputInterface(ABC):
@abstractmethod
def parse(self, args) -> InputModel:
raise NotImplementedError
class CliAdapter(InputInterface):
def parse(self, args) -> InputModel:
parser = argparse.ArgumentParser(description='Process an integer.')
parser.add_argument('integer', type=int, help='An integer value')
args = parser.parse_args(args)
return InputModel(value=args.integer)
class TestAdapter(InputInterface):
def parse(self, args) -> InputModel:
return InputModel(value=10)
def main(input: InputModel):
print(input.value)
if __name__ == "__main__":
# In your main entrypoint
cli = CliAdapter()
input = cli.parse(["10"])
main(input)
# In a pytest fixture and test code
test = TestAdapter()
input = test.parse(None)
main(input)
Your main()
should not care whether it gets the arguments from the cli or from
any other adapter. If it gets a correct InputModel
it should just run.
Comments