test_error.py 13.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#   Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import inspect
16
import os
17
import unittest
18

19
import numpy as np
20

21
import paddle
22
from paddle import fluid
23 24
from paddle.jit.dy2static import error
from paddle.jit.dy2static.origin_info import unwrap
25 26 27


def inner_func():
28
    paddle.tensor.fill_constant(shape=[1, 2], value=9, dtype="int")
29 30 31
    return


32
@paddle.jit.to_static
33 34 35
def func_error_in_compile_time(x):
    x = fluid.dygraph.to_variable(x)
    inner_func()
36
    if paddle.mean(x) < 0:
37 38 39 40 41 42
        x_v = x - 1
    else:
        x_v = x + 1
    return x_v


43
@paddle.jit.to_static
44 45
def func_error_in_compile_time_2(x):
    x = fluid.dygraph.to_variable(x)
46
    x = paddle.reshape(x, shape=[1, 2])
47 48 49
    return x


50
@paddle.jit.to_static
51
def func_error_in_runtime(x):
52
    x = fluid.dygraph.to_variable(x)
53
    two = paddle.tensor.fill_constant(shape=[1], value=2, dtype="int32")
54
    x = paddle.reshape(x, shape=[1, two])
55
    return x
56 57


58 59 60 61 62 63 64 65 66 67 68 69
@unwrap
@paddle.jit.to_static()
def func_decorated_by_other_1():
    return 1


@paddle.jit.to_static()
@unwrap
def func_decorated_by_other_2():
    return 1


70
class LayerErrorInCompiletime(paddle.nn.Layer):
71
    def __init__(self, fc_size=20):
72
        super().__init__()
73
        self._linear = paddle.nn.Linear(fc_size, fc_size)
74 75

    @paddle.jit.to_static(
76 77
        input_spec=[paddle.static.InputSpec(shape=[20, 20], dtype='float32')]
    )
78 79
    def forward(self, x):
        y = self._linear(x)
80
        z = paddle.tensor.fill_constant(shape=[1, 2], value=9, dtype="int")
81
        out = paddle.mean(y[z])
82 83 84
        return out


85
class LayerErrorInCompiletime2(paddle.nn.Layer):
86
    def __init__(self):
87
        super().__init__()
88 89 90 91 92 93 94 95 96

    @paddle.jit.to_static
    def forward(self):
        self.test_func()

    def test_func(self):
        """
        NOTE: The next line has a tab. And this test to check the IndentationError when spaces and tabs are mixed.
	A tab here.
97
        """  # fmt: skip
98 99 100
        return


101 102 103
@paddle.jit.to_static
def func_error_in_runtime_with_empty_line(x):
    x = fluid.dygraph.to_variable(x)
104
    two = paddle.tensor.fill_constant(shape=[1], value=2, dtype="int32")
105

106
    x = paddle.reshape(x, shape=[1, two])
107 108 109 110

    return x


111 112
class SuggestionErrorTestNet(paddle.nn.Layer):
    def __init__(self):
113
        super().__init__()
114 115 116 117 118 119 120
        self.inner_net = SuggestionErrorTestNet2()

    @paddle.jit.to_static
    def forward(self, x):
        return self.inner_net.forward(x)


121
class SuggestionErrorTestNet2:
122
    def __init__(self):
123
        super().__init__()
124
        self.w = paddle.to_tensor([2.0])
125 126 127 128 129 130 131 132 133 134 135

    def forward(self, x):
        out = paddle.matmul(self.w, x)
        return out


def func_suggestion_error_in_runtime(x):
    net = SuggestionErrorTestNet()
    net(x)


136 137 138 139 140 141 142 143 144
class TestFlags(unittest.TestCase):
    def setUp(self):
        self.reset_flags_to_default()

    def reset_flags_to_default(self):
        # Reset flags to use defaut value

        # 1. A flag to set whether to open the dygraph2static error reporting module
        os.environ[error.DISABLE_ERROR_ENV_NAME] = str(
145 146
            error.DEFAULT_DISABLE_NEW_ERROR
        )
