예제 #1
0
def _get_project_path() -> Optional[Path]:
    key = next((i for i in sys.argv if i.startswith("--brownie-project")), "")
    if key == "--brownie-project":
        idx = sys.argv.index(key)
        project_path = Path(sys.argv[idx + 1]).absolute()
    elif key.startswith("--brownie-project="):
        project_path = Path(key[18:]).absolute()
    else:
        return project.check_for_project(".")

    if project_path != project.check_for_project(project_path):
        raise pytest.UsageError(
            f"Unable to load project at '{sys.argv[idx + 1]}'")
    return project_path
예제 #2
0
def main():
    args = docopt(__doc__, more_magic=True)
    _update_argv_from_docopt(args)

    active_project = None
    if project.check_for_project():
        active_project = project.load()
        active_project.load_config()
        print(f"{active_project._name} is the active project.")

    network.connect(CONFIG.argv["network"])

    path, _ = _get_path(args["<filename>"])
    path_str = path.absolute().as_posix()

    try:
        return_value, frame = run(
            args["<filename>"],
            method_name=args["<function>"] or "main",
            args=args["<arg>"],
            _include_frame=True,
        )
        exit_code = 0
    except Exception as e:
        print(color.format_tb(e))
        frame = next(
            (i.frame for i in inspect.trace()[::-1]
             if Path(i.filename).as_posix() == path_str),
            None,
        )
        if frame is None:
            # exception was an internal brownie issue - do not open the console
            sys.exit(1)
        exit_code = 1
        return_value = None

    try:
        if args["--interactive"]:
            # filter internal objects from the namespace prior to opening the console
            globals_dict = {
                k: v
                for k, v in frame.f_globals.items() if not k.startswith("__")
            }
            extra_locals = {
                "_": return_value,
                **globals_dict,
                **frame.f_locals
            }
            shell = Console(active_project, extra_locals)
            shell.interact(
                banner="\nInteractive mode enabled. Use quit() to close.",
                exitmsg="")
    finally:
        # the console terminates from a SystemExit - make sure we still deliver the final gas report
        if CONFIG.argv["gas"]:
            print("\n======= Gas profile =======")
            for line in _build_gas_profile_output():
                print(line)

        sys.exit(exit_code)
예제 #3
0
파일: plugin.py 프로젝트: tbrent/brownie
def pytest_configure(config):
    if project.check_for_project("."):

        try:
            active_project = project.load()
            active_project.load_config()
            active_project._add_to_main_namespace()
        except Exception as e:
            # prevent pytest INTERNALERROR traceback when project fails to compile
            print(f"{color.format_tb(e)}\n")
            raise pytest.UsageError("Unable to load project")

        # enable verbose output if stdout capture is disabled
        if config.getoption("capture") == "no":
            config.option.verbose = True

        if config.getoption("numprocesses"):
            if config.getoption("interactive"):
                raise ValueError("Cannot use --interactive mode with xdist")
            Plugin = PytestBrownieMaster
        elif hasattr(config, "workerinput"):
            Plugin = PytestBrownieXdistRunner
        else:
            Plugin = PytestBrownieRunner

        session = Plugin(config, active_project)
        config.pluginmanager.register(session, "brownie-core")

        if not config.getoption("numprocesses"):
            fixtures = PytestBrownieFixtures(config, active_project)
            config.pluginmanager.register(fixtures, "brownie-fixtures")
예제 #4
0
파일: test.py 프로젝트: syuukawa/brownie
def main():
    args = docopt(__doc__)

    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound

    # ensure imports are possible from anywhere in the project
    project.main._add_to_sys_path(project_path)

    if args["<path>"] is None:
        structure_config = _load_project_structure_config(project_path)
        args["<path>"] = project_path.joinpath(
            structure_config["tests"]).as_posix()

    pytest_args = [args["<path>"]]
    for opt, value in [(i, args[i]) for i in sorted(args)
                       if i.startswith("-") and args[i]]:
        if value is True:
            pytest_args.append(opt)
        elif isinstance(value, str):
            pytest_args.extend([opt, value])

    return_code = pytest.main(pytest_args, ["pytest-brownie"])

    if return_code:
        # only exit with non-zero status to make testing easier
        sys.exit(return_code)
