def _create_endpoint_fstring(
        value: str) -> typing.Union[ast.Str, ast.JoinedStr]:
    """Create the value for the `endpoint=..` parameter in the client.

    If the parameter doesn't contain a var a regular string is returned,
    otherwise an f-string is created and returned.

    Note that this method assumes that the parameters in the endpoint are kept
    'intact'. E.g. the raml specifices `/{container}/{key}` so we assume that
    those variables are available in the scope when we create the f-string.

    """
    parts = []
    last = 0
    value = value.lstrip("/")
    for m in re.finditer("{[^}]+}", value):
        parts.append(ast.Constant(value=value[last:m.start()]))

        identifier = snakeit(value[m.start() + 1:m.end() - 1])
        parts.append(
            ast.FormattedValue(value=ast.Name(identifier),
                               conversion=-1,
                               format_spec=None))
        last = m.end()
    if last != len(value):
        parts.append(ast.Constant(value=value[last:len(value)]))

    # If no values are in the f-string we can just generate a regular string
    if len(parts) == 1:
        return ast.Str(s=parts[0].value, kind=None)

    return ast.JoinedStr(values=parts)
示例#2
0
 def test_invalid_fstring_conversion(self):
     self.check_invalid(
         ast.FormattedValue(
             value=ast.Constant(value="a", kind=None),
             conversion=ord("Y"),  # random character
             format_spec=None,
         ))
示例#3
0
def handle_from_mod_tuple(node):
    """Convert a `BinOp` `%` formatted str with a tuple on the right to an f-string.

    Takes an ast.BinOp representing `"1. %s 2. %s" % (a, b)`
    and converted it to a ast.JoinedStr representing `f"1. {a} 2. {b}"`

    Args:
       node (ast.BinOp): The node to convert to a f-string

    Returns ast.JoinedStr (f-string)
    """

    format_str = node.left.s
    matches = VAR_KEY_PATTERN.findall(format_str)

    if len(node.right.elts) != len(matches):
        raise ValueError("string formatting length mismatch")

    str_vars = list(map(lambda x: x, node.right.elts))

    # build result node
    result_node = ast.JoinedStr()
    result_node.values = []
    str_vars.reverse()
    blocks = VAR_KEY_PATTERN.split(format_str)
    for block in blocks:
        if VAR_KEY_PATTERN.match(block):
            fv = ast.FormattedValue(value=str_vars.pop(),
                                    conversion=-1,
                                    format_spec=None)
            result_node.values.append(fv)
        else:
            result_node.values.append(ast.Str(s=block))

    return result_node
示例#4
0
    def test_fstring(self):
        xml = Parser(self.get_xml("dict.xml"))
        module = xml.parse()
        code = compile(module, "<ast>", "exec")

        mymodule = ast.Module([
            ast.Assign([ast.Name('name', ast.Store())], ast.Str('Batuhan')),
            ast.Assign([ast.Name('age', ast.Store())], ast.Num(15)),
            ast.Expr(
                ast.Call(ast.Name('print', ast.Load()), [
                    ast.JoinedStr(values=[
                        ast.Str(s='Benim adım'),
                        ast.FormattedValue(
                            value=ast.Name(id='name', ctx=ast.Load()),
                            conversion=-1,
                            format_spec=None,
                        ),
                        ast.Str(s=', yaşım'),
                        ast.FormattedValue(
                            value=ast.Name(id='age', ctx=ast.Load()),
                            conversion=-1,
                            format_spec=None,
                        ),
                        ast.Str(s='. Hakkımda geri kalan bilgi:'),
                        ast.FormattedValue(
                            value=ast.Call(
                                func=ast.Name(id='str', ctx=ast.Load()),
                                args=[ast.Num(n=235)],
                                keywords=[],
                            ),
                            conversion=-1,
                            format_spec=None,
                        ),
                        ast.Str(s=''),
                    ])
                ], []))
        ])
        ast.fix_missing_locations(mymodule)
        pprint(module)
        mycode = compile(mymodule, "<ast>", "exec")

        self.assertEqual(code, mycode)
    def visit_FormattedValue(self, node: FormattedValue, *args,
                             **kwargs) -> C.FormattedValue:
        value = self.visit(node.value, *args, **kwargs)
        conversion = self.visit(node.conversion, *args, **kwargs)
        format_spec = self.visit(node.format_spec, *args, **kwargs)

        return C.FormattedValue(
            value=value,
            conversion=conversion,
            format_spec=format_spec,
        )