147 148 149 150 151
        disable_error = int(os.getenv(error.DISABLE_ERROR_ENV_NAME, 999))
        self.assertEqual(disable_error, 0)

        # 2. A flag to set whether to display the simplified error stack
        os.environ[error.SIMPLIFY_ERROR_ENV_NAME] = str(
152 153
            error.DEFAULT_SIMPLIFY_NEW_ERROR
        )
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
        simplify_error = int(os.getenv(error.SIMPLIFY_ERROR_ENV_NAME, 999))
        self.assertEqual(simplify_error, 1)

    def _test_set_flag(self, flag_name, set_value):
        os.environ[flag_name] = str(set_value)
        new_value = int(os.getenv(error.DISABLE_ERROR_ENV_NAME, 999))
        self.assertEqual(new_value, set_value)

    def test_translator_disable_new_error(self):
        self._test_set_flag(error.DISABLE_ERROR_ENV_NAME, 1)

    def test_translator_simplify_new_error(self):
        self._test_set_flag(error.SIMPLIFY_ERROR_ENV_NAME, 0)


class TestErrorBase(unittest.TestCase):
170 171
    def setUp(self):
        self.set_input()
172 173 174
        self.set_func()
        self.set_func_call()
        self.filepath = inspect.getfile(unwrap(self.func_call))
175
        self.set_exception_type()
176
        self.set_message()
177 178 179 180

    def set_input(self):
        self.input = np.ones([3, 2])

181 182
    def set_func(self):
        raise NotImplementedError("Error test should implement set_func")
183

184 185
    def set_func_call(self):
        raise NotImplementedError("Error test should implement set_func_call")
186

187 188
    def set_exception_type(self):
        raise NotImplementedError(
189 190
            "Error test should implement set_exception_type"
        )
191

192 193 194 195 196
    def set_message(self):
        raise NotImplementedError("Error test should implement set_message")

    def reset_flags_to_default(self):
        os.environ[error.DISABLE_ERROR_ENV_NAME] = str(
197 198
            error.DEFAULT_DISABLE_NEW_ERROR
        )
199
        os.environ[error.SIMPLIFY_ERROR_ENV_NAME] = str(
200 201
            error.DEFAULT_SIMPLIFY_NEW_ERROR
        )
202 203 204

    def disable_new_error(self):
        os.environ[error.DISABLE_ERROR_ENV_NAME] = str(
205 206
            1 - error.DEFAULT_DISABLE_NEW_ERROR
        )
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

    def _test_new_error_message(self, new_exception, disable_new_error=0):
        error_message = str(new_exception)

        if disable_new_error:
            # If disable new error, 'In user code:' should not in error_message.
            self.assertNotIn('In transformed code:', error_message)
        else:
            # 1. 'In user code:' must be in error_message because it indicates that
            #  this is an optimized error message
            self.assertIn('In transformed code:', error_message)

            # 2. Check whether the converted static graph code is mapped to the dygraph code.
            for m in self.expected_message:
                self.assertIn(m, error_message)

    def _test_raise_new_exception(self, disable_new_error=0):
224 225
        paddle.disable_static()

226 227 228 229 230 231 232 233 234 235
        if disable_new_error:
            self.disable_new_error()
        else:
            self.reset_flags_to_default()

        # 1. Check whether the new exception type is the same as the old one
        with self.assertRaises(self.exception_type) as new_cm:
            self.func_call()

        new_exception = new_cm.exception
236

237 238
        # 2. Check whether the new_exception is attached ErrorData to indicate that this is a new exception
        error_data = getattr(new_exception, error.ERROR_DATA, None)
239 240
        self.assertIsInstance(error_data, error.ErrorData)

241 242 243
        # 3. Check whether the error message is optimized
        self._test_new_error_message(new_exception, disable_new_error)

244

