1 # Copyright 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
10 from telemetry import decorators
11 from telemetry.core import camel_case
15 def DiscoverModules(start_dir, top_level_dir, pattern='*'):
16 """Discover all modules in |start_dir| which match |pattern|.
19 start_dir: The directory to recursively search.
20 top_level_dir: The top level of the package, for importing.
21 pattern: Unix shell-style pattern for filtering the filenames to import.
27 for dir_path, _, filenames in os.walk(start_dir):
28 for filename in filenames:
29 # Filter out unwanted filenames.
30 if filename.startswith('.') or filename.startswith('_'):
32 if os.path.splitext(filename)[1] != '.py':
34 if not fnmatch.fnmatch(filename, pattern):
38 module_rel_path = os.path.relpath(os.path.join(dir_path, filename),
40 module_name = re.sub(r'[/\\]', '.', os.path.splitext(module_rel_path)[0])
43 module = __import__(module_name, fromlist=[True])
45 modules.append(module)
49 # TODO(dtu): Normalize all discoverable classes to have corresponding module
50 # and class names, then always index by class name.
52 def DiscoverClasses(start_dir, top_level_dir, base_class, pattern='*',
53 index_by_class_name=False):
54 """Discover all classes in |start_dir| which subclass |base_class|.
56 Base classes that contain subclasses are ignored by default.
59 start_dir: The directory to recursively search.
60 top_level_dir: The top level of the package, for importing.
61 base_class: The base class to search for.
62 pattern: Unix shell-style pattern for filtering the filenames to import.
63 index_by_class_name: If True, use class name converted to
64 lowercase_with_underscores instead of module name in return dict keys.
67 dict of {module_name: class} or {underscored_class_name: class}
69 modules = DiscoverModules(start_dir, top_level_dir, pattern)
71 for module in modules:
72 new_classes = DiscoverClassesInModule(
73 module, base_class, index_by_class_name)
74 classes = dict(classes.items() + new_classes.items())
78 def DiscoverClassesInModule(module, base_class, index_by_class_name=False):
79 """Discover all classes in |module| which subclass |base_class|.
81 Base classes that contain subclasses are ignored by default.
84 module: The module to search.
85 base_class: The base class to search for.
86 index_by_class_name: If True, use class name converted to
87 lowercase_with_underscores instead of module name in return dict keys.
90 dict of {module_name: class} or {underscored_class_name: class}
93 for _, obj in inspect.getmembers(module):
94 # Ensure object is a class.
95 if not inspect.isclass(obj):
97 # Include only subclasses of base_class.
98 if not issubclass(obj, base_class):
100 # Exclude the base_class itself.
101 if obj is base_class:
103 # Exclude protected or private classes.
104 if obj.__name__.startswith('_'):
106 # Include only the module in which the class is defined.
107 # If a class is imported by another module, exclude those duplicates.
108 if obj.__module__ != module.__name__:
111 if index_by_class_name:
112 key_name = camel_case.ToUnderscore(obj.__name__)
114 key_name = module.__name__.split('.')[-1]
115 classes[key_name] = obj
121 def _GetUniqueModuleName():
123 return "module_" + str(_counter[0])