예제 #5
0
def pytest_addoption(parser):
    if project.check_for_project("."):
        parser.addoption("--coverage",
                         "-C",
                         action="store_true",
                         help="Evaluate contract test coverage")
        parser.addoption("--gas",
                         "-G",
                         action="store_true",
                         help="Display gas profile for function calls")
        parser.addoption("--update",
                         "-U",
                         action="store_true",
                         help="Only run tests where changes have occurred")
        parser.addoption("--revert-tb",
                         "-R",
                         action="store_true",
                         help="Show detailed traceback on tx reverts")
        parser.addoption(
            "--stateful",
            choices=["true", "false"],
            default=None,
            help="Only run or skip stateful tests (default: run all tests)",
        )
        parser.addoption(
            "--network",
            "-N",
            default=False,
            nargs=1,
            help=
            f"Use a specific network (default {CONFIG.settings['networks']['default']})",
        )
예제 #6
0
def get_ast_hash(path):
    '''Generates a hash based on the AST of a script.

    Args:
        path: path of the script to hash

    Returns: sha1 hash as bytes'''
    with Path(path).open() as fp:
        ast_list = [ast.parse(fp.read(), path)]
    base_path = str(check_for_project(path))
    for obj in [
            i for i in ast_list[0].body
            if type(i) in (ast.Import, ast.ImportFrom)
    ]:
        if type(obj) is ast.Import:
            name = obj.names[0].name
        else:
            name = obj.module
        try:
            origin = importlib.util.find_spec(name).origin
        except Exception as e:
            raise type(e)(f"in {path} - {e}") from None
        if base_path in origin:
            with open(origin) as fp:
                ast_list.append(ast.parse(fp.read(), origin))
    dump = "\n".join(ast.dump(i) for i in ast_list)
    return sha1(dump.encode()).hexdigest()
예제 #7
0
def pytest_configure(config):
    if project.check_for_project("."):

        active_project = project.load()
        active_project.load_config()
        active_project._add_to_main_namespace()

        # enable verbose output if stdout capture is disabled
        if config.getoption("capture") == "no":
            config.option.verbose = True

        if config.getoption("numprocesses"):
            if config.getoption("interactive"):
                raise ValueError("Cannot use --interactive mode with xdist")
            Plugin = PytestBrownieMaster
        elif hasattr(config, "workerinput"):
            Plugin = PytestBrownieXdistRunner
        else:
            Plugin = PytestBrownieRunner

        session = Plugin(config, active_project)
        config.pluginmanager.register(session, "brownie-core")

        if not config.getoption("numprocesses"):
            fixtures = PytestBrownieFixtures(config, active_project)
            config.pluginmanager.register(fixtures, "brownie-fixtures")
예제 #8
0
def main():
    """The main entry point of the MythX plugin for Brownie."""

    args = docopt(__doc__)
    _update_argv_from_docopt(args)

    if CONFIG.argv["mode"] not in ANALYSIS_MODES:
        raise ValidationError(
            "Invalid analysis mode: Must be one of [{}]".format(
                ", ".join(ANALYSIS_MODES)))

    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound

    build = project.load()._build
    submission = SubmissionPipeline(build)

    print("Preparing project data for submission to MythX...")
    submission.prepare_requests()

    print("Sending analysis requests to MythX...")
    submission.send_requests()

    # exit if user wants an async analysis run
    if CONFIG.argv["async"]:
        print(
            "\nAll contracts were submitted successfully. Check the dashboard at "
            "https://dashboard.mythx.io/ for the progress and results of your analyses"
        )
        return

    print("\nWaiting for results...")

    submission.wait_for_jobs()
    submission.generate_stdout_report()
    submission.generate_highlighting_report()

    # erase previous report
    report_path = project_path.joinpath(
        _load_project_structure_config(project_path)["reports"])

    report_path = report_path.joinpath("security.json")
    if report_path.exists():
        report_path.unlink()

    print_console_report(submission.stdout_report)

    # Write report to Brownie directory
    with report_path.open("w+") as fp:
        json.dump(submission.highlight_report, fp, indent=2, sort_keys=True)

    # Launch GUI if user requested it
    if CONFIG.argv["gui"]:
        print("Launching the Brownie GUI")
        gui = importlib.import_module("brownie._gui").Gui
        gui().mainloop()