245 246 247 248 249 250 251
# Situation 1: Call StaticLayer.__call__ to use Dynamic-to-Static
class TestErrorStaticLayerCallInCompiletime(TestErrorBase):
    def set_func(self):
        self.func = func_error_in_compile_time

    def set_input(self):
        self.input = np.ones([3, 2])
252

253 254
    def set_exception_type(self):
        self.exception_type = TypeError
255

256
    def set_message(self):
257
        self.expected_message = [
258
            'File "{}", line 35, in func_error_in_compile_time'.format(
259 260
                self.filepath
            ),
261
            'inner_func()',
262
            f'File "{self.filepath}", line 28, in inner_func',
263
            'def inner_func():',
264
            'paddle.tensor.fill_constant(shape=[1, 2], value=9, dtype="int")',
265 266 267
            '<--- HERE',
            'return',
        ]
268

269 270 271
    def set_func_call(self):
        # NOTE: self.func(self.input) is the StaticLayer().__call__(self.input)
        self.func_call = lambda: self.func(self.input)
272

273 274 275
    def test_error(self):
        for disable_new_error in [0, 1]:
            self._test_raise_new_exception(disable_new_error)
276 277


278
class TestErrorStaticLayerCallInCompiletime_2(
279 280
    TestErrorStaticLayerCallInCompiletime
):
281 282 283 284
    def set_func(self):
        self.func = func_error_in_compile_time_2

    def set_exception_type(self):
285
        self.exception_type = ValueError
286 287

    def set_message(self):
288
        self.expected_message = [
289
            'File "{}", line 46, in func_error_in_compile_time_2'.format(
290 291
                self.filepath
            ),
292 293
            'def func_error_in_compile_time_2(x):',
            'x = fluid.dygraph.to_variable(x)',
294
            'x = paddle.reshape(x, shape=[1, 2])',
295 296 297
            '<--- HERE',
            'return x',
        ]
298 299


300
class TestErrorStaticLayerCallInCompiletime_3(
301 302
    TestErrorStaticLayerCallInCompiletime
):
303 304 305 306 307 308 309 310 311 312 313
    def setUp(self):
        self.reset_flags_to_default()
        self.set_func_call()
        self.filepath = inspect.getfile(unwrap(self.func_call))
        self.set_exception_type()
        self.set_message()

    def set_exception_type(self):
        self.exception_type = IndentationError

    def set_message(self):
314
        self.expected_message = [
315
            f'File "{self.filepath}", line 91, in forward',
316 317 318 319 320
            '@paddle.jit.to_static',
            'def forward(self):',
            'self.test_func()',
            '<--- HERE',
        ]
321 322 323 324 325 326 327 328 329

    def set_func_call(self):
        layer = LayerErrorInCompiletime2()
        self.func_call = lambda: layer()

    def test_error(self):
        self._test_raise_new_exception()


330
class TestErrorStaticLayerCallInRuntime(TestErrorStaticLayerCallInCompiletime):
331 332 333 334
    def set_func(self):
        self.func = func_error_in_runtime

    def set_exception_type(self):
335
        self.exception_type = ValueError
336

337
    def set_message(self):
338
        self.expected_message = [
339
            'File "{}", line 54, in func_error_in_runtime'.format(
340 341
                self.filepath
            ),
342
            'x = fluid.dygraph.to_variable(x)',
343
            'two = paddle.tensor.fill_constant(shape=[1], value=2, dtype="int32")',
344
            'x = paddle.reshape(x, shape=[1, two])',
345 346 347
            '<--- HERE',
            'return x',
        ]
348 349 350 351 352 353 354


class TestErrorStaticLayerCallInRuntime2(TestErrorStaticLayerCallInRuntime):
    def set_func(self):
        self.func = func_error_in_runtime_with_empty_line

    def set_message(self):