示例#6
0
    def get_kernel_embed():
        """A list of kernel embed nodes

        Returns:
            nodes (list): AST nodes which form the following code.

            ```
            import os
            pid = os.fork()
            if os.fork() == 0:
                open(f'{os.environ["HOME"]}/.pynt', 'a').close()
                import IPython
                IPython.start_kernel(user_ns={**locals(), **globals(), **vars()})
            os.waitpid(pid, 0)
            ```

        This is a purely functional method which always return the same thing.

        """
        return [
            ast.Import(names=[ast.alias(name='os', asname=None),]),
            ast.Assign(targets=[ast.Name(id='pid', ctx=ast.Store()),], value=ast.Call(func=ast.Attribute(value=ast.Name(id='os', ctx=ast.Load()), attr='fork', ctx=ast.Load()), args=[], keywords=[])),
            ast.If(
                test=ast.Compare(left=ast.Name(id='pid', ctx=ast.Load()), ops=[ast.Eq(),], comparators=[ast.Num(n=0),]),
                body=[
                    ast.Expr(value=ast.Call(func=ast.Attribute(value=ast.Call(func=ast.Name(id='open', ctx=ast.Load()), args=[
                        ast.JoinedStr(values=[
                            ast.FormattedValue(value=ast.Subscript(value=ast.Attribute(value=ast.Name(id='os', ctx=ast.Load()), attr='environ', ctx=ast.Load()), slice=ast.Index(value=ast.Str(s='HOME')), ctx=ast.Load()), conversion=-1, format_spec=None),
                            ast.Str(s='/.pynt'),
                        ]),
                        ast.Str(s='a'),
                    ], keywords=[]), attr='close', ctx=ast.Load()), args=[], keywords=[])),
                    ast.Import(names=[
                        ast.alias(name='IPython', asname=None),
                    ]),
                    ast.Expr(value=ast.Call(func=ast.Attribute(value=ast.Name(id='IPython', ctx=ast.Load()), attr='start_kernel', ctx=ast.Load()), args=[], keywords=[
                        ast.keyword(arg='user_ns', value=ast.Dict(keys=[
                            None,
                            None,
                            None,
                        ], values=[
                            ast.Call(func=ast.Name(id='locals', ctx=ast.Load()), args=[], keywords=[]),
                            ast.Call(func=ast.Name(id='globals', ctx=ast.Load()), args=[], keywords=[]),
                            ast.Call(func=ast.Name(id='vars', ctx=ast.Load()), args=[], keywords=[]),
                        ])),
                    ])),
            ], orelse=[]),
            ast.Expr(value=ast.Call(func=ast.Attribute(value=ast.Name(id='os', ctx=ast.Load()), attr='waitpid', ctx=ast.Load()), args=[
                ast.Name(id='pid', ctx=ast.Load()),
                ast.Num(n=0),
            ], keywords=[])),
        ]
示例#7
0
def maybe_replace_with_fstring(fs: PercentFormatString,
                               args_node: ast.AST) -> Optional[ast.AST]:
    """If appropriate, emits an error to replace this % format with an f-string."""
    # otherwise there are no f-strings
    if sys.version_info < (3, 6):
        return None
    # there are no bytes f-strings
    if isinstance(fs.pattern, bytes):
        return None
    # if there is a { in the string, we will need escaping in order to use an f-string, which might
    # make the code worse
    if any("{" in piece or "}" in piece for piece in fs.raw_pieces):
        return None
    # special conversion specifiers are rare and more difficult to replace, so just ignore them for
    # now
    if any(
            any([
                cs.mapping_key,
                cs.conversion_flags,
                cs.field_width,
                cs.precision,
                cs.length_modifier,
            ]) for cs in fs.specifiers):
        return None
    # don't attempt fancy conversion types
    if any(cs.conversion_type not in ("d", "s") for cs in fs.specifiers):
        return None
    # only proceed if all the arguments are simple (currently, names or attribute accesses)
    if isinstance(args_node, ast.Tuple):
        if any(not _is_simple_enough(elt) for elt in args_node.elts):
            return None
        substitutions = args_node.elts
    elif len(fs.specifiers) == 1:
        if not _is_simple_enough(args_node):
            return None
        substitutions = [args_node]
    else:
        return None
    # the linter should have given an error in this case
    if len(substitutions) != len(fs.specifiers) != len(fs.raw_pieces) - 1:
        return None
    parts = []
    for raw_piece, substitution in zip(fs.raw_pieces, substitutions):
        if raw_piece:
            parts.append(ast.Str(s=raw_piece))
        parts.append(
            ast.FormattedValue(value=substitution,
                               conversion=-1,
                               format_spec=None))
    if fs.raw_pieces[-1]:
        parts.append(ast.Str(s=fs.raw_pieces[-1]))
    return ast.JoinedStr(values=parts)
