From 1b1d6648690f682e8d6487f2a9ca949015f1a286 Mon Sep 17 00:00:00 2001 From: Yilei Yang Date: Wed, 1 Nov 2017 16:11:06 -0700 Subject: [PATCH] Add gflags -> absl.flags migration guidelines. --- README.md | 9 +- absl_migration/migrate.py | 134 +++++++++++++++++++++++++ absl_migration/migration_guidelines.md | 86 ++++++++++++++++ 3 files changed, 225 insertions(+), 4 deletions(-) create mode 100755 absl_migration/migrate.py create mode 100644 absl_migration/migration_guidelines.md diff --git a/README.md b/README.md index 2fa0d72..54c1855 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -This library has been merged into [Abseil Python Common -Libraries](https://github.com/abseil/abseil-py). +This library has been merged into +[Abseil Python Common Libraries](https://github.com/abseil/abseil-py). -This repository is not maintained and will not be updated. Please use -[Abseil](https://github.com/abseil/abseil-py) instead. +This repository is not maintained and will not be updated. +Please see [the guidelines](absl_migration/migration_guidelines.md) +for migrating to [Abseil](https://github.com/abseil/abseil-py). diff --git a/absl_migration/migrate.py b/absl_migration/migrate.py new file mode 100755 index 0000000..e823a7b --- /dev/null +++ b/absl_migration/migrate.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +"""Helper tool for gflags to absl.flags migration.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import os +import re + + +_MIGRATIONS = [ + (r'\b(g?flags\.)DEFINE_multistring\b', r'\1DEFINE_multi_string'), + (r'\b(g?flags\.)DEFINE_multi_int\b', r'\1DEFINE_multi_integer'), + (r'\b(g?flags\.)RegisterValidator\b', r'\1register_validator'), + (r'\b(g?flags\.)Validator\b', r'\1validator'), + (r'\b(g?flags\.)RegisterMultiFlagsValidator\b', r'\1register_multi_flags_validator'), + (r'\b(g?flags\.)MultiFlagsValidator\b', r'\1multi_flags_validator'), + (r'\b(g?flags\.)MarkFlagAsRequired\b', r'\1mark_flag_as_required'), + (r'\b(g?flags\.)MarkFlagsAsRequired\b', r'\1mark_flags_as_required'), + (r'\b(g?flags\.)MarkFlagsAsMutualExclusive\b', r'\1mark_flags_as_mutual_exclusive'), + (r'\b(g?flags\.)DECLARE_key_flag\b', r'\1declare_key_flag'), + (r'\b(g?flags\.)ADOPT_module_key_flags\b', r'\1adopt_module_key_flags'), + (r'\b(g?flags\.)DISCLAIM_key_flags\b', r'\1disclaim_key_flags'), + (r'\b(g?flags\.)GetHelpWidth\b', r'\1get_help_width'), + (r'\b(g?flags\.)TextWrap\b', r'\1text_wrap'), + (r'\b(g?flags\.)FlagDictToArgs\b', r'\1flag_dict_to_args'), + (r'\b(g?flags\.)DocToHelp\b', r'\1doc_to_help'), + (r'\b(g?flags\.)FlagsError\b', r'\1Error'), + (r'\b(g?flags\.)IllegalFlagValue\b', r'\1IllegalFlagValueError'), + (r'\bFLAGS\.AppendFlagsIntoFile\b', r'FLAGS.append_flags_into_file'), + (r'\bFLAGS\.AppendFlagValues\b', r'FLAGS.append_flag_values'), + (r'\bFLAGS\.FindModuleDefiningFlag\b', r'FLAGS.find_module_defining_flag'), + (r'\bFLAGS\.FindModuleIdDefiningFlag\b', r'FLAGS.find_module_id_defining_flag'), + (r'\bFLAGS\.FlagsByModuleDict\b', r'FLAGS.flags_by_module_dict'), + (r'\bFLAGS\.FlagsByModuleIdDict\b', r'FLAGS.flags_by_module_id_dict'), + (r'\bFLAGS\.FlagsIntoString\b', r'FLAGS.flags_into_string'), + (r'\bFLAGS\.FlagValuesDict\b', r'FLAGS.flag_values_dict'), + (r'\bFLAGS\.IsGnuGetOpt\b', r'FLAGS.is_gnu_getopt'), + (r'\bFLAGS\.IsParsed\b', r'FLAGS.is_parsed'), + (r'\bFLAGS\.KeyFlagsByModuleDict\b', r'FLAGS.key_flags_by_module_dict'), + (r'\bFLAGS\.MainModuleHelp\b', r'FLAGS.main_module_help'), + (r'\bFLAGS\.MarkAsParsed\b', r'FLAGS.mark_as_parsed'), + (r'\bFLAGS\.ModuleHelp\b', r'FLAGS.module_help'), + (r'\bFLAGS\.ReadFlagsFromFiles\b', r'FLAGS.read_flags_from_files'), + (r'\bFLAGS\.RemoveFlagValues\b', r'FLAGS.remove_flag_values'), + (r'\bFLAGS\.Reset\b', r'FLAGS.unparse_flags'), + (r'\bFLAGS\.SetDefault\b', r'FLAGS.set_default'), + (r'\bFLAGS\.WriteHelpInXMLFormat\b', r'FLAGS.write_help_in_xml_format'), + (r'\bFLAGS\.UseGnuGetOpt\(use_gnu_getopt=', r'FLAGS.set_gnu_getopt(gnu_getopt='), + (r'\bFLAGS\.UseGnuGetOpt\(', r'FLAGS.set_gnu_getopt('), +] + + +_LEGACY_APIS_RE = re.compile( + r'\b(' + r'(g?flags\.DEFINE_multistring)|' + r'(g?flags\.DEFINE_multi_int)|' + r'(g?flags\.RegisterValidator)|' + r'(g?flags\.Validator)|' + r'(g?flags\.RegisterMultiFlagsValidator)|' + r'(g?flags\.MultiFlagsValidator)|' + r'(g?flags\.MarkFlagAsRequired)|' + r'(g?flags\.MarkFlagsAsRequired)|' + r'(g?flags\.MarkFlagsAsMutualExclusive)|' + r'(g?flags\.DECLARE_key_flag)|' + r'(g?flags\.ADOPT_module_key_flags)|' + r'(g?flags\.DISCLAIM_key_flags)|' + r'(g?flags\.GetHelpWidth)|' + r'(g?flags\.TextWrap)|' + r'(g?flags\.FlagDictToArgs)|' + r'(g?flags\.DocToHelp)|' + r'(g?flags\.FlagsError)|' + r'(g?flags\.IllegalFlagValue)|' + r'(FLAGS\.AppendFlagsIntoFile)|' + r'(FLAGS\.AppendFlagValues)|' + r'(FLAGS\.FindModuleDefiningFlag)|' + r'(FLAGS\.FindModuleIdDefiningFlag)|' + r'(FLAGS\.FlagsByModuleDict)|' + r'(FLAGS\.FlagsByModuleIdDict)|' + r'(FLAGS\.FlagsIntoString)|' + r'(FLAGS\.FlagValuesDict)|' + r'(FLAGS\.IsGnuGetOpt)|' + r'(FLAGS\.IsParsed)|' + r'(FLAGS\.KeyFlagsByModuleDict)|' + r'(FLAGS\.MainModuleHelp)|' + r'(FLAGS\.MarkAsParsed)|' + r'(FLAGS\.ModuleHelp)|' + r'(FLAGS\.ReadFlagsFromFiles)|' + r'(FLAGS\.RemoveFlagValues)|' + r'(FLAGS\.Reset)|' + r'(FLAGS\.SetDefault)|' + r'(FLAGS\.WriteHelpInXMLFormat)|' + r'(from\ gflags\ import\ (argument_parser|exceptions|flag|flags_formatting_test|flags_unicode_literals_test|flagvalues|validators))' + r')\b') + + +def run(root_dir, migrate): + for root, _, filenames in os.walk(root_dir): + for filename in filenames: + if not filename.endswith('.py'): + continue + filepath = os.path.join(root, filename) + with open(filepath) as f: + content = f.read() + + if migrate: + new_content = content + for m in _MIGRATIONS: + new_content = re.sub(m[0], m[1], new_content) + if new_content != content: + with open(filepath, 'w') as f: + f.write(new_content) + content = new_content + + for index, line in enumerate(content.split('\n')): + if _LEGACY_APIS_RE.search(line): + print('{}:{} {}'.format(filepath, index + 1, line)) + + +def main(): + parser = argparse.ArgumentParser( + description='A gflags -> absl.flags migration tool.') + parser.add_argument('--migrate', dest='migrate', action='store_true') + parser.set_defaults(migrate=False) + parser.add_argument('--root_dir', dest='root_dir', required=True) + args = parser.parse_args() + + run(args.root_dir, args.migrate) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/absl_migration/migration_guidelines.md b/absl_migration/migration_guidelines.md new file mode 100644 index 0000000..d614ec8 --- /dev/null +++ b/absl_migration/migration_guidelines.md @@ -0,0 +1,86 @@ +# gflags to absl.flags Migration Guidelines + +The [python-gflags](https://github.com/google/python-gflags) library has been merged into [Abseil Python Common Libraries](https://github.com/abseil/abseil-py). +As a result, python-gflags will no longer be maintained. +This document tries to help explain how to migrate from `gflags` to `absl.flags`. + +Note if your upstream dependencies have been migrated to `absl.flags`, +we encourage you also migrating to [absl.flags](https://github.com/abseil/abseil-py/tree/master/absl/flags). +Otherwise there will be two global `FLAGS` objects coexisting in the same process. +Both of them define flags and need to parse command-line args. + +## Changes + +This section lists the majors changes in absl.flags. + +### API Renaming + +1. Except the `DEFINE` functions, other method and function names are updated so they conform to PEP8 snake_case style. +1. `DEFINE_multistring` and `DEFINE_multti_int` are renamed to `DEFINE_multi_string` and `DEFINE_multti_integer`, for consistency. +1. Exceptions are renamed so they end with `Error`. Also `FlagsError` renamed to `Error`. +1. All sub-modules have been made private, the public APIs should only be accessed at the package level. + +### Behavior Changes + +1. Flags now use [GNU-style](https://docs.python.org/3/library/getopt.html#getopt.gnu_getopt) parsing by default. To opt-in to non-GNU style, call `FLAGS.set_gnu_getopt(False)` before parsing flags. +1. Accessing flag values before command-line args are parsed now raises the `UnparsedFlagAccessError`. +1. It is no longer legal to define a flag with a default value type that mismatches the flag type. +1. `FLAGS.set_default` no longer overrides the current value if the flag is set by `FLAGS.name = value`, or specified in the command line. + +## Migration Guidelines + +We suggest the following steps for migrating from `gflags` to `absl.flags`: + +1. Upgrade to the latest python-gflags version, which contains the new API names (see [Appendix](#appendix-renamed-apis)). +1. Update the codebase to use the new APIs. + * You can leverage [migrate.py](migrate.py) to perform the renames and sanity checks. Be aware that it only uses regex matching, which may have false positives or miss some cases. +1. Remove the dependency on [python-gflags](https://pypi.python.org/pypi/python-gflags) and add the dependency on [absl-py](https://pypi.python.org/pypi/absl-py). Then replace `import gflags` with `from absl import flags as gflags`. +1. Once step (3) succeeds, remove the import alias and just use `flags`. + +Depending on your project, you can choose to do these at once, or make incremental changes. + +## Appendix: Renamed APIs + +Here is a list of renamed APIs: + +``` +DEFINE_multistring -> DEFINE_multi_string +DEFINE_multi_int -> DEFINE_multi_integer +RegisterValidator -> register_validator +Validator -> validator +RegisterMultiFlagsValidator -> register_multi_flags_validator +MultiFlagsValidator -> multi_flags_validator +MarkFlagAsRequired -> mark_flag_as_required +MarkFlagsAsRequired -> mark_flags_as_required +MarkFlagsAsMutualExclusive -> mark_flags_as_mutual_exclusive +DECLARE_key_flag -> declare_key_flag +ADOPT_module_key_flags -> adopt_module_key_flags +DISCLAIM_key_flags -> disclaim_key_flags +GetHelpWidth -> get_help_width +TextWrap -> text_wrap +FlagDictToArgs -> flag_dict_to_args +DocToHelp -> doc_to_help +FlagsError -> Error +IllegalFlagValue -> IllegalFlagValueError +ArgumentParser.Parse -> ArgumentParser.parse +ArgumentParser.Type -> ArgumentParser.flag_type +FLAGS.AppendFlagsIntoFile -> FLAGS.append_flags_into_file +FLAGS.AppendFlagValues -> FLAGS.append_flag_values +FLAGS.FindModuleDefiningFlag -> FLAGS.find_module_defining_flag +FLAGS.FindModuleIdDefiningFlag -> FLAGS.find_module_id_defining_flag +FLAGS.FlagsByModuleDict -> FLAGS.flags_by_module_dict +FLAGS.FlagsByModuleIdDict -> FLAGS.flags_by_module_id_dict +FLAGS.FlagsIntoString -> FLAGS.flags_into_string +FLAGS.FlagValuesDict -> FLAGS.flag_values_dict +FLAGS.IsGnuGetOpt -> FLAGS.is_gnu_getopt +FLAGS.IsParsed -> FLAGS.is_parsed +FLAGS.KeyFlagsByModuleDict -> FLAGS.key_flags_by_module_dict +FLAGS.MainModuleHelp -> FLAGS.main_module_help +FLAGS.MarkAsParsed -> FLAGS.mark_as_parsed +FLAGS.ModuleHelp -> FLAGS.module_help +FLAGS.ReadFlagsFromFiles -> FLAGS.read_flags_from_files +FLAGS.RemoveFlagValues -> FLAGS.remove_flag_values +FLAGS.Reset -> FLAGS.unparse_flags +FLAGS.SetDefault -> FLAGS.set_default +FLAGS.WriteHelpInXMLFormat -> FLAGS.write_help_in_xml_format +```