355
        self.expected_message = [
356
            'File "{}", line 106, in func_error_in_runtime_with_empty_line'.format(
357 358
                self.filepath
            ),
359
            'two = paddle.tensor.fill_constant(shape=[1], value=2, dtype="int32")',
360
            'x = paddle.reshape(x, shape=[1, two])',
361 362 363
            '<--- HERE',
            'return x',
        ]
364 365


366 367 368 369 370 371 372 373 374 375 376 377
class TestJitSaveInCompiletime(TestErrorBase):
    def setUp(self):
        self.reset_flags_to_default()
        self.set_func_call()
        self.filepath = inspect.getfile(unwrap(self.func_call))
        self.set_exception_type()
        self.set_message()

    def set_exception_type(self):
        self.exception_type = TypeError

    def set_message(self):
378
        self.expected_message = [
379
            f'File "{self.filepath}", line 80, in forward',
380 381
            'def forward(self, x):',
            'y = self._linear(x)',
382
            'z = paddle.tensor.fill_constant(shape=[1, 2], value=9, dtype="int")',
383 384 385 386
            '<--- HERE',
            'out = paddle.mean(y[z])',
            'return out',
        ]
387 388 389

    def set_func_call(self):
        layer = LayerErrorInCompiletime()
390
        self.func_call = lambda: paddle.jit.save(
391 392
            layer, path="./test_dy2stat_error/model"
        )
393 394 395

    def test_error(self):
        self._test_raise_new_exception()
396 397


398 399
@paddle.jit.to_static
def func_ker_error(x):
400
    d = {'x': x}
401 402 403
    y = d['y'] + x
    return y

404

405 406 407 408 409 410
class TestKeyError(unittest.TestCase):
    def test_key_error(self):
        paddle.disable_static()
        with self.assertRaises(error.Dy2StKeyError):
            x = paddle.to_tensor([1])
            func_ker_error(x)
411

412 413 414

@paddle.jit.to_static
def NpApiErr():
415
    a = paddle.to_tensor([1, 2])
416 417 418
    b = np.sum(a.numpy())
    print(b)

419

420 421 422 423 424 425 426 427 428 429 430 431
class TestNumpyApiErr(unittest.TestCase):
    def test_numpy_api_err(self):
        with self.assertRaises(TypeError) as e:
            NpApiErr()

        new_exception = e.exception

        error_data = getattr(new_exception, error.ERROR_DATA, None)
        self.assertIsInstance(error_data, error.ErrorData)

        error_message = str(new_exception)

432 433
        self.assertIn(
            "values will be changed to variables by dy2static, numpy api can not handle variables",
434 435
            error_message,
        )
436 437 438 439


class test_set_state_dict_err_layer(paddle.nn.Layer):
    def __init__(self):
440
        super().__init__()
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
        self.linear = paddle.nn.Linear(5, 2)

    @paddle.jit.to_static
    def forward(self, x):
        old_dict = self.state_dict()
        wgt = old_dict['linear.weight']
        drop_w = paddle.nn.functional.dropout(wgt)
        old_dict['linear.weight'] = drop_w
        # old_dict['linear.weight'][0][0] = 0.01
        self.set_state_dict(old_dict)

        y = self.linear(x)

        return y


class TestSetStateDictErr(unittest.TestCase):
    def test_set_state_dict_err(self):
        with self.assertRaises(ValueError) as e:
            layer = test_set_state_dict_err_layer()
461
            x = paddle.to_tensor([1.0, 2.0, 3.0, 4.0, 5.0])
462 463 464 465 466 467 468 469 470
            y = layer(x)

        new_exception = e.exception

        error_data = getattr(new_exception, error.ERROR_DATA, None)
        self.assertIsInstance(error_data, error.ErrorData)

        error_message = str(new_exception)

471 472
        self.assertIn(
            "This error might happens in dy2static, while calling 'set_state_dict' dynamicly in 'forward', which is not supported. If you only need call 'set_state_dict' once, move it to '__init__'.",
473 474
            error_message,
        )
475 476


477 478
if __name__ == '__main__':
    unittest.main()