Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Unified Diff: recipe_engine/doc.py

Issue 2856003002: [doc] fix doc to work in all known repos. (Closed)
Patch Set: add docstring Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « recipe_engine/config.py ('k') | recipe_engine/recipe_api.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: recipe_engine/doc.py
diff --git a/recipe_engine/doc.py b/recipe_engine/doc.py
index ba24e2c4e1c0583cf245ea83e58ef803154aad18..51a54358246d03e467c7dcb855c0e1bfa72f9fe6 100755
--- a/recipe_engine/doc.py
+++ b/recipe_engine/doc.py
@@ -73,7 +73,9 @@ def _find_value_of(mod_ast, target):
"""
for node in mod_ast.body:
if isinstance(node, ast.Assign):
- if len(node.targets) == 1 and node.targets[0].id == target:
+ if (len(node.targets) == 1 and
+ isinstance(node.targets[0], ast.Name) and
+ node.targets[0].id == target):
return node.value, node.lineno
return None, None
@@ -126,6 +128,8 @@ def _expand_mock_imports(*mock_imports):
return expanded_imports
+ALL_IMPORTS = {} # used in doc_test to ensure everything is actually importable
+KNOWN_OBJECTS = {}
_decorator_imports = {
'recipe_engine.util.returns_placeholder': util.returns_placeholder,
@@ -133,6 +137,7 @@ _decorator_imports = {
'recipe_engine.recipe_api.infer_composite_step': (
recipe_api.infer_composite_step)
}
+KNOWN_OBJECTS.update(_decorator_imports)
_config_imports = {
'recipe_engine.config.ConfigGroup': config.ConfigGroup,
@@ -144,36 +149,36 @@ _config_imports = {
'recipe_engine.config.Static': config.Static,
'recipe_engine.config.Enum': config.Enum,
}
+KNOWN_OBJECTS.update(_config_imports)
_placeholder_imports = {
'recipe_engine.util.OutputPlaceholder': util.OutputPlaceholder,
'recipe_engine.util.InputPlaceholder': util.InputPlaceholder,
'recipe_engine.util.Placeholder': util.Placeholder,
}
+KNOWN_OBJECTS.update(_placeholder_imports)
-MOCK_IMPORTS_PARAMETERS = _expand_mock_imports({
+_property_imports = {
'recipe_engine.recipe_api.Property': recipe_api.Property,
-}, _config_imports)
+}
+KNOWN_OBJECTS.update(_property_imports)
-MOCK_IMPORTS_RETURN_SCHEMA = _expand_mock_imports({
+_return_schema_imports = {
'recipe_engine.config.ReturnSchema': config.ReturnSchema,
'recipe_engine.config.ConfigGroupSchema': config.ConfigGroupSchema,
-}, _config_imports)
+}
+KNOWN_OBJECTS.update(_return_schema_imports)
-MOCK_IMPORTS_RECIPE = _expand_mock_imports({
+_util_imports = {
'recipe_engine.types.freeze': types.freeze,
-}, _decorator_imports, _placeholder_imports)
+}
+KNOWN_OBJECTS.update(_util_imports)
-MOCK_IMPORTS_MODULE = _expand_mock_imports({
+_recipe_api_class_imports = {
'recipe_engine.recipe_api.RecipeApi': recipe_api.RecipeApi,
'recipe_engine.recipe_api.RecipeApiPlain': recipe_api.RecipeApiPlain,
-}, _decorator_imports, _placeholder_imports)
-
-ALL_IMPORTS = {}
-ALL_IMPORTS.update(MOCK_IMPORTS_PARAMETERS)
-ALL_IMPORTS.update(MOCK_IMPORTS_RETURN_SCHEMA)
-ALL_IMPORTS.update(MOCK_IMPORTS_RECIPE)
-ALL_IMPORTS.update(MOCK_IMPORTS_MODULE)
+}
+KNOWN_OBJECTS.update(_recipe_api_class_imports)
def _parse_mock_imports(mod_ast, expanded_imports):
@@ -277,6 +282,34 @@ def parse_deps(uv, mod_ast, relpath):
return ret
+def extract_jsonish_assignments(mod_ast):
+ """This extracts all single assignments where the target is a name, and the
+ value is a simple 'jsonish' statement (aka python literal).
+
+ The result is returned as a dictionary of name to the decoded literal.
+
+ Example:
+ Foo = "hello"
+ Bar = [1, 2, "something"]
+ Other, Things = range(2) # not single assignment
+ Bogus = object() # not a python literal
+ # returns: {"Foo": "hello", "Bar": [1, 2, "something"]}
+ """
+ ret = {}
+ for node in mod_ast.body:
+ if not isinstance(node, ast.Assign):
+ continue
+ if len(node.targets) != 1:
+ continue
+ if not isinstance(node.targets[0], ast.Name):
+ continue
+ try:
+ ret[node.targets[0].id] = ast.literal_eval(node.value)
+ except (KeyError, ValueError):
+ pass
+ return ret
+
+
def parse_parameter(param):
assert isinstance(param, recipe_api.Property), type(param)
default = None
@@ -289,12 +322,18 @@ def parse_parameter(param):
default_json=default)
+MOCK_IMPORTS_PARAMETERS = _expand_mock_imports(
+ _property_imports, _config_imports)
+ALL_IMPORTS.update(MOCK_IMPORTS_PARAMETERS)
+
+
def parse_parameters(mod_ast, relpath):
parameters, lineno = _find_value_of(mod_ast, 'PROPERTIES')
if not parameters:
return None
imports = _parse_mock_imports(mod_ast, MOCK_IMPORTS_PARAMETERS)
+ imports.update(extract_jsonish_assignments(mod_ast))
data = eval(_unparse(parameters), imports)
if not data:
return None
@@ -324,6 +363,11 @@ def parse_func(func_node, relpath, imports):
return ret
+MOCK_IMPORTS_RETURN_SCHEMA = _expand_mock_imports(
+ _return_schema_imports, _config_imports)
+ALL_IMPORTS.update(MOCK_IMPORTS_RETURN_SCHEMA)
+
+
def parse_return_schema(mod_ast, relpath):
imports = _parse_mock_imports(mod_ast, MOCK_IMPORTS_RETURN_SCHEMA)
schema, lineno = _find_value_of(mod_ast, 'RETURN_SCHEMA')
@@ -336,6 +380,11 @@ def parse_return_schema(mod_ast, relpath):
schema=schema.schema_proto())
+MOCK_IMPORTS_RECIPE = _expand_mock_imports(
+ _util_imports, _decorator_imports, _placeholder_imports)
+ALL_IMPORTS.update(MOCK_IMPORTS_RECIPE)
+
+
def parse_recipe(uv, base_dir, relpath, recipe_name):
recipe = _grab_ast(base_dir, relpath)
if not recipe:
@@ -357,6 +406,11 @@ def parse_recipe(uv, base_dir, relpath, recipe_name):
)
+MOCK_IMPORTS_MODULE = _expand_mock_imports(
+ _recipe_api_class_imports, _decorator_imports, _placeholder_imports)
+ALL_IMPORTS.update(MOCK_IMPORTS_MODULE)
+
+
def parse_module(uv, base_dir, relpath, mod_name):
native_relpath = _to_native(relpath)
@@ -375,7 +429,7 @@ def parse_module(uv, base_dir, relpath, mod_name):
api_class = None
for name, val in sorted(classes.iteritems()):
- if any(b.known in KNOWN_RECIPE_API_BASES for b in val.bases):
+ if any(b.known in _recipe_api_class_imports for b in val.bases):
api_class = classes.pop(name)
break
if not api_class:
@@ -420,28 +474,29 @@ def parse_package(uv, base_dir, spec):
return ret
-KNOWN_RECIPE_API_BASES = {
- 'recipe_engine.recipe_api.RecipeApi': recipe_api.RecipeApi,
- 'recipe_engine.recipe_api.RecipeApiPlain': recipe_api.RecipeApiPlain,
-}
-
-
-KNOWN_OBJECTS = {
- 'recipe_engine.recipe_api.non_step': recipe_api.non_step,
- 'recipe_engine.recipe_api.infer_composite_step': (
- recipe_api.infer_composite_step),
-
- 'recipe_engine.util.returns_placeholder': util.returns_placeholder,
-}
-KNOWN_OBJECTS.update(KNOWN_RECIPE_API_BASES)
-
-
RECIPE_ENGINE_URL = 'https://github.com/luci/recipes-py'
def _set_known_objects(base):
source_cache = {}
+ def _add_it(key, fname, target):
+ relpath = os.path.relpath(fname, RECIPE_ENGINE_BASE)
+ for node in source_cache[fname].body:
+ if isinstance(node, ast.ClassDef) and node.name == target:
+ base.known_objects[key].klass.CopyFrom(parse_class(node, relpath, {}))
+ return
+ elif isinstance(node, ast.FunctionDef) and node.name == target:
+ base.known_objects[key].func.CopyFrom(parse_func(node, relpath, {}))
+ return
+ elif isinstance(node, ast.Assign) and node.targets[0].id == target:
+ # This is an alias in the form of:
+ # Target = RealImplementation
dnj 2017/05/03 00:25:31 Actually this was real nice. It took a few minutes
iannucci 2017/05/03 01:09:30 Done.
+ _add_it(key, fname, node.value.id)
+ return
+
+ raise ValueError('could not find %r in %r' % (key, relpath))
+
for k, v in KNOWN_OBJECTS.iteritems():
base.known_objects[k].url = RECIPE_ENGINE_URL
_, target = k.rsplit('.', 1)
@@ -452,16 +507,7 @@ def _set_known_objects(base):
source_lines, _ = inspect.findsource(v)
source_cache[fname] = ast.parse(''.join(source_lines), fname)
- relpath = os.path.relpath(fname, RECIPE_ENGINE_BASE)
- for node in source_cache[fname].body:
- if isinstance(node, ast.ClassDef) and node.name == target:
- base.known_objects[k].klass.CopyFrom(parse_class(node, relpath, {}))
- break
- elif isinstance(node, ast.FunctionDef) and node.name == target:
- base.known_objects[k].func.CopyFrom(parse_func(node, relpath, {}))
- break
- else:
- raise ValueError('could not find %r in %r' % (k, relpath))
+ _add_it(k, fname, target)
def add_subparser(parser):
« no previous file with comments | « recipe_engine/config.py ('k') | recipe_engine/recipe_api.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698