From 7e1b5bd24bb94b2d8e437753857f173e7783981f Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Sun, 23 Aug 2026 20:59:45 -0700 Subject: [PATCH 1/7] Bind names to metaclass fields at runtime --- ultimatepython/advanced/meta_class.py | 36 ++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/ultimatepython/advanced/meta_class.py b/ultimatepython/advanced/meta_class.py index db135ae..f8acaa4 100644 --- a/ultimatepython/advanced/meta_class.py +++ b/ultimatepython/advanced/meta_class.py @@ -71,8 +71,12 @@ def __new__(mcs, name: str, bases: tuple[type, ...], attrs: dict[str, Any]) -> " if isinstance(base, ModelMeta): kls.model_fields.update(base.model_fields) - # Fill model fields from itself - kls.model_fields.update({field_name: field_obj for field_name, field_obj in attrs.items() if isinstance(field_obj, BaseField)}) + # Fill model fields from itself. Each field is "late bound" to its + # declared attribute name here: the field object had no name when it + # was constructed, so we hand it the name at class creation time + for field_name, field_obj in attrs.items(): + if isinstance(field_obj, BaseField): + kls.model_fields[field_name] = field_obj.bind(field_name) # Register a real table (a table with valid `model_name`) to # the metaclass `table` registry. After all the tables are @@ -102,7 +106,19 @@ def __init__(self, table_name: str, table_fields: dict[str, "BaseField"]) -> Non class BaseField(ABC): - """Base field.""" + """Base field. + + A field carries its declared attribute name in `name`. It does not + know that name until the metaclass calls `bind` at class creation + time, which is the classic "late binding" metaclass trick. + """ + + name: str | None = None + + def bind(self, name: str) -> "BaseField": + """Bind this field to its declared attribute name at runtime.""" + self.name = name + return self class CharField(BaseField): @@ -158,9 +174,17 @@ def main() -> None: assert "username" in UserModel.model_fields assert "address" in AddressModel.model_fields - # Real models are registered at runtime with `ModelMeta` - assert UserModel.is_registered - assert AddressModel.is_registered + # Each field is late-bound to its declared attribute name at runtime + assert UserModel.model_fields["username"].name == "username" + assert UserModel.model_fields["password"].name == "password" + assert AddressModel.model_fields["state"].name == "state" + + # Inherited fields keep the name they were bound with in the base class + assert UserModel.model_fields["row_id"].name == "row_id" + assert AddressModel.model_fields["row_id"].name == "row_id" + + # A field built by hand and not yet bound has no name yet + assert IntegerField().name is None # Real models have a `ModelTable` that can be used for DB setup assert isinstance(ModelMeta.tables[UserModel.model_name], ModelTable) From eeccc4c62540bbf1e7c40b1b562597c77cea4cdf Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Sun, 23 Aug 2026 21:02:17 -0700 Subject: [PATCH 2/7] Make primary key selection more robust --- ultimatepython/advanced/meta_class.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ultimatepython/advanced/meta_class.py b/ultimatepython/advanced/meta_class.py index f8acaa4..b341186 100644 --- a/ultimatepython/advanced/meta_class.py +++ b/ultimatepython/advanced/meta_class.py @@ -103,6 +103,10 @@ class ModelTable: def __init__(self, table_name: str, table_fields: dict[str, "BaseField"]) -> None: self.table_name = table_name self.table_fields = table_fields + self.primary_key = next( + (field_name for field_name, field in table_fields.items() if field.primary_key), + None, + ) class BaseField(ABC): @@ -114,6 +118,10 @@ class BaseField(ABC): """ name: str | None = None + primary_key: bool = False + + def __init__(self, *, primary_key: bool = False) -> None: + self.primary_key = primary_key def bind(self, name: str) -> "BaseField": """Bind this field to its declared attribute name at runtime.""" @@ -141,7 +149,7 @@ class BaseModel(metaclass=ModelMeta): """ __abstract__ = True # This is NOT a real table - row_id = IntegerField() + row_id = IntegerField(primary_key=True) class UserModel(BaseModel): @@ -183,6 +191,12 @@ def main() -> None: assert UserModel.model_fields["row_id"].name == "row_id" assert AddressModel.model_fields["row_id"].name == "row_id" + # Primary keys are tracked on the field and the generated table metadata + assert UserModel.model_fields["row_id"].primary_key is True + assert AddressModel.model_fields["row_id"].primary_key is True + assert UserModel.model_table.primary_key == "row_id" + assert AddressModel.model_table.primary_key == "row_id" + # A field built by hand and not yet bound has no name yet assert IntegerField().name is None From 73768820816c0ae785bb9c35e7de9fb11668f50f Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Sun, 23 Aug 2026 21:05:09 -0700 Subject: [PATCH 3/7] Generate table DDL given its payload --- ultimatepython/advanced/meta_class.py | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ultimatepython/advanced/meta_class.py b/ultimatepython/advanced/meta_class.py index b341186..6424283 100644 --- a/ultimatepython/advanced/meta_class.py +++ b/ultimatepython/advanced/meta_class.py @@ -108,6 +108,16 @@ def __init__(self, table_name: str, table_fields: dict[str, "BaseField"]) -> Non None, ) + def ddl(self) -> str: + """Build a simple CREATE TABLE statement for the schema.""" + columns = [] + for field_name, field in self.table_fields.items(): + sql = field.column_definition(field_name) + if field.primary_key: + sql = f"{sql} PRIMARY KEY" + columns.append(sql) + return f"CREATE TABLE {self.table_name} ({', '.join(columns)});" + class BaseField(ABC): """Base field. @@ -128,14 +138,24 @@ def bind(self, name: str) -> "BaseField": self.name = name return self + def column_definition(self, field_name: str) -> str: + """Return the column SQL definition for this type.""" + raise NotImplementedError + class CharField(BaseField): """Character field.""" + def column_definition(self, field_name: str) -> str: + return f"{field_name} VARCHAR(255)" + class IntegerField(BaseField): """Integer field.""" + def column_definition(self, field_name: str) -> str: + return f"{field_name} INTEGER" + class BaseModel(metaclass=ModelMeta): """Base model. @@ -204,6 +224,16 @@ def main() -> None: assert isinstance(ModelMeta.tables[UserModel.model_name], ModelTable) assert isinstance(ModelMeta.tables[AddressModel.model_name], ModelTable) + # A table can generate a simple CREATE TABLE statement from its fields + assert ( + UserModel.model_table.ddl() + == "CREATE TABLE user_rocks (row_id INTEGER PRIMARY KEY, username VARCHAR(255), password VARCHAR(255), age VARCHAR(255), sex VARCHAR(255));" + ) + assert ( + AddressModel.model_table.ddl() + == "CREATE TABLE address (row_id INTEGER PRIMARY KEY, user_id INTEGER, address VARCHAR(255), state VARCHAR(255), zip_code VARCHAR(255));" + ) + # Base model is given special treatment at runtime assert not BaseModel.is_registered assert BaseModel.model_name is None From ce39cb60132a9c05be75c0c5d79733affe0e8c75 Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Sun, 23 Aug 2026 21:14:21 -0700 Subject: [PATCH 4/7] Make field constructors more realistic --- ultimatepython/advanced/meta_class.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ultimatepython/advanced/meta_class.py b/ultimatepython/advanced/meta_class.py index 6424283..024ca63 100644 --- a/ultimatepython/advanced/meta_class.py +++ b/ultimatepython/advanced/meta_class.py @@ -115,6 +115,10 @@ def ddl(self) -> str: sql = field.column_definition(field_name) if field.primary_key: sql = f"{sql} PRIMARY KEY" + if field.default is not None and not field.primary_key: + sql = f"{sql} DEFAULT {field.default!r}" + if not field.nullable and not field.primary_key: + sql = f"{sql} NOT NULL" columns.append(sql) return f"CREATE TABLE {self.table_name} ({', '.join(columns)});" @@ -129,9 +133,13 @@ class BaseField(ABC): name: str | None = None primary_key: bool = False + nullable: bool = True + default: Any = None - def __init__(self, *, primary_key: bool = False) -> None: + def __init__(self, *, primary_key: bool = False, nullable: bool = True, default: Any = None) -> None: self.primary_key = primary_key + self.nullable = nullable + self.default = default def bind(self, name: str) -> "BaseField": """Bind this field to its declared attribute name at runtime.""" @@ -146,13 +154,20 @@ def column_definition(self, field_name: str) -> str: class CharField(BaseField): """Character field.""" + def __init__(self, *, max_length: int = 255, primary_key: bool = False, nullable: bool = True, default: Any = None) -> None: + super().__init__(primary_key=primary_key, nullable=nullable, default=default) + self.max_length = max_length + def column_definition(self, field_name: str) -> str: - return f"{field_name} VARCHAR(255)" + return f"{field_name} VARCHAR({self.max_length})" class IntegerField(BaseField): """Integer field.""" + def __init__(self, *, primary_key: bool = False, nullable: bool = True, default: Any = None) -> None: + super().__init__(primary_key=primary_key, nullable=nullable, default=default) + def column_definition(self, field_name: str) -> str: return f"{field_name} INTEGER" @@ -220,6 +235,10 @@ def main() -> None: # A field built by hand and not yet bound has no name yet assert IntegerField().name is None + # Char fields can carry a max length, which is used in generated SQL + assert UserModel.model_fields["username"].max_length == 255 + assert AddressModel.model_fields["address"].max_length == 255 + # Real models have a `ModelTable` that can be used for DB setup assert isinstance(ModelMeta.tables[UserModel.model_name], ModelTable) assert isinstance(ModelMeta.tables[AddressModel.model_name], ModelTable) From fee1a72b4e4ffd8e48616b7580cd56ea39200fc8 Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Sun, 23 Aug 2026 21:24:48 -0700 Subject: [PATCH 5/7] Satisfy mypy and ruff linting --- ultimatepython/advanced/meta_class.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ultimatepython/advanced/meta_class.py b/ultimatepython/advanced/meta_class.py index 024ca63..0b13ad0 100644 --- a/ultimatepython/advanced/meta_class.py +++ b/ultimatepython/advanced/meta_class.py @@ -131,10 +131,10 @@ class BaseField(ABC): time, which is the classic "late binding" metaclass trick. """ - name: str | None = None - primary_key: bool = False - nullable: bool = True - default: Any = None + name: str | None + primary_key: bool + nullable: bool + default: Any def __init__(self, *, primary_key: bool = False, nullable: bool = True, default: Any = None) -> None: self.primary_key = primary_key @@ -154,6 +154,8 @@ def column_definition(self, field_name: str) -> str: class CharField(BaseField): """Character field.""" + max_length: int + def __init__(self, *, max_length: int = 255, primary_key: bool = False, nullable: bool = True, default: Any = None) -> None: super().__init__(primary_key=primary_key, nullable=nullable, default=default) self.max_length = max_length @@ -229,6 +231,8 @@ def main() -> None: # Primary keys are tracked on the field and the generated table metadata assert UserModel.model_fields["row_id"].primary_key is True assert AddressModel.model_fields["row_id"].primary_key is True + assert UserModel.model_table is not None + assert AddressModel.model_table is not None assert UserModel.model_table.primary_key == "row_id" assert AddressModel.model_table.primary_key == "row_id" @@ -236,14 +240,20 @@ def main() -> None: assert IntegerField().name is None # Char fields can carry a max length, which is used in generated SQL - assert UserModel.model_fields["username"].max_length == 255 - assert AddressModel.model_fields["address"].max_length == 255 + username_field = UserModel.model_fields["username"] + address_field = AddressModel.model_fields["address"] + assert isinstance(username_field, CharField) + assert isinstance(address_field, CharField) + assert username_field.max_length == 255 + assert address_field.max_length == 255 # Real models have a `ModelTable` that can be used for DB setup assert isinstance(ModelMeta.tables[UserModel.model_name], ModelTable) assert isinstance(ModelMeta.tables[AddressModel.model_name], ModelTable) # A table can generate a simple CREATE TABLE statement from its fields + assert UserModel.model_table is not None + assert AddressModel.model_table is not None assert ( UserModel.model_table.ddl() == "CREATE TABLE user_rocks (row_id INTEGER PRIMARY KEY, username VARCHAR(255), password VARCHAR(255), age VARCHAR(255), sex VARCHAR(255));" From 864dabc2ecadadb3ca2dbe5a2fe742c2b99edc24 Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Sun, 23 Aug 2026 21:35:39 -0700 Subject: [PATCH 6/7] Fix BaseField within meta_class --- ultimatepython/advanced/meta_class.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ultimatepython/advanced/meta_class.py b/ultimatepython/advanced/meta_class.py index 0b13ad0..e7c1f9b 100644 --- a/ultimatepython/advanced/meta_class.py +++ b/ultimatepython/advanced/meta_class.py @@ -137,6 +137,7 @@ class BaseField(ABC): default: Any def __init__(self, *, primary_key: bool = False, nullable: bool = True, default: Any = None) -> None: + self.name = None self.primary_key = primary_key self.nullable = nullable self.default = default From 1d4e71cac5bb07548a756bdd3c1bf2dfe88403e0 Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Sun, 23 Aug 2026 21:44:11 -0700 Subject: [PATCH 7/7] Improve meta_class code coverage --- ultimatepython/advanced/meta_class.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ultimatepython/advanced/meta_class.py b/ultimatepython/advanced/meta_class.py index e7c1f9b..4584a14 100644 --- a/ultimatepython/advanced/meta_class.py +++ b/ultimatepython/advanced/meta_class.py @@ -195,7 +195,7 @@ class UserModel(BaseModel): __table_name__ = "user_rocks" # This is a custom table name username = CharField() - password = CharField() + password = CharField(nullable=False, default="guest") age = CharField() sex = CharField() @@ -255,13 +255,12 @@ def main() -> None: # A table can generate a simple CREATE TABLE statement from its fields assert UserModel.model_table is not None assert AddressModel.model_table is not None - assert ( - UserModel.model_table.ddl() - == "CREATE TABLE user_rocks (row_id INTEGER PRIMARY KEY, username VARCHAR(255), password VARCHAR(255), age VARCHAR(255), sex VARCHAR(255));" + assert UserModel.model_table.ddl() == ( + "CREATE TABLE user_rocks " + "(row_id INTEGER PRIMARY KEY, username VARCHAR(255), password VARCHAR(255) DEFAULT 'guest' NOT NULL, age VARCHAR(255), sex VARCHAR(255));" ) - assert ( - AddressModel.model_table.ddl() - == "CREATE TABLE address (row_id INTEGER PRIMARY KEY, user_id INTEGER, address VARCHAR(255), state VARCHAR(255), zip_code VARCHAR(255));" + assert AddressModel.model_table.ddl() == ( + "CREATE TABLE address (row_id INTEGER PRIMARY KEY, user_id INTEGER, address VARCHAR(255), state VARCHAR(255), zip_code VARCHAR(255));" ) # Base model is given special treatment at runtime