select.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #
  2. # Copyright (c) 2021 Project CHIP Authors
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. """Data frame selection utilities."""
  17. import memdf.name
  18. import memdf.util.pretty
  19. import memdf.util.config
  20. from memdf import Config, ConfigDescription, DF
  21. def split_size(config: Config, key: str) -> None:
  22. """Split a name:size configuration value.
  23. When a program supports a size threshold for selection or summary,
  24. this can be specificed for a particular item with a suffix on the
  25. configuration, e.g. `--section=.text:16K`.
  26. Given a configuration key `col.select` referring to such a list of
  27. arguments, this function strips any sizes from those arguments
  28. and stores them as a name:size dictionary in `col.limit`.
  29. """
  30. src = key.split('.')
  31. dst = src[:-1] + ['limit']
  32. splits = [s.split(':') for s in config.getl(src, [])]
  33. config.putl(src, [x[0] for x in splits])
  34. config.putl(dst, {
  35. x[0]: memdf.util.config.parse_size(x[1])
  36. for x in splits if len(x) > 1
  37. })
  38. def get_limit(config: Config, column: str, name: str) -> int:
  39. return config.getl([column, 'limit', name], config.get('report.limit', 0))
  40. def postprocess_selections(config: Config, key: str) -> None:
  41. """Resolve select/ignore command options."""
  42. split_size(config, key)
  43. choice, select = key.split('.')
  44. assert select == 'select'
  45. selections = config.get(key)
  46. if not config.getl([choice, 'ignore-all'], False):
  47. if defaults := config.getl([choice, 'default']):
  48. for i in config.getl([choice, 'ignore']):
  49. if i in defaults:
  50. defaults.remove(i)
  51. selections += defaults
  52. config.put(key, frozenset(selections))
  53. def select_and_ignore_config_desc(key: str) -> ConfigDescription:
  54. return {
  55. Config.group_map(key): {
  56. 'group': 'select'
  57. },
  58. f'{key}.select': {
  59. 'help':
  60. f'{key.capitalize()}(s) to process; otherwise all not ignored',
  61. 'metavar': 'NAME',
  62. 'default': [],
  63. 'argparse': {
  64. 'alias': [f'--{key}'],
  65. },
  66. 'postprocess': postprocess_selections
  67. },
  68. f'{key}.select-all': {
  69. 'help': f'Select all {key}s',
  70. 'default': False,
  71. },
  72. key + '.ignore': {
  73. 'help': f'{key.capitalize()}(s) to ignore',
  74. 'metavar': 'NAME',
  75. 'default': [],
  76. },
  77. f'{key}.ignore-all': {
  78. 'help': f'Ignore all {key}s unless explicitly selected',
  79. 'default': False,
  80. },
  81. }
  82. SECTION_CONFIG = select_and_ignore_config_desc('section')
  83. SYMBOL_CONFIG = select_and_ignore_config_desc('symbol')
  84. REGION_CONFIG = select_and_ignore_config_desc('region')
  85. CONFIG: ConfigDescription = {
  86. Config.group_def('select'): {
  87. 'title': 'selection options',
  88. },
  89. **SECTION_CONFIG,
  90. **SYMBOL_CONFIG,
  91. **REGION_CONFIG,
  92. }
  93. COLLECTED_CHOICES = ['symbol', 'section']
  94. SYNTHETIC_CHOICES = ['region']
  95. SELECTION_CHOICES = COLLECTED_CHOICES + SYNTHETIC_CHOICES
  96. def is_selected(config: Config, column, name) -> bool:
  97. """Test `name` against the configured selection criteria for `column`."""
  98. if config.getl([column, 'select-all']):
  99. return True
  100. if name in config.getl([column, 'select'], []):
  101. return True
  102. return False
  103. def synthesize_region(config: Config, df: DF, column: str) -> DF:
  104. """Add a 'region' column derived from the 'section' column."""
  105. cmap = config.transpose_dictlist(config.get('region.sections', {}))
  106. memdf.util.pretty.debug(cmap)
  107. df[column] = df['section'].map(lambda x: cmap.get(x, memdf.name.UNKNOWN))
  108. return df
  109. SYNTHESIZE = {
  110. 'region': synthesize_region,
  111. }
  112. def synthesize_column(config: Config, df: DF, column: str) -> DF:
  113. if column not in df.columns:
  114. SYNTHESIZE[column](config, df, column)
  115. return df
  116. def select_configured_column(config: Config, df: DF, column: str) -> DF:
  117. """Apply configured selection options to a column"""
  118. if column in df and not config.getl([column, 'select-all']):
  119. selections = config.getl([column, 'select'], [])
  120. if selections:
  121. df = df.loc[df[column].isin(selections)]
  122. return df
  123. def select_configured(config: Config, df: DF, columns=SELECTION_CHOICES) -> DF:
  124. for column in columns:
  125. df = select_configured_column(config, df, column)
  126. return df