Add flake8 linter and Github CI (#19)

* Cleanup code

* Add flake8 lint and github workflow

* Update build matrix

* Relax precision for python3.7
This commit is contained in:
Antonin RAFFIN 2020-05-12 17:55:01 +02:00 committed by GitHub
parent 299c28140e
commit 54f6f5b6fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 130 additions and 45 deletions

View file

@ -24,6 +24,7 @@
- [ ] My change requires a change to the documentation.
- [ ] I have updated the tests accordingly (*required for a bug fix or a new feature*).
- [ ] I have updated the documentation accordingly.
- [ ] I have ensured `pytest` and `pytype` both pass.
- [ ] I have checked the codestyle using `make lint`
- [ ] I have ensured `make pytest` and `make type` both pass.
<!--- This Template is an edited version of the one from https://github.com/evilsocket/pwnagotchi/ -->

43
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,43 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
# Skip CI if [ci skip] in the commit message
if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')"
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7] # 3.8 not supported yet by pytype
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# cpu version of pytorch
pip install torch==1.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
pip install .[extra,tests,docs]
# Use headless version
pip install opencv-python-headless
- name: Type check
run: |
make type
- name: Lint with flake8
run: |
make lint
- name: Test with pytest
run: |
make pytest

View file

@ -99,6 +99,12 @@ Type checking with `pytype`:
make type
```
Codestyle check with `flake8`:
```
make lint
```
Build the documentation:
```

View file

@ -6,6 +6,13 @@ pytest:
type:
pytype
lint:
# stop the build if there are Python syntax errors or undefined names
# see https://lintlyci.github.io/Flake8Rules/
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings.
flake8 . --count --exit-zero --statistics
doc:
cd docs && make html
@ -15,7 +22,7 @@ spelling:
clean:
cd docs && make clean
.PHONY: clean spelling doc
.PHONY: clean spelling doc lint
# Build docker images
# If you do export RELEASE=True, it will also push them

View file

@ -34,6 +34,8 @@ Others:
- Sync ``VecEnvs`` with Stable-Baselines
- Update requirement: ``gym>=0.17``
- Added ``.readthedoc.yml`` file
- Added ``flake8`` and ``make lint`` command
- Added Github workflow
Documentation:
^^^^^^^^^^^^^^

View file

@ -19,3 +19,30 @@ filterwarnings =
[pytype]
inputs = stable_baselines3
[flake8]
ignore = W503,W504 # line breaks before and after binary operators
# Ignore import not used when aliases are defined
per-file-ignores =
./stable_baselines3/__init__.py:F401
./stable_baselines3/common/__init__.py:F401
./stable_baselines3/a2c/__init__.py:F401
./stable_baselines3/ppo/__init__.py:F401
./stable_baselines3/sac/__init__.py:F401
./stable_baselines3/td3/__init__.py:F401
./stable_baselines3/common/vec_env/__init__.py:F401
exclude =
# No need to traverse our git directory
.git,
# There's no value in checking cache directories
__pycache__,
# Don't check the doc
docs/
# This contains our built documentation
build,
# This contains builds of flake8 that we don't want to check
dist
*.egg-info
max-complexity = 15
# The GitHub editor is 127 chars wide
max-line-length = 127

View file

