1import json 2import subprocess 3 4from sphinx.util.console import bold 5import sphinx.util.logging 6 7from .base import PythonMapperBase, SphinxMapperBase 8 9LOGGER = sphinx.util.logging.getLogger(__name__) 10 11 12class GoSphinxMapper(SphinxMapperBase): 13 14 """Auto API domain handler for Go 15 16 Parses directly from Go files. 17 18 :param app: Sphinx application passed in as part of the extension 19 """ 20 21 def load(self, patterns, dirs, ignore=None): 22 """ 23 Load objects from the filesystem into the ``paths`` dictionary. 24 25 """ 26 for _dir in sphinx.util.status_iterator( 27 dirs, bold("[AutoAPI] Loading Data "), "darkgreen", len(dirs) 28 ): 29 data = self.read_file(_dir, ignore=ignore) 30 if data: 31 self.paths[_dir] = data 32 33 return True 34 35 def read_file(self, path, **kwargs): 36 """Read file input into memory, returning deserialized objects 37 38 :param path: Path of file to read 39 :param **kwargs: 40 * ignore (``list``): List of file patterns to ignore 41 """ 42 # TODO support JSON here 43 # TODO sphinx way of reporting errors in logs? 44 45 parser_command = ["godocjson"] 46 47 _ignore = kwargs.get("ignore") 48 if _ignore: 49 parser_command.extend(["-e", "{0}".format("|".join(_ignore))]) 50 51 parser_command.append(path) 52 53 try: 54 parsed_data = json.loads(subprocess.check_output(parser_command)) 55 return parsed_data 56 except IOError: 57 LOGGER.warning( 58 "Error reading file: {0}".format(path), 59 type="autoapi", 60 subtype="not_readable", 61 ) 62 except TypeError: 63 LOGGER.warning( 64 "Error reading file: {0}".format(path), 65 type="autoapi", 66 subtype="not_readable", 67 ) 68 return None 69 70 def create_class(self, data, options=None, **kwargs): 71 """Return instance of class based on Go data 72 73 Data keys handled here: 74 75 _type 76 Set the object class 77 78 consts, types, vars, funcs, methods 79 Recurse into :py:meth:`create_class` to create child object 80 instances 81 82 :param data: dictionary data from godocjson output 83 """ 84 _type = kwargs.get("_type") 85 obj_map = dict((cls.type, cls) for cls in ALL_CLASSES) 86 try: 87 # Contextual type data from children recursion 88 if _type: 89 LOGGER.debug("Forcing Go Type %s" % _type) 90 cls = obj_map[_type] 91 else: 92 cls = obj_map[data["type"]] 93 except KeyError: 94 # this warning intentionally has no (sub-)type 95 LOGGER.warning("Unknown type: %s" % data) 96 else: 97 if cls.inverted_names and "names" in data: 98 # Handle types that have reversed names parameter 99 for name in data["names"]: 100 data_inv = {} 101 data_inv.update(data) 102 data_inv["name"] = name 103 if "names" in data_inv: 104 del data_inv["names"] 105 for obj in self.create_class(data_inv): 106 yield obj 107 else: 108 # Recurse for children 109 obj = cls(data, jinja_env=self.jinja_env, app=self.app) 110 for child_type in ["consts", "types", "vars", "funcs", "methods"]: 111 for child_data in data.get(child_type, []): 112 obj.children += list( 113 self.create_class( 114 child_data, 115 _type=child_type.replace("consts", "const") 116 .replace("types", "type") 117 .replace("vars", "variable") 118 .replace("funcs", "func") 119 .replace("methods", "method"), 120 ) 121 ) 122 yield obj 123 124 125class GoPythonMapper(PythonMapperBase): 126 127 language = "go" 128 inverted_names = False 129 130 def __init__(self, obj, **kwargs): 131 super(GoPythonMapper, self).__init__(obj, **kwargs) 132 self.name = obj.get("name") or obj.get("packageName") 133 self.id = self.name 134 135 # Second level 136 self.imports = obj.get("imports", []) 137 self.children = [] 138 temp_parameters = map( 139 lambda n: {"name": n["name"], "type": n["type"].lstrip("*")}, 140 obj.get("parameters", []), 141 ) 142 self.parameters = list(temp_parameters) 143 self.results = obj.get("results", []) 144 self.docstring = obj.get("doc", "") 145 146 # Go Specific 147 self.notes = obj.get("notes", {}) 148 self.filenames = obj.get("filenames", []) 149 self.bugs = obj.get("bugs", []) 150 151 def __str__(self): 152 return "<{cls} {id}>".format(cls=self.__class__.__name__, id=self.id) 153 154 @property 155 def short_name(self): 156 """Shorten name property""" 157 return self.name.split(".")[-1] 158 159 @property 160 def namespace(self): 161 pieces = self.id.split(".")[:-1] 162 if pieces: 163 return ".".join(pieces) 164 return None 165 166 @property 167 def ref_type(self): 168 return self.type 169 170 @property 171 def ref_directive(self): 172 return self.type 173 174 @property 175 def methods(self): 176 return self.obj.get("methods", []) 177 178 179class GoVariable(GoPythonMapper): 180 type = "var" 181 inverted_names = True 182 183 184class GoMethod(GoPythonMapper): 185 type = "method" 186 ref_directive = "meth" 187 188 def __init__(self, obj, **kwargs): 189 super(GoMethod, self).__init__(obj, **kwargs) 190 self.receiver = obj.get("recv") 191 192 193class GoConstant(GoPythonMapper): 194 type = "const" 195 inverted_names = True 196 197 198class GoFunction(GoPythonMapper): 199 type = "func" 200 ref_type = "function" 201 202 203class GoPackage(GoPythonMapper): 204 type = "package" 205 ref_directive = "pkg" 206 top_level_object = True 207 _RENDER_LOG_LEVEL = "VERBOSE" 208 209 210class GoType(GoPythonMapper): 211 type = "type" 212 213 214ALL_CLASSES = [GoConstant, GoFunction, GoPackage, GoVariable, GoType, GoMethod] 215