File: _maybe_sentinel.py

package info (click to toggle)
python-libcst 1.4.0-1.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,928 kB
  • sloc: python: 76,235; makefile: 10; sh: 2
file content (58 lines) | stat: -rw-r--r-- 2,443 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from enum import auto, Enum


class MaybeSentinel(Enum):
    """
    A :class:`MaybeSentinel` value is used as the default value for some attributes to
    denote that when generating code (when :attr:`Module.code` is evaluated) we should
    optionally include this element in order to generate valid code.

    :class:`MaybeSentinel` is only used for "syntactic trivia" that most users shouldn't
    care much about anyways, like commas, semicolons, and whitespace.

    For example, a function call's :attr:`Arg.comma` value defaults to
    :attr:`MaybeSentinel.DEFAULT`. A comma is required after every argument, except for
    the last one. If a comma is required and :attr:`Arg.comma` is a
    :class:`MaybeSentinel`, one is inserted.

    This makes manual node construction easier, but it also means that we safely add
    arguments to a preexisting function call without manually fixing the commas:

    >>> import libcst as cst
    >>> fn_call = cst.parse_expression("fn(1, 2)")
    >>> new_fn_call = fn_call.with_changes(
    ...     args=[*fn_call.args, cst.Arg(cst.Integer("3"))]
    ... )
    >>> dummy_module = cst.parse_module("")  # we need to use Module.code_for_node
    >>> dummy_module.code_for_node(fn_call)
    'fn(1, 2)'
    >>> dummy_module.code_for_node(new_fn_call)
    'fn(1, 2, 3)'

    Notice that a comma was automatically inserted after the second argument. Since the
    original second argument had no comma, it was initialized to
    :attr:`MaybeSentinel.DEFAULT`. During the code generation of the second argument, a
    comma was inserted to ensure that the resulting code is valid.

    .. warning::
       While this sentinel is used in place of nodes, it is not a :class:`CSTNode`, and
       will not be visited by a :class:`CSTVisitor`.

    Some other libraries, like `RedBaron`_, take other approaches to this problem.
    RedBaron's tree is mutable (LibCST's tree is immutable), and so they're able to
    solve this problem with `"proxy lists"
    <http://redbaron.pycqa.org/en/latest/proxy_list.html>`_. Both approaches come with
    different sets of tradeoffs.

    .. _RedBaron: http://redbaron.pycqa.org/en/latest/index.html
    """

    DEFAULT = auto()

    def __repr__(self) -> str:
        return str(self)