Skip to content

Instantly share code, notes, and snippets.

@Jordan-Cottle
Last active August 16, 2022 19:38
Show Gist options
  • Save Jordan-Cottle/195c8298a4e899e204ed0aa08d46c8ea to your computer and use it in GitHub Desktop.
Save Jordan-Cottle/195c8298a4e899e204ed0aa08d46c8ea to your computer and use it in GitHub Desktop.
Inspect for self does not do a good job of making something private

Examples from https://stackoverflow.com/questions/69152143/private-attributes-in-pydantic

I added the bottom two tests in test.py, while post_pydantic.py is unmodified from https://stackoverflow.com/a/71742829/10778553

Output from running pytest looks like

$ pytest test.py 
======================================================================================================================== test session starts =========================================================================================================================
platform win32 -- Python 3.9.1, pytest-7.1.2, pluggy-1.0.0
rootdir: C:\Users\jdcottle\Documents\test
collected 3 items

test.py .FF                                                                                                                                                                                                                                                     [100%]

============================================================================================================================== FAILURES ============================================================================================================================== 
_________________________________________________________________________________________________________________ test_random_function_has_no_access _________________________________________________________________________________________________________________ 

    def test_random_function_has_no_access():
        """Verify that random functions can't set private attributes on models. """

        def edit_post(self, post: post_pydantic.Post, new_title: str):
            """Nefarious function attempting to circumvent protections. """

            post.title = new_title

        post_id = uuid4()
        post = post_pydantic.Post(post_id=post_id, title="Test Title")
        with pytest.raises(Exception):
>           edit_post(None, post, new_title="This should not work")
E           Failed: DID NOT RAISE <class 'Exception'>

test.py:37: Failed
__________________________________________________________________________________________________________________ test_random_class_has_no_access ___________________________________________________________________________________________________________________ 

    def test_random_class_has_no_access():
        """Verify that random classes can't set private attributes on models. """

        post_id = uuid4()
        post = post_pydantic.Post(post_id=post_id, title="Test Title")
        with pytest.raises(Exception):
>           Foo(post)
E           Failed: DID NOT RAISE <class 'Exception'>

test.py:54: Failed
====================================================================================================================== short test summary info ======================================================================================================================= 
FAILED test.py::test_random_function_has_no_access - Failed: DID NOT RAISE <class 'Exception'>
FAILED test.py::test_random_class_has_no_access - Failed: DID NOT RAISE <class 'Exception'>
==================================================================================================================== 2 failed, 1 passed in 0.53s ===================================================================================================================== 
import inspect
from typing import Optional
from uuid import UUID
from pydantic import BaseModel, Field
class Entity(BaseModel):
"""Base entity class."""
def __setattr__(self, name, value):
if "self" not in inspect.currentframe().f_back.f_locals:
raise Exception("set attr is protected")
super().__setattr__(name, value)
class PostId(UUID):
"""Post unique id."""
class Post(Entity):
"""Post."""
post_id: PostId = Field(description='unique post id')
title: Optional[str] = Field(None, description='title')
def change_title(self, new_title: str) -> None:
"""Changes title."""
self.title = new_title
from uuid import uuid4
import pytest
import post_pydantic
# This test passes, yay!
def test_pydantic():
"""Test pydantic varriant."""
post_id = uuid4()
post = post_pydantic.Post(post_id=post_id)
with pytest.raises(Exception) as e:
post.post_id = uuid4()
assert post.post_id == post_id
assert e.value.args[0] == "set attr is protected"
new_title = "New title"
post.change_title(new_title)
assert post.title == new_title
# This test does not pass
def test_random_function_has_no_access():
"""Verify that random functions can't set private attributes on models. """
def edit_post(self, post: post_pydantic.Post, new_title: str):
"""Nefarious function attempting to circumvent protections. """
post.title = new_title
post_id = uuid4()
post = post_pydantic.Post(post_id=post_id, title="Test Title")
with pytest.raises(Exception):
edit_post(None, post, new_title="This should not work")
assert post.title == "Test Title"
# That last tets was cheeky, nobody would do that right?
# This class is much more real example where it is very easy to accidentally break things
class Foo:
def __init__(self, post) -> None:
self.post = post
self.post.title = "Foo set this title!"
def test_random_class_has_no_access():
"""Verify that random classes can't set private attributes on models. """
post_id = uuid4()
post = post_pydantic.Post(post_id=post_id, title="Test Title")
with pytest.raises(Exception):
Foo(post)
assert post.title == "Test Title"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment