def _make_intersphinx_pairs(pairs): """Convert key/value pairs into something that parso can convert into dict key/values. Args: pairs (iter[tuple[str, str or tuple[str, str or NoneType]]): The intersphinx key/value mapping. Keys are usually the name of Rez packages and each value is the URL to the API documentation that we need to link to. Returns: list[:class:`parso.python.tree.Node`]: The key/value pairs, flattened into parso objects. """ output = [] for row, (key, value) in enumerate(sorted(pairs)): if isinstance(key, six.string_types): key = '"{key}"'.format(key=key) if isinstance(value, six.string_types): value = '"{value}"'.format(value=value) key_length = len(key) value_length = len(value) output.append(tree.String(key, (row, 0), prefix="\n ")) output.append(tree.Operator(":", (row, key_length))) output.append(tree.String(str(value), (row, key_length + len(":")), prefix=" ")) output.append(tree.Operator(",", (row, key_length + len(":") + value_length))) return output
def _make_tests_node(data): """Create the final "tests" data that will be written to a package.py file. Important: This function technically doesn't create a "correct" parso dict. For the sake of simplicity, some corners were cut. For example, value data is converted into a raw :class:`parso.python.tree.String`. It's not technically "correct" but since this function is called just before writing the data into disk and not further modified, it's okay. Args: data (iter[str, :class:`parso.python.tree.PythonBaseNode`]): The key / value information that will be converted into a parso dict. Returns: :class:`parso.python.tree.PythonNode`: An atom PythonNode which has a full dictionary "tests" defined. This will later override an existing "tests" attribute in a package.py or be appended to the file as an assignment. """ nodes = [] for key, value in data: nodes.extend([ tree.String(_escape(key), (0, 0), prefix="\n "), tree.Operator(":", (0, 0)), ]) if isinstance(value, collections.MutableMapping): nodes.append(_make_dict_nodes(sorted(value.items()), prefix=" ")) elif hasattr(value, "get_code"): if hasattr(value, "prefix"): value.prefix = " " nodes.append(value) else: nodes.append(tree.String(_escape_all(value), (0, 0), prefix=" ")) nodes.append(tree.Operator(",", (0, 0))) base = tree.PythonNode("dictorsetmaker", nodes) return tree.PythonNode( "atom", [ tree.Operator("{", (0, 0)), base, tree.Operator("}", (0, 0), prefix="\n") ], )
def _make_dict_nodes(data, prefix=""): """Create a parso node that represents the inner key / value pairs of a Python dict. In terms of the work needed to generate a Rez "tests" attribute, this function is used mainly to create the "inner dict" that a "tests" attribute frequently defines. Args: data (iter[tuple[str, str or list or :class:`parso.python.tree.PythonBaseNode`]]): Returns: :class:`parso.python.tree.PythonNode`: A node that defines the inside of a parso dict. This node is not equivalent to an actual dict and still needs to be wrapped with another :class:`parso.python.tree.PythonNode` to be valid. """ nodes = [] for key, item in data: item = _escape_all(item) nodes.append( tree.String(_escape(key), (0, 0), prefix="\n {prefix}".format(prefix=prefix))) nodes.append(tree.Operator(":", (0, 0))) nodes.append( tree.PythonNode( "atom", [ tree.String(item, (0, 0), prefix=" "), tree.Operator(",", (0, 0)) ], ), ) return tree.PythonNode( "atom", [ tree.Operator("{", (0, 0), prefix=" "), tree.PythonNode("dictorsetmaker", nodes), tree.Operator("}", (0, 0), prefix="\n "), ], )
def convert_leaf(self, pgen_grammar, type, value, prefix, start_pos): # print('leaf', repr(value), token.tok_name[type]) if type == NAME: if value in pgen_grammar.keywords: return tree.Keyword(value, start_pos, prefix) else: return tree.Name(value, start_pos, prefix) elif type == STRING: return tree.String(value, start_pos, prefix) elif type == NUMBER: return tree.Number(value, start_pos, prefix) elif type == NEWLINE: return tree.Newline(value, start_pos, prefix) elif type == ENDMARKER: return tree.EndMarker(value, start_pos, prefix) else: return tree.Operator(value, start_pos, prefix)
def _get_entries(assignment): """Find every help key + value entry from some "help" attribute. If `assignment` is invalid, this function may return an empty list. Args: assignment (:class:`parso.python.tree.ExprStmt`): The node that assigns the "help" attribute, which will be modified. Returns: list[:class:`parso.python.tree.PythonNode`]: The list node that is a parso "list of list of strs". """ root = _get_list_root(assignment) if root: return _get_inner_list_entries(root) string_root = _get_str_root(assignment) if not string_root: return [] if string_root.get_code().strip() in ("''", '""'): return [] node = tree.PythonNode( "testlist_comp", [ tree.String( '"{rez_configuration.DEFAULT_HELP_LABEL}"'.format( rez_configuration=rez_configuration), (0, 0), ), tree.Operator(",", (0, 0)), string_root, ], ) for child in node.children: child.parent = node return [node]
def _get_faked(grammar, module, obj, name=None): result, fake_module = _faked(grammar, module, obj, name) if result is None: # We're not interested in classes. What we want is functions. raise FakeDoesNotExist elif result.type == 'classdef': return result, fake_module else: # Set the docstr which was previously not set (faked modules don't # contain it). assert result.type == 'funcdef' doc = '"""%s"""' % obj.__doc__ # TODO need escapes. suite = result.children[-1] string = tree.String(doc, (0, 0), '') new_line = tree.Newline('\n', (0, 0)) docstr_node = tree.PythonNode('simple_stmt', [string, new_line]) suite.children.insert(1, docstr_node) return result, fake_module
def _make_nodes(data, prefix=""): """Make basic parso nodes to describe `data`. Args: data (iter[str]): Some Rez requirement objects to convert into parso nodes. prefix (str, optional): Leading indentation for each generated node. Default: "". Returns: list[:class:`parso.python.tree.String`]: The parso nodes which represent the given `data`. """ return [ tree.String( '"{requirement}"'.format( requirement=requirement.replace('"', '"')), (0, 0), prefix="\n{prefix}".format(prefix=prefix), ) for requirement in data ]
def _find_type_from_comment_hint(context, node, varlist, name): index = None if varlist.type in ("testlist_star_expr", "exprlist", "testlist"): # something like "a, b = 1, 2" index = 0 for child in varlist.children: if child == name: break if child.type == "operator": continue index += 1 else: return [] comment = parser_utils.get_following_comment_same_line(node) if comment is None: return [] match = re.match(r"^#\s*type:\s*([^#]*)", comment) if not match: return [] annotation = tree.String(repr(str(match.group(1).strip())), node.start_pos) annotation.parent = node.parent return _evaluate_for_annotation(context, annotation, index)
def _write_package_to_disk(package, version): """Update a Rez package on-disk with its new contents. Args: package (:class:`rez.packages_.DeveloperPackage`): Some package on-disk to write out. version (str): The new semantic version that will be written to-disk. """ with open(package, "r") as handler: code = handler.read() graph = parso.parse(code) try: assignment = parso_utility.find_assignment_nodes("version", graph)[-1] except IndexError: assignment = None prefix = " " if assignment: prefix = assignment.children[0].prefix.strip("\n") or prefix node = tree.String('"{version}"'.format(version=version), (0, 0), prefix=prefix) graph = convention.insert_or_append(node, graph, assignment, "version") # Writing to DeveloperPackage objects is currently bugged. # So instead, we disable caching during write. # # Reference: https://github.com/nerdvegas/rez/issues/857 # with filesystem.make_path_writable( os.path.dirname(os.path.dirname(package))): with serialise.open_file_for_write(package) as handler: handler.write(graph.get_code())
def test_unicode_string(): s = tree.String(None, 'bö', (0, 0)) assert repr(s) # Should not raise an Error!
def insert_or_append(node, graph, assignment, attribute): """Add a new `attribute` to `graph`, according to pre-existing data. Args: node (:class:`parso.python.tree.PythonNode`): The main data that will be added to `graph`. graph (:class:`parso.python.tree.PythonNode`): A Python module that may already contain an assignment for `attribute`. If it doesn't the attribute will be added, automatically. assignment (:class:`parso.python.tree.PythonNode` or NoneType): If `graph` has an existing node where `attribute` is defined, this node will represent that position. If this parameter is None, a new position for `attribute` will be automatically found and used. attribute (str): The name of the Rez-related object to use. Returns: :class:`parso.python.tree.PythonNode`: The main module with a modified `attribute`. """ if assignment: if hasattr(node, "children"): node.children[0].prefix = " " assignment.children[-1] = node return graph index = _find_nearest_node_index(graph.children, attribute) if index == -1: graph.children.append( tree.PythonNode( "assignment", [ tree.String( "{attribute} = ".format(attribute=attribute), (0, 0), prefix="\n\n", ) ] + [node], )) return graph _kill_suffix(graph.children[index - 1]) graph.children.insert( index, tree.PythonNode( "assignment", [ tree.String("{attribute} = ".format(attribute=attribute), (0, 0), prefix="\n\n") ] + [node], ), ) _adjust_prefix(graph.children, index) return graph