예제 #9
0
파일: plugin.py 프로젝트: syuukawa/brownie
def pytest_load_initial_conftests():
    if project.check_for_project("."):
        try:
            active_project = project.load()
            active_project.load_config()
            active_project._add_to_main_namespace()
        except Exception as e:
            # prevent pytest INTERNALERROR traceback when project fails to compile
            print(f"{color.format_tb(e)}\n")
            raise pytest.UsageError("Unable to load project")
예제 #10
0
def main():
    args = docopt(__doc__)
    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound
    build_path = project_path.joinpath("build/contracts")
    if args["--all"]:
        shutil.rmtree(build_path, ignore_errors=True)
    project.load(project_path)
    print(f"Brownie project has been compiled at {build_path}")
예제 #11
0
def pytest_configure(config):
    if project.check_for_project("."):

        try:
            active_project = project.load()
            active_project.load_config()
            active_project._add_to_main_namespace()
        except Exception as e:
            # prevent pytest INTERNALERROR traceback when project fails to compile
            print(f"{color.format_tb(e)}\n")
            raise pytest.UsageError("Unable to load project")

        if not config.getoption("showinternal"):
            # do not include brownie internals in tracebacks
            base_path = Path(sys.modules["brownie"].__file__).parent.as_posix()
            for module in [
                v
                for v in sys.modules.values()
                if getattr(v, "__file__", None) and v.__file__.startswith(base_path)
            ]:
                module.__tracebackhide__ = True
                module.__hypothesistracebackhide__ = True

        # enable verbose output if stdout capture is disabled
        if config.getoption("capture") == "no":
            config.option.verbose = True

        # if verbose mode is enabled, also enable hypothesis verbose mode
        if config.option.verbose:
            _modify_hypothesis_settings({"verbosity": 2}, "brownie-verbose")

        if config.getoption("numprocesses"):
            if config.getoption("interactive"):
                raise ValueError("Cannot use --interactive mode with xdist")
            Plugin = PytestBrownieMaster
        elif hasattr(config, "workerinput"):
            Plugin = PytestBrownieXdistRunner
        else:
            Plugin = PytestBrownieRunner

        if config.getoption("interactive"):
            config.option.failfast = True

        if config.getoption("failfast"):
            _modify_hypothesis_settings(
                {"phases": {"explicit": True, "generate": True, "target": True}}, "brownie-failfast"
            )

        session = Plugin(config, active_project)
        config.pluginmanager.register(session, "brownie-core")

        if not config.getoption("numprocesses"):
            fixtures = PytestBrownieFixtures(config, active_project)
            config.pluginmanager.register(fixtures, "brownie-fixtures")
예제 #12
0
def main():
    args = docopt(__doc__)
    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound
    contract_artifact_path = project_path.joinpath("build/contracts")
    interface_artifact_path = project_path.joinpath("build/interfaces")
    if args["--all"]:
        shutil.rmtree(contract_artifact_path, ignore_errors=True)
        shutil.rmtree(interface_artifact_path, ignore_errors=True)
    project.load(project_path)
    print(
        f"Project has been compiled. Build artifacts saved at {contract_artifact_path}"
    )
예제 #13
0
파일: plugin.py 프로젝트: syuukawa/brownie
def pytest_addoption(parser):
    if project.check_for_project("."):
        parser.addoption("--coverage",
                         "-C",
                         action="store_true",
                         help="Evaluate contract test coverage")
        parser.addoption("--gas",
                         "-G",
                         action="store_true",
                         help="Display gas profile for function calls")
        parser.addoption("--update",
                         "-U",
                         action="store_true",
                         help="Only run tests where changes have occurred")
        parser.addoption("--revert-tb",
                         "-R",
                         action="store_true",
                         help="Show detailed traceback on tx reverts")
        parser.addoption(
            "--interactive",
            "-I",
            action="store_true",
            help="Open an interactive console each time a test fails",
        )
        parser.addoption(
            "--stateful",
            choices=["true", "false"],
            default=None,
            help="Only run or skip stateful tests (default: run all tests)",
        )
        parser.addoption(
            "--failfast",
            action="store_true",
            help="Fail hypothesis tests quickly (no shrinking)",
        )
        parser.addoption(
            "--network",
            "-N",
            default=False,
            nargs=1,
            help=
            f"Use a specific network (default {CONFIG.settings['networks']['default']})",
        )
        parser.addoption(
            "--showinternal",
            action="store_true",
            help="Include Brownie internal frames in tracebacks",
        )
