Skip to content

Style guide for sentinels #12

@legendof-selda

Description

@legendof-selda

I propose a style guide when we create a sentinel.
Common standard python sentinels like 'True', 'False', 'None' follow this style

  1. Naming Conventions: The requirement for sentinel names to be in title case and have descriptive names is clear and aligns with Python's naming conventions. This ensures that sentinel names are meaningful and self-explanatory. Example: Undefined, Missing.

  2. Avoiding Conflicts: The guideline to avoid overriding standard Python sentinels like None, True, and False is crucial to prevent confusion and unexpected behavior. It's important to emphasize that sentinels should not interfere with the behavior of built-in constants.

  3. Usage of Sentinels: The suggestion to use sentinels only when None cannot be used as a sentinel value is reasonable. It encourages developers to leverage the existing None sentinel when it fits the purpose as this is normal python standard.

  4. Complexity of Sentinels: The recommendation to keep sentinels simple and not create complex classes or subclasses aligns with the principle of keeping code straightforward and understandable.

  5. Consistency: Sentinels created must be consistent throughout the entire project. The created Sentinels must have the same meaning wherever it is used. The guideline to maintain consistency in the meaning of sentinels throughout a project is essential for code clarity and predictability.

  6. Sentinel Comparison Order: Sentinels cannot be compared with each other. Use Enums instead.

  7. Explicit Boolean Evaluation: Provide Boolean evaluation explicitly rather than setting it to be True by default as this avoids confusion.

    Example:

    Undefined = Sentinel('<undefined>')
    
    # Let's say a function can return `Undefined `
    value = foo()
    
    if value:
       print(f"Value received is {value}")
    else:
      raise ValueError("Value wan't available")

    Here you can see that, it makes more sense that Undefined was False rather than it being True by default. If a sentinel must be True or False depends on the context of the sentinel.
    Example: Undefined should be evaluated to False and Success can be evaluated to True, while EOF doesn't necessarily mean True or False and thus can have an "Invalid State" or it is "ambiguous".
    So, I propose that we should pass in explicitly if a Sentinel is truthy or not, using the truthy argument. The truthy argument can be True, False or None (default). If truthy is None, then we should raise a ValueError. This makes sentinels more explicit and can force the users to give meaning to a sentinel and enforces them to use is operator to evaluate.

    Example:

    Undefined = Sentinel('<undefined>', truthy=False)
    Success= Sentinel('<successful>', truthy=True)
    EOF= Sentinel('<undefined>')
    
    value = Undefined
    if value:
      print("value is defined.")
    
    value = Success
    
    if value:
      print("Function ran successfully")
    
    value = EOF
    
    # This results in a ValueError
    # bool(value)

    This makes the behavior of sentinels more explicit and avoids any ambiguity.

Can we discuss this and possibly add it into PEP 661 Proposal?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions