aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSupImDos <62866982+SupImDos@users.noreply.github.com>2021-09-29 13:51:43 +0800
committerGitHub <noreply@github.com>2021-09-29 07:51:43 +0200
commitee2438c88625080ea6dd560eb90a9096512d5c00 (patch)
tree43b7550f2c4082f74e55de933e43182ca8f1862c
parentfe918fff6983df3a54bd11b968cb4ac7adc57f70 (diff)
downloadastroid-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.py117
-rw-r--r--tests/unittest_brain_signal.py41
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)