argparse_util.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #!/usr/bin/env python3
  2. # mypy: allow-untyped-defs
  3. # Copyright (c) Facebook, Inc. and its affiliates.
  4. # All rights reserved.
  5. #
  6. # This source code is licensed under the BSD-style license found in the
  7. # LICENSE file in the root directory of this source tree.
  8. import os
  9. from argparse import Action
  10. class env(Action):
  11. """
  12. Get argument values from ``PET_{dest}`` before defaulting to the given ``default`` value.
  13. For flags (e.g. ``--standalone``)
  14. use ``check_env`` instead.
  15. .. note:: when multiple option strings are specified, ``dest`` is
  16. the longest option string (e.g. for ``"-f", "--foo"``
  17. the env var to set is ``PET_FOO`` not ``PET_F``)
  18. Example:
  19. ::
  20. parser.add_argument("-f", "--foo", action=env, default="bar")
  21. ./program -> args.foo="bar"
  22. ./program -f baz -> args.foo="baz"
  23. ./program --foo baz -> args.foo="baz"
  24. PET_FOO="env_bar" ./program -f baz -> args.foo="baz"
  25. PET_FOO="env_bar" ./program --foo baz -> args.foo="baz"
  26. PET_FOO="env_bar" ./program -> args.foo="env_bar"
  27. parser.add_argument("-f", "--foo", action=env, required=True)
  28. ./program -> fails
  29. ./program -f baz -> args.foo="baz"
  30. PET_FOO="env_bar" ./program -> args.foo="env_bar"
  31. PET_FOO="env_bar" ./program -f baz -> args.foo="baz"
  32. """
  33. def __init__(self, dest, default=None, required=False, **kwargs) -> None:
  34. env_name = f"PET_{dest.upper()}"
  35. default = os.environ.get(env_name, default)
  36. # ``required`` means that it NEEDS to be present in the command-line args
  37. # rather than "this option requires a value (either set explicitly or default"
  38. # so if we found default then we don't "require" it to be in the command-line
  39. # so set it to False
  40. if default:
  41. required = False
  42. super().__init__(dest=dest, default=default, required=required, **kwargs)
  43. def __call__(self, parser, namespace, values, option_string=None):
  44. setattr(namespace, self.dest, values)
  45. class check_env(Action):
  46. """
  47. Check whether the env var ``PET_{dest}`` exists before defaulting to the given ``default`` value.
  48. Equivalent to
  49. ``store_true`` argparse built-in action except that the argument can
  50. be omitted from the commandline if the env var is present and has a
  51. non-zero value.
  52. .. note:: it is redundant to pass ``default=True`` for arguments
  53. that use this action because a flag should be ``True``
  54. when present and ``False`` otherwise.
  55. Example:
  56. ::
  57. parser.add_argument("--verbose", action=check_env)
  58. ./program -> args.verbose=False
  59. ./program --verbose -> args.verbose=True
  60. PET_VERBOSE=1 ./program -> args.verbose=True
  61. PET_VERBOSE=0 ./program -> args.verbose=False
  62. PET_VERBOSE=0 ./program --verbose -> args.verbose=True
  63. Anti-pattern (don't do this):
  64. ::
  65. parser.add_argument("--verbose", action=check_env, default=True)
  66. ./program -> args.verbose=True
  67. ./program --verbose -> args.verbose=True
  68. PET_VERBOSE=1 ./program -> args.verbose=True
  69. PET_VERBOSE=0 ./program -> args.verbose=False
  70. """
  71. def __init__(self, dest, default=False, **kwargs) -> None:
  72. env_name = f"PET_{dest.upper()}"
  73. default = bool(int(os.environ.get(env_name, "1" if default else "0")))
  74. super().__init__(dest=dest, const=True, default=default, nargs=0, **kwargs)
  75. def __call__(self, parser, namespace, values, option_string=None):
  76. setattr(namespace, self.dest, self.const)