diff options
author | SupImDos <62866982+SupImDos@users.noreply.github.com> | 2021-09-29 13:51:43 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-29 07:51:43 +0200 |
commit | ee2438c88625080ea6dd560eb90a9096512d5c00 (patch) | |
tree | 43b7550f2c4082f74e55de933e43182ca8f1862c | |
parent | fe918fff6983df3a54bd11b968cb4ac7adc57f70 (diff) | |
download | astroid-ee2438c88625080ea6dd560eb90a9096512d5c00.tar.gz |
Feature / Bug Fix: Brain for the stdlib signal module (#1172)
* Added brain for stdlib signal module, for dynamically generated IntEnums.
* Added static definitions for Signals, Handlers and Sigmasks enums.
* Moved sys.platform checks outside of generated code.
* Added note to brain_signal docstring.
* Added unit tests for brain_signal.py.
-rw-r--r-- | astroid/brain/brain_signal.py | 117 | ||||
-rw-r--r-- | tests/unittest_brain_signal.py | 41 |
2 files changed, 158 insertions, 0 deletions
diff --git a/astroid/brain/brain_signal.py b/astroid/brain/brain_signal.py new file mode 100644 index 00000000..46a64139 --- /dev/null +++ b/astroid/brain/brain_signal.py @@ -0,0 +1,117 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +"""Astroid hooks for the signal library. + +The signal module generates the 'Signals', 'Handlers' and 'Sigmasks' IntEnums +dynamically using the IntEnum._convert() classmethod, which modifies the module +globals. Astroid is unable to handle this type of code. + +Without these hooks, the following are erroneously triggered by Pylint: + * E1101: Module 'signal' has no 'Signals' member (no-member) + * E1101: Module 'signal' has no 'Handlers' member (no-member) + * E1101: Module 'signal' has no 'Sigmasks' member (no-member) + +These enums are defined slightly differently depending on the user's operating +system and platform. These platform differences should follow the current +Python typeshed stdlib `signal.pyi` stub file, available at: + +* https://github.com/python/typeshed/blob/master/stdlib/signal.pyi + +Note that the enum.auto() values defined here for the Signals, Handlers and +Sigmasks IntEnums are just dummy integer values, and do not correspond to the +actual standard signal numbers - which may vary depending on the system. +""" + + +import sys + +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def _signals_enums_transform(): + """Generates the AST for 'Signals', 'Handlers' and 'Sigmasks' IntEnums.""" + return parse(_signals_enum() + _handlers_enum() + _sigmasks_enum()) + + +def _signals_enum(): + """Generates the source code for the Signals int enum.""" + signals_enum = """ + import enum + class Signals(enum.IntEnum): + SIGABRT = enum.auto() + SIGEMT = enum.auto() + SIGFPE = enum.auto() + SIGILL = enum.auto() + SIGINFO = enum.auto() + SIGINT = enum.auto() + SIGSEGV = enum.auto() + SIGTERM = enum.auto() + """ + if sys.platform != "win32": + signals_enum += """ + SIGALRM = enum.auto() + SIGBUS = enum.auto() + SIGCHLD = enum.auto() + SIGCONT = enum.auto() + SIGHUP = enum.auto() + SIGIO = enum.auto() + SIGIOT = enum.auto() + SIGKILL = enum.auto() + SIGPIPE = enum.auto() + SIGPROF = enum.auto() + SIGQUIT = enum.auto() + SIGSTOP = enum.auto() + SIGSYS = enum.auto() + SIGTRAP = enum.auto() + SIGTSTP = enum.auto() + SIGTTIN = enum.auto() + SIGTTOU = enum.auto() + SIGURG = enum.auto() + SIGUSR1 = enum.auto() + SIGUSR2 = enum.auto() + SIGVTALRM = enum.auto() + SIGWINCH = enum.auto() + SIGXCPU = enum.auto() + SIGXFSZ = enum.auto() + """ + if sys.platform == "win32": + signals_enum += """ + SIGBREAK = enum.auto() + """ + if sys.platform not in ("darwin", "win32"): + signals_enum += """ + SIGCLD = enum.auto() + SIGPOLL = enum.auto() + SIGPWR = enum.auto() + SIGRTMAX = enum.auto() + SIGRTMIN = enum.auto() + """ + return signals_enum + + +def _handlers_enum(): + """Generates the source code for the Handlers int enum.""" + return """ + import enum + class Handlers(enum.IntEnum): + SIG_DFL = enum.auto() + SIG_IGN = eunm.auto() + """ + + +def _sigmasks_enum(): + """Generates the source code for the Sigmasks int enum.""" + if sys.platform != "win32": + return """ + import enum + class Sigmasks(enum.IntEnum): + SIG_BLOCK = enum.auto() + SIG_UNBLOCK = enum.auto() + SIG_SETMASK = enum.auto() + """ + return "" + + +register_module_extender(AstroidManager(), "signal", _signals_enums_transform) diff --git a/tests/unittest_brain_signal.py b/tests/unittest_brain_signal.py new file mode 100644 index 00000000..5422ecfa --- /dev/null +++ b/tests/unittest_brain_signal.py @@ -0,0 +1,41 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +"""Unit Tests for the signal brain module.""" + + +import sys + +import pytest + +from astroid import builder, nodes + +# Define signal enums +ENUMS = ["Signals", "Handlers", "Sigmasks"] +if sys.platform == "win32": + ENUMS.remove("Sigmasks") # Sigmasks do not exist on Windows + + +@pytest.mark.parametrize("enum_name", ENUMS) +def test_enum(enum_name): + """Tests that the signal module enums are handled by the brain.""" + # Extract node for signal module enum from code + node = builder.extract_node( + f""" + import signal + signal.{enum_name} + """ + ) + + # Check the extracted node + assert isinstance(node, nodes.NodeNG) + node_inf = node.inferred()[0] + assert isinstance(node_inf, nodes.ClassDef) + assert node_inf.display_type() == "Class" + assert node_inf.is_subtype_of("enum.IntEnum") + assert node_inf.qname() == f"signal.{enum_name}" + + # Check enum members + for member in node_inf.body: + assert isinstance(member, nodes.Assign) + for target in member.targets: + assert isinstance(target, nodes.AssignName) |