示例#8
0
def ast_formatted_value(val,
                        fmt_str: str = None,
                        conversion=None) -> ast.FormattedValue:

    if astor.to_source(val)[0] == "{":
        raise FlyntException(
            "values starting with '{' are better left not transformed.")

    format_spec = ast.JoinedStr([ast_string_node(fmt_str)
                                 ]) if fmt_str else None
    conversion = -1 if conversion is None else ord(conversion.replace("!", ""))
    return ast.FormattedValue(value=val,
                              conversion=conversion,
                              format_spec=format_spec)
示例#9
0
def handle_from_mod_dict_name(node):
    """Convert a `BinOp` `%` formatted str with a name representing a Dict on the right to an f-string.

    Takes an ast.BinOp representing `"1. %(key1)s 2. %(key2)s" % mydict`
    and converted it to a ast.JoinedStr representing `f"1. {mydict['key1']} 2. {mydict['key2']}"`

    Args:
       node (ast.BinOp): The node to convert to a f-string

    Returns ast.JoinedStr (f-string)
    """
    # raise ValueError("blah")
    format_str = node.left.s
    matches = MOD_KEY_PATTERN.findall(format_str)
    var_keys = []
    for idx, m in enumerate(matches):
        var_key = MOD_KEY_NAME_PATTERN.match(m)
        if not var_key:
            raise ValueError("could not find dict key")
        var_keys.append(var_key[1])

    # build result node
    result_node = ast.JoinedStr()
    result_node.values = []
    var_keys.reverse()
    blocks = MOD_KEY_PATTERN.split(format_str)
    # loop through the blocks of a string to build up dateh JoinStr.values
    for block in blocks:
        # if this block matches a %(arg)s pattern then inject f-string instead
        if MOD_KEY_PATTERN.match(block):
            fv = ast.FormattedValue(
                value=ast.Subscript(
                    value=node.right,
                    slice=ast.Index(value=ast.Str(s=var_keys.pop()))),
                conversion=-1,
                format_spec=None,
            )

            result_node.values.append(fv)
        else:
            # no match means it's just a literal string
            result_node.values.append(ast.Str(s=block))
    return result_node
示例#10
0
    def visit_BinOp(self, node):

        # transform the left and right arguments of the binary operation:
        node.left = pythonTransformer().visit(node.left)
        node.right = pythonTransformer().visit(node.right)

        # formatted strings with %
        # note: we have extended the pythong syntax slightly, to accommodate both tuples and lists
        # so both '%_%' % (1,2) and '%_%' % [1,2] are successfully transpiled
        if isinstance(node.op, ast.Mod) and isinstance(node.left, ast.Str):
            # transform the node into an f-string node:
            stringFormat = node.left.value
            stringTuple = node.right.elts if (
              isinstance(node.right, ast.Tuple) or isinstance(node.right, ast.List))\
                else [node.right]

            values = []
            tupleIndex = 0
            while True:
                # TODO deal with more complicated formats, such as %.3f
                match = re.search(r'%.', stringFormat)
                if match is None:
                    break
                values.append(ast.Constant(value=stringFormat[0:match.span(0)[0]], kind=None))
                values.append(
                    self.visit_FormattedValue(
                        ast.FormattedValue(
                            value=stringTuple[tupleIndex],
                            conversion=-1,
                            format_spec=None
                        )
                    )
                )
                stringFormat = stringFormat[match.span(0)[1]:]
                tupleIndex += 1

            return ast.JoinedStr(values)

        return node
示例#11
0
 def test_invalid_fstring_backslash(self):
     self.check_invalid(
         ast.FormattedValue(value=ast.Constant(value="\\\\")))
示例#12
0
def generate_formatted_value(max_depth=None):
    var = generate_variable()
    conversion = -1
    format_spec = None
    return ast.FormattedValue(var, conversion, format_spec)