chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# Copyright (c) 2016 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.
|
||||
|
||||
from ..base.framework import require_version
|
||||
from . import ( # noqa: F401
|
||||
cpp_extension,
|
||||
decorator_utils,
|
||||
dlpack,
|
||||
download,
|
||||
gpu_utils,
|
||||
image_util,
|
||||
layers_utils,
|
||||
unique_name,
|
||||
)
|
||||
from .bwd_graph_utils import capture_backward_subgraph_guard # noqa: F401
|
||||
from .deprecated import deprecated
|
||||
from .environments import strtobool as strtobool
|
||||
from .fwd_graph_utils import capture_forward_subgraph_guard # noqa: F401
|
||||
from .install_check import run_check
|
||||
from .layers_utils import ( # noqa: F401
|
||||
_contain_var,
|
||||
_convert_to_tensor_list,
|
||||
_hash_with_id,
|
||||
_is_symmetric_padding,
|
||||
_packed_nest_with_indices,
|
||||
_recursive_assert_same_structure,
|
||||
_sequence_like,
|
||||
_yield_flat_nest,
|
||||
_yield_value,
|
||||
assert_same_structure,
|
||||
check_shape,
|
||||
convert_shape_to_list,
|
||||
convert_to_list,
|
||||
copy_mutable_vars,
|
||||
flatten,
|
||||
get_inputs_outputs_in_block,
|
||||
get_int_tensor_list,
|
||||
get_shape_tensor_inputs,
|
||||
hold_mutable_vars,
|
||||
is_same_shape,
|
||||
is_sequence,
|
||||
map_structure,
|
||||
pack_sequence_as,
|
||||
padding_to_same_structure,
|
||||
to_sequence,
|
||||
try_get_constant_shape_from_tensor,
|
||||
)
|
||||
from .lazy_import import try_import
|
||||
from .op_version import OpLastCheckpointChecker # noqa: F401
|
||||
|
||||
__all__ = [
|
||||
'deprecated',
|
||||
'run_check',
|
||||
'require_version',
|
||||
'try_import',
|
||||
]
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
if name == "data":
|
||||
from importlib import import_module
|
||||
|
||||
mod = import_module(".data", package=__name__)
|
||||
globals()[name] = mod
|
||||
return mod
|
||||
raise AttributeError(f"module 'paddle.utils' has no attribute '{name}'")
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2025 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 paddle
|
||||
from paddle.base.wrapped_decorator import signature_safe_contextmanager
|
||||
|
||||
from .download import check_and_create_dir
|
||||
|
||||
|
||||
@signature_safe_contextmanager
|
||||
def capture_backward_subgraph_guard(
|
||||
dump_dir_path: str, need_dump_grad_tensors: bool = False
|
||||
):
|
||||
assert dump_dir_path is not None, "The dump_dir_path should not be None"
|
||||
check_and_create_dir(dump_dir_path)
|
||||
paddle.base.core.eager._start_capture_backward_viz_subgraph(
|
||||
dump_dir_path, need_dump_grad_tensors
|
||||
)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
paddle.base.core.eager._stop_capture_backward_viz_subgraph()
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
from .cpp_extension import (
|
||||
CUDA_HOME, # noqa: F401
|
||||
IS_WINDOWS, # noqa: F401
|
||||
BuildExtension, # noqa: F401
|
||||
CppExtension,
|
||||
CUDAExtension,
|
||||
_compute_worker_number, # noqa: F401
|
||||
_get_num_workers, # noqa: F401
|
||||
_get_pybind11_abi_build_flags, # noqa: F401
|
||||
load,
|
||||
setup,
|
||||
)
|
||||
from .extension_utils import (
|
||||
_get_cuda_arch_flags, # noqa: F401
|
||||
get_build_directory,
|
||||
load_op_meta_info_and_register_op, # noqa: F401
|
||||
parse_op_info, # noqa: F401
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'CppExtension',
|
||||
'CUDAExtension',
|
||||
'load',
|
||||
'setup',
|
||||
'get_build_directory',
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
from .dataloader import (
|
||||
DataLoader,
|
||||
default_collate,
|
||||
get_worker_info,
|
||||
)
|
||||
from .dataset import (
|
||||
ChainDataset,
|
||||
ConcatDataset,
|
||||
Dataset,
|
||||
IterableDataset,
|
||||
Subset,
|
||||
TensorDataset,
|
||||
random_split,
|
||||
)
|
||||
from .distributed import DistributedSampler
|
||||
from .sampler import (
|
||||
BatchSampler,
|
||||
RandomSampler,
|
||||
Sampler,
|
||||
SequentialSampler,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'default_collate',
|
||||
'get_worker_info',
|
||||
'ChainDataset',
|
||||
'ConcatDataset',
|
||||
'Dataset',
|
||||
'DataLoader',
|
||||
'IterableDataset',
|
||||
'Subset',
|
||||
'random_split',
|
||||
'BatchSampler',
|
||||
'DistributedSampler',
|
||||
'RandomSampler',
|
||||
'Sampler',
|
||||
'SequentialSampler',
|
||||
'TensorDataset',
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
from . import (
|
||||
collate as collate,
|
||||
worker as worker,
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
from paddle.io.dataloader.collate import (
|
||||
default_collate_fn as default_collate, # noqa: F401
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
from paddle.io import (
|
||||
get_worker_info as get_worker_info,
|
||||
)
|
||||
@@ -0,0 +1,88 @@
|
||||
# Copyright (c) 2026 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.
|
||||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from paddle.io import DataLoader as PaddleDataLoader
|
||||
|
||||
from ._utils.collate import (
|
||||
default_collate as default_collate,
|
||||
)
|
||||
from ._utils.worker import (
|
||||
get_worker_info as get_worker_info,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from paddle.io.dataloader import BatchSampler
|
||||
from paddle.io.dataloader.dataset import Dataset
|
||||
from paddle.io.reader import _CollateFn
|
||||
|
||||
|
||||
class DataLoader(PaddleDataLoader):
|
||||
def __init__(
|
||||
self,
|
||||
dataset: Dataset[Any],
|
||||
batch_size: int | None = 1,
|
||||
shuffle: bool = False,
|
||||
sampler: BatchSampler | None = None,
|
||||
batch_sampler: BatchSampler | None = None,
|
||||
num_workers: int = 0,
|
||||
collate_fn: _CollateFn | None = None,
|
||||
pin_memory: bool = False,
|
||||
drop_last: bool = False,
|
||||
timeout: float = 0,
|
||||
worker_init_fn: Callable[[int], None] | None = None,
|
||||
multiprocessing_context=None,
|
||||
generator=None,
|
||||
*,
|
||||
prefetch_factor: int | None = None,
|
||||
persistent_workers: bool = False,
|
||||
pin_memory_device: str = "",
|
||||
in_order: bool = True,
|
||||
) -> None:
|
||||
if (
|
||||
pin_memory is True
|
||||
or multiprocessing_context is not None
|
||||
or generator is not None
|
||||
or prefetch_factor is not None
|
||||
or len(pin_memory_device) > 0
|
||||
or in_order is False
|
||||
):
|
||||
warnings.warn(
|
||||
"pin_memory, multiprocessing_context, generator, prefetch_factor, pin_memory_device, in_order are currently not supported in DataLoader and will be ignored."
|
||||
)
|
||||
|
||||
if sampler is not None:
|
||||
if batch_sampler is not None:
|
||||
raise ValueError(
|
||||
"Cannot specify both sampler and batch_sampler"
|
||||
)
|
||||
batch_sampler = sampler
|
||||
|
||||
super().__init__(
|
||||
dataset=dataset,
|
||||
batch_sampler=batch_sampler,
|
||||
batch_size=batch_size,
|
||||
shuffle=shuffle,
|
||||
drop_last=drop_last,
|
||||
collate_fn=collate_fn,
|
||||
num_workers=num_workers,
|
||||
timeout=timeout,
|
||||
worker_init_fn=worker_init_fn,
|
||||
persistent_workers=persistent_workers,
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
from paddle.io import (
|
||||
ChainDataset as ChainDataset,
|
||||
ConcatDataset as ConcatDataset,
|
||||
Dataset as Dataset,
|
||||
IterableDataset as IterableDataset,
|
||||
Subset as Subset,
|
||||
TensorDataset as TensorDataset,
|
||||
random_split as random_split,
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from paddle.io import DistributedBatchSampler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sized
|
||||
|
||||
|
||||
class DistributedSampler(DistributedBatchSampler):
|
||||
def __init__(
|
||||
self,
|
||||
dataset: Sized,
|
||||
num_replicas: int | None = None,
|
||||
rank: int | None = None,
|
||||
shuffle: bool = True,
|
||||
seed: int = 0,
|
||||
drop_last: bool = False,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
dataset,
|
||||
1,
|
||||
num_replicas,
|
||||
rank,
|
||||
shuffle,
|
||||
drop_last,
|
||||
seed,
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2026 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.
|
||||
|
||||
from paddle.io import (
|
||||
BatchSampler as BatchSampler,
|
||||
RandomSampler as RandomSampler,
|
||||
Sampler as Sampler,
|
||||
SequenceSampler as SequentialSampler, # noqa: F401
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+150
@@ -0,0 +1,150 @@
|
||||
# Copyright (c) 2018 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.
|
||||
"""
|
||||
decorator to deprecate a function or class
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import sys
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
import paddle
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
_InputT = ParamSpec("_InputT")
|
||||
_RetT = TypeVar("_RetT")
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class VisibleDeprecationWarning(UserWarning):
|
||||
"""Visible deprecation warning.
|
||||
|
||||
Since Python 3.7, Python only show the DeprecationWarning if the module
|
||||
is __main__. So we use this warning to make the deprecation warning visible.
|
||||
|
||||
See more details from https://peps.python.org/pep-0565/
|
||||
"""
|
||||
|
||||
|
||||
def deprecated(
|
||||
update_to: str = "",
|
||||
since: str = "",
|
||||
reason: str = "",
|
||||
level: int = 0,
|
||||
) -> Callable[[Callable[_InputT, _RetT]], Callable[_InputT, _RetT]]:
|
||||
"""Decorate a function to signify its deprecation.
|
||||
|
||||
This function wraps a method that will soon be removed and does two things:
|
||||
- The docstring of the API will be modified to include a notice
|
||||
about deprecation."
|
||||
- Raises a :class:`~exceptions.DeprecatedWarning` when old API is called.
|
||||
|
||||
Args:
|
||||
since(str, optional): The version at which the decorated method is considered deprecated.
|
||||
update_to(str, optional): The new API users should use.
|
||||
reason(str, optional): The reason why the API is deprecated.
|
||||
level(int, optional): The deprecated warning log level. It must be
|
||||
an Integer and must be one of 0, 1, 2.
|
||||
If `level == 0`, the warning message will not be showed.
|
||||
If `level == 1`, the warning message will be showed normally.
|
||||
If `level == 2`, it will raise `RuntimeError`.
|
||||
|
||||
Returns:
|
||||
decorator: decorated function or class.
|
||||
"""
|
||||
|
||||
def decorator(func: Callable[_InputT, _RetT]) -> Callable[_InputT, _RetT]:
|
||||
"""construct warning message, and return a decorated function or class."""
|
||||
assert isinstance(update_to, str), 'type of "update_to" must be str.'
|
||||
assert isinstance(since, str), 'type of "since" must be str.'
|
||||
assert isinstance(reason, str), 'type of "reason" must be str.'
|
||||
assert isinstance(level, int) and level >= 0 and level < 3, (
|
||||
'type of "level" must be int and must be one of 0, 1, 2. But '
|
||||
f'received: {level}.'
|
||||
)
|
||||
|
||||
_since = since.strip()
|
||||
_update_to = update_to.strip()
|
||||
_reason = reason.strip()
|
||||
|
||||
msg = f'API "{func.__module__}.{func.__name__}" is deprecated'
|
||||
|
||||
if len(_since) > 0:
|
||||
msg += f" since {_since}"
|
||||
msg += ", and will be removed in future versions."
|
||||
if len(_update_to) > 0:
|
||||
assert _update_to.startswith("paddle."), (
|
||||
f'Argument update_to must start with "paddle.", your value is "{update_to}"'
|
||||
)
|
||||
msg += f' Please use "{_update_to}" instead.'
|
||||
if len(_reason) > 0:
|
||||
msg += f"\n Reason: {_reason}"
|
||||
if func.__doc__:
|
||||
func.__doc__ = (
|
||||
'\n\nWarning:\n ' + msg + '\n\n'
|
||||
) + inspect.cleandoc(func.__doc__)
|
||||
|
||||
if level == 0:
|
||||
return func
|
||||
|
||||
def parse_version(version: str):
|
||||
# Split the version string and convert numeric parts to integers
|
||||
return [int(part) for part in version.split(".") if part.isdigit()]
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT:
|
||||
"""deprecated warning should be fired in 3 circumstances:
|
||||
1. current version is develop version, i.e. "0.0.0", because we assume develop version is always the latest version.
|
||||
2. since version is empty, in this case, API is deprecated in all versions.
|
||||
3. current version is newer than since version.
|
||||
"""
|
||||
|
||||
if level == 2:
|
||||
raise RuntimeError(
|
||||
f'API "{func.__module__}.{func.__name__}" has been deprecated.'
|
||||
)
|
||||
|
||||
warningmsg = f"\033[93m\nWarning:\n{msg} \033[0m"
|
||||
# ensure ANSI escape sequences print correctly in cmd and powershell
|
||||
if sys.platform.lower() == 'win32':
|
||||
warningmsg = f"\nWarning:\n{msg} "
|
||||
|
||||
v_current = parse_version(paddle.__version__)
|
||||
v_current += [0] * (4 - len(v_current))
|
||||
v_since = parse_version(since)
|
||||
v_since += [0] * (4 - len(v_since))
|
||||
if (
|
||||
paddle.__version__ == "0.0.0"
|
||||
or _since == ""
|
||||
or v_current >= v_since
|
||||
):
|
||||
warnings.warn(
|
||||
warningmsg, category=VisibleDeprecationWarning, stacklevel=2
|
||||
)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
@@ -0,0 +1,275 @@
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import warnings
|
||||
from enum import IntEnum
|
||||
from typing import TYPE_CHECKING, Literal, Protocol, TypeVar
|
||||
|
||||
import paddle
|
||||
|
||||
from ..base.core import DenseTensor
|
||||
from ..base.data_feeder import check_type
|
||||
from ..base.framework import in_dygraph_mode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import CapsuleType
|
||||
|
||||
from paddle import Tensor
|
||||
from paddle._typing import PlaceLike
|
||||
|
||||
|
||||
__all__ = [
|
||||
'to_dlpack',
|
||||
'from_dlpack',
|
||||
]
|
||||
|
||||
_T_contra = TypeVar("_T_contra", contravariant=True)
|
||||
|
||||
|
||||
class SupportDLPack(Protocol[_T_contra]):
|
||||
"""
|
||||
ref:
|
||||
https://github.com/numpy/numpy/blob/7e6e48ca7aacae9994d18a3dadbabd2b91c32151/numpy/__init__.pyi#L3068-L3077
|
||||
https://github.com/numpy/numpy/blob/7e6e48ca7aacae9994d18a3dadbabd2b91c32151/numpy/__init__.pyi#L4730-L4731
|
||||
"""
|
||||
|
||||
def __dlpack__(
|
||||
self,
|
||||
*,
|
||||
stream: None | _T_contra = ...,
|
||||
max_version: tuple[int, int] | None = ...,
|
||||
dl_device: tuple[IntEnum, int] | None = None,
|
||||
copy: bool | None = None,
|
||||
) -> CapsuleType: ...
|
||||
|
||||
def __dlpack_device__(self) -> tuple[int, Literal[0]]: ...
|
||||
|
||||
|
||||
class DLDeviceType(enum.IntEnum):
|
||||
kDLCPU = (1,)
|
||||
kDLCUDA = (2,)
|
||||
kDLCUDAHost = (3,)
|
||||
kDLOpenCL = (4,)
|
||||
kDLVulkan = (7,)
|
||||
kDLMetal = (8,)
|
||||
kDLVPI = (9,)
|
||||
kDLROCM = (10,)
|
||||
kDLROCMHost = (11,)
|
||||
kDLExtDev = (12,)
|
||||
kDLCUDAManaged = (13,)
|
||||
kDLOneAPI = (14,)
|
||||
kDLWebGPU = (15,)
|
||||
kDLHexagon = (16,)
|
||||
kDLMAIA = (17,)
|
||||
kDLTrn = (18,)
|
||||
|
||||
|
||||
def to_dlpack(x: Tensor) -> CapsuleType:
|
||||
"""
|
||||
Encodes a tensor to DLPack.
|
||||
|
||||
Args:
|
||||
x (Tensor): The input tensor, and the data type can be ``bool``, ``float16``, ``float32``,
|
||||
``float64``, ``int8``, ``int16``, ``int32``, ``int64``, ``uint8``, ``complex64``,
|
||||
``complex128``.
|
||||
|
||||
Returns:
|
||||
dltensor, and the data type is PyCapsule.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
:name: code-paddle-to-paddle
|
||||
|
||||
>>> import paddle
|
||||
>>> # x is a tensor with shape [2, 4]
|
||||
>>> x = paddle.to_tensor(
|
||||
... [
|
||||
... [0.2, 0.3, 0.5, 0.9],
|
||||
... [0.1, 0.2, 0.6, 0.7],
|
||||
... ]
|
||||
... )
|
||||
>>> dlpack = paddle.to_dlpack(x)
|
||||
>>> print(dlpack)
|
||||
>>> # doctest: +SKIP('the address will change in every run')
|
||||
<capsule object "dltensor" at 0x7f6103c681b0>
|
||||
>>> # doctest: -SKIP
|
||||
|
||||
>>> # dlpack capsule will be renamed to 'used_dltensor' after decoded
|
||||
>>> y = paddle.from_dlpack(dlpack)
|
||||
>>> print(dlpack)
|
||||
>>> # doctest: +SKIP('the address will change in every run')
|
||||
<capsule object "used_dltensor" at 0x7f6103c681b0>
|
||||
|
||||
.. code-block:: pycon
|
||||
:name: code-paddle-to-torch
|
||||
|
||||
>>> # doctest: +SKIP('torch will not be installed')
|
||||
>>> # type: ignore
|
||||
>>> # convert tensor from paddle to other framework using to_dlpack
|
||||
>>> import torch
|
||||
|
||||
>>> x = paddle.randn([2, 4]).to(device="cpu")
|
||||
>>> y = torch.from_dlpack(paddle.to_dlpack(x))
|
||||
>>> print(y.shape)
|
||||
torch.Size([2, 4])
|
||||
>>> # doctest: -SKIP
|
||||
"""
|
||||
if in_dygraph_mode():
|
||||
if not isinstance(x, paddle.Tensor):
|
||||
raise TypeError(
|
||||
"The type of 'x' in to_dlpack must be paddle.Tensor,"
|
||||
f" but received {type(x)}."
|
||||
)
|
||||
|
||||
return x.value().get_tensor()._to_dlpack()
|
||||
|
||||
check_type(x, "x", (DenseTensor), "to_dlpack")
|
||||
return x._to_dlpack()
|
||||
|
||||
|
||||
def from_dlpack(
|
||||
dlpack: SupportDLPack | CapsuleType,
|
||||
*,
|
||||
device: PlaceLike | None = None,
|
||||
copy: bool | None = None,
|
||||
) -> Tensor:
|
||||
"""
|
||||
Decodes a DLPack to a tensor. The returned Paddle tensor will share the memory with
|
||||
the tensor from given dlpack.
|
||||
|
||||
Args:
|
||||
dlpack (SupportDLPack | CapsuleType): A PyCapsule object with the dltensor,
|
||||
or that implements '__dlpack__' and '__dlpack_device__' methods.
|
||||
|
||||
If `dlpack` is a tensor (or ndarray) object, it must support
|
||||
the `__dlpack__` protocol (i.e., have a `dlpack.__dlpack__`
|
||||
method). Otherwise `dlpack` may be a DLPack capsule, which is
|
||||
an opaque `PyCapsule` instance, typically produced by a
|
||||
`to_dlpack` function or method.
|
||||
|
||||
device (PlaceLike, optional): The device of the returned tensor. If not
|
||||
specified, the device will be the same as that of the input `dlpack`.
|
||||
copy (bool, optional): Whether or not to copy the input.
|
||||
If True, the output tensor always copied. If False, the output tensor must never
|
||||
copied, and raise a BufferError in case a copy is deemed necessary. If None, the
|
||||
output tensor must reuse the existing memory buffer if possible and copy otherwise.
|
||||
Default: None.
|
||||
|
||||
Returns:
|
||||
out (Tensor): A tensor decoded from DLPack. The data type of returned tensor
|
||||
can be one of: ``int32``, ``int64``, ``float16``, ``float32`` and ``float64``.
|
||||
The device of returned tensor can be one of: ``CPU``, ``CUDAPlace``, ``CUDAPinnedPlace``.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
:name: code-paddle-from-paddle
|
||||
|
||||
>>> import paddle
|
||||
>>> # From DLPack capsule
|
||||
>>> x = paddle.to_tensor(
|
||||
... [
|
||||
... [0.2, 0.3, 0.5, 0.9],
|
||||
... [0.1, 0.2, 0.6, 0.7],
|
||||
... ],
|
||||
... place="cpu",
|
||||
... )
|
||||
>>> dlpack = paddle.to_dlpack(x)
|
||||
|
||||
>>> y = paddle.from_dlpack(dlpack)
|
||||
>>> # dlpack capsule will be renamed to 'used_dltensor' after decoded
|
||||
>>> print(dlpack)
|
||||
>>> # doctest: +SKIP('the address will change in every run')
|
||||
<capsule object "used_dltensor" at 0x7f6103c681b0>
|
||||
>>> # doctest: -SKIP
|
||||
|
||||
>>> print(y)
|
||||
Tensor(shape=[2, 4], dtype=float32, place=Place(cpu), stop_gradient=True,
|
||||
[[0.20000000, 0.30000001, 0.50000000, 0.89999998],
|
||||
[0.10000000, 0.20000000, 0.60000002, 0.69999999]])
|
||||
>>> # data of tensor x is shared with tensor y
|
||||
>>> y[0, 0] = 10.0
|
||||
>>> print(x)
|
||||
Tensor(shape=[2, 4], dtype=float32, place=Place(gpu:0), stop_gradient=True,
|
||||
[[10. , 0.30000001, 0.50000000, 0.89999998],
|
||||
[0.10000000, 0.20000000, 0.60000002, 0.69999999]])
|
||||
|
||||
.. code-block:: pycon
|
||||
:name: code-paddle-from-numpy
|
||||
|
||||
>>> # Directly from external tensor that implements '__dlpack__' and '__dlpack_device__' methods
|
||||
>>> import paddle
|
||||
>>> import numpy as np
|
||||
>>> x = np.array(
|
||||
... [
|
||||
... [0.2, 0.3, 0.5, 0.9],
|
||||
... [0.1, 0.2, 0.6, 0.7],
|
||||
... ]
|
||||
... )
|
||||
>>> y = paddle.from_dlpack(x)
|
||||
>>> y[0, 0] = 10.0
|
||||
>>> # data of tensor x is shared with tensor y
|
||||
>>> print(x)
|
||||
[[10. 0.3 0.5 0.9]
|
||||
[ 0.1 0.2 0.6 0.7]]
|
||||
"""
|
||||
|
||||
if hasattr(dlpack, "__dlpack__"):
|
||||
kwargs = {}
|
||||
kwargs["max_version"] = (1, 3)
|
||||
if copy is not None:
|
||||
kwargs["copy"] = copy
|
||||
|
||||
if device is not None:
|
||||
place = paddle.base.framework._get_paddle_place(device)
|
||||
kwargs["dl_device"] = paddle.base.core.place_to_dl_device(place)
|
||||
|
||||
dlpack_device = dlpack.__dlpack_device__()
|
||||
# device is CUDA, we need to pass the current
|
||||
# stream
|
||||
if dlpack_device[0] in (DLDeviceType.kDLCUDA,):
|
||||
with warnings.catch_warnings():
|
||||
# ignore deprecation warning
|
||||
warnings.filterwarnings("ignore", category=UserWarning)
|
||||
stream = paddle.device.cuda.current_stream(dlpack_device[1])
|
||||
# cuda_stream is the pointer to the stream and it is a public
|
||||
# attribute, but it is not documented
|
||||
# The array API specify that the default legacy stream must be passed
|
||||
# with a value of 1 for CUDA
|
||||
# https://data-apis.org/array-api/latest/API_specification/array_object.html?dlpack-self-stream-none#dlpack-self-stream-none
|
||||
is_gpu = dlpack_device[0] == DLDeviceType.kDLCUDA
|
||||
stream_ptr = (
|
||||
1 if is_gpu and stream.cuda_stream == 0 else stream.cuda_stream
|
||||
)
|
||||
kwargs["stream"] = stream_ptr
|
||||
try:
|
||||
dlpack_ = dlpack.__dlpack__(**kwargs)
|
||||
except TypeError:
|
||||
# Remove the `max_version` argument if it is not supported
|
||||
kwargs.pop("max_version")
|
||||
dlpack_ = dlpack.__dlpack__(**kwargs)
|
||||
else:
|
||||
# Old versions just call the converter
|
||||
dlpack_ = dlpack
|
||||
|
||||
out: paddle.base.libpaddle.DenseTensor = paddle.base.core.from_dlpack(
|
||||
dlpack_
|
||||
)
|
||||
|
||||
if in_dygraph_mode():
|
||||
out: Tensor = paddle.Tensor(out, place=out._place())
|
||||
|
||||
return out
|
||||
@@ -0,0 +1,478 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import os.path as osp
|
||||
import shutil
|
||||
import sys
|
||||
import tarfile
|
||||
import time
|
||||
import warnings
|
||||
import zipfile
|
||||
from typing import Literal
|
||||
|
||||
import httpx
|
||||
|
||||
try:
|
||||
from tqdm import tqdm
|
||||
except:
|
||||
|
||||
class tqdm:
|
||||
def __init__(self, total=None):
|
||||
self.total = total
|
||||
self.n = 0
|
||||
|
||||
def update(self, n):
|
||||
self.n += n
|
||||
if self.total is None:
|
||||
sys.stderr.write(f"\r{self.n:.1f} bytes")
|
||||
else:
|
||||
sys.stderr.write(f"\r{100 * self.n / float(self.total):.1f}%")
|
||||
sys.stderr.flush()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
sys.stderr.write('\n')
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__all__ = ['get_weights_path_from_url']
|
||||
|
||||
WEIGHTS_HOME = osp.expanduser("~/.cache/paddle/hapi/weights")
|
||||
|
||||
DOWNLOAD_RETRY_LIMIT = 3
|
||||
|
||||
|
||||
def is_url(path: str) -> bool:
|
||||
"""
|
||||
Whether path is URL.
|
||||
Args:
|
||||
path (string): URL string or not.
|
||||
"""
|
||||
return path.startswith('http://') or path.startswith('https://')
|
||||
|
||||
|
||||
def get_weights_path_from_url(url: str, md5sum: str | None = None) -> str:
|
||||
"""Get weights path from WEIGHT_HOME, if not exists,
|
||||
download it from url.
|
||||
|
||||
Args:
|
||||
url (str): download url
|
||||
md5sum (str): md5 sum of download package
|
||||
|
||||
Returns:
|
||||
str: a local path to save downloaded weights.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
>>> resnet18_pretrained_weight_url = 'https://paddle-hapi.bj.bcebos.com/models/resnet18.pdparams'
|
||||
>>> local_weight_path = get_weights_path_from_url(resnet18_pretrained_weight_url)
|
||||
|
||||
"""
|
||||
path = get_path_from_url(url, WEIGHTS_HOME, md5sum)
|
||||
return path
|
||||
|
||||
|
||||
def _map_path(url, root_dir):
|
||||
# parse path after download under root_dir
|
||||
fname = osp.split(url)[-1]
|
||||
fpath = fname
|
||||
return osp.join(root_dir, fpath)
|
||||
|
||||
|
||||
def _get_unique_endpoints(trainer_endpoints):
|
||||
# Sorting is to avoid different environmental variables for each card
|
||||
trainer_endpoints.sort()
|
||||
ips = set()
|
||||
unique_endpoints = set()
|
||||
for endpoint in trainer_endpoints:
|
||||
ip = endpoint.split(":")[0]
|
||||
if ip in ips:
|
||||
continue
|
||||
ips.add(ip)
|
||||
unique_endpoints.add(endpoint)
|
||||
logger.info(f"unique_endpoints {unique_endpoints}")
|
||||
return unique_endpoints
|
||||
|
||||
|
||||
def get_path_from_url(
|
||||
url: str,
|
||||
root_dir: str,
|
||||
md5sum: str | None = None,
|
||||
check_exist: bool = True,
|
||||
decompress: bool = True,
|
||||
method: Literal['wget', 'get'] = 'get',
|
||||
) -> str:
|
||||
"""Download from given url to root_dir.
|
||||
if file or directory specified by url is exists under
|
||||
root_dir, return the path directly, otherwise download
|
||||
from url and decompress it, return the path.
|
||||
|
||||
Args:
|
||||
url (str): download url
|
||||
root_dir (str): root dir for downloading, it should be
|
||||
WEIGHTS_HOME or DATASET_HOME
|
||||
md5sum (str|None, optional): md5 sum of download package
|
||||
decompress (bool, optional): decompress zip or tar file. Default is `True`
|
||||
method (str, optional): which download method to use. Support `wget` and `get`. Default is `get`.
|
||||
|
||||
Returns:
|
||||
str: a local path to save downloaded models & weights & datasets.
|
||||
"""
|
||||
|
||||
from paddle.distributed import ParallelEnv
|
||||
|
||||
assert is_url(url), f"downloading from {url} not a url"
|
||||
# parse path after download to decompress under root_dir
|
||||
fullpath = _map_path(url, root_dir)
|
||||
# Mainly used to solve the problem of downloading data from different
|
||||
# machines in the case of multiple machines. Different ips will download
|
||||
# data, and the same ip will only download data once.
|
||||
unique_endpoints = _get_unique_endpoints(ParallelEnv().trainer_endpoints[:])
|
||||
if osp.exists(fullpath) and check_exist and _md5check(fullpath, md5sum):
|
||||
logger.info(f"Found {fullpath}")
|
||||
else:
|
||||
if ParallelEnv().current_endpoint in unique_endpoints:
|
||||
fullpath = _download(url, root_dir, md5sum, method=method)
|
||||
else:
|
||||
while not os.path.exists(fullpath):
|
||||
time.sleep(1)
|
||||
|
||||
if ParallelEnv().current_endpoint in unique_endpoints:
|
||||
if decompress and (
|
||||
tarfile.is_tarfile(fullpath) or zipfile.is_zipfile(fullpath)
|
||||
):
|
||||
fullpath = _decompress(fullpath)
|
||||
|
||||
return fullpath
|
||||
|
||||
|
||||
def _get_download(url, fullname):
|
||||
# using requests.get method
|
||||
fname = osp.basename(fullname)
|
||||
try:
|
||||
with httpx.stream(
|
||||
"GET", url, timeout=None, follow_redirects=True
|
||||
) as req:
|
||||
if req.status_code != 200:
|
||||
raise RuntimeError(
|
||||
f"Downloading from {url} failed with code "
|
||||
f"{req.status_code}!"
|
||||
)
|
||||
|
||||
tmp_fullname = fullname + "_tmp"
|
||||
total_size = req.headers.get('content-length')
|
||||
with open(tmp_fullname, 'wb') as f:
|
||||
if total_size:
|
||||
with tqdm(total=(int(total_size) + 1023) // 1024) as pbar:
|
||||
for chunk in req.iter_bytes(chunk_size=1024):
|
||||
f.write(chunk)
|
||||
pbar.update(1)
|
||||
else:
|
||||
for chunk in req.iter_bytes(chunk_size=1024):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
shutil.move(tmp_fullname, fullname)
|
||||
return fullname
|
||||
|
||||
except Exception as e: # requests.exceptions.ConnectionError
|
||||
logger.info(f"Downloading {fname} from {url} failed with exception {e}")
|
||||
return False
|
||||
|
||||
|
||||
_download_methods = {'get': _get_download}
|
||||
|
||||
|
||||
def _download(url, path, md5sum=None, method='get'):
|
||||
"""
|
||||
Download from url, save to path.
|
||||
|
||||
url (str): download url
|
||||
path (str): download to given path
|
||||
md5sum (str): md5 sum of download package
|
||||
method (str): which download method to use. Support `wget` and `get`. Default is `get`.
|
||||
|
||||
"""
|
||||
assert method in _download_methods, f'make sure `{method}` implemented'
|
||||
|
||||
if not osp.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
fname = osp.split(url)[-1]
|
||||
fullname = osp.join(path, fname)
|
||||
retry_cnt = 0
|
||||
|
||||
logger.info(f"Downloading {fname} from {url}")
|
||||
while not (osp.exists(fullname) and _md5check(fullname, md5sum)):
|
||||
logger.info(f"md5check {fullname} and {md5sum}")
|
||||
if retry_cnt < DOWNLOAD_RETRY_LIMIT:
|
||||
retry_cnt += 1
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f"Download from {url} failed. Retry limit reached"
|
||||
)
|
||||
|
||||
if not _download_methods[method](url, fullname):
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
return fullname
|
||||
|
||||
|
||||
def _md5check(fullname, md5sum=None):
|
||||
if md5sum is None:
|
||||
return True
|
||||
|
||||
logger.info(f"File {fullname} md5 checking...")
|
||||
md5 = hashlib.md5()
|
||||
with open(fullname, 'rb') as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
md5.update(chunk)
|
||||
calc_md5sum = md5.hexdigest()
|
||||
|
||||
if calc_md5sum != md5sum:
|
||||
logger.info(
|
||||
f"File {fullname} md5 check failed, {calc_md5sum}(calc) != "
|
||||
f"{md5sum}(base)"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _decompress(fname):
|
||||
"""
|
||||
Decompress for zip and tar file
|
||||
"""
|
||||
logger.info(f"Decompressing {fname}...")
|
||||
|
||||
# For protecting decompressing interrupted,
|
||||
# decompress to fpath_tmp directory firstly, if decompress
|
||||
# succeeded, move decompress files to fpath and delete
|
||||
# fpath_tmp and remove download compress file.
|
||||
|
||||
if tarfile.is_tarfile(fname):
|
||||
uncompressed_path = _uncompress_file_tar(fname)
|
||||
elif zipfile.is_zipfile(fname):
|
||||
uncompressed_path = _uncompress_file_zip(fname)
|
||||
else:
|
||||
raise TypeError(f"Unsupported compress file type {fname}")
|
||||
|
||||
return uncompressed_path
|
||||
|
||||
|
||||
def _uncompress_file_zip(filepath):
|
||||
with zipfile.ZipFile(filepath, 'r') as files:
|
||||
file_list = files.namelist()
|
||||
file_dir = os.path.dirname(filepath)
|
||||
|
||||
if _is_a_single_file(file_list):
|
||||
rootpath = file_list[0]
|
||||
uncompressed_path = os.path.join(file_dir, rootpath)
|
||||
_safe_extract_zip(files, file_dir)
|
||||
elif _is_a_single_dir(file_list):
|
||||
# `strip(os.sep)` to remove `os.sep` in the tail of path
|
||||
rootpath = os.path.splitext(file_list[0].strip(os.sep))[0].split(
|
||||
os.sep
|
||||
)[-1]
|
||||
uncompressed_path = os.path.join(file_dir, rootpath)
|
||||
_safe_extract_zip(files, file_dir)
|
||||
else:
|
||||
rootpath = os.path.splitext(filepath)[0].split(os.sep)[-1]
|
||||
uncompressed_path = os.path.join(file_dir, rootpath)
|
||||
if not os.path.exists(uncompressed_path):
|
||||
os.makedirs(uncompressed_path)
|
||||
_safe_extract_zip(files, os.path.join(file_dir, rootpath))
|
||||
|
||||
return uncompressed_path
|
||||
|
||||
|
||||
def _safe_extract_tar(tar, path, members=None, on_unsafe='skip'):
|
||||
"""
|
||||
Safely extract tar files to prevent path traversal attacks.
|
||||
|
||||
Security measures:
|
||||
1. Verify resolved paths are within target directory
|
||||
2. Skip or reject symlinks, hardlinks and other special files
|
||||
3. Only extract regular files and directories
|
||||
"""
|
||||
if on_unsafe not in ('skip', 'raise'):
|
||||
raise ValueError(
|
||||
f"on_unsafe must be one of 'skip' or 'raise', but got {on_unsafe!r}"
|
||||
)
|
||||
|
||||
members_to_check = members if members is not None else tar.getmembers()
|
||||
extract_members = []
|
||||
|
||||
for member in members_to_check:
|
||||
if not _safe_extract_member(member, path, 'tar'):
|
||||
raise ValueError(
|
||||
f"Attempted path traversal in tar file: {member.name}"
|
||||
)
|
||||
|
||||
# Skip or reject symlinks, hardlinks, and other special files to prevent symlink attacks
|
||||
if member.issym():
|
||||
if on_unsafe == 'raise':
|
||||
raise ValueError(
|
||||
"Unsafe tar member rejected for security: "
|
||||
f"{member.name} (symbolic link)"
|
||||
)
|
||||
warnings.warn(
|
||||
f"Skipping symbolic link in tar for security: {member.name}",
|
||||
category=UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
continue
|
||||
elif member.islnk():
|
||||
if on_unsafe == 'raise':
|
||||
raise ValueError(
|
||||
"Unsafe tar member rejected for security: "
|
||||
f"{member.name} (hard link)"
|
||||
)
|
||||
warnings.warn(
|
||||
f"Skipping hard link in tar for security: {member.name}",
|
||||
category=UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
continue
|
||||
elif not (member.isfile() or member.isdir()):
|
||||
if on_unsafe == 'raise':
|
||||
raise ValueError(
|
||||
"Unsafe tar member rejected for security: "
|
||||
f"{member.name} (special file)"
|
||||
)
|
||||
warnings.warn(
|
||||
f"Skipping special file in tar for security: {member.name}",
|
||||
category=UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
continue
|
||||
|
||||
extract_members.append(member)
|
||||
|
||||
tar.extractall(path, members=extract_members)
|
||||
|
||||
|
||||
def _safe_extract_zip(zip, path, members=None):
|
||||
members_to_check = members if members is not None else zip.infolist()
|
||||
|
||||
for member in members_to_check:
|
||||
if not _safe_extract_member(member, path, 'zip'):
|
||||
raise ValueError(
|
||||
f"Attempted path traversal in zip file: {member.filename}"
|
||||
)
|
||||
zip.extractall(path, members=members_to_check)
|
||||
|
||||
|
||||
def _uncompress_file_tar(filepath, mode="r:*"):
|
||||
with tarfile.open(filepath, mode) as files:
|
||||
file_list = files.getnames()
|
||||
file_dir = os.path.dirname(filepath)
|
||||
|
||||
if _is_a_single_file(file_list):
|
||||
rootpath = file_list[0]
|
||||
uncompressed_path = os.path.join(file_dir, rootpath)
|
||||
_safe_extract_tar(files, file_dir)
|
||||
elif _is_a_single_dir(file_list):
|
||||
rootpath = os.path.splitext(file_list[0].strip(os.sep))[0].split(
|
||||
os.sep
|
||||
)[-1]
|
||||
uncompressed_path = os.path.join(file_dir, rootpath)
|
||||
_safe_extract_tar(files, file_dir)
|
||||
else:
|
||||
rootpath = os.path.splitext(filepath)[0].split(os.sep)[-1]
|
||||
uncompressed_path = os.path.join(file_dir, rootpath)
|
||||
if not os.path.exists(uncompressed_path):
|
||||
os.makedirs(uncompressed_path)
|
||||
_safe_extract_tar(files, os.path.join(file_dir, rootpath))
|
||||
|
||||
return uncompressed_path
|
||||
|
||||
|
||||
def _safe_extract_member(member, target_dir, archive_type='tar'):
|
||||
# Get member name
|
||||
if archive_type == 'tar':
|
||||
member_name = member.name
|
||||
else: # zip
|
||||
member_name = member.filename
|
||||
|
||||
# Reject absolute paths
|
||||
if os.path.isabs(member_name):
|
||||
warnings.warn(
|
||||
f"Rejected absolute path in archive: {member_name}",
|
||||
category=UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return False
|
||||
|
||||
# Resolve target path and normalize
|
||||
target_path = os.path.normpath(os.path.join(target_dir, member_name))
|
||||
target_path = os.path.abspath(target_path)
|
||||
|
||||
# Ensure resolved path is within target_dir
|
||||
if not target_path.startswith(os.path.abspath(target_dir) + os.sep):
|
||||
warnings.warn(
|
||||
f"Rejected path traversal attempt: {member_name} -> {target_path}",
|
||||
category=UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _is_a_single_file(file_list):
|
||||
if len(file_list) == 1 and file_list[0].find(os.sep) < 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _is_a_single_dir(file_list):
|
||||
new_file_list = []
|
||||
for file_path in file_list:
|
||||
if '/' in file_path:
|
||||
file_path = file_path.replace('/', os.sep)
|
||||
elif '\\' in file_path:
|
||||
file_path = file_path.replace('\\', os.sep)
|
||||
new_file_list.append(file_path)
|
||||
|
||||
file_name = new_file_list[0].split(os.sep)[0]
|
||||
for i in range(1, len(new_file_list)):
|
||||
if file_name != new_file_list[i].split(os.sep)[0]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check_and_create_dir(path):
|
||||
if path is None:
|
||||
return
|
||||
assert isinstance(path, str), "path must be string type"
|
||||
if os.path.exists(path):
|
||||
if not os.path.isdir(path):
|
||||
raise NotADirectoryError(f" path:'{path}' must be directory ")
|
||||
else:
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except Exception as e:
|
||||
raise OSError(f"Create '{path}' failed : {e}")
|
||||
@@ -0,0 +1,155 @@
|
||||
# Copyright (c) 2023 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def strtobool(val):
|
||||
val = val.lower()
|
||||
if val in ['y', 'yes', 't', 'true', 'on', '1']:
|
||||
return True
|
||||
elif val in ['n', 'no', 'f', 'false', 'off', '0']:
|
||||
return False
|
||||
else:
|
||||
raise ValueError(f"Invalid truth value {val!r}")
|
||||
|
||||
|
||||
class EnvironmentVariable(Generic[T]):
|
||||
name: str
|
||||
default: T
|
||||
|
||||
def __init__(self, name: str, default: T):
|
||||
self.name = name
|
||||
self.default = default
|
||||
self._last_env_value: str | None = None
|
||||
self._cached_value: T | None = None
|
||||
|
||||
def get(self) -> T:
|
||||
_current_env_value = os.getenv(self.name)
|
||||
if (
|
||||
self._cached_value is None
|
||||
or self._last_env_value != _current_env_value
|
||||
):
|
||||
self._cached_value = self.parse_from_string()
|
||||
self._last_env_value = _current_env_value
|
||||
return self._cached_value
|
||||
|
||||
def set(self, value: T) -> None:
|
||||
os.environ[self.name] = self.convert_to_string(value)
|
||||
self._cached_value = value
|
||||
|
||||
def parse_from_string(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
def convert_to_string(self, value: T) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def delete(self) -> None:
|
||||
del os.environ[self.name]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Env({self.name}={self.get()!r})"
|
||||
|
||||
|
||||
class StringEnvironmentVariable(EnvironmentVariable[str]):
|
||||
def __init__(self, name: str, default: str):
|
||||
super().__init__(name, default)
|
||||
assert isinstance(default, str), "default must be a string"
|
||||
|
||||
def parse_from_string(self) -> str:
|
||||
return os.getenv(self.name, self.default)
|
||||
|
||||
def convert_to_string(self, value: str) -> str:
|
||||
assert isinstance(value, str), "value must be a string"
|
||||
return value
|
||||
|
||||
|
||||
class BooleanEnvironmentVariable(EnvironmentVariable[bool]):
|
||||
def __init__(self, name: str, default: bool):
|
||||
super().__init__(name, default)
|
||||
assert isinstance(default, bool), "default must be a boolean"
|
||||
|
||||
def parse_from_string(self) -> bool:
|
||||
default = str(self.default)
|
||||
env_str = os.getenv(self.name, default)
|
||||
return strtobool(env_str)
|
||||
|
||||
def convert_to_string(self, value: bool) -> str:
|
||||
assert isinstance(value, bool), "value must be a boolean"
|
||||
return str(value).lower()
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
raise ValueError(
|
||||
"BooleanEnvironmentVariable does not support bool(), "
|
||||
"please use get() instead."
|
||||
)
|
||||
|
||||
|
||||
class IntegerEnvironmentVariable(EnvironmentVariable[int]):
|
||||
def __init__(self, name: str, default: int):
|
||||
super().__init__(name, default)
|
||||
assert isinstance(default, int) and not isinstance(default, bool), (
|
||||
"default must be an integer"
|
||||
)
|
||||
|
||||
def parse_from_string(self) -> int:
|
||||
try:
|
||||
return int(os.getenv(self.name, str(self.default)))
|
||||
except ValueError:
|
||||
return self.default
|
||||
|
||||
def convert_to_string(self, value: int) -> str:
|
||||
assert isinstance(value, int) and not isinstance(value, bool), (
|
||||
"value must be an integer"
|
||||
)
|
||||
return str(value)
|
||||
|
||||
|
||||
class StringListEnvironmentVariable(EnvironmentVariable[list[str]]):
|
||||
def __init__(self, name: str, default: list[str]):
|
||||
super().__init__(name, default)
|
||||
assert isinstance(default, list), "default must be a list"
|
||||
|
||||
def parse_from_string(self) -> list[str]:
|
||||
return os.getenv(self.name, ",".join(self.default)).split(",")
|
||||
|
||||
def convert_to_string(self, value: list[str]) -> str:
|
||||
assert isinstance(value, list), "value must be a list"
|
||||
assert all(isinstance(x, str) for x in value), (
|
||||
"value must be a list of strings"
|
||||
)
|
||||
return ",".join(value)
|
||||
|
||||
|
||||
class EnvironmentVariableGuard(Generic[T]):
|
||||
variable: EnvironmentVariable[T]
|
||||
original_value: T
|
||||
|
||||
def __init__(self, variable: EnvironmentVariable[T], value: T):
|
||||
self.variable = variable
|
||||
self.original_value = variable.get()
|
||||
self.variable.set(value)
|
||||
|
||||
def __enter__(self) -> Self:
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback) -> None:
|
||||
self.variable.set(self.original_value)
|
||||
@@ -0,0 +1,377 @@
|
||||
# Copyright (c) 2022 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 copy
|
||||
|
||||
_FLOPS_COMPUTE_FUNC_MAP = {}
|
||||
|
||||
|
||||
def prod(s):
|
||||
p = 1
|
||||
for v in s:
|
||||
p *= v
|
||||
return p
|
||||
|
||||
|
||||
def flops(op_type: str, input_shapes: dict, attrs: dict) -> int:
|
||||
"""
|
||||
count FLOPs for operation.
|
||||
|
||||
Args:
|
||||
op_type (str): the type of operation.
|
||||
input_shapes (dict): the shapes of inputs.
|
||||
attrs (dict): the attributes of the operation.
|
||||
|
||||
Returns:
|
||||
the total FLOPs of the operation.
|
||||
"""
|
||||
|
||||
if op_type not in _FLOPS_COMPUTE_FUNC_MAP:
|
||||
return 0
|
||||
else:
|
||||
func = _FLOPS_COMPUTE_FUNC_MAP[op_type]
|
||||
try:
|
||||
flops = func(input_shapes, attrs)
|
||||
except Exception as e:
|
||||
return 0
|
||||
return flops
|
||||
|
||||
|
||||
def register_flops(op_type):
|
||||
"""
|
||||
register flops computation function for operation.
|
||||
"""
|
||||
|
||||
def register(func):
|
||||
global _FLOPS_COMPUTE_FUNC_MAP
|
||||
_FLOPS_COMPUTE_FUNC_MAP[op_type] = func
|
||||
return func
|
||||
|
||||
return register
|
||||
|
||||
|
||||
@register_flops("c_embedding")
|
||||
def _c_embedding_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for c_embedding op.
|
||||
For c_embedding(input):
|
||||
equation: flops = 0
|
||||
"""
|
||||
return 0
|
||||
|
||||
|
||||
@register_flops("conv2d")
|
||||
def _conv2d_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for conv2d op.
|
||||
For conv2d(input,filter):
|
||||
active_elements = batch_size * numel(output)
|
||||
conv_flops = 2 * macs_per_position_conv * active_elements
|
||||
bias_flops = out_channels * active_elements
|
||||
equation: flops = conv_flops + bias_flops
|
||||
"""
|
||||
|
||||
bias = (
|
||||
input_shapes.get('Bias')[0]
|
||||
if len(input_shapes.get('Bias')) > 0
|
||||
else None
|
||||
)
|
||||
input = input_shapes.get('Input')[0]
|
||||
weight = input_shapes.get('Filter')[0]
|
||||
|
||||
padding = attrs.get('paddings')
|
||||
stride = attrs.get('strides')
|
||||
dilation = attrs.get('dilations')
|
||||
groups = attrs.get('groups')
|
||||
|
||||
batch_size = input[0]
|
||||
in_channels = input[1]
|
||||
out_channels = weight[0]
|
||||
kernel_dims = list(weight[2:])
|
||||
input_dims = list(input[2:])
|
||||
length = len(input_dims)
|
||||
|
||||
paddings = (
|
||||
padding
|
||||
if isinstance(padding, list)
|
||||
else [
|
||||
padding,
|
||||
]
|
||||
* length
|
||||
)
|
||||
strides = (
|
||||
stride
|
||||
if isinstance(stride, list)
|
||||
else [
|
||||
stride,
|
||||
]
|
||||
* length
|
||||
)
|
||||
dilations = (
|
||||
dilation
|
||||
if isinstance(dilation, list)
|
||||
else [
|
||||
dilation,
|
||||
]
|
||||
* length
|
||||
)
|
||||
|
||||
output_dims = []
|
||||
for idx, input_dim in enumerate(input_dims):
|
||||
output_dim = (
|
||||
input_dim
|
||||
+ 2 * paddings[idx]
|
||||
- (dilations[idx] * (kernel_dims[idx] - 1) + 1)
|
||||
) // strides[idx] + 1
|
||||
output_dims.append(output_dim)
|
||||
filters_per_channel = out_channels // groups
|
||||
macs_conv_per_position = (
|
||||
prod(kernel_dims) * in_channels * filters_per_channel
|
||||
)
|
||||
active_elements = batch_size * prod(output_dims)
|
||||
overall_conv_macs = macs_conv_per_position * active_elements
|
||||
overall_conv_flops = 2 * overall_conv_macs
|
||||
|
||||
overall_bias_flops = 0
|
||||
|
||||
if bias is not None:
|
||||
overall_bias_flops = out_channels * active_elements
|
||||
|
||||
return overall_conv_flops + overall_bias_flops
|
||||
|
||||
|
||||
@register_flops("dropout")
|
||||
def _dropout_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for dropout op.
|
||||
For dropout(input):
|
||||
equation: flops = 0
|
||||
"""
|
||||
return 0
|
||||
|
||||
|
||||
def _elementwise_flops_compute(input_shapes, attrs):
|
||||
input_x = input_shapes.get("X")[0]
|
||||
input_y = input_shapes.get("Y")[0]
|
||||
dim_x = len(input_x)
|
||||
dim_y = len(input_y)
|
||||
dim_output = max(dim_x, dim_y)
|
||||
output = []
|
||||
for i in range(dim_output):
|
||||
in_x = input_x[dim_x - 1 - i] if i < dim_x else 1
|
||||
in_y = input_y[dim_y - 1 - i] if i < dim_y else 1
|
||||
output.append(max(in_x, in_y))
|
||||
return prod(output)
|
||||
|
||||
|
||||
@register_flops("elementwise_add")
|
||||
def _elementwise_add_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for elementwise_add op.
|
||||
For elementwise_add(input,other):
|
||||
input_shapes = [shape_of_input, shape_of_other]
|
||||
shape_of_input = [dim1, dim2, dim3 ...]
|
||||
shape_of_other = [odim1, odim2, odim3...]
|
||||
equation: flops = max(dim1, odim1) * max(dim2, odim2) * max()...
|
||||
"""
|
||||
return _elementwise_flops_compute(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("elementwise_mul")
|
||||
def _elementwise_mul_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for elementwise_mul op.
|
||||
For elementwise_mul(input,other):
|
||||
input_shapes = [shape_of_input, shape_of_other]
|
||||
shape_of_input = [dim1, dim2, dim3 ...]
|
||||
shape_of_other = [odim1, odim2, odim3...]
|
||||
equation: flops = max(dim1, odim1) * max(dim2, odim2)* max()...
|
||||
"""
|
||||
return _elementwise_flops_compute(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("elementwise_div")
|
||||
def _elementwise_div_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for elementwise_div op.
|
||||
For elementwise_div(input,other):
|
||||
input_shapes = [shape_of_input, shape_of_other]
|
||||
shape_of_input = [dim1, dim2, dim3 ...]
|
||||
shape_of_other = [odim1, odim2, odim3...]
|
||||
equation: flops = max(dim1,odim1)*max(dim2,odim2)*max()...
|
||||
"""
|
||||
return _elementwise_flops_compute(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("gelu")
|
||||
def _gelu_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for gelu op.
|
||||
For gelu(input):
|
||||
equation: flops = 5 * (numel)total number of elements in the input tensor.
|
||||
"""
|
||||
input = input_shapes.get('X')[0]
|
||||
return prod(input) * 5
|
||||
|
||||
|
||||
@register_flops("layer_norm")
|
||||
def _layer_norm_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for layer_norm op.
|
||||
For layer_norm(input):
|
||||
equation:
|
||||
1): WITHOUT epsilon flops = 7 * (numel)total number of elements in the input tensor.
|
||||
2): WITH epsilon flops = 8 * (numel)total number of elements in the input tensor.
|
||||
"""
|
||||
input = input_shapes.get('X')[0]
|
||||
flops = prod(input) * 7
|
||||
if attrs.get('epsilon'):
|
||||
flops += prod(input)
|
||||
return flops
|
||||
|
||||
|
||||
@register_flops("matmul")
|
||||
def _matmul_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for matmul op.
|
||||
For matmul(input,other):
|
||||
input_shapes = [shape_of_input, shape_of_other]
|
||||
shape_of_input = [dim1,dim2 ...dim_n_1,dim_n] length:n
|
||||
shape_of_other = [odim1,odim2 ... odim(n-m)... odim_m_1,dim_m] length:m
|
||||
suppose n > m and dim_n = odim_m_1:
|
||||
shape_of_output = [dim1, dim2 ... max(dim(n-m), odim(n-m)), max(dim(n-m+1), odim(n-m+1)) ... dim_n_1, dim_m]
|
||||
equation: flops = 2 * numel(output) * dim_n
|
||||
"""
|
||||
|
||||
x_shape = copy.deepcopy(
|
||||
input_shapes.get("X", input_shapes.get("x", [[0]]))[0]
|
||||
)
|
||||
y_shape = copy.deepcopy(
|
||||
input_shapes.get("Y", input_shapes.get("y", [[0]]))[0]
|
||||
)
|
||||
if attrs.get('transpose_X') or attrs.get('transpose_x'):
|
||||
x_shape[-1], x_shape[-2] = x_shape[-2], x_shape[-1]
|
||||
|
||||
if attrs.get('transpose_Y') or attrs.get('transpose_y'):
|
||||
y_shape[-1], y_shape[-2] = y_shape[-2], y_shape[-1]
|
||||
dim_x = len(x_shape)
|
||||
dim_y = len(y_shape)
|
||||
output_len = max(dim_x, dim_y)
|
||||
output_shape = []
|
||||
|
||||
for idx in range(output_len, 2, -1):
|
||||
x_idx = x_shape[dim_x - idx] if idx <= dim_x else 1
|
||||
y_idx = y_shape[dim_y - idx] if idx <= dim_y else 1
|
||||
output_shape.append(max(x_idx, y_idx))
|
||||
|
||||
macs = prod(output_shape) * x_shape[-2] * x_shape[-1] * y_shape[-1]
|
||||
return 2 * macs
|
||||
|
||||
|
||||
@register_flops("matmul_v2")
|
||||
def _matmul_v2_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for matmul_v2 op.
|
||||
For matmul_v2(input,other):
|
||||
input_shapes = [shape_of_input, shape_of_other]
|
||||
shape_of_input = [dim1, dim2 ...dim_n_1, dim_n] length:n
|
||||
shape_of_other = [odim1, odim2 ... odim(n-m) ... odim_m_1, dim_m] length:m
|
||||
suppose n > m and dim_n = odim_m_1:
|
||||
shape_of_output = [dim1, dim2 ... max(dim(n-m), odim(n-m)), max(dim(n-m+1), odim(n-m+1))...dim_n_1, dim_m]
|
||||
equation: flops = 2 * numel(outputs) * dim_n
|
||||
"""
|
||||
x_shape = copy.deepcopy(input_shapes.get('X')[0])
|
||||
y_shape = copy.deepcopy(input_shapes.get('Y')[0])
|
||||
if attrs.get('trans_x'):
|
||||
x_shape[-1], x_shape[-2] = x_shape[-2], x_shape[-1]
|
||||
if attrs.get('trans_y'):
|
||||
y_shape[-1], y_shape[-2] = y_shape[-2], y_shape[-1]
|
||||
dim_x = len(x_shape)
|
||||
dim_y = len(y_shape)
|
||||
output_len = max(dim_x, dim_y)
|
||||
output_shape = []
|
||||
for idx in range(output_len, 2, -1):
|
||||
x_idx = x_shape[dim_x - idx] if idx <= dim_x else 1
|
||||
y_idx = y_shape[dim_y - idx] if idx <= dim_y else 1
|
||||
output_shape.append(max(x_idx, y_idx))
|
||||
|
||||
macs = prod(output_shape) * x_shape[-2] * x_shape[-1] * y_shape[-1]
|
||||
return 2 * macs
|
||||
|
||||
|
||||
def _relu_class_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for relu_like ops.
|
||||
For elu/leaky_relu/prelu/relu/relu6/silu (input):
|
||||
equation: flops = (numel)total number of elements in the input tensor.
|
||||
"""
|
||||
input = input_shapes.get('X')[0]
|
||||
return prod(input)
|
||||
|
||||
|
||||
@register_flops("elu")
|
||||
def _elu_flops(input_shapes, attrs):
|
||||
return _relu_class_flops(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("leaky_relu")
|
||||
def _leaky_relu_flops(input_shapes, attrs):
|
||||
return _relu_class_flops(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("prelu")
|
||||
def _prelu_flops(input_shapes, attrs):
|
||||
return _relu_class_flops(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("relu")
|
||||
def _relu_flops(input_shapes, attrs):
|
||||
return _relu_class_flops(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("relu6")
|
||||
def _relu6_flops(input_shapes, attrs):
|
||||
return _relu_class_flops(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("silu")
|
||||
def _silu_flops(input_shapes, attrs):
|
||||
return _relu_class_flops(input_shapes, attrs)
|
||||
|
||||
|
||||
@register_flops("reshape2")
|
||||
def _reshape2_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for reshape2 op.
|
||||
For reshape2(input):
|
||||
equation: flops = 0
|
||||
"""
|
||||
return 0
|
||||
|
||||
|
||||
@register_flops("softmax")
|
||||
def _softmax_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for softmax op.
|
||||
For softmax(input):
|
||||
equation: flops = 3 * (numel)total number of elements in the input tensor.
|
||||
"""
|
||||
input = input_shapes.get('X')[0]
|
||||
return prod(input) * 3
|
||||
|
||||
|
||||
@register_flops("transpose2")
|
||||
def _transpose2_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for transpose2 op.
|
||||
For transpose2(input):
|
||||
equation: flops = 0
|
||||
"""
|
||||
return 0
|
||||
|
||||
|
||||
@register_flops("pool")
|
||||
def _pool_flops(input_shapes, attrs):
|
||||
"""FLOPs computation for pool op.
|
||||
For pool(input):
|
||||
equation: flops = (numel)total number of elements in the input tensor.
|
||||
"""
|
||||
input = input_shapes.get('X')[0]
|
||||
return prod(input)
|
||||
@@ -0,0 +1,282 @@
|
||||
# Copyright (c) 2025 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 io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
|
||||
import paddle
|
||||
from paddle.base.wrapped_decorator import signature_safe_contextmanager
|
||||
|
||||
|
||||
def _parse_tensors(input_str):
|
||||
input_tuples = re.findall(
|
||||
r"\(\s*(\w+)\s*,\s*(\{.*?\}|\[.*?\])\s*\)", input_str, re.DOTALL
|
||||
)
|
||||
tensors_list = []
|
||||
for var_name, value_str in input_tuples:
|
||||
# Only one Tensor
|
||||
if value_str.startswith('{'):
|
||||
tensor_info = _parse_tensor_info(value_str)
|
||||
tensors_list.append((var_name, [tensor_info]))
|
||||
# Tensor list
|
||||
elif value_str.startswith('['):
|
||||
list_content = value_str.strip('[]')
|
||||
dict_strs = _split_list_elements(list_content)
|
||||
tensor_list = []
|
||||
for dict_str in dict_strs:
|
||||
tensor_info = _parse_tensor_info(dict_str)
|
||||
if tensor_info:
|
||||
tensor_list.append(tensor_info)
|
||||
tensors_list.append((var_name, tensor_list))
|
||||
return tensors_list
|
||||
|
||||
|
||||
def _parse_api_name(debug_str):
|
||||
api_name_match = re.search(r"API_Name:\s*(\w+)", debug_str)
|
||||
return api_name_match.group(1) if api_name_match else None
|
||||
|
||||
|
||||
def _parse_input_tensors(debug_str):
|
||||
input_match = re.search(r"Input:\s*(\[.*?\] )", debug_str, re.DOTALL)
|
||||
return _parse_tensors(input_match.group(1)) if input_match else []
|
||||
|
||||
|
||||
def _parse_output_tensors(debug_str):
|
||||
output_match = re.search(r"Output:\s*(\[.*?\] )", debug_str, re.DOTALL)
|
||||
return _parse_tensors(output_match.group(1)) if output_match else []
|
||||
|
||||
|
||||
def _parse_tensor_info(dict_str):
|
||||
result = {}
|
||||
lines = dict_str.strip().split('\n')
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line and ':' in line:
|
||||
key, value = line.split(':', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if key == "Place":
|
||||
place_match = re.search(r"Place\((\w+:\d+)\)", value)
|
||||
if place_match:
|
||||
value = place_match.group(1)
|
||||
elif key == "Shape":
|
||||
value = [int(x.strip()) for x in value.split(',') if x.strip()]
|
||||
elif value == "None":
|
||||
value = None
|
||||
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
def _split_list_elements(list_str):
|
||||
elements = []
|
||||
current = []
|
||||
brace_count = 0
|
||||
|
||||
for char in list_str:
|
||||
if char == '{':
|
||||
brace_count += 1
|
||||
elif char == '}':
|
||||
brace_count -= 1
|
||||
|
||||
if char == ',' and brace_count == 0:
|
||||
elements.append(''.join(current).strip())
|
||||
current = []
|
||||
else:
|
||||
current.append(char)
|
||||
|
||||
if current:
|
||||
elements.append(''.join(current).strip())
|
||||
|
||||
return elements
|
||||
|
||||
|
||||
def parse_debug_info(debug_str):
|
||||
result = {"API_Name": None, "Input": [], "Output": []}
|
||||
|
||||
result["API_Name"] = _parse_api_name(debug_str)
|
||||
result["Input"] = _parse_input_tensors(debug_str)
|
||||
result["Output"] = _parse_output_tensors(debug_str)
|
||||
return result
|
||||
|
||||
|
||||
class Edge:
|
||||
def __init__(self, tensor_info: dict, source=""):
|
||||
self.name = tensor_info["Name"]
|
||||
self.shape = tensor_info["Shape"]
|
||||
self.dtype = tensor_info["Dtype"]
|
||||
self.source = source
|
||||
|
||||
def get_name(self) -> str:
|
||||
return self.name
|
||||
|
||||
def get_source(self) -> str:
|
||||
return self.source
|
||||
|
||||
def set_source(self, source):
|
||||
self.source = source
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Edge(name='{self.name}', source='{self.source}',shape='{self.shape}',dtype='{self.dtype}')"
|
||||
|
||||
def get_edge_info(self):
|
||||
return f"{self.name}\nshape:{self.shape}\ndtype:{self.dtype}"
|
||||
|
||||
|
||||
class Graph:
|
||||
def __init__(self):
|
||||
from graphviz import Digraph
|
||||
|
||||
self.dot = Digraph()
|
||||
self.orange_box_attrs = {
|
||||
'style': 'rounded,filled,bold',
|
||||
'shape': 'box',
|
||||
'color': '#FFE4B5',
|
||||
'fillcolor': '#FFE4B5',
|
||||
'fontcolor': '#ffffff',
|
||||
'width': '1.3',
|
||||
'height': '0.84',
|
||||
'fontname': 'Arial',
|
||||
}
|
||||
self.grey_box_attrs = {
|
||||
'style': 'rounded,filled,bold',
|
||||
'shape': 'box',
|
||||
'color': '#999999',
|
||||
'fillcolor': '#999999',
|
||||
'fontcolor': '#ffffff',
|
||||
'width': '1.3',
|
||||
'height': '0.84',
|
||||
'fontname': 'Arial',
|
||||
}
|
||||
|
||||
self.edges = {}
|
||||
self.nodes = []
|
||||
|
||||
def add_node(self, name: str, node_attr=""):
|
||||
if not node_attr:
|
||||
node_attr = self.grey_box_attrs
|
||||
self.dot.node(name, name, **node_attr)
|
||||
self.nodes.append(name)
|
||||
|
||||
# Link the src and dst node by edge
|
||||
def add_edge(self, dst: str, edge: Edge):
|
||||
edge_name = edge.get_name()
|
||||
if edge_name not in self.edges:
|
||||
# The Edge is not stored in the graph,
|
||||
# so the Edge's source node maybe not in the graph
|
||||
if not edge.get_source():
|
||||
src_name = edge.get_name() + "_SourceNode"
|
||||
edge.set_source(src_name)
|
||||
self.add_node(src_name, self.orange_box_attrs)
|
||||
else:
|
||||
edge = self.edges[edge_name]
|
||||
src = edge.get_source()
|
||||
self.dot.edge(src, dst, label=edge.get_edge_info())
|
||||
|
||||
# Store an edge, not link the src and dst node
|
||||
def store_edge(self, edge: Edge):
|
||||
edge_name = edge.get_name()
|
||||
self.edges[edge_name] = edge
|
||||
|
||||
def render(self, file_path):
|
||||
self.dot.render(file_path, format='svg')
|
||||
|
||||
|
||||
class GraphBuilder:
|
||||
def __init__(self):
|
||||
self.graph = Graph()
|
||||
|
||||
def build_graph(self, forward_debug_infos: list):
|
||||
for info in forward_debug_infos:
|
||||
debug_info = parse_debug_info(info)
|
||||
api_name = debug_info['API_Name']
|
||||
# Add a node for the API
|
||||
self.graph.add_node(api_name)
|
||||
# Store the Edge
|
||||
for out_param in debug_info["Output"]:
|
||||
var_name = out_param[0]
|
||||
tensors = out_param[1]
|
||||
for tensor_info in tensors:
|
||||
# When we do not know the edge's dst, we should store it to the Graph
|
||||
edge = Edge(tensor_info, api_name)
|
||||
self.graph.store_edge(edge)
|
||||
# Link the Edge
|
||||
for input_param in debug_info["Input"]:
|
||||
var_name = input_param[0]
|
||||
tensors = input_param[1]
|
||||
for tensor_info in tensors:
|
||||
edge = Edge(tensor_info)
|
||||
self.graph.add_edge(dst=api_name, edge=edge)
|
||||
|
||||
def save_graph(self, file_path):
|
||||
self.graph.render(file_path)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def capture_stderr():
|
||||
with tempfile.TemporaryFile(mode='w+b') as temp_file:
|
||||
original_stderr_fd = sys.stderr.fileno()
|
||||
saved_stderr_fd = os.dup(original_stderr_fd)
|
||||
stderr_output = io.StringIO()
|
||||
try:
|
||||
os.dup2(temp_file.fileno(), original_stderr_fd)
|
||||
sys.stderr.flush()
|
||||
yield stderr_output
|
||||
finally:
|
||||
sys.stderr.flush()
|
||||
os.dup2(saved_stderr_fd, original_stderr_fd)
|
||||
os.close(saved_stderr_fd)
|
||||
|
||||
temp_file.seek(0)
|
||||
stderr_output.write(temp_file.read().decode())
|
||||
|
||||
|
||||
@signature_safe_contextmanager
|
||||
def capture_forward_subgraph_guard(file_path: str):
|
||||
log = ""
|
||||
stderr_buffer = io.StringIO()
|
||||
origin_enable_unique_name_status = paddle.framework.get_flags(
|
||||
"FLAGS_enable_unique_name"
|
||||
)["FLAGS_enable_unique_name"]
|
||||
try:
|
||||
paddle.set_flags({"FLAGS_enable_unique_name": True})
|
||||
# Redirect the stderr to the buffer,because the glog info will be printed to the stderr
|
||||
with (
|
||||
capture_stderr() as stderr_buffer,
|
||||
paddle.base.framework.vlog_guard(3),
|
||||
):
|
||||
yield
|
||||
finally:
|
||||
paddle.set_flags(
|
||||
{"FLAGS_enable_unique_name": origin_enable_unique_name_status}
|
||||
)
|
||||
log = stderr_buffer.getvalue()
|
||||
builder = GraphBuilder()
|
||||
|
||||
def get_first_indent(s):
|
||||
match = re.match(r'^\t*', s)
|
||||
return match.group(0)
|
||||
|
||||
# Find the parts describing the input and output of the API in the massive logs
|
||||
indent = get_first_indent(log.lstrip('\n'))
|
||||
pattern_str = r'\n' + indent + r'Forward Debug Info \{.*? ] } '
|
||||
pattern = re.compile(pattern_str, re.DOTALL)
|
||||
matches = pattern.findall(log)
|
||||
# Build the forward graph
|
||||
builder.build_graph(matches)
|
||||
builder.save_graph(file_path)
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2016, Serge Guelton
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
|
||||
# Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
|
||||
# Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
|
||||
# Neither the name of HPCProject, Serge Guelton nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
|
||||
# representation. See https://github.com/serge-sans-paille/gast for details.
|
||||
|
||||
from .gast import *
|
||||
from ast import NodeVisitor, NodeTransformer, iter_fields
|
||||
@@ -0,0 +1,602 @@
|
||||
# Copyright (c) 2016, Serge Guelton
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
|
||||
# Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
|
||||
# Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
|
||||
# Neither the name of HPCProject, Serge Guelton nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
|
||||
# representation. See https://github.com/serge-sans-paille/gast for details.
|
||||
|
||||
from .astn import AstToGAst, GAstToAst
|
||||
from . import gast
|
||||
import ast
|
||||
import sys
|
||||
|
||||
|
||||
class Ast3ToGAst(AstToGAst):
|
||||
if sys.version_info.minor == 12:
|
||||
|
||||
def visit_TypeVar(self, node):
|
||||
new_node = gast.TypeVar(
|
||||
self._visit(node.name),
|
||||
self._visit(node.bound),
|
||||
None
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_TypeVarTuple(self, node):
|
||||
new_node = gast.TypeVarTuple(
|
||||
self._visit(node.name),
|
||||
None
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_ParamSpec(self, node):
|
||||
new_node = gast.ParamSpec(
|
||||
self._visit(node.name),
|
||||
None
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
if sys.version_info.minor < 10:
|
||||
|
||||
def visit_alias(self, node):
|
||||
new_node = gast.alias(
|
||||
self._visit(node.name),
|
||||
self._visit(node.asname),
|
||||
)
|
||||
new_node.lineno = new_node.col_offset = None
|
||||
new_node.end_lineno = new_node.end_col_offset = None
|
||||
return new_node
|
||||
|
||||
if sys.version_info.minor < 9:
|
||||
|
||||
def visit_ExtSlice(self, node):
|
||||
new_node = gast.Tuple(self._visit(node.dims), gast.Load())
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_Index(self, node):
|
||||
return self._visit(node.value)
|
||||
|
||||
def visit_Assign(self, node):
|
||||
new_node = gast.Assign(
|
||||
self._visit(node.targets),
|
||||
self._visit(node.value),
|
||||
None, # type_comment
|
||||
)
|
||||
|
||||
gast.copy_location(new_node, node)
|
||||
new_node.end_lineno = new_node.end_col_offset = None
|
||||
return new_node
|
||||
|
||||
if sys.version_info.minor < 8:
|
||||
def visit_Module(self, node):
|
||||
new_node = gast.Module(
|
||||
self._visit(node.body),
|
||||
[] # type_ignores
|
||||
)
|
||||
return new_node
|
||||
|
||||
def visit_Num(self, node):
|
||||
new_node = gast.Constant(
|
||||
node.n,
|
||||
None,
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_Ellipsis(self, node):
|
||||
new_node = gast.Constant(
|
||||
Ellipsis,
|
||||
None,
|
||||
)
|
||||
gast.copy_location(new_node, node)
|
||||
new_node.end_lineno = new_node.end_col_offset = None
|
||||
return new_node
|
||||
|
||||
def visit_Str(self, node):
|
||||
new_node = gast.Constant(
|
||||
node.s,
|
||||
None,
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_Bytes(self, node):
|
||||
new_node = gast.Constant(
|
||||
node.s,
|
||||
None,
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_FunctionDef(self, node):
|
||||
new_node = gast.FunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
None, # type_comment
|
||||
[], # type_params
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncFunctionDef(self, node):
|
||||
new_node = gast.AsyncFunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
None, # type_comment
|
||||
[], # type_params
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_For(self, node):
|
||||
new_node = gast.For(
|
||||
self._visit(node.target),
|
||||
self._visit(node.iter),
|
||||
self._visit(node.body),
|
||||
self._visit(node.orelse),
|
||||
None, # type_comment
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncFor(self, node):
|
||||
new_node = gast.AsyncFor(
|
||||
self._visit(node.target),
|
||||
self._visit(node.iter),
|
||||
self._visit(node.body),
|
||||
self._visit(node.orelse),
|
||||
None, # type_comment
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_With(self, node):
|
||||
new_node = gast.With(
|
||||
self._visit(node.items),
|
||||
self._visit(node.body),
|
||||
None, # type_comment
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncWith(self, node):
|
||||
new_node = gast.AsyncWith(
|
||||
self._visit(node.items),
|
||||
self._visit(node.body),
|
||||
None, # type_comment
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_Call(self, node):
|
||||
if sys.version_info.minor < 5:
|
||||
if node.starargs:
|
||||
star = gast.Starred(self._visit(node.starargs),
|
||||
gast.Load())
|
||||
gast.copy_location(star, node)
|
||||
starred = [star]
|
||||
else:
|
||||
starred = []
|
||||
|
||||
if node.kwargs:
|
||||
kw = gast.keyword(None, self._visit(node.kwargs))
|
||||
gast.copy_location(kw, node.kwargs)
|
||||
kwargs = [kw]
|
||||
else:
|
||||
kwargs = []
|
||||
else:
|
||||
starred = kwargs = []
|
||||
|
||||
new_node = gast.Call(
|
||||
self._visit(node.func),
|
||||
self._visit(node.args) + starred,
|
||||
self._visit(node.keywords) + kwargs,
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_NameConstant(self, node):
|
||||
if node.value is None:
|
||||
new_node = gast.Constant(None, None)
|
||||
elif node.value is True:
|
||||
new_node = gast.Constant(True, None)
|
||||
elif node.value is False:
|
||||
new_node = gast.Constant(False, None)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_arguments(self, node):
|
||||
new_node = gast.arguments(
|
||||
self._visit(node.args),
|
||||
[], # posonlyargs
|
||||
self._visit(node.vararg),
|
||||
self._visit(node.kwonlyargs),
|
||||
self._visit(node.kw_defaults),
|
||||
self._visit(node.kwarg),
|
||||
self._visit(node.defaults),
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_Name(self, node):
|
||||
new_node = gast.Name(
|
||||
node.id, # micro-optimization here, don't call self._visit
|
||||
self._visit(node.ctx),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_arg(self, node):
|
||||
if sys.version_info.minor < 8:
|
||||
extra_arg = None
|
||||
else:
|
||||
extra_arg = self._visit(node.type_comment)
|
||||
|
||||
new_node = gast.Name(
|
||||
node.arg, # micro-optimization here, don't call self._visit
|
||||
gast.Param(),
|
||||
self._visit(node.annotation),
|
||||
extra_arg # type_comment
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_ExceptHandler(self, node):
|
||||
if node.name:
|
||||
new_node = gast.ExceptHandler(
|
||||
self._visit(node.type),
|
||||
gast.Name(node.name, gast.Store(), None, None),
|
||||
self._visit(node.body))
|
||||
return ast.copy_location(new_node, node)
|
||||
else:
|
||||
return self.generic_visit(node)
|
||||
|
||||
if sys.version_info.minor < 6:
|
||||
|
||||
def visit_comprehension(self, node):
|
||||
new_node = gast.comprehension(
|
||||
target=self._visit(node.target),
|
||||
iter=self._visit(node.iter),
|
||||
ifs=self._visit(node.ifs),
|
||||
is_async=0,
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
if 8 <= sys.version_info.minor < 12:
|
||||
def visit_FunctionDef(self, node):
|
||||
new_node = gast.FunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
self._visit(node.type_comment),
|
||||
[], # type_params
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncFunctionDef(self, node):
|
||||
new_node = gast.AsyncFunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
self._visit(node.type_comment),
|
||||
[], # type_params
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
if sys.version_info.minor < 12:
|
||||
|
||||
def visit_ClassDef(self, node):
|
||||
new_node = gast.ClassDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.bases),
|
||||
self._visit(node.keywords),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
[], # type_params
|
||||
)
|
||||
return gast.copy_location(new_node, node)
|
||||
|
||||
|
||||
class GAstToAst3(GAstToAst):
|
||||
if sys.version_info.minor == 12:
|
||||
def visit_TypeVar(self, node):
|
||||
new_node = ast.TypeVar(
|
||||
self._visit(node.name),
|
||||
self._visit(node.bound)
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_TypeVarTuple(self, node):
|
||||
new_node = ast.TypeVarTuple(
|
||||
self._visit(node.name),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_ParamSpec(self, node):
|
||||
new_node = ast.ParamSpec(
|
||||
self._visit(node.name),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
if sys.version_info.minor < 10:
|
||||
def visit_alias(self, node):
|
||||
new_node = ast.alias(
|
||||
self._visit(node.name),
|
||||
self._visit(node.asname)
|
||||
)
|
||||
return new_node
|
||||
|
||||
if sys.version_info.minor < 9:
|
||||
def visit_Subscript(self, node):
|
||||
def adjust_slice(s):
|
||||
if isinstance(s, ast.Slice):
|
||||
return s
|
||||
else:
|
||||
return ast.Index(s)
|
||||
if isinstance(node.slice, gast.Tuple):
|
||||
if any(isinstance(elt, gast.slice) for elt in node.slice.elts):
|
||||
new_slice = ast.ExtSlice(
|
||||
[adjust_slice(x) for x in
|
||||
self._visit(node.slice.elts)])
|
||||
else:
|
||||
value = ast.Tuple(self._visit(node.slice.elts), ast.Load())
|
||||
ast.copy_location(value, node.slice)
|
||||
new_slice = ast.Index(value)
|
||||
else:
|
||||
new_slice = adjust_slice(self._visit(node.slice))
|
||||
ast.copy_location(new_slice, node.slice)
|
||||
|
||||
new_node = ast.Subscript(
|
||||
self._visit(node.value),
|
||||
new_slice,
|
||||
self._visit(node.ctx),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_Assign(self, node):
|
||||
new_node = ast.Assign(
|
||||
self._visit(node.targets),
|
||||
self._visit(node.value),
|
||||
)
|
||||
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
if sys.version_info.minor < 8:
|
||||
|
||||
def visit_Module(self, node):
|
||||
new_node = ast.Module(self._visit(node.body))
|
||||
return new_node
|
||||
|
||||
def visit_Constant(self, node):
|
||||
if node.value is None:
|
||||
new_node = ast.NameConstant(node.value)
|
||||
elif node.value is Ellipsis:
|
||||
new_node = ast.Ellipsis()
|
||||
elif isinstance(node.value, bool):
|
||||
new_node = ast.NameConstant(node.value)
|
||||
elif isinstance(node.value, (int, float, complex)):
|
||||
new_node = ast.Num(node.value)
|
||||
elif isinstance(node.value, str):
|
||||
new_node = ast.Str(node.value)
|
||||
else:
|
||||
new_node = ast.Bytes(node.value)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def _make_arg(self, node):
|
||||
if node is None:
|
||||
return None
|
||||
|
||||
if sys.version_info.minor < 8:
|
||||
extra_args = tuple()
|
||||
else:
|
||||
extra_args = self._visit(node.type_comment),
|
||||
|
||||
new_node = ast.arg(
|
||||
self._visit(node.id),
|
||||
self._visit(node.annotation),
|
||||
*extra_args
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_Name(self, node):
|
||||
new_node = ast.Name(
|
||||
self._visit(node.id),
|
||||
self._visit(node.ctx),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_ExceptHandler(self, node):
|
||||
if node.name:
|
||||
new_node = ast.ExceptHandler(
|
||||
self._visit(node.type),
|
||||
node.name.id,
|
||||
self._visit(node.body))
|
||||
return ast.copy_location(new_node, node)
|
||||
else:
|
||||
return self.generic_visit(node)
|
||||
|
||||
if sys.version_info.minor < 5:
|
||||
|
||||
def visit_Call(self, node):
|
||||
if node.args and isinstance(node.args[-1], gast.Starred):
|
||||
args = node.args[:-1]
|
||||
starargs = node.args[-1].value
|
||||
else:
|
||||
args = node.args
|
||||
starargs = None
|
||||
|
||||
if node.keywords and node.keywords[-1].arg is None:
|
||||
keywords = node.keywords[:-1]
|
||||
kwargs = node.keywords[-1].value
|
||||
else:
|
||||
keywords = node.keywords
|
||||
kwargs = None
|
||||
|
||||
new_node = ast.Call(
|
||||
self._visit(node.func),
|
||||
self._visit(args),
|
||||
self._visit(keywords),
|
||||
self._visit(starargs),
|
||||
self._visit(kwargs),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_ClassDef(self, node):
|
||||
self.generic_visit(node)
|
||||
new_node = ast.ClassDef(
|
||||
name=self._visit(node.name),
|
||||
bases=self._visit(node.bases),
|
||||
keywords=self._visit(node.keywords),
|
||||
body=self._visit(node.body),
|
||||
decorator_list=self._visit(node.decorator_list),
|
||||
starargs=None,
|
||||
kwargs=None,
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
elif sys.version_info.minor < 8:
|
||||
|
||||
def visit_FunctionDef(self, node):
|
||||
new_node = ast.FunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncFunctionDef(self, node):
|
||||
new_node = ast.AsyncFunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_For(self, node):
|
||||
new_node = ast.For(
|
||||
self._visit(node.target),
|
||||
self._visit(node.iter),
|
||||
self._visit(node.body),
|
||||
self._visit(node.orelse),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncFor(self, node):
|
||||
new_node = ast.AsyncFor(
|
||||
self._visit(node.target),
|
||||
self._visit(node.iter),
|
||||
self._visit(node.body),
|
||||
self._visit(node.orelse),
|
||||
None, # type_comment
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_With(self, node):
|
||||
new_node = ast.With(
|
||||
self._visit(node.items),
|
||||
self._visit(node.body),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncWith(self, node):
|
||||
new_node = ast.AsyncWith(
|
||||
self._visit(node.items),
|
||||
self._visit(node.body),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_Call(self, node):
|
||||
new_node = ast.Call(
|
||||
self._visit(node.func),
|
||||
self._visit(node.args),
|
||||
self._visit(node.keywords),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
if 5 <= sys.version_info.minor < 12:
|
||||
def visit_ClassDef(self, node):
|
||||
new_node = ast.ClassDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.bases),
|
||||
self._visit(node.keywords),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
if 8 <= sys.version_info.minor < 12:
|
||||
def visit_FunctionDef(self, node):
|
||||
new_node = ast.FunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
self._visit(node.type_comment),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def visit_AsyncFunctionDef(self, node):
|
||||
new_node = ast.AsyncFunctionDef(
|
||||
self._visit(node.name),
|
||||
self._visit(node.args),
|
||||
self._visit(node.body),
|
||||
self._visit(node.decorator_list),
|
||||
self._visit(node.returns),
|
||||
self._visit(node.type_comment),
|
||||
)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
|
||||
|
||||
def visit_arguments(self, node):
|
||||
extra_args = [self._make_arg(node.vararg),
|
||||
[self._make_arg(n) for n in node.kwonlyargs],
|
||||
self._visit(node.kw_defaults),
|
||||
self._make_arg(node.kwarg),
|
||||
self._visit(node.defaults), ]
|
||||
if sys.version_info.minor >= 8:
|
||||
new_node = ast.arguments(
|
||||
[self._make_arg(arg) for arg in node.posonlyargs],
|
||||
[self._make_arg(n) for n in node.args],
|
||||
*extra_args
|
||||
)
|
||||
else:
|
||||
new_node = ast.arguments(
|
||||
[self._make_arg(n) for n in node.args],
|
||||
*extra_args
|
||||
)
|
||||
return new_node
|
||||
|
||||
|
||||
def ast_to_gast(node):
|
||||
return Ast3ToGAst().visit(node)
|
||||
|
||||
|
||||
def gast_to_ast(node):
|
||||
return GAstToAst3().visit(node)
|
||||
@@ -0,0 +1,74 @@
|
||||
# Copyright (c) 2016, Serge Guelton
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
|
||||
# Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
|
||||
# Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
|
||||
# Neither the name of HPCProject, Serge Guelton nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
|
||||
# representation. See https://github.com/serge-sans-paille/gast for details.
|
||||
|
||||
import ast
|
||||
from . import gast
|
||||
|
||||
|
||||
def _generate_translators(to):
|
||||
|
||||
class Translator(ast.NodeTransformer):
|
||||
|
||||
def _visit(self, node):
|
||||
if isinstance(node, ast.AST):
|
||||
return self.visit(node)
|
||||
elif isinstance(node, list):
|
||||
return [self._visit(n) for n in node]
|
||||
else:
|
||||
return node
|
||||
|
||||
def generic_visit(self, node):
|
||||
class_name = type(node).__name__
|
||||
if not hasattr(to, class_name):
|
||||
# handle nodes that are not part of the AST
|
||||
return
|
||||
cls = getattr(to, class_name)
|
||||
new_node = cls(
|
||||
**{
|
||||
field: self._visit(getattr(node, field))
|
||||
for field in node._fields
|
||||
if hasattr(node, field)
|
||||
}
|
||||
)
|
||||
|
||||
for attr in node._attributes:
|
||||
try:
|
||||
setattr(new_node, attr, getattr(node, attr))
|
||||
except AttributeError:
|
||||
pass
|
||||
return new_node
|
||||
|
||||
return Translator
|
||||
|
||||
|
||||
AstToGAst = _generate_translators(gast)
|
||||
|
||||
GAstToAst = _generate_translators(ast)
|
||||
@@ -0,0 +1,718 @@
|
||||
# Copyright (c) 2016, Serge Guelton
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
|
||||
# Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
|
||||
# Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
|
||||
# Neither the name of HPCProject, Serge Guelton nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from this
|
||||
# software without specific prior written permission.
|
||||
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# NOTE(paddle-dev): We introduce third-party library Gast as unified AST
|
||||
# representation. See https://github.com/serge-sans-paille/gast for details.
|
||||
|
||||
import sys as _sys
|
||||
import ast as _ast
|
||||
from ast import boolop, cmpop, excepthandler, expr, expr_context, operator
|
||||
from ast import slice, stmt, unaryop, mod, AST
|
||||
from ast import iter_child_nodes, walk
|
||||
|
||||
try:
|
||||
from ast import TypeIgnore
|
||||
except ImportError:
|
||||
class TypeIgnore(AST):
|
||||
pass
|
||||
|
||||
try:
|
||||
from ast import pattern
|
||||
except ImportError:
|
||||
class pattern(AST):
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
from ast import type_param
|
||||
except ImportError:
|
||||
class type_param(AST):
|
||||
pass
|
||||
|
||||
|
||||
def _make_node(Name, Fields, Attributes, Bases):
|
||||
|
||||
# This constructor is used a lot during conversion from ast to gast,
|
||||
# then as the primary way to build ast nodes. So we tried to optimized it
|
||||
# for speed and not for readability.
|
||||
def create_node(self, *args, **kwargs):
|
||||
if len(args) > len(Fields):
|
||||
raise TypeError(
|
||||
"{} constructor takes at most {} positional arguments".
|
||||
format(Name, len(Fields)))
|
||||
|
||||
# it's faster to iterate rather than zipping or enumerate
|
||||
for i in range(len(args)):
|
||||
setattr(self, Fields[i], args[i])
|
||||
if kwargs: # cold branch
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
setattr(_sys.modules[__name__],
|
||||
Name,
|
||||
type(Name,
|
||||
Bases,
|
||||
{'__init__': create_node,
|
||||
'_fields': Fields,
|
||||
'_field_types': {},
|
||||
'_attributes': Attributes}))
|
||||
|
||||
def _fill_field_types(Name, FieldTypes):
|
||||
node = getattr(_sys.modules[__name__], Name)
|
||||
assert len(node._fields) == len(FieldTypes), Name
|
||||
node._field_types.update(zip(node._fields, FieldTypes))
|
||||
|
||||
_nodes = (
|
||||
# mod
|
||||
('Module', (('body', 'type_ignores'), (), (mod,))),
|
||||
('Interactive', (('body',), (), (mod,))),
|
||||
('Expression', (('body',), (), (mod,))),
|
||||
('FunctionType', (('argtypes', 'returns'), (), (mod,))),
|
||||
('Suite', (('body',), (), (mod,))),
|
||||
|
||||
# stmt
|
||||
('FunctionDef', (('name', 'args', 'body', 'decorator_list', 'returns',
|
||||
'type_comment', 'type_params'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('AsyncFunctionDef', (('name', 'args', 'body', 'decorator_list', 'returns',
|
||||
'type_comment', 'type_params',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('ClassDef', (('name', 'bases', 'keywords', 'body', 'decorator_list',
|
||||
'type_params',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Return', (('value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Delete', (('targets',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Assign', (('targets', 'value', 'type_comment'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('TypeAlias', (('name', 'type_params', 'value'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('AugAssign', (('target', 'op', 'value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('AnnAssign', (('target', 'annotation', 'value', 'simple',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Print', (('dest', 'values', 'nl',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('For', (('target', 'iter', 'body', 'orelse', 'type_comment'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('AsyncFor', (('target', 'iter', 'body', 'orelse', 'type_comment'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('While', (('test', 'body', 'orelse',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('If', (('test', 'body', 'orelse',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('With', (('items', 'body', 'type_comment'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('AsyncWith', (('items', 'body', 'type_comment'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Match', (('subject', 'cases'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Raise', (('exc', 'cause',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Try', (('body', 'handlers', 'orelse', 'finalbody',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('TryStar', (('body', 'handlers', 'orelse', 'finalbody',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Assert', (('test', 'msg',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Import', (('names',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('ImportFrom', (('module', 'names', 'level',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Exec', (('body', 'globals', 'locals',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Global', (('names',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Nonlocal', (('names',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Expr', (('value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Pass', ((), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Break', ((), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
('Continue', ((),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(stmt,))),
|
||||
|
||||
# expr
|
||||
|
||||
('BoolOp', (('op', 'values',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('NamedExpr', (('target', 'value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('BinOp', (('left', 'op', 'right',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('UnaryOp', (('op', 'operand',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Lambda', (('args', 'body',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('IfExp', (('test', 'body', 'orelse',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Dict', (('keys', 'values',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Set', (('elts',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('ListComp', (('elt', 'generators',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('SetComp', (('elt', 'generators',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('DictComp', (('key', 'value', 'generators',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('GeneratorExp', (('elt', 'generators',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Await', (('value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Yield', (('value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('YieldFrom', (('value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Compare', (('left', 'ops', 'comparators',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Call', (('func', 'args', 'keywords',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Repr', (('value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('FormattedValue', (('value', 'conversion', 'format_spec',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Interpolation', (('value', 'str', 'conversion', 'format_spec',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('JoinedStr', (('values',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('TemplateStr', (('values',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Constant', (('value', 'kind'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Attribute', (('value', 'attr', 'ctx',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Subscript', (('value', 'slice', 'ctx',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Starred', (('value', 'ctx',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Name', (('id', 'ctx', 'annotation', 'type_comment'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('List', (('elts', 'ctx',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
('Tuple', (('elts', 'ctx',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(expr,))),
|
||||
|
||||
# expr_context
|
||||
('Load', ((), (), (expr_context,))),
|
||||
('Store', ((), (), (expr_context,))),
|
||||
('Del', ((), (), (expr_context,))),
|
||||
('AugLoad', ((), (), (expr_context,))),
|
||||
('AugStore', ((), (), (expr_context,))),
|
||||
('Param', ((), (), (expr_context,))),
|
||||
|
||||
# slice
|
||||
('Slice', (('lower', 'upper', 'step'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset',),
|
||||
(slice,))),
|
||||
|
||||
# boolop
|
||||
('And', ((), (), (boolop,))),
|
||||
('Or', ((), (), (boolop,))),
|
||||
|
||||
# operator
|
||||
('Add', ((), (), (operator,))),
|
||||
('Sub', ((), (), (operator,))),
|
||||
('Mult', ((), (), (operator,))),
|
||||
('MatMult', ((), (), (operator,))),
|
||||
('Div', ((), (), (operator,))),
|
||||
('Mod', ((), (), (operator,))),
|
||||
('Pow', ((), (), (operator,))),
|
||||
('LShift', ((), (), (operator,))),
|
||||
('RShift', ((), (), (operator,))),
|
||||
('BitOr', ((), (), (operator,))),
|
||||
('BitXor', ((), (), (operator,))),
|
||||
('BitAnd', ((), (), (operator,))),
|
||||
('FloorDiv', ((), (), (operator,))),
|
||||
|
||||
# unaryop
|
||||
('Invert', ((), (), (unaryop, AST,))),
|
||||
('Not', ((), (), (unaryop, AST,))),
|
||||
('UAdd', ((), (), (unaryop, AST,))),
|
||||
('USub', ((), (), (unaryop, AST,))),
|
||||
|
||||
# cmpop
|
||||
('Eq', ((), (), (cmpop,))),
|
||||
('NotEq', ((), (), (cmpop,))),
|
||||
('Lt', ((), (), (cmpop,))),
|
||||
('LtE', ((), (), (cmpop,))),
|
||||
('Gt', ((), (), (cmpop,))),
|
||||
('GtE', ((), (), (cmpop,))),
|
||||
('Is', ((), (), (cmpop,))),
|
||||
('IsNot', ((), (), (cmpop,))),
|
||||
('In', ((), (), (cmpop,))),
|
||||
('NotIn', ((), (), (cmpop,))),
|
||||
|
||||
# comprehension
|
||||
('comprehension', (('target', 'iter', 'ifs', 'is_async'), (), (AST,))),
|
||||
|
||||
# excepthandler
|
||||
('ExceptHandler', (('type', 'name', 'body'),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(excepthandler,))),
|
||||
|
||||
# arguments
|
||||
('arguments', (('args', 'posonlyargs', 'vararg', 'kwonlyargs',
|
||||
'kw_defaults', 'kwarg', 'defaults'), (), (AST,))),
|
||||
|
||||
# keyword
|
||||
('keyword', (('arg', 'value'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset'),
|
||||
(AST,))),
|
||||
|
||||
# alias
|
||||
('alias', (('name', 'asname'),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset'),
|
||||
(AST,))),
|
||||
|
||||
# withitem
|
||||
('withitem', (('context_expr', 'optional_vars'), (), (AST,))),
|
||||
|
||||
# match_case
|
||||
('match_case', (('pattern', 'guard', 'body'), (), (AST,))),
|
||||
|
||||
# pattern
|
||||
('MatchValue', (('value',),
|
||||
('lineno', 'col_offset', 'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
('MatchSingleton', (('value',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
('MatchSequence', (('patterns',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
('MatchMapping', (('keys', 'patterns', 'rest'),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
('MatchClass', (('cls', 'patterns', 'kwd_attrs', 'kwd_patterns'),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
('MatchStar', (('name',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
('MatchAs', (('pattern', 'name'),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
('MatchOr', (('patterns',),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(pattern,))),
|
||||
|
||||
# type_ignore
|
||||
('type_ignore', ((), ('lineno', 'tag'), (TypeIgnore,))),
|
||||
|
||||
# type_param
|
||||
('TypeVar', (('name', 'bound', 'default_value'),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(type_param,))),
|
||||
('ParamSpec', (('name', 'default_value'),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(type_param,))),
|
||||
('TypeVarTuple', (('name', 'default_value'),
|
||||
('lineno', 'col_offset',
|
||||
'end_lineno', 'end_col_offset'),
|
||||
(type_param,))),
|
||||
)
|
||||
|
||||
for _name, _descr in _nodes:
|
||||
_make_node(_name, *_descr)
|
||||
|
||||
# As an exception to gast rule that states that all nodes are identical for all
|
||||
# python version, we don't fill the field type for python with a version lower
|
||||
# than 3.10. Those version lack type support to be compatible with the more
|
||||
# modern representation anyway. The _field_types still exists though, but it's
|
||||
# always empty.
|
||||
if _sys.version_info >= (3, 10):
|
||||
|
||||
_node_types = (
|
||||
# mod
|
||||
('Module', (list[stmt], list[type_ignore])),
|
||||
('Interactive', (list[stmt],)),
|
||||
('Expression', (expr,)),
|
||||
('FunctionType', ('argtypes', 'returns'),),
|
||||
('Suite', (list[stmt],),),
|
||||
|
||||
# stmt
|
||||
('FunctionDef', (str, arguments, list[stmt], list[expr], expr | None, str | None, list[type_param]),),
|
||||
('AsyncFunctionDef', (str, arguments, list[stmt], list[expr], expr | None, str | None, list[type_param]),),
|
||||
('ClassDef', (str, list[expr], list[keyword], list[stmt], list[expr], list[type_param])),
|
||||
('Return', (expr | None,)),
|
||||
('Delete', (list[expr],)),
|
||||
('Assign', (list[expr], expr, str | None),),
|
||||
('TypeAlias', (expr, list[type_param], expr),),
|
||||
('AugAssign', (expr, operator, expr), ),
|
||||
('AnnAssign', (expr, expr, expr | None, int), ),
|
||||
('Print', (expr | None, list[expr], bool), ),
|
||||
('For', (expr, expr, list[stmt], list[stmt], str | None), ),
|
||||
('AsyncFor', (expr, expr, list[stmt], list[stmt], str | None), ),
|
||||
('While', (expr, list[stmt], list[stmt]), ),
|
||||
('If', (expr, list[stmt], list[stmt]), ),
|
||||
('With', (list[withitem], list[stmt], str | None), ),
|
||||
('AsyncWith', (list[withitem], list[stmt], str | None), ),
|
||||
('Match', (expr, match_case), ),
|
||||
('Raise', (expr | None, expr | None), ),
|
||||
('Try', (list[stmt], list[excepthandler], list[stmt], list[stmt]), ),
|
||||
('TryStar', (list[stmt], list[excepthandler], list[stmt], list[stmt]), ),
|
||||
('Assert', (expr, expr | None), ),
|
||||
('Import', (list[alias],), ),
|
||||
('ImportFrom', (str|None, list[alias], int | None), ),
|
||||
('Exec', (expr, expr | None, expr | None), ),
|
||||
('Global', (list[str],), ),
|
||||
('Nonlocal', (list[str],), ),
|
||||
('Expr', (expr,), ),
|
||||
|
||||
# expr
|
||||
|
||||
('BoolOp', (boolop, list[expr]), ),
|
||||
('NamedExpr', (expr, expr), ),
|
||||
('BinOp', (expr, operator, expr), ),
|
||||
('UnaryOp', (unaryop, expr), ),
|
||||
('Lambda', (arguments, expr), ),
|
||||
('IfExp', (expr, expr, expr), ),
|
||||
('Dict', (list[expr], list[expr]), ),
|
||||
('Set', (list[expr],), ),
|
||||
('ListComp', (expr, list[comprehension]), ),
|
||||
('SetComp', (expr, list[comprehension]), ),
|
||||
('DictComp', (expr, expr, list[comprehension]), ),
|
||||
('GeneratorExp', (expr, list[comprehension]), ),
|
||||
('Await', (expr,), ),
|
||||
('Yield', (expr | None,), ),
|
||||
('YieldFrom', (expr,), ),
|
||||
('Compare', (expr, list[cmpop], list[expr]), ),
|
||||
('Call', (expr, list[expr], list[keyword]), ),
|
||||
('Repr', (expr,), ),
|
||||
('FormattedValue', (expr, int, expr | None), ),
|
||||
('Interpolation', (expr, str, int, expr | None), ),
|
||||
('JoinedStr', (list[expr],), ),
|
||||
('TemplateStr', (list[expr],), ),
|
||||
('Constant', (object, str | None), ),
|
||||
('Attribute', (expr, str, expr_context), ),
|
||||
('Subscript', (expr, expr, expr_context), ),
|
||||
('Starred', (expr, expr_context), ),
|
||||
('Name', (str, expr_context, expr, str | None), ),
|
||||
('List', (list[expr], expr_context), ),
|
||||
('Tuple', (list[expr], expr_context), ),
|
||||
('Slice', (expr | None, expr | None, expr | None), ),
|
||||
|
||||
# comprehension
|
||||
('comprehension', (expr, expr, list[expr], int), ),
|
||||
|
||||
# excepthandler
|
||||
('ExceptHandler', (expr | None, str | None, list[stmt]), ),
|
||||
|
||||
# arguments
|
||||
('arguments', (list[expr], list[expr], expr | None, list[expr], list[expr], expr | None, list[expr]), ),
|
||||
|
||||
# keyword
|
||||
('keyword', (str | None, expr), ),
|
||||
|
||||
# alias
|
||||
('alias', (str, str | None), ),
|
||||
|
||||
# withitem
|
||||
('withitem', (expr, expr | None), ),
|
||||
|
||||
# match_case
|
||||
('match_case', (pattern, expr, list[stmt]), ),
|
||||
|
||||
# pattern
|
||||
('MatchValue', (expr,), ),
|
||||
('MatchSingleton', (object,), ),
|
||||
('MatchSequence', (list[pattern],), ),
|
||||
('MatchMapping', (list[expr], list[pattern], str | None), ),
|
||||
('MatchClass', (expr, list[pattern], list[str], list[pattern]), ),
|
||||
('MatchStar', (str | None,), ),
|
||||
('MatchAs', (pattern | None, str | None), ),
|
||||
('MatchOr', (list[pattern],), ),
|
||||
|
||||
# type_param
|
||||
('TypeVar', (str, expr | None, expr | None), ),
|
||||
('ParamSpec', (str, expr | None), ),
|
||||
('TypeVarTuple', (str, expr | None), ),
|
||||
)
|
||||
|
||||
for _name, _types in _node_types:
|
||||
_fill_field_types(_name, _types)
|
||||
|
||||
from .ast3 import ast_to_gast, gast_to_ast
|
||||
|
||||
|
||||
def parse(*args, **kwargs):
|
||||
return ast_to_gast(_ast.parse(*args, **kwargs))
|
||||
|
||||
|
||||
def literal_eval(node_or_string):
|
||||
if isinstance(node_or_string, AST):
|
||||
node_or_string = gast_to_ast(node_or_string)
|
||||
return _ast.literal_eval(node_or_string)
|
||||
|
||||
|
||||
def get_docstring(node, clean=True):
|
||||
if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
|
||||
raise TypeError("%r can't have docstrings" % node.__class__.__name__)
|
||||
if not(node.body and isinstance(node.body[0], Expr)):
|
||||
return None
|
||||
node = node.body[0].value
|
||||
if isinstance(node, Constant) and isinstance(node.value, str):
|
||||
text = node.value
|
||||
else:
|
||||
return None
|
||||
if clean:
|
||||
import inspect
|
||||
text = inspect.cleandoc(text)
|
||||
return text
|
||||
|
||||
|
||||
# the following are directly imported from python3.8's Lib/ast.py #
|
||||
|
||||
def copy_location(new_node, old_node):
|
||||
"""
|
||||
Copy source location (`lineno`, `col_offset`, `end_lineno`, and
|
||||
`end_col_offset` attributes) from *old_node* to *new_node* if possible,
|
||||
and return *new_node*.
|
||||
"""
|
||||
for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
|
||||
if attr in old_node._attributes and attr in new_node._attributes \
|
||||
and hasattr(old_node, attr):
|
||||
setattr(new_node, attr, getattr(old_node, attr))
|
||||
return new_node
|
||||
|
||||
|
||||
def fix_missing_locations(node):
|
||||
"""
|
||||
When you compile a node tree with compile(), the compiler expects lineno
|
||||
and col_offset attributes for every node that supports them. This is
|
||||
rather tedious to fill in for generated nodes, so this helper adds these
|
||||
attributes recursively where not already set, by setting them to the values
|
||||
of the parent node. It works recursively starting at *node*.
|
||||
"""
|
||||
def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
|
||||
if 'lineno' in node._attributes:
|
||||
if not hasattr(node, 'lineno'):
|
||||
node.lineno = lineno
|
||||
else:
|
||||
lineno = node.lineno
|
||||
if 'end_lineno' in node._attributes:
|
||||
if not hasattr(node, 'end_lineno'):
|
||||
node.end_lineno = end_lineno
|
||||
else:
|
||||
end_lineno = node.end_lineno
|
||||
if 'col_offset' in node._attributes:
|
||||
if not hasattr(node, 'col_offset'):
|
||||
node.col_offset = col_offset
|
||||
else:
|
||||
col_offset = node.col_offset
|
||||
if 'end_col_offset' in node._attributes:
|
||||
if not hasattr(node, 'end_col_offset'):
|
||||
node.end_col_offset = end_col_offset
|
||||
else:
|
||||
end_col_offset = node.end_col_offset
|
||||
for child in iter_child_nodes(node):
|
||||
_fix(child, lineno, col_offset, end_lineno, end_col_offset)
|
||||
_fix(node, 1, 0, 1, 0)
|
||||
return node
|
||||
|
||||
|
||||
if _sys.version_info.major == 3 and _sys.version_info.minor >= 8:
|
||||
get_source_segment = _ast.get_source_segment
|
||||
else:
|
||||
# No end_lineno no end_col_offset info set for those version, so always
|
||||
# return None
|
||||
def get_source_segment(source, node, padded=False):
|
||||
return None
|
||||
|
||||
|
||||
def increment_lineno(node, n=1):
|
||||
"""
|
||||
Increment the line number and end line number of each node in the tree
|
||||
starting at *node* by *n*. This is useful to "move code" to a different
|
||||
location in a file.
|
||||
"""
|
||||
for child in walk(node):
|
||||
if 'lineno' in child._attributes:
|
||||
child.lineno = (getattr(child, 'lineno', 0) or 0) + n
|
||||
if 'end_lineno' in child._attributes:
|
||||
child.end_lineno = (getattr(child, 'end_lineno', 0) or 0) + n
|
||||
return node
|
||||
|
||||
# Code import from Lib/ast.py
|
||||
#
|
||||
# minor changes: getattr(x, y, ...) is None => getattr(x, y, 42) is None
|
||||
#
|
||||
def dump(
|
||||
node, annotate_fields=True, include_attributes=False,
|
||||
# *, # removed for compatibility with python2 :-/
|
||||
indent=None, show_empty=False,
|
||||
):
|
||||
"""
|
||||
Return a formatted dump of the tree in node. This is mainly useful for
|
||||
debugging purposes. If annotate_fields is true (by default),
|
||||
the returned string will show the names and the values for fields.
|
||||
If annotate_fields is false, the result string will be more compact by
|
||||
omitting unambiguous field names. Attributes such as line
|
||||
numbers and column offsets are not dumped by default. If this is wanted,
|
||||
include_attributes can be set to true. If indent is a non-negative
|
||||
integer or string, then the tree will be pretty-printed with that indent
|
||||
level. None (the default) selects the single line representation.
|
||||
If show_empty is False, then empty lists and fields that are None
|
||||
will be omitted from the output for better readability.
|
||||
"""
|
||||
def _format(node, level=0):
|
||||
if indent is not None:
|
||||
level += 1
|
||||
prefix = '\n' + indent * level
|
||||
sep = ',\n' + indent * level
|
||||
else:
|
||||
prefix = ''
|
||||
sep = ', '
|
||||
if isinstance(node, AST):
|
||||
cls = type(node)
|
||||
args = []
|
||||
args_buffer = []
|
||||
allsimple = True
|
||||
keywords = annotate_fields
|
||||
for name in node._fields:
|
||||
try:
|
||||
value = getattr(node, name)
|
||||
except AttributeError:
|
||||
keywords = True
|
||||
continue
|
||||
if value is None and getattr(cls, name, 42) is None:
|
||||
keywords = True
|
||||
continue
|
||||
if not show_empty:
|
||||
if value == []:
|
||||
if not keywords:
|
||||
args_buffer.append(repr(value))
|
||||
continue
|
||||
if not keywords:
|
||||
args.extend(args_buffer)
|
||||
args_buffer = []
|
||||
value, simple = _format(value, level)
|
||||
allsimple = allsimple and simple
|
||||
if keywords:
|
||||
args.append('%s=%s' % (name, value))
|
||||
else:
|
||||
args.append(value)
|
||||
if include_attributes and node._attributes:
|
||||
for name in node._attributes:
|
||||
try:
|
||||
value = getattr(node, name)
|
||||
except AttributeError:
|
||||
continue
|
||||
if value is None and getattr(cls, name, 42) is None:
|
||||
continue
|
||||
value, simple = _format(value, level)
|
||||
allsimple = allsimple and simple
|
||||
args.append('%s=%s' % (name, value))
|
||||
if allsimple and len(args) <= 3:
|
||||
return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
|
||||
return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
|
||||
elif isinstance(node, list):
|
||||
if not node:
|
||||
return '[]', True
|
||||
return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
|
||||
return repr(node), True
|
||||
|
||||
if not isinstance(node, AST):
|
||||
raise TypeError('expected AST, got %r' % node.__class__.__name__)
|
||||
if indent is not None and not isinstance(indent, str):
|
||||
indent = ' ' * indent
|
||||
return _format(node)[0]
|
||||
@@ -0,0 +1,291 @@
|
||||
# Copyright (c) 2025 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.
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
|
||||
from paddle.base import core
|
||||
|
||||
|
||||
def _print_tensor_in_gpu(tensor):
|
||||
"""
|
||||
Print a GPU tensor's dtype, shape, and all data values directly from
|
||||
the device using a single-thread CUDA kernel (device-side printf).
|
||||
|
||||
This function is **CUDA Graph safe**: no host/device memory transfer
|
||||
is performed (shape is passed via kernel-argument registers), so it
|
||||
can be called inside a CUDA Graph capture region.
|
||||
|
||||
Args:
|
||||
tensor (paddle.Tensor): A GPU DenseTensor to print. Must already
|
||||
reside on a CUDA device (call ``tensor.cuda()`` first if needed).
|
||||
|
||||
Raises:
|
||||
ValueError: If PaddlePaddle is not compiled with CUDA support.
|
||||
InvalidArgument: If the tensor is not a DenseTensor or not on GPU.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +REQUIRES(env:GPU)
|
||||
>>> import paddle
|
||||
>>> paddle.device.set_device('gpu')
|
||||
>>> x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
|
||||
>>> paddle.utils.gpu_utils._print_tensor_in_gpu(x)
|
||||
|
||||
"""
|
||||
if not core.is_compiled_with_cuda():
|
||||
raise ValueError(
|
||||
"paddle.utils._print_tensor_in_gpu is not supported in "
|
||||
"CPU-only PaddlePaddle. Please reinstall PaddlePaddle with GPU "
|
||||
"support to call this API."
|
||||
)
|
||||
core.eager._print_tensor_in_gpu(tensor)
|
||||
|
||||
|
||||
# Mapping from the dtype name printed by DebugPrintGPUTensor to a numpy dtype.
|
||||
_DTYPE_STR_TO_NUMPY = {
|
||||
'FLOAT32': np.float32,
|
||||
'FLOAT64': np.float64,
|
||||
'FLOAT16': np.float16,
|
||||
'BFLOAT16': np.float32, # NumPy has no bfloat16; promote to float32
|
||||
'INT32': np.int32,
|
||||
'INT64': np.int64,
|
||||
'INT16': np.int16,
|
||||
'INT8': np.int8,
|
||||
'UINT8': np.uint8,
|
||||
'BOOL': np.bool_,
|
||||
}
|
||||
|
||||
# Mapping from the dtype name to the paddle dtype string.
|
||||
_DTYPE_STR_TO_PADDLE = {
|
||||
'FLOAT32': 'float32',
|
||||
'FLOAT64': 'float64',
|
||||
'FLOAT16': 'float16',
|
||||
'BFLOAT16': 'bfloat16',
|
||||
'INT32': 'int32',
|
||||
'INT64': 'int64',
|
||||
'INT16': 'int16',
|
||||
'INT8': 'int8',
|
||||
'UINT8': 'uint8',
|
||||
'BOOL': 'bool',
|
||||
}
|
||||
|
||||
|
||||
def _parse_tensor_from_gpu_print(text):
|
||||
"""
|
||||
Reconstruct a ``paddle.Tensor`` from the text output produced by
|
||||
:func:`paddle.utils.gpu_utils._print_tensor_in_gpu`.
|
||||
|
||||
The expected input format is the output written to stdout by the
|
||||
``DebugPrintGPUTensor`` CUDA kernel::
|
||||
|
||||
[TensorDebug] dtype : FLOAT32
|
||||
[TensorDebug] shape : [2, 3]
|
||||
[TensorDebug] numel : 6
|
||||
[TensorDebug] data :
|
||||
[[1, 2, 3],
|
||||
[4, 5, 6]]
|
||||
|
||||
Special cases handled:
|
||||
|
||||
* **Scalar (0-D tensor)** – the data line is ``[TensorDebug] data : <value>``
|
||||
(no newline before the value).
|
||||
* **Empty tensor (numel == 0)** – the data line ends with ``[]``.
|
||||
|
||||
Args:
|
||||
text (str): The captured stdout string from a call to
|
||||
``paddle.utils.gpu_utils._print_tensor_in_gpu``.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: A GPU tensor with the dtype and values recovered from
|
||||
*text*. Call ``.cpu()`` on the result if you need a CPU tensor.
|
||||
|
||||
Raises:
|
||||
ValueError: If *text* cannot be parsed (missing header lines, unknown
|
||||
dtype, mismatched numel, …).
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> import os, sys, tempfile
|
||||
>>> # Capture the C-level stdout written by the CUDA printf kernel.
|
||||
>>> def capture(tensor):
|
||||
... with tempfile.NamedTemporaryFile(mode='w+', suffix='.txt', delete=False) as f:
|
||||
... path = f.name
|
||||
... sys.stdout.flush()
|
||||
... old = os.dup(1)
|
||||
... fd = os.open(path, os.O_WRONLY | os.O_TRUNC)
|
||||
... os.dup2(fd, 1)
|
||||
... os.close(fd)
|
||||
... paddle.utils.gpu_utils._print_tensor_in_gpu(tensor)
|
||||
... paddle.device.synchronize()
|
||||
... sys.stdout.flush()
|
||||
... os.dup2(old, 1)
|
||||
... os.close(old)
|
||||
... text = open(path).read()
|
||||
... os.remove(path)
|
||||
... return text
|
||||
>>> x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
|
||||
>>> text = capture(x)
|
||||
>>> y = paddle.utils.gpu_utils._parse_tensor_from_gpu_print(text)
|
||||
>>> print(y)
|
||||
"""
|
||||
import paddle # local import to avoid circular imports at module load time
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 1. Extract header fields #
|
||||
# ------------------------------------------------------------------ #
|
||||
dtype_m = re.search(r'\[TensorDebug\] dtype\s*:\s*(\w+)', text)
|
||||
shape_m = re.search(r'\[TensorDebug\] shape\s*:\s*\[([^\]]*)\]', text)
|
||||
numel_m = re.search(r'\[TensorDebug\] numel\s*:\s*(\d+)', text)
|
||||
data_m = re.search(r'\[TensorDebug\] data\s*:(.*)', text, re.DOTALL)
|
||||
|
||||
if not dtype_m:
|
||||
raise ValueError(
|
||||
"_parse_tensor_from_gpu_print: could not find "
|
||||
"'[TensorDebug] dtype' line in the provided text."
|
||||
)
|
||||
if not shape_m:
|
||||
raise ValueError(
|
||||
"_parse_tensor_from_gpu_print: could not find "
|
||||
"'[TensorDebug] shape' line in the provided text."
|
||||
)
|
||||
if not numel_m:
|
||||
raise ValueError(
|
||||
"_parse_tensor_from_gpu_print: could not find "
|
||||
"'[TensorDebug] numel' line in the provided text."
|
||||
)
|
||||
if not data_m:
|
||||
raise ValueError(
|
||||
"_parse_tensor_from_gpu_print: could not find "
|
||||
"'[TensorDebug] data' line in the provided text."
|
||||
)
|
||||
|
||||
dtype_str = dtype_m.group(1).strip().upper()
|
||||
shape_raw = shape_m.group(1).strip()
|
||||
numel = int(numel_m.group(1).strip())
|
||||
data_raw = data_m.group(1).strip()
|
||||
|
||||
if dtype_str not in _DTYPE_STR_TO_NUMPY:
|
||||
raise ValueError(
|
||||
f"_parse_tensor_from_gpu_print: unknown dtype '{dtype_str}'. "
|
||||
f"Supported: {list(_DTYPE_STR_TO_NUMPY.keys())}"
|
||||
)
|
||||
|
||||
np_dtype = _DTYPE_STR_TO_NUMPY[dtype_str]
|
||||
paddle_dtype = _DTYPE_STR_TO_PADDLE[dtype_str]
|
||||
|
||||
# Parse shape: "" means scalar (0-D), otherwise comma-separated ints.
|
||||
if shape_raw == '':
|
||||
shape = []
|
||||
else:
|
||||
shape = [int(s.strip()) for s in shape_raw.split(',') if s.strip()]
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 2. Parse data section #
|
||||
# ------------------------------------------------------------------ #
|
||||
gpu_place = paddle.CUDAPlace(0)
|
||||
|
||||
if numel == 0:
|
||||
# Empty tensor – no data values to parse.
|
||||
arr = np.empty(shape, dtype=np_dtype)
|
||||
t = paddle.to_tensor(arr, dtype=paddle_dtype, place=gpu_place)
|
||||
return t
|
||||
|
||||
if len(shape) == 0:
|
||||
# Scalar (0-D tensor): data_raw is the single value directly.
|
||||
value_str = data_raw
|
||||
arr = np.array(_parse_value(value_str, dtype_str), dtype=np_dtype)
|
||||
t = paddle.to_tensor(arr, dtype=paddle_dtype, place=gpu_place)
|
||||
return t
|
||||
|
||||
# General N-D case: strip all brackets, whitespace, and split by commas.
|
||||
# The nested bracket notation produced by PrintTensorKernel uses standard
|
||||
# Python list syntax, so ast.literal_eval can reconstruct the nested list
|
||||
# directly – but only for numeric dtypes. For 'BOOL', PrintValue emits
|
||||
# "True"/"False" which are valid Python literals too.
|
||||
flat_values = _extract_flat_values(data_raw, dtype_str, numel)
|
||||
|
||||
arr = np.array(flat_values, dtype=np_dtype).reshape(shape)
|
||||
t = paddle.to_tensor(arr, dtype=paddle_dtype, place=gpu_place)
|
||||
return t
|
||||
|
||||
|
||||
def _parse_value(value_str, dtype_str):
|
||||
"""Parse a single scalar value string from the debug output."""
|
||||
value_str = value_str.strip()
|
||||
if dtype_str == 'BOOL':
|
||||
if value_str == 'True':
|
||||
return True
|
||||
elif value_str == 'False':
|
||||
return False
|
||||
else:
|
||||
raise ValueError(
|
||||
f"_parse_tensor_from_gpu_print: cannot parse bool value "
|
||||
f"'{value_str}'."
|
||||
)
|
||||
return float(value_str)
|
||||
|
||||
|
||||
def _extract_flat_values(data_raw, dtype_str, numel):
|
||||
"""
|
||||
Extract a flat list of Python scalars from the nested-bracket string
|
||||
produced by PrintTensorKernel.
|
||||
|
||||
Strategy: remove all bracket characters and split the remaining string
|
||||
on commas / whitespace to get individual token strings, then convert
|
||||
each token to the appropriate scalar type.
|
||||
"""
|
||||
# Remove all '[' and ']' characters.
|
||||
flat_str = data_raw.replace('[', '').replace(']', '')
|
||||
|
||||
# Split on commas (values are separated by ", " or ",\n indent").
|
||||
tokens = re.split(r'[,\s]+', flat_str)
|
||||
tokens = [t.strip() for t in tokens if t.strip()]
|
||||
|
||||
if len(tokens) != numel:
|
||||
raise ValueError(
|
||||
f"_parse_tensor_from_gpu_print: expected {numel} values but "
|
||||
f"found {len(tokens)} tokens in the data section. "
|
||||
f"Parsed tokens: {tokens[:20]}{'...' if len(tokens) > 20 else ''}"
|
||||
)
|
||||
|
||||
if dtype_str == 'BOOL':
|
||||
result = []
|
||||
for tok in tokens:
|
||||
if tok == 'True':
|
||||
result.append(True)
|
||||
elif tok == 'False':
|
||||
result.append(False)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"_parse_tensor_from_gpu_print: cannot parse bool token "
|
||||
f"'{tok}'."
|
||||
)
|
||||
return result
|
||||
|
||||
# Integer dtypes must preserve exact values.
|
||||
# GPU printf may emit scientific notation (e.g. "1e+06"), so use
|
||||
# Decimal for lossless string-to-integer conversion.
|
||||
from decimal import Decimal
|
||||
|
||||
_INT_DTYPES = {'INT8', 'INT16', 'INT32', 'INT64', 'UINT8'}
|
||||
if dtype_str in _INT_DTYPES:
|
||||
return [int(Decimal(t)) for t in tokens]
|
||||
|
||||
return [float(t) for t in tokens]
|
||||
@@ -0,0 +1,235 @@
|
||||
# Copyright (c) 2016 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.
|
||||
|
||||
from io import StringIO
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def resize_image(img, target_size):
|
||||
"""
|
||||
Resize an image so that the shorter edge has length target_size.
|
||||
img: the input image to be resized.
|
||||
target_size: the target resized image size.
|
||||
"""
|
||||
percent = target_size / float(min(img.size[0], img.size[1]))
|
||||
resized_size = (
|
||||
int(round(img.size[0] * percent)),
|
||||
int(round(img.size[1] * percent)),
|
||||
)
|
||||
img = img.resize(resized_size, Image.ANTIALIAS)
|
||||
return img
|
||||
|
||||
|
||||
def flip(im):
|
||||
"""
|
||||
Return the flipped image.
|
||||
Flip an image along the horizontal direction.
|
||||
im: input image, (K x H x W) ndarrays
|
||||
"""
|
||||
if len(im.shape) == 3:
|
||||
return im[:, :, ::-1]
|
||||
else:
|
||||
return im[:, ::-1]
|
||||
|
||||
|
||||
def crop_img(im, inner_size, color=True, test=True):
|
||||
"""
|
||||
Return cropped image.
|
||||
The size of the cropped image is inner_size * inner_size.
|
||||
im: (K x H x W) ndarrays
|
||||
inner_size: the cropped image size.
|
||||
color: whether it is color image.
|
||||
test: whether in test mode.
|
||||
If False, does random cropping and flipping.
|
||||
If True, crop the center of images.
|
||||
"""
|
||||
if color:
|
||||
height, width = (
|
||||
max(inner_size, im.shape[1]),
|
||||
max(inner_size, im.shape[2]),
|
||||
)
|
||||
padded_im = np.zeros((3, height, width))
|
||||
startY = (height - im.shape[1]) / 2
|
||||
startX = (width - im.shape[2]) / 2
|
||||
endY, endX = startY + im.shape[1], startX + im.shape[2]
|
||||
padded_im[:, startY:endY, startX:endX] = im
|
||||
else:
|
||||
im = im.astype('float32')
|
||||
height, width = (
|
||||
max(inner_size, im.shape[0]),
|
||||
max(inner_size, im.shape[1]),
|
||||
)
|
||||
padded_im = np.zeros((height, width))
|
||||
startY = (height - im.shape[0]) / 2
|
||||
startX = (width - im.shape[1]) / 2
|
||||
endY, endX = startY + im.shape[0], startX + im.shape[1]
|
||||
padded_im[startY:endY, startX:endX] = im
|
||||
if test:
|
||||
startY = (height - inner_size) / 2
|
||||
startX = (width - inner_size) / 2
|
||||
else:
|
||||
startY = np.random.randint(0, height - inner_size + 1)
|
||||
startX = np.random.randint(0, width - inner_size + 1)
|
||||
endY, endX = startY + inner_size, startX + inner_size
|
||||
if color:
|
||||
pic = padded_im[:, startY:endY, startX:endX]
|
||||
else:
|
||||
pic = padded_im[startY:endY, startX:endX]
|
||||
if (not test) and (np.random.randint(2) == 0):
|
||||
pic = flip(pic)
|
||||
return pic
|
||||
|
||||
|
||||
def decode_jpeg(jpeg_string):
|
||||
np_array = np.array(Image.open(StringIO(jpeg_string)))
|
||||
if len(np_array.shape) == 3:
|
||||
np_array = np.transpose(np_array, (2, 0, 1))
|
||||
return np_array
|
||||
|
||||
|
||||
def preprocess_img(im, img_mean, crop_size, is_train, color=True):
|
||||
"""
|
||||
Does data augmentation for images.
|
||||
If is_train is false, cropping the center region from the image.
|
||||
If is_train is true, randomly crop a region from the image,
|
||||
and random does flipping.
|
||||
im: (K x H x W) ndarrays
|
||||
"""
|
||||
im = im.astype('float32')
|
||||
test = not is_train
|
||||
pic = crop_img(im, crop_size, color, test)
|
||||
pic -= img_mean
|
||||
return pic.flatten()
|
||||
|
||||
|
||||
def load_meta(meta_path, mean_img_size, crop_size, color=True):
|
||||
"""
|
||||
Return the loaded meta file.
|
||||
Load the meta image, which is the mean of the images in the dataset.
|
||||
The mean image is subtracted from every input image so that the expected mean
|
||||
of each input image is zero.
|
||||
"""
|
||||
mean = np.load(meta_path)['data_mean']
|
||||
border = (mean_img_size - crop_size) / 2
|
||||
if color:
|
||||
assert mean_img_size * mean_img_size * 3 == mean.shape[0]
|
||||
mean = mean.reshape(3, mean_img_size, mean_img_size)
|
||||
mean = mean[
|
||||
:, border : border + crop_size, border : border + crop_size
|
||||
].astype('float32')
|
||||
else:
|
||||
assert mean_img_size * mean_img_size == mean.shape[0]
|
||||
mean = mean.reshape(mean_img_size, mean_img_size)
|
||||
mean = mean[
|
||||
border : border + crop_size, border : border + crop_size
|
||||
].astype('float32')
|
||||
return mean
|
||||
|
||||
|
||||
def load_image(img_path, is_color=True):
|
||||
"""
|
||||
Load image and return.
|
||||
img_path: image path.
|
||||
is_color: is color image or not.
|
||||
"""
|
||||
img = Image.open(img_path)
|
||||
img.load()
|
||||
return img
|
||||
|
||||
|
||||
def oversample(img, crop_dims):
|
||||
"""
|
||||
image : iterable of (H x W x K) ndarrays
|
||||
crop_dims: (height, width) tuple for the crops.
|
||||
Returned data contains ten crops of input image, namely,
|
||||
four corner patches and the center patch as well as their
|
||||
horizontal reflections.
|
||||
"""
|
||||
# Dimensions and center.
|
||||
im_shape = np.array(img[0].shape)
|
||||
crop_dims = np.array(crop_dims)
|
||||
im_center = im_shape[:2] / 2.0
|
||||
|
||||
# Make crop coordinates
|
||||
h_indices = (0, im_shape[0] - crop_dims[0])
|
||||
w_indices = (0, im_shape[1] - crop_dims[1])
|
||||
crops_ix = np.empty((5, 4), dtype=int)
|
||||
curr = 0
|
||||
for i in h_indices:
|
||||
for j in w_indices:
|
||||
crops_ix[curr] = (i, j, i + crop_dims[0], j + crop_dims[1])
|
||||
curr += 1
|
||||
crops_ix[4] = np.tile(im_center, (1, 2)) + np.concatenate(
|
||||
[-crop_dims / 2.0, crop_dims / 2.0]
|
||||
)
|
||||
crops_ix = np.tile(crops_ix, (2, 1))
|
||||
|
||||
# Extract crops
|
||||
crops = np.empty(
|
||||
(10 * len(img), crop_dims[0], crop_dims[1], im_shape[-1]),
|
||||
dtype=np.float32,
|
||||
)
|
||||
ix = 0
|
||||
for im in img:
|
||||
for crop in crops_ix:
|
||||
crops[ix] = im[crop[0] : crop[2], crop[1] : crop[3], :]
|
||||
ix += 1
|
||||
crops[ix - 5 : ix] = crops[ix - 5 : ix, :, ::-1, :] # flip for mirrors
|
||||
return crops
|
||||
|
||||
|
||||
class ImageTransformer:
|
||||
def __init__(
|
||||
self, transpose=None, channel_swap=None, mean=None, is_color=True
|
||||
):
|
||||
self.is_color = is_color
|
||||
self.set_transpose(transpose)
|
||||
self.set_channel_swap(channel_swap)
|
||||
self.set_mean(mean)
|
||||
|
||||
def set_transpose(self, order):
|
||||
if order is not None:
|
||||
if self.is_color:
|
||||
assert 3 == len(order)
|
||||
self.transpose = order
|
||||
|
||||
def set_channel_swap(self, order):
|
||||
if order is not None:
|
||||
if self.is_color:
|
||||
assert 3 == len(order)
|
||||
self.channel_swap = order
|
||||
|
||||
def set_mean(self, mean):
|
||||
if mean is not None:
|
||||
# mean value, may be one value per channel
|
||||
if mean.ndim == 1:
|
||||
mean = mean[:, np.newaxis, np.newaxis]
|
||||
else:
|
||||
# elementwise mean
|
||||
if self.is_color:
|
||||
assert len(mean.shape) == 3
|
||||
self.mean = mean
|
||||
|
||||
def transformer(self, data):
|
||||
if self.transpose is not None:
|
||||
data = data.transpose(self.transpose)
|
||||
if self.channel_swap is not None:
|
||||
data = data[self.channel_swap, :, :]
|
||||
if self.mean is not None:
|
||||
data -= self.mean
|
||||
return data
|
||||
@@ -0,0 +1,94 @@
|
||||
# Copyright (c) 2022 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.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
import paddle # noqa: F401
|
||||
from paddle.base.framework import stride_ops
|
||||
from paddle.base.wrapped_decorator import wrap_decorator
|
||||
from paddle.framework import in_dynamic_mode, in_pir_mode
|
||||
|
||||
_InputT = ParamSpec("_InputT")
|
||||
_RetT = TypeVar("_RetT")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from paddle.pir import Value
|
||||
|
||||
|
||||
def check_view_value(value: Value) -> bool:
|
||||
# check if the value is a view tensor
|
||||
if value.get_defining_op().name() in stride_ops:
|
||||
# TODO(ooooo-create): The `x = stride_op(x)` shouldn't return True.
|
||||
return True
|
||||
all_used_ops = value.all_used_ops()
|
||||
if len(all_used_ops) == 0:
|
||||
return False
|
||||
for op in all_used_ops:
|
||||
if op.name() in stride_ops and op.operand_source(0).is_same(value):
|
||||
# TODO(ooooo-create): The `y = stride_op(x).clone()` and `y = stride_op(x) + op` should also return False.
|
||||
# Now is True.
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# NOTE(pangyoki): The Inplace APIs with underline(`_`) is only valid for the method of calling `_C_ops`
|
||||
# in dygraph mode. If static graph mode is used, the inplace mechanism will not be used, and the static method
|
||||
# of the original API will be called.
|
||||
# NOTE(GGBond8488): Simply run the original version of the API under the static graph mode has a low
|
||||
# probability that the result is inconsistent with the dynamic graph.
|
||||
def _inplace_apis_in_dygraph_only_(
|
||||
func: Callable[_InputT, _RetT],
|
||||
) -> Callable[_InputT, _RetT]:
|
||||
def __impl__(*args: _InputT.args, **kwargs: _InputT.kwargs) -> _RetT:
|
||||
if not in_dynamic_mode():
|
||||
origin_api_name = func.__name__[:-1]
|
||||
warnings.warn(
|
||||
f"In static graph mode, {func.__name__}() is the same as {origin_api_name}() and does not perform inplace operation."
|
||||
)
|
||||
from ..base.dygraph.base import in_to_static_mode
|
||||
|
||||
if in_to_static_mode():
|
||||
stride_in_no_check_dy2st_diff = os.environ.get(
|
||||
"stride_in_no_check_dy2st_diff", "0"
|
||||
)
|
||||
if in_pir_mode():
|
||||
if (
|
||||
stride_in_no_check_dy2st_diff != '1'
|
||||
and check_view_value(args[0])
|
||||
):
|
||||
raise ValueError(
|
||||
f'Sorry about what\'s happened. In to_static mode, {func.__name__}\'s output variable is a viewed Tensor in dygraph. This will result in inconsistent calculation behavior between dynamic and static graphs. You must find the location of the strided API be called, and call paddle.assign() before inplace input.'
|
||||
)
|
||||
else:
|
||||
for arg in args:
|
||||
if hasattr(arg, "is_view_var") and arg.is_view_var:
|
||||
raise ValueError(
|
||||
f'Sorry about what\'s happened. In to_static mode, {func.__name__}\'s output variable {arg.name} is a viewed Tensor in dygraph. This will result in inconsistent calculation behavior between dynamic and static graphs. You must find the location of the strided API be called, and call {arg.name} = paddle.assign({arg.name}).'
|
||||
)
|
||||
|
||||
origin_func = f"{func.__module__}.{origin_api_name}"
|
||||
return eval(origin_func)(*args, **kwargs)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return __impl__
|
||||
|
||||
|
||||
inplace_apis_in_dygraph_only = wrap_decorator(_inplace_apis_in_dygraph_only_)
|
||||
@@ -0,0 +1,302 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def _simple_network():
|
||||
"""
|
||||
Define a simple network composed by a single linear layer.
|
||||
"""
|
||||
input = paddle.static.data(
|
||||
name="input", shape=[None, 2, 2], dtype="float32"
|
||||
)
|
||||
weight = paddle.create_parameter(
|
||||
shape=[2, 3],
|
||||
dtype="float32",
|
||||
attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Constant(0.1)),
|
||||
)
|
||||
bias = paddle.create_parameter(shape=[3], dtype="float32")
|
||||
linear_out = paddle.nn.functional.linear(x=input, weight=weight, bias=bias)
|
||||
out = paddle.tensor.sum(linear_out)
|
||||
return input, out, weight
|
||||
|
||||
|
||||
def _prepare_data():
|
||||
"""
|
||||
Prepare feeding data for simple network. The shape is [1, 2, 2].
|
||||
|
||||
"""
|
||||
# Prepare the feeding data.
|
||||
np_input_single = np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32)
|
||||
return np_input_single.reshape(1, 2, 2)
|
||||
|
||||
|
||||
def _is_cuda_available():
|
||||
"""
|
||||
Check whether CUDA is available.
|
||||
"""
|
||||
try:
|
||||
assert len(paddle.static.cuda_places()) > 0
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
"You are using GPU version PaddlePaddle, but there is no GPU "
|
||||
"detected on your machine. Maybe CUDA devices is not set properly."
|
||||
f"\n Original Error is {e}"
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def _is_xpu_available():
|
||||
"""
|
||||
Check whether XPU is available.
|
||||
"""
|
||||
try:
|
||||
assert len(paddle.static.xpu_places()) > 0
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
"You are using XPU version PaddlePaddle, but there is no XPU "
|
||||
"detected on your machine. Maybe XPU devices is not set properly."
|
||||
f"\n Original Error is {e}"
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def _run_dygraph_single(use_cuda, use_xpu, use_custom, custom_device_name):
|
||||
"""
|
||||
Testing the simple network in dygraph mode using one CPU/GPU/XPU.
|
||||
|
||||
Args:
|
||||
use_cuda (bool): Whether running with CUDA.
|
||||
use_xpu (bool): Whether running with XPU.
|
||||
"""
|
||||
paddle.disable_static()
|
||||
if use_cuda:
|
||||
paddle.set_device('gpu')
|
||||
elif use_xpu:
|
||||
paddle.set_device('xpu')
|
||||
elif use_custom:
|
||||
paddle.set_device(custom_device_name)
|
||||
else:
|
||||
paddle.set_device('cpu')
|
||||
weight_attr = paddle.ParamAttr(
|
||||
name="weight", initializer=paddle.nn.initializer.Constant(value=0.5)
|
||||
)
|
||||
bias_attr = paddle.ParamAttr(
|
||||
name="bias", initializer=paddle.nn.initializer.Constant(value=1.0)
|
||||
)
|
||||
linear = paddle.nn.Linear(
|
||||
2, 4, weight_attr=weight_attr, bias_attr=bias_attr
|
||||
)
|
||||
input_np = _prepare_data()
|
||||
input_tensor = paddle.to_tensor(input_np)
|
||||
linear_out = linear(input_tensor)
|
||||
out = paddle.tensor.sum(linear_out)
|
||||
out.backward()
|
||||
opt = paddle.optimizer.Adam(
|
||||
learning_rate=0.001, parameters=linear.parameters()
|
||||
)
|
||||
opt.step()
|
||||
|
||||
|
||||
def _run_static_single(use_cuda, use_xpu, use_custom, custom_device_name):
|
||||
"""
|
||||
Testing the simple network with executor running directly, using one CPU/GPU/XPU.
|
||||
|
||||
Args:
|
||||
use_cuda (bool): Whether running with CUDA.
|
||||
use_xpu (bool): Whether running with XPU.
|
||||
"""
|
||||
paddle.enable_static()
|
||||
with paddle.static.scope_guard(paddle.static.Scope()):
|
||||
train_prog = paddle.static.Program()
|
||||
startup_prog = paddle.static.Program()
|
||||
startup_prog.random_seed = 1
|
||||
with paddle.static.program_guard(train_prog, startup_prog):
|
||||
input, out, weight = _simple_network()
|
||||
param_grads = paddle.static.append_backward(
|
||||
out, parameter_list=[weight]
|
||||
)[0]
|
||||
|
||||
if use_cuda:
|
||||
place = paddle.CUDAPlace(0)
|
||||
elif use_xpu:
|
||||
place = paddle.XPUPlace(0)
|
||||
elif use_custom:
|
||||
place = paddle.CustomPlace(custom_device_name, 0)
|
||||
else:
|
||||
place = paddle.CPUPlace()
|
||||
|
||||
exe = paddle.static.Executor(place)
|
||||
exe.run(startup_prog)
|
||||
exe.run(
|
||||
train_prog,
|
||||
feed={input.name: _prepare_data()},
|
||||
fetch_list=[out, param_grads[1]],
|
||||
)
|
||||
paddle.disable_static()
|
||||
|
||||
|
||||
def train_for_run_parallel():
|
||||
"""
|
||||
train script for parallel training check
|
||||
"""
|
||||
|
||||
# to avoid cyclic import
|
||||
class LinearNet(paddle.nn.Layer):
|
||||
"""
|
||||
simple fc network for parallel training check
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._linear1 = paddle.nn.Linear(10, 10)
|
||||
self._linear2 = paddle.nn.Linear(10, 1)
|
||||
|
||||
def forward(self, x):
|
||||
"""
|
||||
forward
|
||||
"""
|
||||
return self._linear2(self._linear1(x))
|
||||
|
||||
paddle.distributed.init_parallel_env()
|
||||
|
||||
layer = LinearNet()
|
||||
dp_layer = paddle.DataParallel(layer)
|
||||
|
||||
loss_fn = paddle.nn.MSELoss()
|
||||
adam = paddle.optimizer.Adam(
|
||||
learning_rate=0.001, parameters=dp_layer.parameters()
|
||||
)
|
||||
|
||||
inputs = paddle.randn([10, 10], 'float32')
|
||||
outputs = dp_layer(inputs)
|
||||
labels = paddle.randn([10, 1], 'float32')
|
||||
loss = loss_fn(outputs, labels)
|
||||
|
||||
loss.backward()
|
||||
adam.step()
|
||||
adam.clear_grad()
|
||||
|
||||
|
||||
def _run_parallel(device_list):
|
||||
"""
|
||||
Testing the simple network in data parallel mode, using multiple CPU/GPU.
|
||||
|
||||
Args:
|
||||
use_cuda (bool): Whether running with CUDA.
|
||||
use_xpu (bool): Whether running with XPU.
|
||||
device_list (int): The specified devices.
|
||||
"""
|
||||
paddle.distributed.spawn(train_for_run_parallel, nprocs=len(device_list))
|
||||
|
||||
|
||||
def run_check() -> None:
|
||||
"""
|
||||
Check whether PaddlePaddle is installed correctly and running successfully
|
||||
on your system.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
|
||||
>>> # doctest: +SKIP('the output will change in different run')
|
||||
>>> paddle.utils.run_check()
|
||||
Running verify PaddlePaddle program ...
|
||||
I0818 15:35:08.335391 30540 program_interpreter.cc:173] New Executor is Running.
|
||||
I0818 15:35:08.398319 30540 interpreter_util.cc:529] Standalone Executor is Used.
|
||||
PaddlePaddle works well on 1 CPU.
|
||||
PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.
|
||||
"""
|
||||
|
||||
print("Running verify PaddlePaddle program ... ")
|
||||
|
||||
use_cuda = False
|
||||
use_xpu = False
|
||||
use_custom = False
|
||||
custom_device_name = None
|
||||
|
||||
if paddle.is_compiled_with_cuda():
|
||||
use_cuda = _is_cuda_available()
|
||||
elif paddle.is_compiled_with_xpu():
|
||||
use_xpu = _is_xpu_available()
|
||||
elif len(paddle.framework.core.get_all_custom_device_type()) > 0:
|
||||
use_custom = True
|
||||
if len(paddle.framework.core.get_all_custom_device_type()) > 1:
|
||||
logging.warning(
|
||||
f"More than one kind of custom devices detected, but run check would only be executed on {paddle.framework.core.get_all_custom_device_type()[0]}."
|
||||
)
|
||||
|
||||
if use_cuda:
|
||||
device_str = "GPU"
|
||||
device_list = paddle.static.cuda_places()
|
||||
elif use_xpu:
|
||||
device_str = "XPU"
|
||||
device_list = paddle.static.xpu_places()
|
||||
elif use_custom:
|
||||
device_str = paddle.framework.core.get_all_custom_device_type()[0]
|
||||
custom_device_name = device_str
|
||||
device_list = list(
|
||||
range(
|
||||
paddle.framework.core.get_custom_device_count(
|
||||
custom_device_name
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
device_str = "CPU"
|
||||
device_list = paddle.static.cpu_places(device_count=1)
|
||||
device_count = len(device_list)
|
||||
|
||||
_run_static_single(use_cuda, use_xpu, use_custom, custom_device_name)
|
||||
_run_dygraph_single(use_cuda, use_xpu, use_custom, custom_device_name)
|
||||
print(f"PaddlePaddle works well on 1 {device_str}.")
|
||||
|
||||
try:
|
||||
if len(device_list) > 1:
|
||||
if use_custom:
|
||||
import os
|
||||
|
||||
os.environ['PADDLE_DISTRI_BACKEND'] = "xccl"
|
||||
_run_parallel(device_list)
|
||||
print(f"PaddlePaddle works well on {device_count} {device_str}s.")
|
||||
print(
|
||||
"PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now."
|
||||
)
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
f"PaddlePaddle meets some problem with {device_count} {device_str}s. This may be caused by:"
|
||||
"\n 1. There is not enough GPUs visible on your system"
|
||||
"\n 2. Some GPUs are occupied by other process now"
|
||||
"\n 3. NVIDIA-NCCL2 is not installed correctly on your system. Please follow instruction on https://github.com/NVIDIA/nccl-tests "
|
||||
"\n to test your NCCL, or reinstall it following https://docs.nvidia.com/deeplearning/sdk/nccl-install-guide/index.html"
|
||||
)
|
||||
|
||||
logging.warning(f"\n Original Error is: {e}")
|
||||
print(
|
||||
f"PaddlePaddle is installed successfully ONLY for single {device_str}! "
|
||||
"Let's start deep learning with PaddlePaddle now."
|
||||
)
|
||||
raise e
|
||||
@@ -0,0 +1,607 @@
|
||||
# Copyright (c) 2018 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import typing
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable, Sequence
|
||||
from typing import Any, TypeGuard, TypeVar
|
||||
from uuid import uuid4
|
||||
from weakref import WeakKeyDictionary
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
from paddle.pir.core import convert_nptype_to_datatype
|
||||
|
||||
from ..base.data_feeder import check_dtype, convert_dtype
|
||||
from ..base.framework import (
|
||||
Block,
|
||||
Variable,
|
||||
in_dygraph_mode,
|
||||
)
|
||||
from ..pir import Value
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from paddle._typing import NestedStructure, ShapeLike
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_U = TypeVar("_U")
|
||||
|
||||
|
||||
class NotSupportedTensorArgumentError(TypeError):
|
||||
def __init__(self, msg, name: str):
|
||||
super().__init__(msg)
|
||||
self.name = name
|
||||
|
||||
|
||||
def convert_to_list(value, n, name, dtype=int):
|
||||
"""
|
||||
Converts a single numerical type or iterable of numerical
|
||||
types into a numerical type list.
|
||||
|
||||
Arguments:
|
||||
value: The value to validate and convert. Could an int, or any iterable
|
||||
of ints.
|
||||
n: The size of the list to be returned.
|
||||
name: The name of the argument being validated, e.g. "stride" or
|
||||
"filter_size". This is only used to format error messages.
|
||||
dtype: the numerical type of the element of the list to be returned.
|
||||
|
||||
Returns:
|
||||
A list of n dtypes.
|
||||
|
||||
Raises:
|
||||
ValueError: If something else than an int/long or iterable thereof was
|
||||
passed.
|
||||
"""
|
||||
if isinstance(value, dtype):
|
||||
return [value] * n
|
||||
else:
|
||||
if isinstance(value, (Variable, paddle.pir.Value)):
|
||||
raise NotSupportedTensorArgumentError(
|
||||
f"`{name}` required numerical type with `{dtype}`, but received Tensor.",
|
||||
name,
|
||||
)
|
||||
try:
|
||||
value_list = list(value)
|
||||
except TypeError:
|
||||
raise ValueError(
|
||||
f"The {name}'s type must be list or tuple. Received: {value}"
|
||||
)
|
||||
if len(value_list) != n:
|
||||
raise ValueError(
|
||||
f"The {name}'s length must be {n}. Received: {value}"
|
||||
)
|
||||
for single_value in value_list:
|
||||
if isinstance(single_value, (Variable, paddle.pir.Value)):
|
||||
raise NotSupportedTensorArgumentError(
|
||||
f"`{name}` required numerical type with `{dtype}`, but received Tensor.",
|
||||
name,
|
||||
)
|
||||
try:
|
||||
dtype(single_value)
|
||||
except (ValueError, TypeError):
|
||||
raise ValueError(
|
||||
f"The {name}'s type must be a list or tuple of {n} {dtype}. "
|
||||
+ f"Received: {value} including element {single_value} of type {type(single_value)}"
|
||||
)
|
||||
return value_list
|
||||
|
||||
|
||||
def is_sequence(seq: Any) -> TypeGuard[typing.Sequence[Any] | dict[str, Any]]:
|
||||
"""
|
||||
Whether `seq` is an entry or nested structure
|
||||
"""
|
||||
if isinstance(seq, dict):
|
||||
return True
|
||||
return isinstance(seq, Sequence) and not isinstance(seq, str)
|
||||
|
||||
|
||||
class UniqueIdMap(WeakKeyDictionary):
|
||||
def __init__(self):
|
||||
super().__init__(self)
|
||||
self.data = defaultdict(uuid4)
|
||||
|
||||
|
||||
uniqueidmap = UniqueIdMap()
|
||||
|
||||
|
||||
def uniqueid(obj):
|
||||
if isinstance(obj, str):
|
||||
return (hash(obj),)
|
||||
elif isinstance(obj, list):
|
||||
return (id(obj),)
|
||||
else:
|
||||
return (uniqueidmap[obj].int,)
|
||||
|
||||
|
||||
def _hash_with_id(*args):
|
||||
"""
|
||||
Return int hash value calculated by id(arg) or tuple(id1,id2, ...).
|
||||
"""
|
||||
assert len(args) > 0
|
||||
info = ()
|
||||
for v in args:
|
||||
info = info + uniqueid(v)
|
||||
return hash(info)
|
||||
|
||||
|
||||
def _sorted(dict_):
|
||||
"""
|
||||
Returns a sorted list of the dict keys, with error if keys not sortable.
|
||||
"""
|
||||
try:
|
||||
return sorted(dict_.keys())
|
||||
except TypeError:
|
||||
raise TypeError("nest only supports dicts with sortable keys.")
|
||||
|
||||
|
||||
def _yield_value(iterable):
|
||||
if isinstance(iterable, dict):
|
||||
for key in _sorted(iterable):
|
||||
yield iterable[key]
|
||||
else:
|
||||
yield from iterable
|
||||
|
||||
|
||||
def _yield_flat_nest(nest):
|
||||
for n in _yield_value(nest):
|
||||
if is_sequence(n):
|
||||
yield from _yield_flat_nest(n)
|
||||
else:
|
||||
yield n
|
||||
|
||||
|
||||
def to_sequence(nest):
|
||||
if is_sequence(nest):
|
||||
return nest
|
||||
else:
|
||||
return [nest]
|
||||
|
||||
|
||||
def flatten(nest: NestedStructure[_T]) -> typing.Sequence[_T]:
|
||||
"""
|
||||
:alias_main: paddle.flatten
|
||||
:alias: paddle.flatten,paddle.tensor.flatten,paddle.tensor.manipulation.flatten
|
||||
:old_api: paddle.base.layers.flatten
|
||||
|
||||
Traverse all entries in the nested structure and put them into an list.
|
||||
"""
|
||||
if is_sequence(nest):
|
||||
return list(_yield_flat_nest(nest))
|
||||
else:
|
||||
return [nest]
|
||||
|
||||
|
||||
def _sequence_like(instance, args):
|
||||
"""
|
||||
Convert the sequence `args` to the same type as `instance`.
|
||||
"""
|
||||
if isinstance(instance, dict):
|
||||
result = dict(zip(_sorted(instance), args))
|
||||
return type(instance)((key, result[key]) for key in instance.keys())
|
||||
elif (
|
||||
isinstance(instance, tuple)
|
||||
and hasattr(instance, "_fields")
|
||||
and isinstance(instance._fields, Sequence)
|
||||
and all(isinstance(f, str) for f in instance._fields)
|
||||
):
|
||||
# This is a namedtuple
|
||||
return type(instance)(*args)
|
||||
else:
|
||||
# Not a namedtuple
|
||||
return type(instance)(args)
|
||||
|
||||
|
||||
def _packed_nest_with_indices(structure, flat, index):
|
||||
"""
|
||||
Helper function for pack_sequence_as.
|
||||
"""
|
||||
packed = []
|
||||
for s in _yield_value(structure):
|
||||
if is_sequence(s):
|
||||
new_index, child = _packed_nest_with_indices(s, flat, index)
|
||||
packed.append(_sequence_like(s, child))
|
||||
index = new_index
|
||||
else:
|
||||
# Paddle requires python version > 3.7, so dict is always OrderedDict
|
||||
packed.append(
|
||||
flat[index]
|
||||
if not isinstance(flat, dict)
|
||||
else list(flat.values())[index]
|
||||
)
|
||||
index += 1
|
||||
return index, packed
|
||||
|
||||
|
||||
def pack_sequence_as(structure, flat_sequence):
|
||||
"""
|
||||
Pack a given flattened sequence into a given structure.
|
||||
"""
|
||||
if not is_sequence(flat_sequence):
|
||||
raise TypeError("flat_sequence must be a sequence")
|
||||
if not is_sequence(structure):
|
||||
if len(flat_sequence) != 1:
|
||||
raise ValueError(
|
||||
f"Structure is a scalar but len(flat_sequence) == {len(flat_sequence)} > 1"
|
||||
)
|
||||
return flat_sequence[0]
|
||||
flat_structure = flatten(structure)
|
||||
if len(flat_structure) != len(flat_sequence):
|
||||
raise ValueError(
|
||||
f"Could not pack sequence. Structure had {len(flat_structure)} elements, but flat_sequence "
|
||||
f"had {len(flat_sequence)} elements. Structure: {structure}, flat_sequence: {flat_sequence}."
|
||||
)
|
||||
_, packed = _packed_nest_with_indices(structure, flat_sequence, 0)
|
||||
return _sequence_like(structure, packed)
|
||||
|
||||
|
||||
def map_structure(
|
||||
func: Callable[[_T], _U], *structure: NestedStructure[_T]
|
||||
) -> NestedStructure[_U]:
|
||||
"""
|
||||
Apply `func` to each entry in `structure` and return a new structure.
|
||||
"""
|
||||
flat_structure = [flatten(s) for s in structure]
|
||||
entries = zip(*flat_structure)
|
||||
return pack_sequence_as(structure[0], [func(*x) for x in entries])
|
||||
|
||||
|
||||
def hold_mutable_vars(structure):
|
||||
"""
|
||||
Returns whether structure holds sequence like `list/dict`.
|
||||
"""
|
||||
for s in structure:
|
||||
if is_sequence(s):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def copy_mutable_vars(structure):
|
||||
"""
|
||||
Returns vars copied from sequence without mutable property.
|
||||
"""
|
||||
flat_structure = copy.copy(flatten(structure))
|
||||
return pack_sequence_as(structure, flat_structure)
|
||||
|
||||
|
||||
def _recursive_assert_same_structure(nest1, nest2, check_types, skip_if):
|
||||
"""
|
||||
Helper function for `assert_same_structure`.
|
||||
"""
|
||||
if skip_if is not None and (skip_if(nest1) or skip_if(nest2)):
|
||||
return
|
||||
is_sequence_nest1 = is_sequence(nest1)
|
||||
if is_sequence_nest1 != is_sequence(nest2):
|
||||
raise ValueError(
|
||||
"The two structures don't have the same nested structure.\n\n"
|
||||
f"First structure: {nest1}\n\nSecond structure: {nest2}."
|
||||
)
|
||||
if not is_sequence_nest1:
|
||||
return # finished checking
|
||||
if check_types:
|
||||
type_nest1 = type(nest1)
|
||||
type_nest2 = type(nest2)
|
||||
if type_nest1 != type_nest2:
|
||||
raise TypeError(
|
||||
"The two structures don't have the same sequence type. First "
|
||||
f"structure has type {type_nest1}, while second structure has type {type_nest2}."
|
||||
)
|
||||
if isinstance(nest1, dict):
|
||||
keys1 = set(nest1.keys())
|
||||
keys2 = set(nest2.keys())
|
||||
if keys1 != keys2:
|
||||
raise ValueError(
|
||||
"The two dictionaries don't have the same set of keys. First "
|
||||
f"structure has keys {keys1}, while second structure has keys {keys2}."
|
||||
)
|
||||
nest1_as_sequence = list(_yield_value(nest1))
|
||||
nest2_as_sequence = list(_yield_value(nest2))
|
||||
if len(nest1_as_sequence) != len(nest2_as_sequence):
|
||||
raise ValueError(
|
||||
"The two structures don't have the same number of elements.\n\n"
|
||||
f"First structure ({len(nest1_as_sequence)} elements): {nest1}\n\n"
|
||||
f"Second structure ({len(nest2_as_sequence)} elements): {nest2}"
|
||||
)
|
||||
for n1, n2 in zip(nest1_as_sequence, nest2_as_sequence):
|
||||
_recursive_assert_same_structure(n1, n2, check_types, skip_if)
|
||||
|
||||
|
||||
def padding_to_same_structure(nest1, nest2, obj=None):
|
||||
def _padding_to_same_structure_single(value, obj):
|
||||
def change_none_to_obj(x):
|
||||
if x is None:
|
||||
return obj
|
||||
return x
|
||||
|
||||
if is_sequence(value):
|
||||
value = pack_sequence_as(
|
||||
value, [change_none_to_obj(item) for item in flatten(value)]
|
||||
)
|
||||
else:
|
||||
value = change_none_to_obj(value)
|
||||
return value
|
||||
|
||||
nest1 = _padding_to_same_structure_single(nest1, obj)
|
||||
nest2 = _padding_to_same_structure_single(nest2, obj)
|
||||
return nest1, nest2
|
||||
|
||||
|
||||
def assert_same_structure(nest1, nest2, check_types=True, skip_if=None):
|
||||
"""
|
||||
Confirm two nested structures with the same structure.
|
||||
"""
|
||||
if skip_if is not None and (skip_if(nest1) or skip_if(nest2)):
|
||||
return
|
||||
len_nest1 = len(flatten(nest1)) if is_sequence(nest1) else 1
|
||||
len_nest2 = len(flatten(nest2)) if is_sequence(nest2) else 1
|
||||
if len_nest1 != len_nest2 and skip_if is None:
|
||||
raise ValueError(
|
||||
"The two structures don't have the same number of "
|
||||
f"elements.\n\nFirst structure ({len_nest1} elements): {nest1}\n\n"
|
||||
f"Second structure ({len_nest2} elements): {nest2}"
|
||||
)
|
||||
_recursive_assert_same_structure(nest1, nest2, check_types, skip_if)
|
||||
|
||||
|
||||
def _is_symmetric_padding(padding, data_dim):
|
||||
"""
|
||||
Check whether padding is symmetrical.
|
||||
"""
|
||||
assert len(padding) == data_dim * 2 or len(padding) == data_dim
|
||||
is_sys = True
|
||||
if len(padding) == data_dim * 2:
|
||||
for i in range(data_dim):
|
||||
if padding[i * 2] != padding[i * 2 + 1]:
|
||||
is_sys = False
|
||||
return is_sys
|
||||
|
||||
|
||||
def _contain_var(list_or_tuple):
|
||||
"""
|
||||
Check whether list or tuple contains variable / Value.
|
||||
"""
|
||||
for item in list_or_tuple:
|
||||
if isinstance(item, (Variable, paddle.pir.Value)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_int_tensor_list(ele_list, default_dtype='int64'):
|
||||
int_tensor_list = []
|
||||
for ele in ele_list:
|
||||
if isinstance(ele, paddle.pir.Value):
|
||||
ele.stop_gradient = True
|
||||
if convert_dtype(ele.dtype) != default_dtype:
|
||||
ele = paddle.cast(x=ele, dtype=default_dtype)
|
||||
if ele.shape != []:
|
||||
ele = paddle.reshape(ele, [])
|
||||
int_tensor_list.append(ele)
|
||||
else:
|
||||
temp_out = paddle.tensor.fill_constant(
|
||||
shape=[],
|
||||
dtype=convert_nptype_to_datatype(np.dtype(default_dtype)),
|
||||
value=ele,
|
||||
force_cpu=True,
|
||||
)
|
||||
int_tensor_list.append(temp_out)
|
||||
return int_tensor_list
|
||||
|
||||
|
||||
def get_shape_tensor_inputs(inputs, attrs, shape, op_type):
|
||||
from paddle.tensor import fill_constant
|
||||
|
||||
def _get_attr_shape(list_shape):
|
||||
attr_shape = []
|
||||
for idx, dim in enumerate(list_shape):
|
||||
if isinstance(dim, Variable):
|
||||
attr_shape.append(-1)
|
||||
else:
|
||||
attr_shape.append(dim)
|
||||
return attr_shape
|
||||
|
||||
def _get_shape_tensor(list_shape):
|
||||
shape_tensor_list = []
|
||||
for idx, dim in enumerate(list_shape):
|
||||
if isinstance(dim, Variable):
|
||||
dim.stop_gradient = True
|
||||
check_dtype(
|
||||
dim.dtype,
|
||||
'shape[' + str(idx) + ']',
|
||||
['int32', 'int64'],
|
||||
op_type,
|
||||
f'(When type of shape in {op_type} is list or tuple.)',
|
||||
)
|
||||
if convert_dtype(dim.dtype) == 'int64':
|
||||
dim = paddle.cast(x=dim, dtype='int32')
|
||||
shape_tensor_list.append(dim)
|
||||
else:
|
||||
temp_out = fill_constant([], 'int32', dim, force_cpu=True)
|
||||
shape_tensor_list.append(temp_out)
|
||||
return shape_tensor_list
|
||||
|
||||
if isinstance(shape, Variable):
|
||||
shape.stop_gradient = True
|
||||
check_dtype(
|
||||
shape.dtype,
|
||||
'shape',
|
||||
['int32', 'int64'],
|
||||
'fill_constant',
|
||||
f'(When type of shape in {op_type} is Variable.)',
|
||||
)
|
||||
if convert_dtype(shape.dtype) == 'int64':
|
||||
shape = paddle.cast(shape, 'int32')
|
||||
inputs["ShapeTensor"] = shape
|
||||
elif isinstance(shape, (list, tuple)):
|
||||
attrs["shape"] = _get_attr_shape(shape)
|
||||
if _contain_var(shape):
|
||||
inputs['ShapeTensorList'] = _get_shape_tensor(shape)
|
||||
else:
|
||||
raise TypeError("Shape only supports Variable, or list, or tuple.")
|
||||
|
||||
|
||||
def _convert_to_tensor_list(old_list, dtype="int32"):
|
||||
"""
|
||||
Converts all elements of a list to Variable / Value.
|
||||
"""
|
||||
from paddle.tensor import fill_constant
|
||||
|
||||
if _contain_var(old_list):
|
||||
for ele in old_list:
|
||||
if isinstance(ele, paddle.pir.Value):
|
||||
dtype = ele.dtype
|
||||
|
||||
new_list_tensor = []
|
||||
for ele in old_list:
|
||||
if isinstance(ele, (Variable, paddle.pir.Value)):
|
||||
ele.stop_gradient = True
|
||||
new_list_tensor.append(ele)
|
||||
else:
|
||||
assert isinstance(ele, int)
|
||||
temp_out = fill_constant([1], dtype, ele, force_cpu=True)
|
||||
new_list_tensor.append(temp_out)
|
||||
return new_list_tensor
|
||||
|
||||
|
||||
def convert_shape_to_list(shape):
|
||||
"""
|
||||
Convert shape(list, tuple, variable) to list in imperative mode
|
||||
"""
|
||||
if isinstance(shape, (list, tuple)):
|
||||
shape_out = []
|
||||
for x in shape:
|
||||
if isinstance(x, Variable):
|
||||
# skip item if size = 0
|
||||
if x.size > 0:
|
||||
shape_out.append(x.item(0))
|
||||
else:
|
||||
shape_out.append(x)
|
||||
shape = shape_out
|
||||
else:
|
||||
if in_dygraph_mode():
|
||||
shape = shape.astype(int).tolist()
|
||||
return shape
|
||||
|
||||
|
||||
def check_shape(shape):
|
||||
"""
|
||||
Check shape type and shape elements type before passing it to fill_constant
|
||||
"""
|
||||
if isinstance(shape, (Variable, Value)):
|
||||
check_dtype(shape.dtype, 'shape', ['int32', 'int64'], 'fill_constant')
|
||||
elif isinstance(shape, (list, tuple)):
|
||||
for ele in shape:
|
||||
if not isinstance(ele, (Variable, Value)):
|
||||
if ele < 0:
|
||||
raise ValueError(
|
||||
"All elements in ``shape`` must be positive when it's a list or tuple"
|
||||
)
|
||||
if not isinstance(ele, int):
|
||||
raise TypeError(
|
||||
"All elements in ``shape`` must be integers when it's a list or tuple"
|
||||
)
|
||||
else:
|
||||
check_dtype(
|
||||
ele.dtype,
|
||||
'element of shape',
|
||||
['int32', 'int64'],
|
||||
'fill_constant',
|
||||
)
|
||||
|
||||
|
||||
def try_get_constant_shape_from_tensor(shape_tensor):
|
||||
"""Try to get shape from a tensor with constant value.
|
||||
|
||||
For example,
|
||||
|
||||
import paddle
|
||||
paddle.enable_static()
|
||||
data = paddle.static.data(name="x", shape=[-1, 2], dtype='float32')
|
||||
shape = paddle.shape(data) # shape should be [-1, 2] instead of [-1, -1]
|
||||
x = paddle.uniform(shape)
|
||||
print(x.shape)
|
||||
# (-1, 2)
|
||||
|
||||
"""
|
||||
if not in_dygraph_mode():
|
||||
try:
|
||||
if shape_tensor.op is not None:
|
||||
generate_op = shape_tensor.op
|
||||
if generate_op.type == 'shape':
|
||||
var = shape_tensor.block.vars[
|
||||
generate_op.input_arg_names[0]
|
||||
]
|
||||
return var.shape
|
||||
except:
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_inputs_outputs_in_block(block):
|
||||
"""
|
||||
Returns the inputs and outputs variable used in this block but not
|
||||
created in this block.
|
||||
"""
|
||||
assert isinstance(block, Block), (
|
||||
"input non-Block argument for get_inputs_outputs_in_block."
|
||||
)
|
||||
assert block.parent_idx != -1, (
|
||||
"input block should be a sub-block, not main block."
|
||||
)
|
||||
|
||||
# Find input/output var names of all ops in block
|
||||
inner_inputs = set()
|
||||
inner_outputs = set()
|
||||
for op in block.ops:
|
||||
for iname in op.input_names:
|
||||
for in_var_name in op.input(iname):
|
||||
if not block.has_var(in_var_name):
|
||||
# variable not created in this block
|
||||
inner_inputs.add(in_var_name)
|
||||
for oname in op.output_names:
|
||||
for out_var_name in op.output(oname):
|
||||
if not block.has_var(out_var_name):
|
||||
# variable not created in this block
|
||||
inner_outputs.add(out_var_name)
|
||||
|
||||
return inner_inputs, inner_outputs
|
||||
|
||||
|
||||
def is_same_shape(shape1: ShapeLike, shape2: ShapeLike) -> bool:
|
||||
"""
|
||||
Check whether two shapes are the same. Deal with the dynamic shape.
|
||||
"""
|
||||
if paddle.in_dynamic_mode():
|
||||
return shape1 == shape2
|
||||
|
||||
def is_tensor(x):
|
||||
return isinstance(x, (paddle.static.Variable, paddle.pir.Value))
|
||||
|
||||
def is_dynamic_axis(axis):
|
||||
return is_tensor(axis) or axis == -1
|
||||
|
||||
if is_tensor(shape1) or is_tensor(shape2):
|
||||
return True
|
||||
if len(shape1) != len(shape2):
|
||||
return False
|
||||
for s1, s2 in zip(shape1, shape2):
|
||||
if is_dynamic_axis(s1) or is_dynamic_axis(s2):
|
||||
continue
|
||||
if s1 != s2:
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright (c) 2018 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.
|
||||
"""Lazy imports for heavy dependencies."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from types import ModuleType
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def try_import(module_name: str, err_msg: str | None = None) -> ModuleType:
|
||||
"""Try importing a module, with an informative error message on failure."""
|
||||
install_name = module_name
|
||||
|
||||
if module_name.find('.') > -1:
|
||||
install_name = module_name.split('.')[0]
|
||||
|
||||
if module_name == 'cv2':
|
||||
install_name = 'opencv-python'
|
||||
|
||||
try:
|
||||
mod = importlib.import_module(module_name)
|
||||
return mod
|
||||
except ImportError:
|
||||
if err_msg is None:
|
||||
err_msg = (
|
||||
f"Failed importing {module_name}. This likely means that some paddle modules "
|
||||
"require additional dependencies that have to be "
|
||||
f"manually installed (usually with `pip install {install_name}`). "
|
||||
)
|
||||
raise ImportError(err_msg)
|
||||
@@ -0,0 +1,71 @@
|
||||
# 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.
|
||||
|
||||
from ..base import core
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def Singleton(cls):
|
||||
_instance = {}
|
||||
|
||||
def _singleton(*args, **kargs):
|
||||
if cls not in _instance:
|
||||
_instance[cls] = cls(*args, **kargs)
|
||||
return _instance[cls]
|
||||
|
||||
return _singleton
|
||||
|
||||
|
||||
class OpUpdateInfoHelper:
|
||||
def __init__(self, info):
|
||||
self._info = info
|
||||
|
||||
def verify_key_value(self, name=''):
|
||||
result = False
|
||||
key_funcs = {
|
||||
core.OpAttrInfo: 'name',
|
||||
core.OpInputOutputInfo: 'name',
|
||||
}
|
||||
if name == '':
|
||||
result = True
|
||||
elif type(self._info) in key_funcs:
|
||||
if getattr(self._info, key_funcs[type(self._info)])() == name:
|
||||
result = True
|
||||
return result
|
||||
|
||||
|
||||
@Singleton
|
||||
class OpLastCheckpointChecker:
|
||||
def __init__(self):
|
||||
self.raw_version_map = core.get_op_version_map()
|
||||
self.checkpoints_map = {}
|
||||
self._construct_map()
|
||||
|
||||
def _construct_map(self):
|
||||
for op_name in self.raw_version_map:
|
||||
last_checkpoint = self.raw_version_map[op_name].checkpoints()[-1]
|
||||
infos = last_checkpoint.version_desc().infos()
|
||||
self.checkpoints_map[op_name] = infos
|
||||
|
||||
def filter_updates(self, op_name, type=core.OpUpdateType.kInvalid, key=''):
|
||||
updates = []
|
||||
if op_name in self.checkpoints_map:
|
||||
for update in self.checkpoints_map[op_name]:
|
||||
if (update.type() == type) or (
|
||||
type == core.OpUpdateType.kInvalid
|
||||
):
|
||||
if OpUpdateInfoHelper(update.info()).verify_key_value(key):
|
||||
updates.append(update.info())
|
||||
return updates
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
from ..base.unique_name import (
|
||||
generate,
|
||||
generate_with_ignorable_key, # noqa: F401
|
||||
guard,
|
||||
switch,
|
||||
)
|
||||
|
||||
__all__ = ['generate', 'switch', 'guard']
|
||||
Reference in New Issue
Block a user