Skip to content

Commit

Permalink
Merge pull request #81 from rdnfn/dev/general
Browse files Browse the repository at this point in the history
v0.5.1
  • Loading branch information
rdnfn authored Jun 28, 2022
2 parents c51e9a6 + 7efe3e6 commit c92068e
Show file tree
Hide file tree
Showing 18 changed files with 147 additions and 51 deletions.
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
version: 0.5.0
version: 0.5.1
url: https://github.com/rdnfn/beobench
authors:
- given-names: Arduin
Expand Down
23 changes: 20 additions & 3 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@
History
=======

0.5.1 (2022-06-28)
------------------

* Features:

* Add pretty logging based on loguru package. Now all Beobench output is clearly marked as such.

* Improvements

* Enable adding wrapper without setting config.
* Add ``demo.yaml`` simple example config.

* Fixes

* Update Sinergym integration to latest Sinergym version.

0.5.0 (2022-05-26)
------------------

Expand All @@ -11,15 +27,16 @@ History
* Support for automatically running multiple samples/trials of same experiment via ``num_samples`` config parameter.
* Configs named `.beobench.yml` will be automatically parsed when Beobench is run in directory containing such a config. This allows users to set e.g. wandb API keys without referring to the config in every Beobench command call.
* Configs from experiments now specify the Beobench version used. When trying to rerun an experiment this version will be checked, and an error thrown if there is a mismatch between installed and requested version.
* Add improved high-level API for getting started. This uses the CLI arguments ``--method``, ``--gym`` and ``--env``. Example usage: ``beobench run --method ppo --gym sinergym --env Eplus-5Zone-hot-continuous-v1``.
* Add improved high-level API for getting started. This uses the CLI arguments ``--method``, ``--gym`` and ``--env``. Example usage: ``beobench run --method ppo --gym sinergym --env Eplus-5Zone-hot-continuous-v1`` (#55).

* Improvements

* Add ``CITATION.cff`` file to citing software easier.
* Add ``CITATION.cff`` file to make citing software easier.
* By default, docker builds of experiment images are now skipped if an image with tag corresponding to installed Beobench version already exists.
* Remove outdated guides and add yaml configuration description from docs.
* Remove outdated guides and add yaml configuration description from docs (#38, #76, #78).
* Add support for logging multidimensional actions to wandb.
* Add support for logging summary metrics on every env reset to wandb.
* Energym config now uses ``name`` argument like other integrations (#34).

* Fixes

Expand Down
2 changes: 1 addition & 1 deletion beobench/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__author__ = """Beobench authors"""
__email__ = "-"
__version__ = "0.5.0"
__version__ = "0.5.1"

from beobench.utils import restart
from beobench.experiment.scheduler import run
2 changes: 1 addition & 1 deletion beobench/beobench_contrib
2 changes: 1 addition & 1 deletion beobench/data/configs/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,4 @@ general:
# experiment once.
num_samples: 1
# Beobench version
version: 0.5.0
version: 0.5.1
16 changes: 16 additions & 0 deletions beobench/data/configs/demo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
agent:
origin: random_action
config:
stop:
timesteps_total: 10
env:
gym: sinergym
config:
name: Eplus-5Zone-hot-continuous-v1
wrappers:
- origin: general
class: WandbLogger
general:
wandb_entity: beobench
wandb_project: demo
# wandb_api_key: HIDDEN
3 changes: 2 additions & 1 deletion beobench/experiment/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
import random
import os
from beobench.logging import logger

import beobench
import beobench.utils
Expand Down Expand Up @@ -138,7 +139,7 @@ def get_user() -> dict:
"""

if os.path.isfile(USER_CONFIG_PATH):
print(f"Beobench: recognised user config at '{USER_CONFIG_PATH}'.")
logger.info(f"Recognised user config at '{USER_CONFIG_PATH}'.")
user_config = parse(USER_CONFIG_PATH)
else:
user_config = {}
Expand Down
26 changes: 13 additions & 13 deletions beobench/experiment/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import subprocess
import os
import docker
from loguru import logger

import beobench
from beobench.constants import AVAILABLE_INTEGRATIONS
Expand Down Expand Up @@ -74,7 +75,7 @@ def build_experiment_container(
)
package_build_context = True

print(f"Beobench: recognised integration named {gym_name}.")
logger.info(f"Recognised integration named {gym_name}.")
else:
# get alphanumeric name from context
context_name = "".join(e for e in build_context if e.isalnum())
Expand All @@ -88,15 +89,14 @@ def build_experiment_container(

# skip build if image already exists.
if not force_build and check_image_exists(stage2_image_tag):
print(f"Beobench: existing image found ({stage2_image_tag}). Skipping build.")
logger.info(f"Existing image found ({stage2_image_tag}). Skipping build.")
return stage2_image_tag

print(
f"Beobench: image not found ({stage2_image_tag}) or forced",
"rebuild. Building image.",
logger.warning(
f"Image not found ({stage2_image_tag}) or forced rebuild. Building image.",
)

print(f"Building experiment base image `{stage0_image_tag}`...")
logger.info(f"Building experiment base image `{stage0_image_tag}`...")

with contextlib.ExitStack() as stack:
# if using build context from beobench package, get (potentially temp.) build
Expand All @@ -114,7 +114,7 @@ def build_experiment_container(
build_context,
]
env = os.environ.copy()
print("Running command: " + " ".join(stage0_build_args))
logger.info("Running command: " + " ".join(stage0_build_args))
subprocess.check_call(
stage0_build_args,
env=env, # this enables accessing dockerfile in subdir
Expand Down Expand Up @@ -146,7 +146,7 @@ def build_experiment_container(
with subprocess.Popen(
["cat", stage1_dockerfile], stdout=subprocess.PIPE
) as proc:
print("Running command: " + " ".join(stage1_build_args))
logger.info("Running command: " + " ".join(stage1_build_args))
subprocess.check_call(
stage1_build_args,
stdin=proc.stdout,
Expand Down Expand Up @@ -191,14 +191,14 @@ def build_experiment_container(
with subprocess.Popen(
["cat", stage2_dockerfile], stdout=subprocess.PIPE
) as proc:
print("Running command: " + " ".join(stage2_build_args))
logger.info("Running command: " + " ".join(stage2_build_args))
subprocess.check_call(
stage2_build_args,
stdin=proc.stdout,
env=env, # this enables accessing dockerfile in subdir
)

print("Experiment gym image build finished.")
logger.info("Experiment gym image build finished.")

return stage2_image_tag

Expand All @@ -213,10 +213,10 @@ def create_docker_network(network_name: str) -> None:
network_name (str): name of docker network.
"""

print("Creating docker network ...")
logger.info("Creating docker network ...")
try:
args = ["docker", "network", "create", network_name]
subprocess.check_call(args)
print("Docker network created.")
logger.info("Docker network created.")
except subprocess.CalledProcessError:
print("No new network created. Network may already exist.")
logger.info("No new network created. Network may already exist.")
6 changes: 5 additions & 1 deletion beobench/experiment/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ def create_env(env_config: dict = None) -> object:

for wrapper_dict in config["wrappers"]:
wrapper = _get_wrapper(wrapper_dict)
env = wrapper(env, **wrapper_dict["config"])
if "config" in wrapper_dict.keys():
wrapper_config = wrapper_dict["config"]
else:
wrapper_config = {}
env = wrapper(env, **wrapper_config)

return env

Expand Down
17 changes: 12 additions & 5 deletions beobench/experiment/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@
import beobench.experiment.containers
import beobench.experiment.config_parser
import beobench.utils
import beobench.logging
from beobench.logging import logger
from beobench.constants import CONTAINER_DATA_DIR, CONTAINER_RO_DIR, AVAILABLE_AGENTS

beobench.logging.setup()


def run(
config: Union[str, dict, pathlib.Path, list] = None,
Expand Down Expand Up @@ -94,7 +98,7 @@ def run(
num_samples (int, optional): number of experiment samples to run. This defaults
to a single sample, i.e. just running the experiment once.
"""
print("Beobench: starting experiment run ...")
logger.info("Starting experiment run ...")
# parsing relevant kwargs and adding them to config
kwarg_config = _create_config_from_kwargs(
local_dir=local_dir,
Expand Down Expand Up @@ -134,9 +138,9 @@ def run(
for i in range(1, num_samples + 1):
# TODO: enable checking whether something is run in container
# and do not print the statement below if inside experiment container.
print(
logger.info(
(
f"Beobench: running experiment in container with environment "
f"Running experiment in container with environment "
f"{config['env']['name']}"
f" and agent from {config['agent']['origin']}. Sample {i} of"
f" {num_samples}."
Expand All @@ -152,6 +156,8 @@ def run(
# Execute experiment
# (this is usually reached from inside an experiment container)

logger.info("Running agent script.")

container_ro_dir_abs = CONTAINER_RO_DIR.absolute()
args = [
"python",
Expand Down Expand Up @@ -301,9 +307,10 @@ def _build_and_run_in_container(config: dict) -> None:
arg_str = " ".join(args)
if wandb_api_key:
arg_str = arg_str.replace(wandb_api_key, "<API_KEY_HIDDEN>")
print(f"Executing docker command: {arg_str}")
logger.info(f"Executing docker command: {arg_str}")

subprocess.check_call(args)
# subprocess.check_call(args)
beobench.utils.run_command(args, process_name="container")


def _create_config_from_kwargs(**kwargs) -> dict:
Expand Down
37 changes: 37 additions & 0 deletions beobench/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Logging utilities for Beobench."""

from loguru import logger
import sys


def setup(include_time=False) -> None:
"""Setup Beobench loguru logging setup."""
if include_time:
time_str = "<light-black>[{time:YYYY-MM-DD, HH:mm:ss.SSSS}]</light-black> "
else:
time_str = ""
logger.remove()
logger.level("INFO", color="")
logger.add(
sys.stdout,
colorize=True,
format=(
"<blue><b>Beobench</b></blue> "
"<y>⚡️</y>"
f"{time_str}"
"<level>{message}</level>"
),
)


def log_subprocess(pipe, process_name="subprocess"):
"""Log subprocess pipe.
Adapted from from https://stackoverflow.com/a/21978778.
Color setting of context is described in https://stackoverflow.com/a/33206814.
"""
for line in iter(pipe.readline, b""): # b'\n'-separated lines
context = f"\033[34m{process_name}:\033[0m" # .decode("ascii")
line = line.decode("utf-8").rstrip()
logger.info(f"{context} {line}")
28 changes: 25 additions & 3 deletions beobench/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"""Module with a number of utility functions."""

import docker
import subprocess

import beobench.logging
from beobench.logging import logger


def check_if_in_notebook() -> bool:
Expand Down Expand Up @@ -87,17 +91,19 @@ def merge_dicts(
def shutdown() -> None:
"""Shut down all beobench and BOPTEST containers."""

print("Stopping any remaining beobench and BOPTEST docker containers...")
beobench.logging.setup()

logger.info("Stopping any remaining beobench and BOPTEST docker containers...")

client = docker.from_env()
container_num = 0
for container in client.containers.list():
if "auto_beobench" in container.name or "auto_boptest" in container.name:
print(f"Stopping container {container.name}")
logger.info(f"Stopping container {container.name}")
container.stop(timeout=0)
container_num += 1

print(f"Stopped {container_num} container(s).")
logger.info(f"Stopped {container_num} container(s).")


def restart() -> None:
Expand All @@ -110,3 +116,19 @@ def restart() -> None:
"""

shutdown()


def run_command(cmd_line_args, process_name):
"""Run command and log its output."""

process = subprocess.Popen( # pylint: disable=consider-using-with
cmd_line_args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
with process.stdout:
beobench.logging.log_subprocess(
process.stdout,
process_name=process_name,
)
_ = process.wait() # 0 means success
2 changes: 1 addition & 1 deletion beobench/wrappers/energym.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, env: gym.Env, info_obs_weights: dict):
def step(self, action):
obs, _, done, info = self.env.step(action)

reward = sum(
reward = sum( # pylint: disable=consider-using-generator
[info["obs"][key] * value for key, value in self.info_obs_weights.items()]
)
return obs, reward, done, info
8 changes: 1 addition & 7 deletions pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,6 @@ disable=abstract-method,
# mypackage.mymodule.MyReporterClass.
output-format=text

# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]". This option is deprecated
# and it will be removed in Pylint 2.0.
files-output=no

# Tells whether to display a full report or only the messages
reports=no

Expand Down Expand Up @@ -283,7 +277,7 @@ single-line-if-stmt=yes
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=
# no-space-check=

# Maximum number of lines in a module
max-module-lines=99999
Expand Down
Loading

0 comments on commit c92068e

Please sign in to comment.