2424
2525
2626def blender (drb1 , drb3 = "" , drb4 = "" , drb5 = "" ):
27+ """Blend DRB1 typing with DRB3/4/5 to determine expected DRBX expression
28+
29+ The DRB locus region contains multiple genes (DRB1, DRB3, DRB4, DRB5) but
30+ only certain combinations are expressed based on DRB1 allele families.
31+ This function validates that the provided DRBX typing matches the expected
32+ pattern based on DRB1 and returns the appropriate DRBX expression.
33+
34+ Args:
35+ drb1: DRB1 typing (e.g., 'DRB1*03:01+DRB1*04:01')
36+ drb3: DRB3 typing if present
37+ drb4: DRB4 typing if present
38+ drb5: DRB5 typing if present
39+
40+ Returns:
41+ Expected DRBX expression based on DRB1 families, or empty string
42+
43+ Raises:
44+ DRBXBlenderError: If provided DRBX doesn't match expected pattern
45+ """
46+ # Parse DRB1 typing to extract allele families
2747 try :
2848 drb1_1 , drb1_2 = drb1 .split ("+" )
2949 drb1_allele_1 = drb1_1 .split ("*" )[1 ]
3050 drb1_allele_2 = drb1_2 .split ("*" )[1 ]
31- drb1_fam_1 = drb1_allele_1 .split (":" )[0 ]
32- drb1_fam_2 = drb1_allele_2 .split (":" )[0 ]
51+ drb1_fam_1 = drb1_allele_1 .split (":" )[0 ] # First field (family)
52+ drb1_fam_2 = drb1_allele_2 .split (":" )[0 ] # First field (family)
3353 except Exception :
3454 return ""
55+ # Map DRB1 families to expected DRBX genes (3, 4, 5, or 0 for none)
3556 x1 = expdrbx (drb1_fam_1 )
3657 x2 = expdrbx (drb1_fam_2 )
58+ # Create sorted combination code (e.g., '34', '44', '00')
3759 xx = "" .join (sorted ([x1 , x2 ]))
3860
61+ # Handle case where no DRBX genes should be expressed
3962 if xx == "00" :
4063 if drb3 != "" :
4164 raise DRBXBlenderError ("DRB3" , "none" )
@@ -45,17 +68,17 @@ def blender(drb1, drb3="", drb4="", drb5=""):
4568 raise DRBXBlenderError ("DRB5" , "none" )
4669 return ""
4770
48- # handle 03
71+ # Handle heterozygous case: one allele expresses DRB3, other doesn't
4972 if xx == "03" :
5073 if drb4 != "" :
5174 raise DRBXBlenderError ("DRB4" , "none" )
5275 if drb5 != "" :
5376 raise DRBXBlenderError ("DRB5" , "none" )
5477 if drb3 != "" :
55- # if 2 copies
78+ # Check if DRB3 has two copies (homozygous DRB3-expressing alleles)
5679 drb3_g = drb3 .split ("+" )
5780 if len (drb3_g ) == 2 :
58- # homozygous, return one copy
81+ # If homozygous, return one copy
5982 if drb3_g [1 ] == drb3_g [0 ]:
6083 return drb3_g [0 ]
6184 else :
@@ -64,17 +87,17 @@ def blender(drb1, drb3="", drb4="", drb5=""):
6487 return drb3
6588 return ""
6689
67- # handle 04
90+ # Handle heterozygous case: one allele expresses DRB4, other doesn't
6891 if xx == "04" :
6992 if drb3 != "" :
7093 raise DRBXBlenderError ("DRB3" , "none" )
7194 if drb5 != "" :
7295 raise DRBXBlenderError ("DRB5" , "none" )
7396 if drb4 != "" :
74- # if 2 copies
97+ # Check if DRB4 has two copies (homozygous DRB4-expressing alleles)
7598 drb4_g = drb4 .split ("+" )
7699 if len (drb4_g ) == 2 :
77- # homozygous, return one copy
100+ # If homozygous, return one copy
78101 if drb4_g [1 ] == drb4_g [0 ]:
79102 return drb4_g [0 ]
80103 else :
@@ -83,25 +106,25 @@ def blender(drb1, drb3="", drb4="", drb5=""):
83106 return drb4
84107 return ""
85108
86- # handle 05
109+ # Handle heterozygous case: one allele expresses DRB5, other doesn't
87110 if xx == "05" :
88111 if drb3 != "" :
89112 raise DRBXBlenderError ("DRB3" , "none" )
90113 if drb4 != "" :
91114 raise DRBXBlenderError ("DRB4" , "none" )
92115 if drb5 != "" :
93- # if 2 copies
116+ # Check if DRB5 has two copies (homozygous DRB5-expressing alleles)
94117 drb5_g = drb5 .split ("+" )
95118 if len (drb5_g ) == 2 :
96- # homozygous, return one copy
119+ # If homozygous, return one copy
97120 if drb5_g [1 ] == drb5_g [0 ]:
98121 return drb5_g [0 ]
99122 else :
100123 raise DRBXBlenderError ("DRB5 het" , "hom" )
101124 else :
102125 return drb5
103126 return ""
104- # handle 33
127+ # Handle homozygous DRB3-expressing case
105128 if xx == "33" :
106129 if drb4 != "" :
107130 raise DRBXBlenderError ("DRB4" , "none" )
@@ -111,7 +134,7 @@ def blender(drb1, drb3="", drb4="", drb5=""):
111134 return drb3
112135 return ""
113136
114- # handle 44
137+ # Handle homozygous DRB4-expressing case
115138 if xx == "44" :
116139 if drb3 != "" :
117140 raise DRBXBlenderError ("DRB3" , "none" )
@@ -121,7 +144,7 @@ def blender(drb1, drb3="", drb4="", drb5=""):
121144 return drb4
122145 return ""
123146
124- # handle 55
147+ # Handle homozygous DRB5-expressing case
125148 if xx == "55" :
126149 if drb3 != "" :
127150 raise DRBXBlenderError ("DRB3" , "none" )
@@ -131,28 +154,28 @@ def blender(drb1, drb3="", drb4="", drb5=""):
131154 return drb5
132155 return ""
133156
134- # handle 34
157+ # Handle heterozygous case: one allele expresses DRB3, other expresses DRB4
135158 if xx == "34" :
136159 if drb5 != "" :
137160 raise DRBXBlenderError ("DRB5" , "none" )
138161 retg = []
139162
140163 if drb3 != "" :
141- # if 2 copies
164+ # Process DRB3 typing
142165 drb3_g = drb3 .split ("+" )
143166 if len (drb3_g ) == 2 :
144- # homozygous, return one copy
167+ # If homozygous, return one copy
145168 if drb3_g [1 ] == drb3_g [0 ]:
146169 retg .append (drb3_g [0 ])
147170 else :
148171 raise DRBXBlenderError ("DRB3 het" , "hom" )
149172 elif len (drb3_g ) == 1 :
150173 retg .append (drb3_g [0 ])
151174 if drb4 != "" :
152- # if 2 copies
175+ # Process DRB4 typing
153176 drb4_g = drb4 .split ("+" )
154177 if len (drb4_g ) == 2 :
155- # homozygous, return one copy
178+ # If homozygous, return one copy
156179 if drb4_g [1 ] == drb4_g [0 ]:
157180 retg .append (drb4_g [0 ])
158181 else :
@@ -162,28 +185,28 @@ def blender(drb1, drb3="", drb4="", drb5=""):
162185
163186 return "+" .join (retg )
164187
165- # handle 35
188+ # Handle heterozygous case: one allele expresses DRB3, other expresses DRB5
166189 if xx == "35" :
167190 if drb4 != "" :
168191 raise DRBXBlenderError ("DRB4" , "none" )
169192 retg = []
170193
171194 if drb3 != "" :
172- # if 2 copies
195+ # Process DRB3 typing
173196 drb3_g = drb3 .split ("+" )
174197 if len (drb3_g ) == 2 :
175- # homozygous, return one copy
198+ # If homozygous, return one copy
176199 if drb3_g [1 ] == drb3_g [0 ]:
177200 retg .append (drb3_g [0 ])
178201 else :
179202 raise DRBXBlenderError ("DRB3 het" , "hom" )
180203 elif len (drb3_g ) == 1 :
181204 retg .append (drb3_g [0 ])
182205 if drb5 != "" :
183- # if 2 copies
206+ # Process DRB5 typing
184207 drb5_g = drb5 .split ("+" )
185208 if len (drb5_g ) == 2 :
186- # homozygous, return one copy
209+ # If homozygous, return one copy
187210 if drb5_g [1 ] == drb5_g [0 ]:
188211 retg .append (drb5_g [0 ])
189212 else :
@@ -193,28 +216,28 @@ def blender(drb1, drb3="", drb4="", drb5=""):
193216
194217 return "+" .join (retg )
195218
196- # handle 45
219+ # Handle heterozygous case: one allele expresses DRB4, other expresses DRB5
197220 if xx == "45" :
198221 if drb3 != "" :
199222 raise DRBXBlenderError ("DRB3" , "none" )
200223 retg = []
201224
202225 if drb4 != "" :
203- # if 2 copies
226+ # Process DRB4 typing
204227 drb4_g = drb4 .split ("+" )
205228 if len (drb4_g ) == 2 :
206- # homozygous, return one copy
229+ # If homozygous, return one copy
207230 if drb4_g [1 ] == drb4_g [0 ]:
208231 retg .append (drb4_g [0 ])
209232 else :
210233 raise DRBXBlenderError ("DRB4 het" , "hom" )
211234 elif len (drb4_g ) == 1 :
212235 retg .append (drb4_g [0 ])
213236 if drb5 != "" :
214- # if 2 copies
237+ # Process DRB5 typing
215238 drb5_g = drb5 .split ("+" )
216239 if len (drb5_g ) == 2 :
217- # homozygous, return one copy
240+ # If homozygous, return one copy
218241 if drb5_g [1 ] == drb5_g [0 ]:
219242 retg .append (drb5_g [0 ])
220243 else :
@@ -229,17 +252,45 @@ def blender(drb1, drb3="", drb4="", drb5=""):
229252
230253
231254def expdrbx (drb1_fam ):
255+ """Map DRB1 allele family to expected DRBX gene expression
256+
257+ Different DRB1 allele families are associated with expression of
258+ different DRBX genes based on linkage disequilibrium patterns.
259+
260+ Args:
261+ drb1_fam: DRB1 allele family (first field, e.g., '03', '04', '15')
262+
263+ Returns:
264+ String indicating expected DRBX gene:
265+ '3' for DRB3, '4' for DRB4, '5' for DRB5, '0' for none
266+ """
267+ # DRB1 families associated with DRB3 expression
232268 if drb1_fam in ["03" , "05" , "06" , "11" , "12" , "13" , "14" ]:
233269 return "3"
270+ # DRB1 families associated with DRB4 expression
234271 if drb1_fam in ["04" , "07" , "09" ]:
235272 return "4"
273+ # DRB1 families associated with DRB5 expression
236274 if drb1_fam in ["02" , "15" , "16" ]:
237275 return "5"
276+ # DRB1 families with no associated DRBX expression
238277 return "0"
239278
240279
241280class DRBXBlenderError (Exception ):
281+ """Exception raised when DRBX typing doesn't match expected pattern
282+
283+ This error occurs when the provided DRB3/4/5 typing is inconsistent
284+ with what should be expressed based on the DRB1 allele families.
285+ """
286+
242287 def __init__ (self , found , expected ):
288+ """Initialize the error with found and expected values
289+
290+ Args:
291+ found: What was actually provided in the typing
292+ expected: What should have been provided based on DRB1
293+ """
243294 self .found = found
244295 self .expected = expected
245296
0 commit comments