-
Notifications
You must be signed in to change notification settings - Fork 9
/
utils.py
58 lines (42 loc) · 1.67 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""Utility module for ``soulsgym``."""
from __future__ import annotations
from threading import Lock
from typing import Any, Union
from weakref import WeakValueDictionary
import numpy as np
import psutil
def get_pid(process_name: str) -> int:
"""Get the ID of a process.
Args:
process_name: The name of the process.
Returns:
The process ID.
Raises:
RuntimeError: No process with name ``process_name`` currently open.
"""
for proc in psutil.process_iter():
if proc.name() == process_name:
return proc.pid
raise RuntimeError(f"Process {process_name} not open")
def wrap_to_pi(x: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""Wrap an angle into the interval of [-pi, pi].
Args:
x: Single angle or array of angles in radians.
Returns:
The wrapped angle.
"""
return ((x + np.pi) % (2 * np.pi)) - np.pi
class Singleton(type):
"""Metaclass for singletons."""
# Enables automatic __del__ call after all instances have gone out of scope
# See https://stackoverflow.com/questions/43619748/destroying-a-singleton-object-in-python
_instances = WeakValueDictionary()
_lock = Lock()
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
"""Create the class if no object is already instantiated, otherwise return the instance."""
with cls._lock:
if cls not in cls._instances:
# This variable declaration is required to force a strong reference on the instance
instance = super(Singleton, cls).__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]