@ -1,6 +1,4 @@
import os
import sys
import subprocess
from setuptools import setup, find_packages
with open(os.path.join('stable_baselines3', 'version.txt'), 'r') as file_handler:
@ -63,14 +61,14 @@ from stable_baselines3 import PPO
model = PPO('MlpPolicy', 'CartPole-v1').learn(10000)
```
"""
""" # noqa:E501
setup(name='stable_baselines3',
packages=[package for package in find_packages()
if package.startswith('stable_baselines3')],
package_data={
'stable_baselines3': ['py.typed', 'version.txt']
'stable_baselines3': ['py.typed', 'version.txt']
},
install_requires=[
'gym>=0.17',
@ -84,28 +82,32 @@ setup(name='stable_baselines3',
'matplotlib'
],
extras_require={
'tests': [
'pytest',
'pytest-cov',
'pytest-env',
'pytest-xdist',
'pytype',
],
'docs': [
'sphinx',
'sphinx-autobuild',
'sphinx-rtd-theme',
# For spelling
'sphinxcontrib.spelling',
# Type hints support
# 'sphinx-autodoc-typehints'
],
'extra': [
# For render
'opencv-python',
# For atari games,
'atari_py~=0.2.0', 'pillow'
]
'tests': [
# Run tests and coverage
'pytest',
'pytest-cov',
'pytest-env',
'pytest-xdist',
# Type check
'pytype',
# Lint code
'flake8>=3.8'
],
'docs': [
'sphinx',
'sphinx-autobuild',
'sphinx-rtd-theme',
# For spelling
'sphinxcontrib.spelling',
# Type hints support
# 'sphinx-autodoc-typehints'
],
'extra': [
# For render
'opencv-python',
# For atari games,
'atari_py~=0.2.0', 'pillow'
]
},
description='Pytorch version of Stable Baselines, implementations of reinforcement learning algorithms.',
author='Antonin Raffin',

View file

@ -4,9 +4,7 @@ from typing import Dict, Any, Optional, Callable, Type, Union
import gym
from stable_baselines3.common import logger
from stable_baselines3.common.monitor import Monitor
from stable_baselines3.common.utils import set_random_seed
from stable_baselines3.common.atari_wrappers import AtariWrapper
from stable_baselines3.common.vec_env import DummyVecEnv, SubprocVecEnv

View file

@ -1,4 +1,4 @@
from typing import List, Union, Optional
from typing import Union, Optional
import numpy as np
from gym import Env, Space

View file

@ -71,8 +71,8 @@ class OrnsteinUhlenbeckActionNoise(ActionNoise):
super(OrnsteinUhlenbeckActionNoise, self).__init__()
def __call__(self) -> np.ndarray:
noise = self.noise_prev + self._theta * (self._mu - self.noise_prev) * self._dt + \
self._sigma * np.sqrt(self._dt) * np.random.normal(size=self._mu.shape)
noise = (self.noise_prev + self._theta * (self._mu - self.noise_prev) * self._dt
+ self._sigma * np.sqrt(self._dt) * np.random.normal(size=self._mu.shape))
self.noise_prev = noise
return noise

View file

@ -6,7 +6,7 @@ from stable_baselines3.common.vec_env.base_vec_env import VecEnv, VecEnvWrapper
from stable_baselines3.common.preprocessing import is_image_space
if typing.TYPE_CHECKING:
from stable_baselines3.common.type_aliases import GymStepReturn
from stable_baselines3.common.type_aliases import GymStepReturn # noqa: F401
class VecTransposeImage(VecEnvWrapper):

View file

@ -42,6 +42,7 @@ def test_squashed_gaussian(model_class):
actions = dist.get_actions()
assert th.max(th.abs(actions)) <= 1.0
def test_sde_distribution():
n_actions = 1
deterministic_actions = th.ones(N_SAMPLES, n_actions) * 0.1
@ -95,4 +96,4 @@ def test_categorical():
actions = dist.get_actions()
entropy = dist.entropy()
log_prob = dist.log_prob(actions)
assert th.allclose(entropy.mean(), -log_prob.mean(), rtol=1e-4)
assert th.allclose(entropy.mean(), -log_prob.mean(), rtol=2e-4)

View file

@ -46,6 +46,7 @@ def test_high_dimension_action_space():
env = FakeImageEnv()
# Patch the action space
env.action_space = spaces.Box(low=-1, high=1, shape=(20,), dtype=np.float32)
# Patch to avoid error
def patched_step(_action):
return env.observation_space.sample(), 0.0, False, {}

View file

@ -230,7 +230,6 @@ def test_save_load_policy(model_class, policy_str):
del policy, actor
policy = policy_class.load("./logs/policy.pkl")
if actor_class is not None:
actor = actor_class.load("./logs/actor.pkl")

View file

@ -37,7 +37,7 @@ def test_make_vec_env(env_id, n_envs, vec_env_cls, wrapper_class):
@pytest.mark.parametrize("env_id", ['BreakoutNoFrameskip-v4'])
@pytest.mark.parametrize("n_envs", [1, 2])
@pytest.mark.parametrize("wrapper_kwargs", [None, dict(clip_reward=False, screen_size=60)])
def test_make_vec_env(env_id, n_envs, wrapper_kwargs):
def test_make_atari_env(env_id, n_envs, wrapper_kwargs):
env_id = 'BreakoutNoFrameskip-v4'
env = make_atari_env(env_id, n_envs,
wrapper_kwargs=wrapper_kwargs, monitor_dir=None, seed=0)
@ -55,15 +55,14 @@ def test_make_vec_env(env_id, n_envs, wrapper_kwargs):
if wrapper_kwargs is not None:
assert obs.shape == (n_envs, 60, 60, 1)
assert wrapped_atari_env.observation_space.shape == (60, 60, 1)
assert wrapped_atari_env.clip_reward == False
assert wrapped_atari_env.clip_reward is False
else:
assert obs.shape == (n_envs, 84, 84, 1)
assert wrapped_atari_env.observation_space.shape == (84, 84, 1)
assert wrapped_atari_env.clip_reward == True
assert wrapped_atari_env.clip_reward is True
assert np.max(np.abs(reward)) < 1.0
def test_custom_vec_env(tmp_path):
"""
Stand alone test for a special case (passing a custom VecEnv class) to avoid doubling the number of tests.
@ -73,7 +72,6 @@ def test_custom_vec_env(tmp_path):
monitor_dir=monitor_dir, seed=0,
vec_env_cls=SubprocVecEnv, vec_env_kwargs={'start_method': None})
assert env.num_envs == 1
assert isinstance(env, SubprocVecEnv)
assert os.path.isdir(monitor_dir)

View file

@ -68,5 +68,4 @@ def test_check_nan():
else:
assert False
env.step(np.array([[0, 1], [0, 1]]))

View file

@ -3,7 +3,8 @@ import pytest
import numpy as np
from stable_baselines3.common.running_mean_std import RunningMeanStd
from stable_baselines3.common.vec_env import DummyVecEnv, VecNormalize, VecFrameStack, sync_envs_normalization, unwrap_vec_normalize
from stable_baselines3.common.vec_env import (DummyVecEnv, VecNormalize, VecFrameStack, sync_envs_normalization,
unwrap_vec_normalize)
from stable_baselines3 import SAC, TD3
ENV_ID = 'Pendulum-v0'
@ -53,8 +54,8 @@ def _make_warmstart_cartpole():
def test_runningmeanstd():
"""Test RunningMeanStd object"""
for (x_1, x_2, x_3) in [
(np.random.randn(3), np.random.randn(4), np.random.randn(5)),
(np.random.randn(3, 2), np.random.randn(4, 2), np.random.randn(5, 2))]:
(np.random.randn(3), np.random.randn(4), np.random.randn(5)),
(np.random.randn(3, 2), np.random.randn(4, 2), np.random.randn(5, 2))]:
rms = RunningMeanStd(epsilon=0.0, shape=x_1.shape[1:])
x_cat = np.concatenate([x_1, x_2, x_3], axis=0)