Python Reflection and Dynamic Parameter Handling for API Integration
Object-Oriented Programming Concepts
Class variables are defined directly within a class, while member variables reside in the __init__ method. When naming conflicts occur between them, member variables take precedence during instance access.
Python supports three method types:
- Static methods (
@staticmethod) - Class methods (
@classmethod) - Instance methods
class Example:
class_var = "initial_value"
def __init__(self, instance_val):
self.instance_val = instance_val
@classmethod
def class_method(cls):
print(cls.class_var)
@staticmethod
def static_method():
print(Example.class_var)
def instance_method(self):
print(self.instance_val)
if __name__ == '__main__':
obj = Example('custom_value')
print(obj.instance_val)
obj.instance_method()
Example.static_method()
Example.class_method()
Reflection Mechanism
Reflection enables runtime inspection and modification of class attributes without instantiation.
class Example:
class_val = "default"
def __init__(self, instance_val):
self.instance_val = instance_val
if __name__ == '__main__':
# Retrieve attribute
attr_value = getattr(Example, "class_val")
print("Class attribute:", attr_value)
# Add attribute
setattr(Example, "dynamic_attr", [1, 2, 3])
new_attr = getattr(Example, "dynamic_attr")
print("New attribute:", new_attr)
# Check and remove attribute
if hasattr(Example, "dynamic_attr"):
delattr(Example, "dynamic_attr")
print("Attribute removed")
# Access instance attribute
instance = Example('runtime_value')
member_val = getattr(instance, "instance_val")
print("Member value:", member_val)
Dynamic Parameter Substitution
Combine reflection with regex for dynamic configuration handling:
import re
class ConfigSubstitution:
@staticmethod
def replace_tokens(input_str):
pattern = r'#\{(.*?)\}#'
while re.search(pattern, input_str):
match = re.search(pattern, input_str)
token = match.group(1)
token_value = getattr(Configuration, token)
input_str = re.sub(pattern, token_value, input_str, 1)
return input_str
class Configuration:
username = "test_user"
password = "secure_pass"
if __name__ == '__main__':
data = '{"user":"#{username}#","pass":"#{password}#"}'
result = ConfigSubstitution.replace_tokens(data)
print(result)
API Parameter Handling Without Reflection
Alternative approach using environment variables:
import re
env_vars = {"api_user": "api_user1", "api_pass": "pass123"}
class ParameterHandler:
@staticmethod
def substitute_params(data_str):
pattern = r'#\{(.*?)\}#'
while re.search(pattern, data_str):
match = re.search(pattern, data_str)
key = match.group(1)
value = env_vars.get(key)
data_str = re.sub(pattern, value, data_str, 1)
return data_str
if __name__ == '__main__':
payload = '{"user":"#{api_user}#","pass":"#{api_pass}#"}'
processed = ParameterHandler.substitute_params(payload)
print(processed)
API Testing Framework Implementation
import requests
import json
import jsonpath
import re
shared_env = {}
class ApiClient:
BASE_URL = "http://api.example.com"
def __init__(self, test_case):
self.test_case = test_case
def execute_request(self):
headers = self.process_dynamic_values(self.test_case.get("headers", "{}"))
params = self.process_dynamic_values(self.test_case.get("params", "{}"))
endpoint = self.BASE_URL + self.test_case["endpoint"]
method = self.test_case["method"]
try:
if method.lower() == "get":
response = requests.get(endpoint, params=json.loads(params), headers=json.loads(headers))
self.extract_response_data(response)
return response
elif method.lower() == "post":
# Additional content-type handling would go here
pass
except Exception as e:
print(f"Request failed: {e}")
def extract_response_data(self, response):
extraction_rules = self.test_case.get("extraction_rules")
if not extraction_rules:
return
rules = json.loads(extraction_rules)
for var_name, json_path in rules.items():
value = jsonpath.jsonpath(response.json(), json_path)[0]
shared_env[var_name] = value
def process_dynamic_values(self, data):
if not data:
return data
pattern = r'\$\{(.*?)\}\$'
while re.search(pattern, data):
match = re.search(pattern, data)
key = match.group(1)
value = str(shared_env.get(key, ""))
data = re.sub(pattern, value, data, 1)
return data
File Upload Implementation
class FileUploadClient(ApiClient):
def execute_request(self):
headers = self.process_dynamic_values(self.test_case.get("headers", "{}"))
params = self.process_dynamic_values(self.test_case.get("params", "{}"))
endpoint = self.BASE_URL + self.test_case["endpoint"]
method = self.test_case["method"]
if method.lower() == "post" and "multipart/form-data" in headers:
headers_dict = json.loads(headers)
headers_dict.pop("Content-Type", None)
files = eval(params)
return requests.post(endpoint, files=files, headers=headers_dict)
elif method.lower() == "put":
return requests.put(endpoint, data=eval(params), headers=json.loads(headers))