Skip to content

Commit

Permalink
Merge pull request #501 from matnad/cmd_settings-to-config
Browse files Browse the repository at this point in the history
Cmd settings to config
  • Loading branch information
iamdefinitelyahuman authored May 9, 2020
2 parents 269c71a + 74790f1 commit 025329e
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 9 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ This changelog format is based on [Keep a Changelog](https://keepachangelog.com/
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/iamdefinitelyahuman/brownie)
### Added
- `Wei.to` for unit conversion ([#501](https://github.com/iamdefinitelyahuman/brownie/pull/501))
- Exposed `block_time`, `default_balance` and `time` ganache-cli parameters ([#501](https://github.com/iamdefinitelyahuman/brownie/pull/501))

### Changed
- `brownie-config.yaml` can now specify ganache-cli parameters ([#501](https://github.com/iamdefinitelyahuman/brownie/pull/501))

## [1.8.3](https://github.com/iamdefinitelyahuman/brownie/tree/v1.8.3) - 2020-05-06
### Changed
Expand Down
3 changes: 3 additions & 0 deletions brownie/_cli/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
"fork",
"mnemonic",
"account_keys_path",
"block_time",
"default_balance",
"time",
)


Expand Down
30 changes: 26 additions & 4 deletions brownie/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ def set_active_network(self, id_: str = None) -> Dict:
key = "development" if "cmd" in network else "live"
network["settings"] = self.settings["networks"][key].copy()

if key == "development" and "fork" in network["cmd_settings"]:
if (
key == "development"
and isinstance(network["cmd_settings"], dict)
and "fork" in network["cmd_settings"]
):

fork = network["cmd_settings"]["fork"]
if fork in self.networks:
Expand Down Expand Up @@ -149,7 +153,7 @@ def _get_project_config_path(project_path: Path):


def _load_config(project_path: Path) -> Dict:
# Loads configuration data from a file, returns as a dict
"""Loads configuration data from a file, returns as a dict"""
path = _get_project_config_path(project_path)
if path is None:
return {}
Expand All @@ -163,7 +167,7 @@ def _load_config(project_path: Path) -> Dict:


def _load_project_config(project_path: Path) -> None:
# Loads configuration settings from a project's brownie-config.yaml
"""Loads configuration settings from a project's brownie-config.yaml"""
config_path = project_path.joinpath("brownie-config")
config_data = _load_config(config_path)
if not config_data:
Expand All @@ -178,6 +182,22 @@ def _load_project_config(project_path: Path) -> None:
)
del config_data["network"]

# Update the network config cmd_settings with project specific cmd_settings
if "networks" in config_data and isinstance(config_data["networks"], dict):
for network, values in config_data["networks"].items():
if (
network != "default"
and network in CONFIG.networks.keys()
and "cmd_settings" in values
and isinstance(values["cmd_settings"], dict)
):
if "cmd_settings" in CONFIG.networks[network]:
_recursive_update(
CONFIG.networks[network]["cmd_settings"], values["cmd_settings"]
)
else:
CONFIG.networks[network]["cmd_settings"] = values["cmd_settings"]

CONFIG.settings._unlock()
_recursive_update(CONFIG.settings, config_data)
CONFIG.settings._lock()
Expand Down Expand Up @@ -215,7 +235,9 @@ def _modify_hypothesis_settings(settings, name, parent):


def _recursive_update(original: Dict, new: Dict) -> None:
# merges project config with brownie default config
"""Recursively merges a new dict into the original dict"""
if not original:
original = {}
for k in new:
if k in original and isinstance(new[k], dict):
_recursive_update(original[k], new[k])
Expand Down
18 changes: 16 additions & 2 deletions brownie/convert/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@

class Wei(int):

'''Integer subclass that converts a value to wei and allows comparison against
"""Integer subclass that converts a value to wei and allows comparison against
similarly formatted values.
Useful for the following formats:
* a string specifying the unit: "10 ether", "300 gwei", "0.25 shannon"
* a large float in scientific notation, where direct conversion to int
would cause inaccuracy: 8.3e32
* bytes: b'\xff\xff'
* hex strings: "0x330124"'''
* hex strings: "0x330124\""""

# Known typing error: https://github.com/python/mypy/issues/4290
def __new__(cls, value: Any) -> Any: # type: ignore
Expand Down Expand Up @@ -74,6 +74,20 @@ def __add__(self, other: Any) -> "Wei":
def __sub__(self, other: Any) -> "Wei":
return Wei(super().__sub__(_to_wei(other)))

def to(self, unit: str) -> "Fixed":
"""
Returns a converted denomination of the stored wei value.
Accepts any valid ether unit denomination as string, like:
"gwei", "milliether", "finney", "ether".
:param unit: An ether denomination like "ether" or "gwei"
:return: A 'Fixed' type number in the specified denomination
"""
try:
return Fixed(self * Fixed(10) ** -UNITS[unit])
except KeyError:
raise TypeError(f'Cannot convert wei to unknown unit: "{unit}". ') from None


def _to_wei(value: WeiInputTypes) -> int:
original = value
Expand Down
1 change: 1 addition & 0 deletions brownie/data/default-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ networks:
gas_price: 0
reverting_tx_gas_limit: 6721975
default_contract_owner: true
cmd_settings: null
live:
gas_limit: auto
gas_price: auto
Expand Down
4 changes: 4 additions & 0 deletions brownie/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,7 @@ class BrownieCompilerWarning(Warning):

class BrownieEnvironmentWarning(Warning):
pass


class InvalidArgumentWarning(BrownieEnvironmentWarning):
pass
60 changes: 58 additions & 2 deletions brownie/network/rpc.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#!/usr/bin/python3

import atexit
import datetime
import gc
import sys
import threading
import time
import warnings
import weakref
from subprocess import DEVNULL, PIPE
from typing import Any, Dict, List, Optional, Tuple, Union
Expand All @@ -14,7 +16,13 @@

from brownie._config import EVM_EQUIVALENTS
from brownie._singleton import _Singleton
from brownie.exceptions import RPCConnectionError, RPCProcessError, RPCRequestError
from brownie.convert import Wei
from brownie.exceptions import (
InvalidArgumentWarning,
RPCConnectionError,
RPCProcessError,
RPCRequestError,
)

from .web3 import web3

Expand All @@ -26,6 +34,9 @@
"fork": "--fork",
"mnemonic": "--mnemonic",
"account_keys_path": "--acctKeys",
"block_time": "--blockTime",
"default_balance": "--defaultBalanceEther",
"time": "--time",
}

EVM_VERSIONS = ["byzantium", "constantinople", "petersburg", "istanbul"]
Expand Down Expand Up @@ -81,8 +92,16 @@ def launch(self, cmd: str, **kwargs: Dict) -> None:
kwargs.setdefault("evm_version", EVM_DEFAULT) # type: ignore
if kwargs["evm_version"] in EVM_EQUIVALENTS:
kwargs["evm_version"] = EVM_EQUIVALENTS[kwargs["evm_version"]] # type: ignore
kwargs = _validate_cmd_settings(kwargs)
for key, value in [(k, v) for k, v in kwargs.items() if v]:
cmd += f" {CLI_FLAGS[key]} {value}"
try:
cmd += f" {CLI_FLAGS[key]} {value}"
except KeyError:
warnings.warn(
f"Ignoring invalid commandline setting for ganache-cli: "
f'"{key}" with value "{value}".',
InvalidArgumentWarning,
)
print(f"Launching '{cmd}'...")
self._time_offset = 0
self._snapshot_id = False
Expand All @@ -98,6 +117,7 @@ def launch(self, cmd: str, **kwargs: Dict) -> None:
if web3.isConnected():
self._reset_id = self._current_id = self._snap()
_notify_registry(0)
self._time_offset = self._request("evm_increaseTime", [0])
return
time.sleep(0.1)
if type(self._rpc) is psutil.Popen:
Expand Down Expand Up @@ -130,6 +150,7 @@ def attach(self, laddr: Union[str, Tuple]) -> None:
) from None
print(f"Attached to local RPC client listening at '{laddr[0]}:{laddr[1]}'...")
self._rpc = psutil.Process(proc.pid)
self._time_offset = self._request("evm_increaseTime", [0])
if web3.provider:
self._reset_id = self._current_id = self._snap()
_notify_registry(0)
Expand Down Expand Up @@ -372,3 +393,38 @@ def _check_connections(proc: psutil.Process, laddr: Tuple) -> bool:
return laddr in [i.laddr for i in proc.connections()]
except psutil.AccessDenied:
return False


def _validate_cmd_settings(cmd_settings: dict) -> dict:
CMD_TYPES = {
"port": int,
"gas_limit": int,
"block_time": int,
"time": datetime.datetime,
"accounts": int,
"evm_version": str,
"mnemonic": str,
"account_keys_path": str,
"fork": str,
}
for cmd, value in cmd_settings.items():
if (
cmd in CLI_FLAGS.keys()
and cmd in CMD_TYPES.keys()
and not isinstance(value, CMD_TYPES[cmd])
):
raise TypeError(
f'Wrong type for cmd_settings "{cmd}": {value}. '
f"Found {type(value).__name__}, but expected {CMD_TYPES[cmd].__name__}."
)

if "default_balance" in cmd_settings:
try:
cmd_settings["default_balance"] = int(cmd_settings["default_balance"])
except ValueError:
# convert any input to ether, then format it properly
default_eth = Wei(cmd_settings["default_balance"]).to("ether")
cmd_settings["default_balance"] = (
default_eth.quantize(1) if default_eth > 1 else default_eth.normalize()
)
return cmd_settings
4 changes: 4 additions & 0 deletions docs/api-brownie.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ Warnings
Raised on unexpected environment conditions.

.. py:exception:: brownie.exceptions.InvalidArgumentWarning
Raised on non-critical, invalid arguments passed to a method, function or config file.

``brownie._config``
===================

Expand Down
12 changes: 12 additions & 0 deletions docs/api-convert.rst
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ Wei
>>> Wei("1 ether") - "0.75 ether"
250000000000000000
.. py:classmethod:: Wei.to(unit)
Returns a :class:`Fixed <brownie.convert.datatypes.Fixed>` number converted to the specified unit.
Attempting a conversion to an unknown unit raises a ``TypeError``.
.. code-block:: python
>>> from brownie import Wei
>>> Wei("20 gwei").to("ether")
Fixed('2.0000000000E-8')
``brownie.convert.normalize``
=============================
Expand Down
28 changes: 28 additions & 0 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,33 @@ Networks

.. py:attribute:: networks.development
This setting is only available for development networks.

.. py:attribute:: cmd_settings
Additional commandline parameters, which are passed into Ganache as commandline arguments. These settings will update the network specific settings defined in :ref:`network management<adding-network>` whenever the project with this configuration file is active.

The following example shows all commandline settings with their default value. ``fork`` has no default value and ``time`` will default to the current time. See :ref:`adding a development network<adding-network>` for more details on the arguments.

.. code-block:: yaml
networks:
development:
gas_limit: 6721975
gas_price: 0
reverting_tx_gas_limit: 6721975
default_contract_owner: true
cmd_settings:
port: 8545
gas_limit: 6721975
accounts: 10
evm_version: istanbul
fork: None
mnemonic: brownie
block_time: 0
default_balance: 100
time: 2020-05-08T14:54:08+0000
.. py:attribute:: networks.live
Default settings for development and live environments.
Expand Down Expand Up @@ -68,6 +95,7 @@ Networks

live default: ``false``


.. _config-solc:


Expand Down
9 changes: 9 additions & 0 deletions docs/network-management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ The following fields are optional for live networks:

* ``explorer``: API url used by :func:`Contract.from_explorer <Contract.from_explorer>` to fetch source code. If this field is not given, you will not be able to fetch source code when using this network.


.. _adding-network:

Development Networks
********************

Expand All @@ -96,6 +99,12 @@ The following optional fields may be given for development networks, which are p
* ``mnemonic``: A mnemonic to use when generating local accounts.
* ``evm_version``: The EVM ruleset to use. Default is the most recent available.
* ``fork``: If given, the local client will fork from another currently running Ethereum client. The value may be an HTTP location and port of the other client, e.g. ``http://localhost:8545``, or the ID of a production network, e.g. ``mainnet``. See :ref:`Using a Forked Development Network <network-management-fork>`.
* ``block_time``: The time waited between mining blocks. Defaults to instant mining.
* ``default_balance``: The starting balance for all unlocked accounts. Can be given as unit string like "1000 ether" or "50 gwei" or as an number **in Ether**. Will default to 100 ether.
* ``time``: Date (ISO 8601) that the first block should start. Use this feature, along with :func:`Rpc.sleep <Rpc.sleep>` to test time-dependent code. Defaults to the current time.

.. note::
These optional commandline fields can also be specified on a project level in the project's ``brownie-config.yaml`` file. See the :ref:`configuration files<config>`.

.. note::

Expand Down
22 changes: 21 additions & 1 deletion tests/convert/test_wei.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/python3
import pytest

from brownie.convert import Wei
from brownie.convert.datatypes import Fixed, Wei


def test_nonetype():
Expand Down Expand Up @@ -64,3 +65,22 @@ def test_gt():
def test_ge():
assert Wei("2 ether") >= "1 ether"
assert Wei("2 ether") >= "2 ether"


@pytest.mark.parametrize(
"conversion_tuples",
(
("999999", "gwei"),
("50", "ether"),
("0", "milliether"),
("0.1", "kwei"),
("0.00001", "ether"),
),
)
def test_to(conversion_tuples):
assert Wei(" ".join(conversion_tuples)).to(conversion_tuples[1]) == Fixed(conversion_tuples[0])


def test_raise_to():
with pytest.raises(TypeError):
Wei("1 ether").to("foo")
Loading

0 comments on commit 025329e

Please sign in to comment.