提交 936554c1 authored 作者: Michael Osthege's avatar Michael Osthege 提交者: Brandon T. Willard

Deprecate change_flags and _config_print in theano.configparser

上级 9b2ad15f
......@@ -9,6 +9,25 @@ from theano.configdefaults import default_blas_ldflags
from theano.configparser import ConfigParam
def test_api_deprecation_warning():
# accessing through configdefaults.config is the new best practice
with pytest.warns(None):
root = configdefaults.config
assert isinstance(str(root), str)
# accessing through configparser.config is discouraged
root = configparser.config
with pytest.warns(DeprecationWarning, match="instead"):
root.add(
"test_deprecationwarning",
"A config var from a test case.",
configparser.StrParam("test_default"),
)
with pytest.warns(DeprecationWarning, match="instead"):
with root.change_flags(test_deprecationwarning="new_value"):
pass
def test_invalid_default():
# Ensure an invalid default value found in the Theano code only causes
# a crash if it is not overridden by the user.
......
......@@ -64,7 +64,12 @@ for p in sys.path:
raise RuntimeError("You have the theano directory in your Python path.")
from theano.configdefaults import config
from theano.configparser import change_flags
from theano.utils import deprecated
change_flags = deprecated("Use theano.config.change_flags instead!")(
config.change_flags
)
# This is the api version for ops that generate C code. External ops
......
......@@ -2232,10 +2232,15 @@ SUPPORTED_DNN_CONV_PRECISION = (
"float64",
)
# TODO: we should add the configs explicitly to an instance of the TheanoConfigParser
# Even if we treat it as a singleton, we should implement as if it wasn't, so we can
# test it independently.
config = theano.configparser.config
# Eventually, the instance of `TheanoConfigParser` should be created right here,
# where it is also populated with settings. But for a transition period, it
# remains as `configparser._config`, while everybody accessing it through
# `configparser.config` is flooded with deprecation warnings. These warnings
# instruct one to use `theano.config`, which is an alias for
# `theano.configdefaults.config`.
config = theano.configparser._config
# The functions below register config variables into the config instance above.
add_basic_configvars()
add_dnn_configvars()
add_magma_configvars()
......
......@@ -15,6 +15,8 @@ from configparser import (
from functools import wraps
from io import StringIO
from theano.utils import deprecated
_logger = logging.getLogger("theano.configparser")
......@@ -161,8 +163,11 @@ class TheanoConfigParser:
# The user provided a value, filter it now.
configparam.__get__(self, type(self), delete_key=True)
except KeyError:
_logger.error(
f"Suppressed KeyError in AddConfigVar for parameter '{name}'!"
# This is raised because the underlying `ConfigParser` in
# `self._theano_cfg` does not contain an entry for the given
# section and/or value.
_logger.info(
f"Suppressed KeyError in TheanoConfigParser.add for parameter '{name}'!"
)
# the ConfigParam implements __get__/__set__, enabling us to create a property:
......@@ -263,7 +268,7 @@ class ConfigParam:
self.in_c_key = None
# Note that we do not call `self.filter` on the default value: this
# will be done automatically in AddConfigVar, potentially with a
# will be done automatically in TheanoConfigParser.add, potentially with a
# more appropriate user-provided default value.
# Calling `filter` here may actually be harmful if the default value is
# invalid and causes a crash or has unwanted side effects.
......@@ -527,8 +532,48 @@ def _create_default_config():
return config
config = _create_default_config()
# aliasing for old API
AddConfigVar = config.add
change_flags = config.change_flags
_config_print = config.config_print
# will be overwritten by configdefaults
class ConfigProxy:
def __init__(self, actual):
ConfigProxy._actual = actual
def __getattr__(self, attr):
if attr == "_actual":
return ConfigProxy._actual
warnings.warn(
"Accessing config through `theano.configparser.config` is deprecated. "
"Use `theano.config` instead.",
DeprecationWarning,
stacklevel=2,
)
return getattr(self._actual, attr)
def __setattr__(self, attr, value):
if attr == "_actual":
return setattr(ConfigProxy._actual, attr, value)
warnings.warn(
"Accessing config through `theano.configparser.config` is deprecated. "
"Use `theano.config` instead.",
DeprecationWarning,
stacklevel=2,
)
return setattr(self._actual, attr, value)
# Create the actual instance of the config. This one should eventually move to
# `configdefaults`:
_config = _create_default_config()
# The old API often imported the default config object from `configparser`.
# These imports/accesses should be replaced with `theano.config`, so this wraps
# it with warnings:
config = ConfigProxy(_config)
# We can't alias the methods of the `config` variable above without already
# triggering the warning. Instead, we wrap the methods of the actual instance
# with warnings:
change_flags = deprecated("Use theano.config.change_flags instead!")(
_config.change_flags
)
_config_print = deprecated("Use theano.config.config_print instead!")(
_config.config_print
)
"""Utility functions for Theano."""
import inspect
import traceback
import warnings
from collections import OrderedDict
from collections.abc import Callable
from functools import wraps
__all__ = [
......@@ -110,3 +114,35 @@ def maybe_add_to_os_environ_pathlist(var, newpath):
os.environ[var] = newpaths
except Exception:
pass
def deprecated(message: str = ""):
"""
This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used first time and filter is set for show DeprecationWarning.
Taken from https://stackoverflow.com/a/40899499/4473230
"""
def decorator_wrapper(func):
@wraps(func)
def function_wrapper(*args, **kwargs):
current_call_source = "|".join(
traceback.format_stack(inspect.currentframe())
)
if current_call_source not in function_wrapper.last_call_source:
warnings.warn(
"Function {} is now deprecated! {}".format(func.__name__, message),
category=DeprecationWarning,
stacklevel=2,
)
function_wrapper.last_call_source.add(current_call_source)
return func(*args, **kwargs)
function_wrapper.last_call_source = set()
return function_wrapper
return decorator_wrapper
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论