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
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)
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")
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)
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']})", )
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()
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")
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()
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")
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}")
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")
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}" )
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", )
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="")
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()
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()
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)
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"])
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}" )
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()
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
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")
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()))
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)
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('/')
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()