Module opshin.rewrite.rewrite_import_hashlib

Expand source code
from typing import Optional
from enum import Enum, auto

from ..util import CompilingNodeTransformer
from ..typed_ast import *

"""
Checks that there was an import of dataclass if there are any class definitions
"""


@dataclass(frozen=True, unsafe_hash=True)
class HashType(ClassType):
    """A pseudo class that is the result of python hash functions that need a 'digest' call"""

    def attribute_type(self, attr) -> "Type":
        if attr == "digest":
            return InstanceType(FunctionType(frozenlist([]), ByteStringInstanceType))
        raise NotImplementedError("HashType only has attribute 'digest'")

    def attribute(self, attr) -> plt.AST:
        if attr == "digest":
            return plt.Lambda(["self"], plt.Var("self"))
        raise NotImplementedError("HashType only has attribute 'digest'")

    def __ge__(self, other):
        return isinstance(other, HashType)


HashInstanceType = InstanceType(HashType())


class PythonHashlib(Enum):
    sha256 = auto()
    sha3_256 = auto()
    blake2b = auto()


PythonHashlibTypes = {
    PythonHashlib.sha256: InstanceType(
        FunctionType(
            frozenlist([ByteStringInstanceType]),
            HashInstanceType,
        )
    ),
    PythonHashlib.sha3_256: InstanceType(
        FunctionType(
            frozenlist([ByteStringInstanceType]),
            HashInstanceType,
        )
    ),
    PythonHashlib.blake2b: InstanceType(
        FunctionType(
            frozenlist([ByteStringInstanceType]),
            HashInstanceType,
        )
    ),
}

PythonHashlibImpls = {
    PythonHashlib.sha256: force_params(
        plt.Lambda(["x"], plt.Lambda(["_"], plt.Sha2_256(plt.Var("x"))))
    ),
    PythonHashlib.sha3_256: force_params(
        plt.Lambda(["x"], plt.Lambda(["_"], plt.Sha3_256(plt.Var("x"))))
    ),
    PythonHashlib.blake2b: force_params(
        plt.Lambda(["x"], plt.Lambda(["_"], plt.Blake2b_256(plt.Var("x"))))
    ),
}


class RewriteImportHashlib(CompilingNodeTransformer):
    step = "Resolving imports and usage of hashlib"

    imports_hashlib = False

    def visit_ImportFrom(self, node: ImportFrom) -> typing.List[AST]:
        if node.module != "hashlib":
            return node
        additional_assigns = []
        for n in node.names:
            imported_fun = None
            for h in PythonHashlib:
                if h.name == n.name:
                    imported_fun = h
            assert (
                imported_fun is not None
            ), f"Unsupported function import from hashlib '{n.name}"
            typ = PythonHashlibTypes[imported_fun]
            imported_name = n.name if n.asname is None else n.asname
            additional_assigns.append(
                TypedAssign(
                    targets=[TypedName(id=imported_name, typ=typ, ctx=Store())],
                    value=RawPlutoExpr(typ=typ, expr=PythonHashlibImpls[imported_fun]),
                )
            )
        return additional_assigns

Classes

class HashType

A pseudo class that is the result of python hash functions that need a 'digest' call

Expand source code
@dataclass(frozen=True, unsafe_hash=True)
class HashType(ClassType):
    """A pseudo class that is the result of python hash functions that need a 'digest' call"""

    def attribute_type(self, attr) -> "Type":
        if attr == "digest":
            return InstanceType(FunctionType(frozenlist([]), ByteStringInstanceType))
        raise NotImplementedError("HashType only has attribute 'digest'")

    def attribute(self, attr) -> plt.AST:
        if attr == "digest":
            return plt.Lambda(["self"], plt.Var("self"))
        raise NotImplementedError("HashType only has attribute 'digest'")

    def __ge__(self, other):
        return isinstance(other, HashType)

Ancestors

Methods

def attribute(self, attr) ‑> pluthon.pluthon_ast.AST

Inherited from: ClassType.attribute

The attributes of this class. Needs to be a lambda that expects as first argument the object itself

Expand source code
def attribute(self, attr) -> plt.AST:
    if attr == "digest":
        return plt.Lambda(["self"], plt.Var("self"))
    raise NotImplementedError("HashType only has attribute 'digest'")
def attribute_type(self, attr) ‑> Type

Inherited from: ClassType.attribute_type

The types of the named attributes of this class

Expand source code
def attribute_type(self, attr) -> "Type":
    if attr == "digest":
        return InstanceType(FunctionType(frozenlist([]), ByteStringInstanceType))
    raise NotImplementedError("HashType only has attribute 'digest'")
def binop(self, binop: ast.operator, other: ast.AST) ‑> pluthon.pluthon_ast.AST

