File: __init__.py

package info (click to toggle)
brian 2.9.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,872 kB
  • sloc: python: 51,820; cpp: 2,033; makefile: 108; sh: 72
file content (262 lines) | stat: -rw-r--r-- 7,975 bytes parent folder | download
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
"""
Brian 2
"""

import logging
import signal


def _check_dependencies():
    """Check basic dependencies"""
    import sys

    missing = []
    try:
        import numpy
    except ImportError as ex:
        sys.stderr.write(f"Importing numpy failed: '{ex}'\n")
        missing.append("numpy")
    try:
        import sympy
    except ImportError as ex:
        sys.stderr.write(f"Importing sympy failed: '{ex}'\n")
        missing.append("sympy")
    try:
        import pyparsing
    except ImportError as ex:
        sys.stderr.write(f"Importing pyparsing failed: '{ex}'\n")
        missing.append("pyparsing")
    try:
        import jinja2
    except ImportError as ex:
        sys.stderr.write(f"Importing Jinja2 failed: '{ex}'\n")
        missing.append("jinja2")

    if len(missing):
        raise ImportError(
            f"Some required dependencies are missing:\n{', '.join(missing)}"
        )


_check_dependencies()

try:
    from pylab import *
except ImportError:
    # Do the non-matplotlib pylab imports manually
    # don't let numpy's datetime hide stdlib
    import datetime

    import numpy.ma as ma
    from numpy import *
    from numpy.fft import *
    from numpy.linalg import *
    from numpy.random import *

# Make sure that Brian's unit-aware functions are used, even when directly
# using names prefixed with numpy or np
import brian2.numpy_ as numpy
import brian2.numpy_ as np

try:
    from ._version import __version__, __version_tuple__
except ImportError:
    try:
        from setuptools_scm import get_version

        __version__ = get_version(
            root="..",
            relative_to=__file__,
            version_scheme="post-release",
            local_scheme="no-local-version",
        )
        __version_tuple__ = tuple(int(x) for x in __version__.split(".")[:3])
    except ImportError:
        logging.getLogger("brian2").warning(
            "Cannot determine Brian version, running from source and "
            "setuptools_scm is not installed."
        )
        __version__ = "unknown"
        __version_tuple__ = (0, 0, 0)

# delete some annoying names from the namespace
if "x" in globals():
    del x
if "f" in globals():
    del f
if "rate" in globals():
    del rate

__docformat__ = "restructuredtext en"

from brian2.only import *
from brian2.only import test


# Check for outdated dependency versions
def _check_dependency_version(name, version):
    import sys

    from packaging.version import Version

    from .core.preferences import prefs
    from .utils.logger import get_logger

    logger = get_logger(__name__)

    module = sys.modules[name]
    if not isinstance(module.__version__, str):  # mocked module
        return
    if not Version(module.__version__) >= Version(version):
        message = (
            f"{name} is outdated (got version {module.__version__}, need version"
            f" {version})"
        )
        if prefs.core.outdated_dependency_error:
            raise ImportError(message)
        else:
            logger.warn(message, "outdated_dependency")


def _check_dependency_versions():
    for name, version in [("numpy", "1.10"), ("sympy", "1.2"), ("jinja2", "2.7")]:
        _check_dependency_version(name, version)


_check_dependency_versions()

# Initialize the logging system
BrianLogger.initialize()
logger = get_logger(__name__)


# Check the caches
def _get_size_recursively(dirname):
    import os

    total_size = 0
    for dirpath, _, filenames in os.walk(dirname):
        for fname in filenames:
            # When other simulations are running, files may disappear while
            # we walk through the directory (in particular with Cython, where
            # we delete the source files after compilation by default)
            try:
                size = os.path.getsize(os.path.join(dirpath, fname))
                total_size += size
            except OSError:
                pass  # ignore the file
    return total_size


#: Stores the cache directory for code generation targets
_cache_dirs_and_extensions = {}


def check_cache(target):
    cache_dir, _ = _cache_dirs_and_extensions.get(target, (None, None))
    if cache_dir is None:
        return
    size = _get_size_recursively(cache_dir)
    size_in_mb = int(round(size / 1024.0 / 1024.0))
    if size_in_mb > prefs.codegen.max_cache_dir_size:
        logger.info(
            f"Cache size for target '{target}': {size_in_mb} MB.\n"
            f"You can call clear_cache('{target}') to delete all "
            "files from the cache or manually delete files in the "
            f"'{cache_dir}' directory."
        )
    else:
        logger.debug(f"Cache size for target '{target}': {size_in_mb} MB")


def clear_cache(target):
    """
    Clears the on-disk cache with the compiled files for a given code generation
    target.

    Parameters
    ----------
    target : str
        The code generation target (e.g. ``'cython'``)

    Raises
    ------
    ValueError
        If the given code generation target does not have an on-disk cache
    IOError
        If the cache directory contains unexpected files, suggesting that
        deleting it would also delete files unrelated to the cache.
    """
    import os
    import shutil

    cache_dir, extensions = _cache_dirs_and_extensions.get(target, (None, None))
    if cache_dir is None:
        raise ValueError(f'No cache directory registered for target "{target}".')
    cache_dir = os.path.abspath(cache_dir)  # just to make sure...
    for folder, _, files in os.walk(cache_dir):
        for f in files:
            for ext in extensions:
                if f.endswith(ext):
                    break
            else:
                raise OSError(
                    f"The cache directory for target '{target}' contains "
                    f"the file '{os.path.join(folder, f)}' of an unexpected type and "
                    "will therefore not be removed. Delete files in "
                    f"'{cache_dir}' manually"
                )

    logger.debug(f"Clearing cache for target '{target}' (directory '{cache_dir}').")
    shutil.rmtree(cache_dir)


def _check_caches():
    from brian2.codegen.runtime.cython_rt.extension_manager import (
        get_cython_cache_dir,
        get_cython_extensions,
    )

    for target, (dirname, extensions) in [
        ("cython", (get_cython_cache_dir(), get_cython_extensions()))
    ]:
        _cache_dirs_and_extensions[target] = (dirname, extensions)
        if prefs.codegen.max_cache_dir_size > 0:
            check_cache(target)


_check_caches()


class _InterruptHandler:
    """
    Class to turn a Ctrl+C interruption (SIGINT signal) into a `stop` signal for
    a running simulation (i.e., finish simulating the current time step and then
    stop). This handler is activated by default, but can be switched off by
    setting the `core.stop_on_keyboard_interrupt` preference to ``False``.
    Note that this will only handle interruptions during a `Network.run`,
    interrupting at any other time will raise a `KeyboardInterrupt` in the
    usual way. In case that finishing the current time step takes a long time
    (or hangs for some reason), interrupting with Ctrl+C a second time will
    force the usual interrupt, regardless of the preference setting.
    """

    def __init__(self, previous_handler):
        self.previous_handler = previous_handler

    def __call__(self, signalnum, stack_frame):
        if (
            not prefs.core.stop_on_keyboard_interrupt
            or not Network._globally_running
            or Network._globally_stopped
        ):
            self.previous_handler(signalnum, stack_frame)
        else:
            logging.getLogger("brian2").warning(
                "Simulation stop requested. Press Ctrl+C again to interrupt."
            )
            Network._globally_stopped = True


_int_handler = _InterruptHandler(signal.getsignal(signal.SIGINT))
signal.signal(signal.SIGINT, _int_handler)