Skip to main content
Glama
test_lsp_python_props.py5.1 kB
""" Tests for Python class property extraction in lsp_utils module. """ import ast from pathlib import Path from unittest.mock import MagicMock, mock_open, patch import pytest from yellhorn_mcp.utils.lsp_utils import _class_attributes_from_ast, extract_python_api def test_class_attributes_from_ast(): """Test extracting attributes from Python class AST.""" # Class with typed and untyped attributes source = """ class User: name: str age: int = 0 is_active = True _private = "hidden" __very_private = "very hidden" """ tree = ast.parse(source) node = tree.body[0] # First node is our class attrs = _class_attributes_from_ast(node) # Should include public attributes assert "name: str" in attrs assert "age: int" in attrs assert "is_active" in attrs # Should not include private attributes assert not any("_private" in attr for attr in attrs) assert not any("__very_private" in attr for attr in attrs) def test_extract_python_api_with_class_attributes(): """Test extracting Python API with class attributes.""" with patch("builtins.open", MagicMock()) as mock_open: mock_file = MagicMock() mock_file.__enter__.return_value.read.return_value = """ class User: \"\"\"A user model.\"\"\" name: str age: int = 0 is_active = True _private = "hidden" def get_display_name(self) -> str: \"\"\"Return user's display name.\"\"\" return self.name def _private_method(self): pass from dataclasses import dataclass @dataclass class Product: \"\"\"A product model.\"\"\" id: int name: str price: float = 0.0 _internal_code: str = "unknown" def get_price_with_tax(self, tax_rate: float) -> float: \"\"\"Calculate price with tax.\"\"\" return self.price * (1 + tax_rate) # This simulates a Pydantic model class Order: \"\"\"An order model.\"\"\" id: int products: list total: float _status: str = "pending" def calculate_total(self) -> float: \"\"\"Calculate order total.\"\"\" pass """ mock_open.return_value = mock_file with patch("pathlib.Path.is_file", return_value=True): with patch("ast.unparse", return_value="str"): # Mock unparse for Python < 3.9 signatures = extract_python_api(Path("/mock/file.py")) # Check for class signatures with docstrings assert "class User # A user model." in signatures assert "class Product # A product model." in signatures assert "class Order # An order model." in signatures # Check for attributes assert " name: str" in signatures assert " age: int" in signatures assert " is_active" in signatures # Check for dataclass properties assert " id: int" in signatures assert " name: str" in signatures assert " price: float" in signatures # Check for pydantic-style model properties assert " id: int" in signatures assert " products: list" in signatures assert " total: float" in signatures # Check for methods with typed parameters and return types found_display_name = False found_price_with_tax = False found_calculate_total = False for sig in signatures: if "def User.get_display_name" in sig: found_display_name = True # Just check for the presence of return type annotation assert "->" in sig if "def Product.get_price_with_tax" in sig: found_price_with_tax = True # Just check for the presence of type annotation, not the exact type assert "tax_rate:" in sig assert "->" in sig if "def Order.calculate_total" in sig: found_calculate_total = True # Just check for the presence of return type annotation assert "->" in sig assert found_display_name, "Could not find get_display_name method with return type" assert ( found_price_with_tax ), "Could not find get_price_with_tax method with parameter and return types" assert ( found_calculate_total ), "Could not find calculate_total method with return type" # Check for exclusion of private items assert not any("_private" in sig for sig in signatures) assert not any("_internal_code" in sig for sig in signatures) assert not any("_status" in sig for sig in signatures) assert not any("_private_method" in sig for sig in signatures)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/msnidal/yellhorn-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server