<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def extract_subjects(subject_info_xml, primary_str):
 """Extract a set of authenticated subjects from a DataONE SubjectInfo. - See subject_info_tree for details. Args: subject_info_xml : str A SubjectInfo XML document. primary_str : str A DataONE subject, typically a DataONE compliant serialization of the DN of the DataONE X.509 v3 certificate extension from which the SubjectInfo was extracted. The primary subject can be viewed as the root of a tree. Any subject in the SubjectInfo that is directly or indirectly connected to the root subject is included in the returned set of authenticated subjects. Returns: set: Set of authenticated subjects. Will always include the primary subject. - All subjects in the returned set are equivalent to ``primary_str`` for the purpose of access control for private science objects. - If SubjectInfo does not contain all relevant records, it is still considered to be valid, but the authenticated set will be incomplete. - Only the subject strings and relationships in SubjectInfo are used by this function. Other information about subjects, such as name and email address, is ignored. - No attempt should be made to infer type of subject from the content of a subject string. Subject strings should be handled as random Unicode sequences, each of which may designate an person subject, an equivalent subject, or a group subject. - To determine if an action is authorized, the returned set is checked against the authorized_set for a given object. If one or more subjects exist in both sets, the action is authorized. The check can be performed with high performance using a set union operation in Python or an inner join in Postgres. - Subject types are only known and relevant while processing the SubjectInfo type. - The type of each subject in the authenticated_subjects and allowed_subjects lists are unknown and irrelevant. Notes: Procedure: The set of authenticated subjects is generated from the SubjectInfo and primary subject using the following procedure: - Start with empty set of subjects - Add authenticatedUser - If ``subject`` is not in set of subjects: - Add ``subject`` - Iterate over Person records - If Person.subject is ``subject``: - If Person.verified is present and set: - Add "verifiedUser" - Iterate over Person.equivalentIdentity: - Recursively add those subjects - Iterate over Person.isMemberOf - Recursively add those subjects, but ONLY check Group subjects - Iterate over Group records - If any Group.hasMember is ``subject``: - Recursively add Group.subject (not group members) Handling of various invalid SubjectInfo and corner cases: - SubjectInfo XML doc that is not well formed - Return an exception that includes a useful error message with the line number of the issue - person.isMemberOf and group.hasMember should always form pairs referencing each other. - One side of the pair is missing - Process the available side as normal - person.isMemberOf subject references a person or equivalent instead of a group - Only Group subjects are searched for isMemberOf references, so only the referenced Group subject is added to the list of authorized subjects - Multiple Person or Group records conflict by using the same subject - The records are handled as equivalents - person.isMemberOf subject does not reference a known subject - If the Person containing the dangling isMemberOf IS NOT connected with the authenticated subject, the whole record, including the isMemberOf subject is simply ignored - If it IS connected with an authenticated subject, the isMemberOf subject is authenticated and recursive processing of the subject is skipped - Circular references - Handled by skipping recursive add for subjects that are already added - See the unit tests for example SubjectInfo XML documents for each of these issues and the expected results. """

    subject_info_pyxb = deserialize_subject_info(subject_info_xml)
    subject_info_tree = gen_subject_info_tree(subject_info_pyxb, primary_str)
    return subject_info_tree.get_subject_set()