|
12 | 12 | import builtins as bltns
|
13 | 13 | import collections
|
14 | 14 | import contextlib
|
15 |
| -import copy |
16 | 15 | import dataclasses as dc
|
17 | 16 | import enum
|
18 | 17 | import functools
|
|
50 | 49 | # Local imports.
|
51 | 50 | import libclinic
|
52 | 51 | import libclinic.cpp
|
53 |
| -from libclinic import ClinicError, fail, warn |
| 52 | +from libclinic import ( |
| 53 | + ClinicError, Sentinels, VersionTuple, |
| 54 | + fail, warn, unspecified, unknown) |
| 55 | +from libclinic.function import ( |
| 56 | + Module, Class, Function, Parameter, |
| 57 | + ClassDict, ModuleDict, FunctionKind, |
| 58 | + CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW, |
| 59 | + GETTER, SETTER) |
54 | 60 |
|
55 | 61 |
|
56 | 62 | # TODO:
|
|
70 | 76 | LIMITED_CAPI_REGEX = re.compile(r'# *define +Py_LIMITED_API')
|
71 | 77 |
|
72 | 78 |
|
73 |
| -class Sentinels(enum.Enum): |
74 |
| - unspecified = "unspecified" |
75 |
| - unknown = "unknown" |
76 |
| - |
77 |
| - def __repr__(self) -> str: |
78 |
| - return f"<{self.value.capitalize()}>" |
79 |
| - |
80 |
| - |
81 |
| -unspecified: Final = Sentinels.unspecified |
82 |
| -unknown: Final = Sentinels.unknown |
83 |
| - |
84 |
| - |
85 | 79 | # This one needs to be a distinct class, unlike the other two
|
86 | 80 | class Null:
|
87 | 81 | def __repr__(self) -> str:
|
@@ -2096,9 +2090,7 @@ def dump(self) -> str:
|
2096 | 2090 | extensions['py'] = PythonLanguage
|
2097 | 2091 |
|
2098 | 2092 |
|
2099 |
| -ClassDict = dict[str, "Class"] |
2100 | 2093 | DestinationDict = dict[str, Destination]
|
2101 |
| -ModuleDict = dict[str, "Module"] |
2102 | 2094 |
|
2103 | 2095 |
|
2104 | 2096 | class Parser(Protocol):
|
@@ -2418,38 +2410,6 @@ def parse(self, block: Block) -> None:
|
2418 | 2410 | block.output = s.getvalue()
|
2419 | 2411 |
|
2420 | 2412 |
|
2421 |
| -@dc.dataclass(repr=False) |
2422 |
| -class Module: |
2423 |
| - name: str |
2424 |
| - module: Module | Clinic |
2425 |
| - |
2426 |
| - def __post_init__(self) -> None: |
2427 |
| - self.parent = self.module |
2428 |
| - self.modules: ModuleDict = {} |
2429 |
| - self.classes: ClassDict = {} |
2430 |
| - self.functions: list[Function] = [] |
2431 |
| - |
2432 |
| - def __repr__(self) -> str: |
2433 |
| - return "<clinic.Module " + repr(self.name) + " at " + str(id(self)) + ">" |
2434 |
| - |
2435 |
| - |
2436 |
| -@dc.dataclass(repr=False) |
2437 |
| -class Class: |
2438 |
| - name: str |
2439 |
| - module: Module | Clinic |
2440 |
| - cls: Class | None |
2441 |
| - typedef: str |
2442 |
| - type_object: str |
2443 |
| - |
2444 |
| - def __post_init__(self) -> None: |
2445 |
| - self.parent = self.cls or self.module |
2446 |
| - self.classes: ClassDict = {} |
2447 |
| - self.functions: list[Function] = [] |
2448 |
| - |
2449 |
| - def __repr__(self) -> str: |
2450 |
| - return "<clinic.Class " + repr(self.name) + " at " + str(id(self)) + ">" |
2451 |
| - |
2452 |
| - |
2453 | 2413 | unsupported_special_methods: set[str] = set("""
|
2454 | 2414 |
|
2455 | 2415 | __abs__
|
@@ -2522,201 +2482,9 @@ def __repr__(self) -> str:
|
2522 | 2482 | """.strip().split())
|
2523 | 2483 |
|
2524 | 2484 |
|
2525 |
| -class FunctionKind(enum.Enum): |
2526 |
| - INVALID = enum.auto() |
2527 |
| - CALLABLE = enum.auto() |
2528 |
| - STATIC_METHOD = enum.auto() |
2529 |
| - CLASS_METHOD = enum.auto() |
2530 |
| - METHOD_INIT = enum.auto() |
2531 |
| - METHOD_NEW = enum.auto() |
2532 |
| - GETTER = enum.auto() |
2533 |
| - SETTER = enum.auto() |
2534 |
| - |
2535 |
| - @functools.cached_property |
2536 |
| - def new_or_init(self) -> bool: |
2537 |
| - return self in {FunctionKind.METHOD_INIT, FunctionKind.METHOD_NEW} |
2538 |
| - |
2539 |
| - def __repr__(self) -> str: |
2540 |
| - return f"<clinic.FunctionKind.{self.name}>" |
2541 |
| - |
2542 |
| - |
2543 |
| -INVALID: Final = FunctionKind.INVALID |
2544 |
| -CALLABLE: Final = FunctionKind.CALLABLE |
2545 |
| -STATIC_METHOD: Final = FunctionKind.STATIC_METHOD |
2546 |
| -CLASS_METHOD: Final = FunctionKind.CLASS_METHOD |
2547 |
| -METHOD_INIT: Final = FunctionKind.METHOD_INIT |
2548 |
| -METHOD_NEW: Final = FunctionKind.METHOD_NEW |
2549 |
| -GETTER: Final = FunctionKind.GETTER |
2550 |
| -SETTER: Final = FunctionKind.SETTER |
2551 |
| - |
2552 |
| -ParamDict = dict[str, "Parameter"] |
2553 | 2485 | ReturnConverterType = Callable[..., "CReturnConverter"]
|
2554 | 2486 |
|
2555 | 2487 |
|
2556 |
| -@dc.dataclass(repr=False) |
2557 |
| -class Function: |
2558 |
| - """ |
2559 |
| - Mutable duck type for inspect.Function. |
2560 |
| -
|
2561 |
| - docstring - a str containing |
2562 |
| - * embedded line breaks |
2563 |
| - * text outdented to the left margin |
2564 |
| - * no trailing whitespace. |
2565 |
| - It will always be true that |
2566 |
| - (not docstring) or ((not docstring[0].isspace()) and (docstring.rstrip() == docstring)) |
2567 |
| - """ |
2568 |
| - parameters: ParamDict = dc.field(default_factory=dict) |
2569 |
| - _: dc.KW_ONLY |
2570 |
| - name: str |
2571 |
| - module: Module | Clinic |
2572 |
| - cls: Class | None |
2573 |
| - c_basename: str |
2574 |
| - full_name: str |
2575 |
| - return_converter: CReturnConverter |
2576 |
| - kind: FunctionKind |
2577 |
| - coexist: bool |
2578 |
| - return_annotation: object = inspect.Signature.empty |
2579 |
| - docstring: str = '' |
2580 |
| - # docstring_only means "don't generate a machine-readable |
2581 |
| - # signature, just a normal docstring". it's True for |
2582 |
| - # functions with optional groups because we can't represent |
2583 |
| - # those accurately with inspect.Signature in 3.4. |
2584 |
| - docstring_only: bool = False |
2585 |
| - critical_section: bool = False |
2586 |
| - target_critical_section: list[str] = dc.field(default_factory=list) |
2587 |
| - |
2588 |
| - def __post_init__(self) -> None: |
2589 |
| - self.parent = self.cls or self.module |
2590 |
| - self.self_converter: self_converter | None = None |
2591 |
| - self.__render_parameters__: list[Parameter] | None = None |
2592 |
| - |
2593 |
| - @functools.cached_property |
2594 |
| - def displayname(self) -> str: |
2595 |
| - """Pretty-printable name.""" |
2596 |
| - if self.kind.new_or_init: |
2597 |
| - assert isinstance(self.cls, Class) |
2598 |
| - return self.cls.name |
2599 |
| - else: |
2600 |
| - return self.name |
2601 |
| - |
2602 |
| - @functools.cached_property |
2603 |
| - def fulldisplayname(self) -> str: |
2604 |
| - parent: Class | Module | Clinic | None |
2605 |
| - if self.kind.new_or_init: |
2606 |
| - parent = getattr(self.cls, "parent", None) |
2607 |
| - else: |
2608 |
| - parent = self.parent |
2609 |
| - name = self.displayname |
2610 |
| - while isinstance(parent, (Module, Class)): |
2611 |
| - name = f"{parent.name}.{name}" |
2612 |
| - parent = parent.parent |
2613 |
| - return name |
2614 |
| - |
2615 |
| - @property |
2616 |
| - def render_parameters(self) -> list[Parameter]: |
2617 |
| - if not self.__render_parameters__: |
2618 |
| - l: list[Parameter] = [] |
2619 |
| - self.__render_parameters__ = l |
2620 |
| - for p in self.parameters.values(): |
2621 |
| - p = p.copy() |
2622 |
| - p.converter.pre_render() |
2623 |
| - l.append(p) |
2624 |
| - return self.__render_parameters__ |
2625 |
| - |
2626 |
| - @property |
2627 |
| - def methoddef_flags(self) -> str | None: |
2628 |
| - if self.kind.new_or_init: |
2629 |
| - return None |
2630 |
| - flags = [] |
2631 |
| - match self.kind: |
2632 |
| - case FunctionKind.CLASS_METHOD: |
2633 |
| - flags.append('METH_CLASS') |
2634 |
| - case FunctionKind.STATIC_METHOD: |
2635 |
| - flags.append('METH_STATIC') |
2636 |
| - case _ as kind: |
2637 |
| - acceptable_kinds = {FunctionKind.CALLABLE, FunctionKind.GETTER, FunctionKind.SETTER} |
2638 |
| - assert kind in acceptable_kinds, f"unknown kind: {kind!r}" |
2639 |
| - if self.coexist: |
2640 |
| - flags.append('METH_COEXIST') |
2641 |
| - return '|'.join(flags) |
2642 |
| - |
2643 |
| - def __repr__(self) -> str: |
2644 |
| - return f'<clinic.Function {self.name!r}>' |
2645 |
| - |
2646 |
| - def copy(self, **overrides: Any) -> Function: |
2647 |
| - f = dc.replace(self, **overrides) |
2648 |
| - f.parameters = { |
2649 |
| - name: value.copy(function=f) |
2650 |
| - for name, value in f.parameters.items() |
2651 |
| - } |
2652 |
| - return f |
2653 |
| - |
2654 |
| - |
2655 |
| -VersionTuple = tuple[int, int] |
2656 |
| - |
2657 |
| - |
2658 |
| -@dc.dataclass(repr=False, slots=True) |
2659 |
| -class Parameter: |
2660 |
| - """ |
2661 |
| - Mutable duck type of inspect.Parameter. |
2662 |
| - """ |
2663 |
| - name: str |
2664 |
| - kind: inspect._ParameterKind |
2665 |
| - _: dc.KW_ONLY |
2666 |
| - default: object = inspect.Parameter.empty |
2667 |
| - function: Function |
2668 |
| - converter: CConverter |
2669 |
| - annotation: object = inspect.Parameter.empty |
2670 |
| - docstring: str = '' |
2671 |
| - group: int = 0 |
2672 |
| - # (`None` signifies that there is no deprecation) |
2673 |
| - deprecated_positional: VersionTuple | None = None |
2674 |
| - deprecated_keyword: VersionTuple | None = None |
2675 |
| - right_bracket_count: int = dc.field(init=False, default=0) |
2676 |
| - |
2677 |
| - def __repr__(self) -> str: |
2678 |
| - return f'<clinic.Parameter {self.name!r}>' |
2679 |
| - |
2680 |
| - def is_keyword_only(self) -> bool: |
2681 |
| - return self.kind == inspect.Parameter.KEYWORD_ONLY |
2682 |
| - |
2683 |
| - def is_positional_only(self) -> bool: |
2684 |
| - return self.kind == inspect.Parameter.POSITIONAL_ONLY |
2685 |
| - |
2686 |
| - def is_vararg(self) -> bool: |
2687 |
| - return self.kind == inspect.Parameter.VAR_POSITIONAL |
2688 |
| - |
2689 |
| - def is_optional(self) -> bool: |
2690 |
| - return not self.is_vararg() and (self.default is not unspecified) |
2691 |
| - |
2692 |
| - def copy( |
2693 |
| - self, |
2694 |
| - /, |
2695 |
| - *, |
2696 |
| - converter: CConverter | None = None, |
2697 |
| - function: Function | None = None, |
2698 |
| - **overrides: Any |
2699 |
| - ) -> Parameter: |
2700 |
| - function = function or self.function |
2701 |
| - if not converter: |
2702 |
| - converter = copy.copy(self.converter) |
2703 |
| - converter.function = function |
2704 |
| - return dc.replace(self, **overrides, function=function, converter=converter) |
2705 |
| - |
2706 |
| - def get_displayname(self, i: int) -> str: |
2707 |
| - if i == 0: |
2708 |
| - return 'argument' |
2709 |
| - if not self.is_positional_only(): |
2710 |
| - return f'argument {self.name!r}' |
2711 |
| - else: |
2712 |
| - return f'argument {i}' |
2713 |
| - |
2714 |
| - def render_docstring(self) -> str: |
2715 |
| - lines = [f" {self.name}"] |
2716 |
| - lines.extend(f" {line}" for line in self.docstring.split("\n")) |
2717 |
| - return "\n".join(lines).rstrip() |
2718 |
| - |
2719 |
| - |
2720 | 2488 | CConverterClassT = TypeVar("CConverterClassT", bound=type["CConverter"])
|
2721 | 2489 |
|
2722 | 2490 | def add_c_converter(
|
|
0 commit comments