Inherited from: ClassType.binop

Implements a binary operation between self and other

def binop_type(self, binop: ast.operator, other: Type) ‑> Type

Inherited from: ClassType.binop_type

Type of a binary operation between self and other.

def cmp(self, op: ast.cmpop, o: Type) ‑> pluthon.pluthon_ast.AST

Inherited from: ClassType.cmp

The implementation of comparing this type to type o via operator op. Returns a lambda that expects as first argument the object itself and as second …

def constr(self) ‑> pluthon.pluthon_ast.AST

Inherited from: ClassType.constr

The constructor for this class

def constr_type(self) ‑> InstanceType

Inherited from: ClassType.constr_type

The type of the constructor for this class

def copy_only_attributes(self) ‑> pluthon.pluthon_ast.AST

Inherited from: ClassType.copy_only_attributes

Returns a copy of this type with only the declared attributes (mapped to builtin values, thus checking atomic types too). For anything but record …

def id_map(self, skip_constructor: bool = False) ‑> str

Inherited from: ClassType.id_map

Returns a map from the constructor id to a descriptive typestring

def stringify(self, recursive: bool = False) ‑> pluthon.pluthon_ast.AST

Inherited from: ClassType.stringify

Returns a stringified version of the object …

def unop(self, unop: ast.unaryop) ‑> pluthon.pluthon_ast.AST

Inherited from: ClassType.unop

Implements a unary operation on self

def unop_type(self, unop: ast.unaryop) ‑> Type

Inherited from: ClassType.unop_type

Type of a unary operation on self.

class PythonHashlib (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code
class PythonHashlib(Enum):
    sha256 = auto()
    sha3_256 = auto()
    blake2b = auto()

Ancestors

  • enum.Enum

Class variables

var blake2b
var sha256
var sha3_256
class RewriteImportHashlib

A :class:NodeVisitor subclass that walks the abstract syntax tree and allows modification of nodes.

The NodeTransformer will walk the AST and use the return value of the visitor methods to replace or remove the old node. If the return value of the visitor method is None, the node will be removed from its location, otherwise it is replaced with the return value. The return value may be the original node in which case no replacement takes place.

Here is an example transformer that rewrites all occurrences of name lookups (foo) to data['foo']::

class RewriteName(NodeTransformer):

   def visit_Name(self, node):
       return Subscript(
           value=Name(id='data', ctx=Load()),
           slice=Constant(value=node.id),
           ctx=node.ctx
       )

Keep in mind that if the node you're operating on has child nodes you must either transform the child nodes yourself or call the :meth:generic_visit method for the node first.

For nodes that were part of a collection of statements (that applies to all statement nodes), the visitor may also return a list of nodes rather than just a single node.

Usually you use the transformer like this::

node = YourTransformer().visit(node)

Expand source code
class RewriteImportHashlib(CompilingNodeTransformer):
    step = "Resolving imports and usage of hashlib"

    imports_hashlib = False

    def visit_ImportFrom(self, node: ImportFrom) -> typing.List[AST]:
        if node.module != "hashlib":
            return node
        additional_assigns = []
        for n in node.names:
            imported_fun = None
            for h in PythonHashlib:
                if h.name == n.name:
                    imported_fun = h
            assert (
                imported_fun is not None
            ), f"Unsupported function import from hashlib '{n.name}"
            typ = PythonHashlibTypes[imported_fun]
            imported_name = n.name if n.asname is None else n.asname
            additional_assigns.append(
                TypedAssign(
                    targets=[TypedName(id=imported_name, typ=typ, ctx=Store())],
                    value=RawPlutoExpr(typ=typ, expr=PythonHashlibImpls[imported_fun]),
                )
            )
        return additional_assigns

Ancestors

Class variables

var imports_hashlib
var step

Methods

def visit(self, node)

Inherited from: CompilingNodeTransformer.visit

Visit a node.

def visit_ImportFrom(self, node: ast.ImportFrom) ‑> List[ast.AST]
Expand source code
def visit_ImportFrom(self, node: ImportFrom) -> typing.List[AST]:
    if node.module != "hashlib":
        return node
    additional_assigns = []
    for n in node.names:
        imported_fun = None
        for h in PythonHashlib:
            if h.name == n.name:
                imported_fun = h
        assert (
            imported_fun is not None
        ), f"Unsupported function import from hashlib '{n.name}"
        typ = PythonHashlibTypes[imported_fun]
        imported_name = n.name if n.asname is None else n.asname
        additional_assigns.append(
            TypedAssign(
                targets=[TypedName(id=imported_name, typ=typ, ctx=Store())],
                value=RawPlutoExpr(typ=typ, expr=PythonHashlibImpls[imported_fun]),
            )
        )
    return additional_assigns