Implement the behavior of @dataclass. Note that this may be executed multiple times on the same class, so everything here must be idempotent. This runs after the main semantic analysis pass, so you can assume that there are no placeholders.
| 205 | |
| 206 | |
| 207 | class DataclassTransformer: |
| 208 | """Implement the behavior of @dataclass. |
| 209 | |
| 210 | Note that this may be executed multiple times on the same class, so |
| 211 | everything here must be idempotent. |
| 212 | |
| 213 | This runs after the main semantic analysis pass, so you can assume that |
| 214 | there are no placeholders. |
| 215 | """ |
| 216 | |
| 217 | def __init__( |
| 218 | self, |
| 219 | cls: ClassDef, |
| 220 | # Statement must also be accepted since class definition itself may be passed as the reason |
| 221 | # for subclass/metaclass-based uses of `typing.dataclass_transform` |
| 222 | reason: Expression | Statement, |
| 223 | spec: DataclassTransformSpec, |
| 224 | api: SemanticAnalyzerPluginInterface, |
| 225 | ) -> None: |
| 226 | self._cls = cls |
| 227 | self._reason = reason |
| 228 | self._spec = spec |
| 229 | self._api = api |
| 230 | |
| 231 | def transform(self) -> bool: |
| 232 | """Apply all the necessary transformations to the underlying |
| 233 | dataclass so as to ensure it is fully type checked according |
| 234 | to the rules in PEP 557. |
| 235 | """ |
| 236 | info = self._cls.info |
| 237 | attributes = self.collect_attributes() |
| 238 | if attributes is None: |
| 239 | # Some definitions are not ready. We need another pass. |
| 240 | return False |
| 241 | for attr in attributes: |
| 242 | if attr.type is None: |
| 243 | return False |
| 244 | decorator_arguments = { |
| 245 | "init": self._get_bool_arg("init", True), |
| 246 | "eq": self._get_bool_arg("eq", self._spec.eq_default), |
| 247 | "order": self._get_bool_arg("order", self._spec.order_default), |
| 248 | "frozen": self._get_bool_arg("frozen", self._spec.frozen_default), |
| 249 | "slots": self._get_bool_arg("slots", False), |
| 250 | "match_args": self._get_bool_arg("match_args", True), |
| 251 | } |
| 252 | |
| 253 | # If there are no attributes, it may be that the semantic analyzer has not |
| 254 | # processed them yet. In order to work around this, we can simply skip generating |
| 255 | # __init__ if there are no attributes, because if the user truly did not define any, |
| 256 | # then the object default __init__ with an empty signature will be present anyway. |
| 257 | if ( |
| 258 | decorator_arguments["init"] |
| 259 | and ("__init__" not in info.names or info.names["__init__"].plugin_generated) |
| 260 | and attributes |
| 261 | ): |
| 262 | args = [ |
| 263 | attr.to_argument(info, of="__init__") |
| 264 | for attr in attributes |
no outgoing calls
no test coverage detected
searching dependent graphs…