Pattern matching on constants that aren't in a namespace (especially sentinels)

To gauge sentiment, I created a poll to ask which syntax people prefer.

I’ve used the same example for all snippets below. (Thanks to @cdce8p and @a-reich whose posts have been the source for them.)

This is the context for the example:

MISSING = sentinel("MISSING")
teamname = "Team A"
attrname = "var"

1. Status quo

This already works in Python today:

match data:
    case _x if _x == MISSING:
        pass
    case {'team': name} if name == teamname:
        pass
    case ast.Attribute(attrname=n) if n == attrname:
        pass

Generalizations of the existing dot syntax

namespace.constant already works with the current syntax, so these proposals try to generalize it.

2. Leading dot

match data:
    case .MISSING:
        pass
    case {'team': .teamname}:
        pass
    case ast.Attribute(attrname=.attrname):
        pass

3. global/local

(requires local as a new (soft?) keyword)

match data:
    case global.MISSING:
        pass
    case {'team': local.teamname}:
        pass
    case ast.Attribute(attrname=local.attrname):
        pass

4. Only global

(it’s already a keyword and covers the sentinel use-case)

match data:
    case global.MISSING:
        pass
    case {'team': name} if name == teamname:
        pass
    case ast.Attribute(attrname=n) if n == attrname:
        pass

5. globals/locals

(these are currently builtins)

match data:
    case globals.MISSING:
        pass
    case {'team': locals.teamname}:
        pass
    case ast.Attribute(attrname=locals.attrname):
        pass

Dotless syntax proposals

These proposals are not modeled after the existing dot-based syntax.

6. prefixed is

match data:
    case is MISSING:
        pass
    case {'team': is teamname}:
        pass
    case ast.Attribute(attrname is attrname):
        pass

7. prefixed ==

match data:
    case ==MISSING:
        pass
    case {'team': ==teamname}:
        pass
    case ast.Attribute(attrname==attrname):
        pass

8. as is

match data:
    case MISSING as is:
        pass
    case {'team': teamname as is}:
        pass
    case ast.Attribute(attrname=attrname as is):
        pass

9. Other keyword-based syntax

For example with value

match data:
    case value MISSING:
        pass
    case {'team': value teamname}:
        pass
    case ast.Attribute(attrname=value attrname):
        pass

Other keywords proposed here: with, for.

10. Curly brackets

(this is currently invalid syntax, so it would be available to be reinterpreted)

match data:
    case {MISSING}:
        pass
    case {'team': {teamname}}:
        pass
    case ast.Attribute(attrname={attrname}):
        pass
  • (1) Status quo
  • (2) Leading dot
  • (3) global/local
  • (4) Only global
  • (5) globals/locals
  • (6) prefixed “is”
  • (7) prefixed “==”
  • (8) “as is”
  • (9) Other keyword-based syntax
  • (10) Curly brackets
0 voters
1 Like

This is actually a breaking change since a soft keyword can only be introduced safely in a situation, where a name cannot appear (like in type foo=bar, where it impossible to have two names left of the assignment). Since it is possible to have a class local, it is easily possible that case local.foo has already a meaning.

That is an interesting option and would not even require any changes to the syntax. It would be sufficient if globals and locals would provide a __getattr__ function.

Emulation of a __getattr__ member for globals
>>> import builtins
>>> class _Globals:
...     def __call__(self):
...         return builtins.globals()
...     def __getattr__(self, name):
...         return globals()[name]
...         
>>> globals = _Globals()
>>> 
>>> x = 9
>>> globals.x
9
>>> y = 10
>>> match 10:
...     case globals.x:
...         print("Matched x")
...     case globals.y:
...         print("Matched y")
1 Like

Changing the meaning of case (global|globals|local|locals).<ATTR> is a breaking change. A simple search on GitHub shows case globals.ATTR is not unheard of.

1 Like

No, you are conflating different things here. case global.<ATTR> is a syntax error currently, so changing it would certainly not be a breaking change! Changing case local.<ATTR> would be a breaking change as I just said. Changing the grammar for case (globals|locals).<ATTR> would be a breaking change, but just allowing attribute lookup for these two builtin function would not be a breaking change.

2 Likes

Not only that, it should be considered bad practice to overwrite built-in names anyways, so globals = ... should work in user code, but should be avoided too.