예제 #14
0
def main():
    args = docopt(__doc__)
    _update_argv_from_docopt(args)

    if project.check_for_project():
        active_project = project.load()
        active_project.load_config()
        print(f"{active_project._name} is the active project.")
    else:
        active_project = None
        print("No project was loaded.")

    network.connect(CONFIG.argv["network"])

    shell = Console(active_project)
    shell.interact(banner="Brownie environment is ready.", exitmsg="")
예제 #15
0
파일: plugin.py 프로젝트: zhouhaw/brownie
def pytest_load_initial_conftests(early_config):
    capsys = early_config.pluginmanager.get_plugin("capturemanager")
    if project.check_for_project("."):
        # suspend stdout capture to display compilation data
        capsys.suspend()
        try:
            active_project = project.load()

            active_project.load_config()
            active_project._add_to_main_namespace()
        except Exception as e:
            # prevent pytest INTERNALERROR traceback when project fails to compile
            print(f"{color.format_tb(e)}\n")
            raise pytest.UsageError("Unable to load project")
        finally:
            capsys.resume()
예제 #16
0
def main():
    args = docopt(__doc__)
    _update_argv_from_docopt(args)

    if project.check_for_project():
        active_project = project.load()
        active_project.load_config()
        print(f"{active_project._name} is the active project.")
    else:
        raise ProjectNotFound

    network.connect(ARGV["network"])

    run(args["<filename>"], method_name=args["<function>"] or "main")
    if ARGV["gas"]:
        _print_gas_profile()
예제 #17
0
파일: run.py 프로젝트: samwerner/brownie-v2
def main():
    args = docopt(__doc__)
    _update_argv_from_docopt(args)

    if project.check_for_project():
        active_project = project.load()
        active_project.load_config()
        print(f"{active_project._name} is the active project.")
    else:
        raise ProjectNotFound

    network.connect(CONFIG.argv["network"])

    path, _ = _get_path(args["<filename>"])
    path_str = path.absolute().as_posix()

    try:
        run(args["<filename>"], method_name=args["<function>"] or "main")
    except Exception as e:
        print(color.format_tb(e))

        if args["--interactive"]:
            frame = next(
                (i.frame for i in inspect.trace()[::-1]
                 if Path(i.filename).as_posix() == path_str),
                None,
            )
            if frame is not None:
                globals_dict = {
                    k: v
                    for k, v in frame.f_globals.items()
                    if not k.startswith("__")
                }

                shell = Console(active_project, {
                    **globals_dict,
                    **frame.f_locals
                })
                shell.interact(
                    banner="\nInteractive mode enabled. Use quit() to close.",
                    exitmsg="")
        sys.exit(1)

    if CONFIG.argv["gas"]:
        print("\n======= Gas profile =======")
        for line in _build_gas_profile_output():
            print(line)
예제 #18
0
파일: test.py 프로젝트: thanos/brownie
def main():
    args = docopt(__doc__)

    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound

    if args["<path>"] is None:
        args["<path>"] = project_path.joinpath("tests").as_posix()

    pytest_args = [args["<path>"]]
    for opt, value in [(i, args[i]) for i in sorted(args) if i.startswith("-") and args[i]]:
        if value is True:
            pytest_args.append(opt)
        elif isinstance(value, str):
            pytest_args.extend([opt, value])

    pytest.main(pytest_args, ["pytest-brownie"])
예제 #19
0
파일: compile.py 프로젝트: zhouhaw/brownie
def main():
    args = docopt(__doc__)
    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound

    build_path = project_path.joinpath(
        _load_project_structure_config(project_path)["build"])

    contract_artifact_path = build_path.joinpath("contracts")
    interface_artifact_path = build_path.joinpath("interfaces")

    if args["--all"]:
        shutil.rmtree(contract_artifact_path, ignore_errors=True)
        shutil.rmtree(interface_artifact_path, ignore_errors=True)
    elif args["<contract>"]:
        for name in args["<contract>"]:
            path = contract_artifact_path.joinpath(f"{name}.json")
            if path.exists():
                path.unlink()

    proj = project.load()

    if args["--size"]:
        print("============ Deployment Bytecode Sizes ============")
        codesize = []
        for contract in proj:
            bytecode = contract._build["deployedBytecode"]
            if bytecode:
                codesize.append((contract._name, len(bytecode) // 2))
        indent = max(len(i[0]) for i in codesize)
        for name, size in sorted(codesize, key=lambda k: k[1], reverse=True):
            pct = size / 24577
            pct_color = color(
                next((i[1] for i in CODESIZE_COLORS if pct >= i[0]), ""))
            print(
                f"  {name:<{indent}}  -  {size:>6,}B  ({pct_color}{pct:.2%}{color})"
            )
        print()

    print(
        f"Project has been compiled. Build artifacts saved at {contract_artifact_path}"
    )
예제 #20
0
def main():
    args = docopt(__doc__)
    update_argv_from_docopt(args)

    if project.check_for_project():
        active_project = project.load()
        active_project.load_config()
        print(f"{active_project._name} is the active project.")
    else:
        active_project = None
        print("No project was loaded.")

    network.connect(ARGV['network'])

    run(args['<filename>'],
        method_name=args['<function>'] or "main",
        project=active_project)
    if ARGV['gas']:
        print_gas_profile()
예제 #21
0
파일: ethpm.py 프로젝트: trnhgquan/brownie
def main():
    args = docopt(__doc__)
    try:
        fn = getattr(sys.modules[__name__], f"_{args['<command>']}")
    except AttributeError:
        print("Invalid command. Try brownie ethpm --help")
        return
    project_path = check_for_project(".")
    if project_path is None:
        raise ProjectNotFound
    if not project_path.joinpath("ethpm-config.yaml").exists():
        shutil.copy(
            BROWNIE_FOLDER.joinpath("data/ethpm-config.yaml"),
            project_path.joinpath("ethpm-config.yaml"),
        )
    try:
        fn(project_path, *args["<arguments>"])
    except TypeError:
        print(f"Invalid arguments for command '{args['<command>']}'. Try brownie ethpm --help")
        return
예제 #22
0
def pytest_configure(config):
    if project.check_for_project("."):

        active_project = project.load()
        active_project.load_config()
        active_project._add_to_main_namespace()

        if config.getoption("numprocesses"):
            Plugin = PytestBrownieMaster
        elif hasattr(config, "workerinput"):
            Plugin = PytestBrownieXdistRunner
        else:
            Plugin = PytestBrownieRunner

        session = Plugin(config, active_project)
        config.pluginmanager.register(session, "brownie-core")

        if not config.getoption("numprocesses"):
            fixtures = PytestBrownieFixtures(config, active_project)
            config.pluginmanager.register(fixtures, "brownie-fixtures")
예제 #23
0
def main():

    print("Brownie v{} - Python development framework for Ethereum\n".format(__version__))

    if len(sys.argv) > 1 and sys.argv[1][0] != "-":
        try:
            idx = next(sys.argv.index(i) for i in sys.argv if i[0] == "-")
            opts = sys.argv[idx:]
            sys.argv = sys.argv[:idx]
        except StopIteration:
            opts = []

    args = docopt(__doc__)
    sys.argv += opts


    cmd_list = [i.name[:-3] for i in Path(__file__).parent.glob('*.py') if i.name[0] != "_"]
    if args['<command>'] not in cmd_list:
        sys.exit("Invalid command. Try 'brownie --help' for available commands.")

    if args['<command>'] not in ("init", "bake"):
        path = project.check_for_project('.')
        if not path:
            sys.exit(
                "ERROR: Brownie environment has not been initiated for this folder."
                "\nType 'brownie init' to create the file structure."
            )
        for container in project.load(path):
            setattr(brownie, container._name, container)
            brownie.__all__.append(container._name)
        brownie.a = brownie.accounts
        brownie.__all__.append('a')

    try:
        importlib.import_module("brownie.cli."+args['<command>']).main()
    except Exception:
        print(color.format_tb(sys.exc_info()))
예제 #24
0
파일: test.py 프로젝트: zhouhaw/brownie
def main():
    if "-h" in sys.argv or "--help" in sys.argv:
        # display the help screen and exit
        docopt(__doc__)

    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound

    # ensure imports are possible from anywhere in the project
    project.main._add_to_sys_path(project_path)

    pytest_args = sys.argv[sys.argv.index("test") + 1:]
    if not pytest_args or pytest_args[0].startswith("-"):
        structure_config = _load_project_structure_config(project_path)
        pytest_args.insert(
            0,
            project_path.joinpath(structure_config["tests"]).as_posix())

    return_code = pytest.main(pytest_args, ["pytest-brownie"])

    if return_code:
        # only exit with non-zero status to make testing easier
        sys.exit(return_code)
예제 #25
0
def test_check_for_project():
    path = project.check_for_project('tests/brownie-test-project')
    assert path == project.check_for_project('tests/brownie-test-project/contracts')
    assert not project.check_for_project('/')
예제 #26
0
def main():
    args = docopt(__doc__)
    _update_argv_from_docopt(args)

    project_path = project.check_for_project(".")
    if project_path is None:
        raise ProjectNotFound

    build = project.load()._build

    print("Preparing project data for submission to MythX...")
    contracts, libraries = get_contract_types(build)

    job_data = assemble_contract_jobs(build, contracts)
    job_data = update_contract_jobs_with_dependencies(build, contracts,
                                                      libraries, job_data)

    client, authenticated = get_mythx_client()

    job_uuids = send_to_mythx(job_data, client, authenticated)

    # exit if user wants an async analysis run
    if ARGV["async"] and authenticated:
        print(
            "\nAll contracts were submitted successfully. Check the dashboard at "
            "https://dashboard.mythx.io/ for the progress and results of your analyses"
        )
        return

    print("\nWaiting for results...")
    wait_for_jobs(job_uuids, client)

    # assemble report json
    source_to_name = get_contract_locations(build)
    highlight_report = {"highlights": {"MythX": {}}}
    stdout_report = {}
    for c, uuid in enumerate(job_uuids, start=1):
        print(
            f"Generating report for job {color['value']}{uuid}{color} ({c}/{len(job_uuids)})"
        )
        if authenticated:
            print("You can also check the results at {}{}\n".format(
                DASHBOARD_BASE_URL, uuid))

        update_report(client, uuid, highlight_report, stdout_report,
                      source_to_name)

    # erase previous report
    report_path = Path("reports/security.json")
    if report_path.exists():
        report_path.unlink()

    total_issues = sum(x for i in stdout_report.values() for x in i.values())
    if not total_issues:
        notify("SUCCESS", "No issues found!")
        return

    # display console report
    total_high_severity = sum(i.get("HIGH", 0) for i in stdout_report.values())
    if total_high_severity:
        notify(
            "WARNING",
            f"Found {total_issues} issues including {total_high_severity} high severity!"
        )
    else:
        print(f"Found {total_issues} issues:")
    for name in sorted(stdout_report):
        print(f"\n  contract: {color['contract']}{name}{color}")
        for key in [
                i for i in ("HIGH", "MEDIUM", "LOW")
                if i in stdout_report[name]
        ]:
            c = color("bright " + SEVERITY_COLOURS[key])
            print(f"    {key.title()}: {c}{stdout_report[name][key]}{color}")

    # Write report to Brownie directory
    with report_path.open("w+") as fp:
        json.dump(highlight_report, fp, indent=2, sort_keys=True)

    # Launch GUI if user requested it
    if ARGV["gui"]:
        print("Launching the Brownie GUI")
        Gui = importlib.import_module("brownie._gui").Gui
        Gui().mainloop()