Search code examples
pythonpython-typingpyright

How can I dynamically define a function signature for pylance etc.?


I am creating a framework built on pydantic.BaseModel that will manage the putting and getting of records from dynamodb.

An example of a model is as follows:

class User(DynamoDanticBaseModel):
   user_id: str
   first_name: str

   @classmethod
   def primary_key(cls) -> str:
      return "USER#{user_id}"

   @classmethod
   def sort_key(cls) -> str:
      return "USER"

Using __init_subclass__, I can fetch the formatable strings and validate that the required parts of the PK/SK are defined on the class.

Defined generically is a classmethod get that uses these values to call boto3 underneath and fetch the a record, fill in the model and return the new model instance.

user = User(user_id='example', first_name='Bacon')
user.put(dynamo_table_ref)

user = User.get(dynamo_table_ref, user_id='example')
user.first_name # => "Bacon"

This all works. My issue is that the get methods interface is: def get(cls, table, **kwargs):. A developer cannot see what params are needed.

Is there a way to layout my code, or a pattern I should use, that would mean that I could have the signature hint show the required parameters? Dynamic to the required parameters defined in the primary key and sort key method?

e.g. def get(cls, table, user_id: str):


Solution

  • Unfortunately, no. It's not possible to define a parameter based on a dynamic string (or a Literal['foo']/LiteralString, for that matter).

    The best you can do is to copy the generated __init__'s signature over, but you can't change their requiredness.

    The second best solution is to write a Mypy plugin. This, of course, won't work with any other type checkers, including Pyright/Pylance.