results.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # MODULE: grass.benchmark
  2. #
  3. # AUTHOR(S): Vaclav Petras <wenzeslaus gmail com>
  4. #
  5. # PURPOSE: Benchmarking for GRASS GIS modules
  6. #
  7. # COPYRIGHT: (C) 2021 Vaclav Petras, and by the GRASS Development Team
  8. #
  9. # This program is free software under the GNU General Public
  10. # License (>=v2). Read the file COPYING that comes with GRASS
  11. # for details.
  12. """Handling of raw results from benchmarking"""
  13. import copy
  14. import json
  15. from types import SimpleNamespace
  16. class ResultsEncoder(json.JSONEncoder):
  17. """Results encoder for JSON which handles SimpleNamespace objects"""
  18. def default(self, o):
  19. """Handle additional types"""
  20. if isinstance(o, SimpleNamespace):
  21. return o.__dict__
  22. return super().default(o)
  23. def save_results(data):
  24. """Save results structure to JSON.
  25. If the provided object does not have results attribute,
  26. it is assumed that the list which should be results attribute was provided,
  27. so the provided object object is saved under new ``results`` key.
  28. Returns JSON as str.
  29. """
  30. if not hasattr(data, "results"):
  31. data = dict(results=data)
  32. return json.dumps(data, cls=ResultsEncoder)
  33. def save_results_to_file(results, filename):
  34. """Saves results to as file as JSON.
  35. See :func:`save_results` for details.
  36. """
  37. text = save_results(results)
  38. with open(filename, "w", encoding="utf-8") as file:
  39. file.write(text)
  40. def load_results(data):
  41. """Load results structure from JSON.
  42. Takes str, returns nested structure with SimpleNamespace instead of the
  43. default dictionary object. Use attribute access to access by key
  44. (not dict-like syntax).
  45. """
  46. return json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
  47. def load_results_from_file(filename):
  48. """Loads results from a JSON file.
  49. See :func:`load_results` for details.
  50. """
  51. with open(filename, "r", encoding="utf-8") as file:
  52. return load_results(file.read())
  53. def join_results(results, prefixes=None, select=None, prefixes_as_labels=False):
  54. """Join multiple lists of results together
  55. The *results* argument either needs to be a list of result objects
  56. or an object with attribute *results* which is the list of result objects.
  57. This allows for results loaded from a file to be combined with a simple list.
  58. The function always returns just a simple list of result objects.
  59. """
  60. if not prefixes:
  61. prefixes = [None] * len(results)
  62. joined = []
  63. for result_list, prefix in zip(results, prefixes):
  64. if hasattr(result_list, "results"):
  65. # This is the actual list in the full results structure.
  66. result_list = result_list.results
  67. for result in result_list:
  68. if select and not select(result):
  69. continue
  70. result = copy.deepcopy(result)
  71. if prefix:
  72. if prefixes_as_labels:
  73. result.label = prefix
  74. else:
  75. result.label = f"{prefix}: {result.label}"
  76. joined.append(result)
  77. return joined
  78. def join_results_from_files(
  79. source_filenames, prefixes=None, select=None, prefixes_as_labels=False
  80. ):
  81. """Join multiple files into one results object."""
  82. to_merge = []
  83. for result_file in source_filenames:
  84. to_merge.append(load_results_from_file(result_file))
  85. return join_results(
  86. to_merge,
  87. prefixes=prefixes,
  88. select=select,
  89. prefixes_as_labels=prefixes_as_labels,
  90. )