We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bio.exPASy.cellosaurus.parse misses some keys
### Setup

I am reporting a problem with Biopython version, Python version, and operating
system as follows:

```python
Python 3.9.16 (main, Mar  8 2023, 04:29:44) 
[Clang 14.0.6 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys; print(sys.version)
3.9.16 (main, Mar  8 2023, 04:29:44) 
[Clang 14.0.6 ]
>>> import platform; print(platform.python_implementation()); print(platform.platform())
CPython
macOS-10.16-x86_64-i386-64bit
>>> import Bio; print(Bio.__version__)
1.81
```

(*Please copy and run the above in your Python, and copy-and-paste the output*)

### Expected behaviour
Bio.exPASy.cellosaurus.parse returns a dict containing all the key/value pairs from [cellosaurus.txt](https://ftp.expasy.org/databases/cellosaurus/cellosaurus.txt) file. And, the text file lists the key at the beginning of the file as follow.
---------  ---------------------------     ----------------------
 Line code  Content                         Occurrence in an entry
 ---------  ---------------------------     ----------------------
 ID         Identifier (cell line name)     Once; starts an entry
 AC         Accession (CVCL_xxxx)           Once
 AS         Secondary accession number(s)   Optional; once
 SY         Synonyms                        Optional; once
 DR         Cross-references                Optional; once or more
 RX         References identifiers          Optional: once or more
 WW         Web pages                       Optional; once or more
 CC         Comments                        Optional; once or more
 ST         STR profile data                Optional; twice or more
 DI         Diseases                        Optional; once or more
 OX         Species of origin               Once or more
 HI         Hierarchy                       Optional; once or more
 OI         Originate from same individual  Optional; once or more
 SX         Sex of cell                     Optional; once
 AG         Age of donor at sampling        Optional; once
 CA         Category                        Once
 DT         Date (entry history)            Once
 //         Terminator                      Once; ends an entry

### Actual behaviour

But, actual implementation misses to parse out AG and DT.
    def __init__(self):
        """Initialize the class."""
        dict.__init__(self)
        self["ID"] = ""
        self["AC"] = ""
        self["AS"] = ""
        self["SY"] = ""
        self["DR"] = []
        self["RX"] = []
        self["WW"] = []
        self["CC"] = []
        self["ST"] = []
        self["DI"] = []
        self["OX"] = []
        self["HI"] = []
        self["OI"] = []
        self["SX"] = ""
        self["CA"] = ""

### Steps to reproduce

Well, missing keys are pretty obvious. And, Age and Date wasn't returned from parse.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `Bio/ExPASy/cellosaurus.py`
Content:
```
1 # Copyright 2016 by Stephen Marshall.  All rights reserved.
2 # This code is part of the Biopython distribution and governed by its
3 # license.  Please see the LICENSE file that should have been included
4 # as part of this package.
5 
6 """Parser for the cellosaurus.txt file from ExPASy.
7 
8 See https://web.expasy.org/cellosaurus/
9 
10 Tested with the release of Version 18 (July 2016).
11 
12 Functions:
13  - read       Reads a file containing one cell line entry
14  - parse      Reads a file containing multiple cell line entries
15 
16 Classes:
17  - Record     Holds cell line data.
18 
19 Examples
20 --------
21 This example downloads the Cellosaurus database and parses it. Note that
22 urlopen returns a stream of bytes, while the parser expects a stream of plain
23 string, so we use TextIOWrapper to convert bytes to string using the UTF-8
24 encoding. This is not needed if you download the cellosaurus.txt file in
25 advance and open it (see the comment below).
26 
27     >>> from urllib.request import urlopen
28     >>> from io import TextIOWrapper
29     >>> from Bio.ExPASy import cellosaurus
30     >>> url = "ftp://ftp.expasy.org/databases/cellosaurus/cellosaurus.txt"
31     >>> bytestream = urlopen(url)
32     >>> textstream = TextIOWrapper(bytestream, "UTF-8")
33     >>> # alternatively, use
34     >>> # textstream = open("cellosaurus.txt")
35     >>> # if you downloaded the cellosaurus.txt file in advance.
36     >>> records = cellosaurus.parse(textstream)
37     >>> for record in records:
38     ...     if 'Homo sapiens' in record['OX'][0]:
39     ...         print(record['ID'])  # doctest:+ELLIPSIS
40     ...
41     #15310-LN
42     #W7079
43     (L)PC6
44     0.5alpha
45     ...
46 
47 """
48 
49 
50 def parse(handle):
51     """Parse cell line records.
52 
53     This function is for parsing cell line files containing multiple
54     records.
55 
56     Arguments:
57      - handle   - handle to the file.
58 
59     """
60     while True:
61         record = __read(handle)
62         if not record:
63             break
64         yield record
65 
66 
67 def read(handle):
68     """Read one cell line record.
69 
70     This function is for parsing cell line files containing
71     exactly one record.
72 
73     Arguments:
74      - handle   - handle to the file.
75 
76     """
77     record = __read(handle)
78     # We should have reached the end of the record by now
79     remainder = handle.read()
80     if remainder:
81         raise ValueError("More than one cell line record found")
82     return record
83 
84 
85 class Record(dict):
86     """Holds information from an ExPASy Cellosaurus record as a Python dictionary.
87 
88     Each record contains the following keys:
89 
90      ---------  ---------------------------     ----------------------
91      Line code  Content                         Occurrence in an entry
92      ---------  ---------------------------     ----------------------
93      ID         Identifier (cell line name)     Once; starts an entry
94      AC         Accession (CVCL_xxxx)           Once
95      AS         Secondary accession number(s)   Optional; once
96      SY         Synonyms                        Optional; once
97      DR         Cross-references                Optional; once or more
98      RX         References identifiers          Optional: once or more
99      WW         Web pages                       Optional; once or more
100      CC         Comments                        Optional; once or more
101      ST         STR profile data                Optional; once or more
102      DI         Diseases                        Optional; once or more
103      OX         Species of origin               Once or more
104      HI         Hierarchy                       Optional; once or more
105      OI         Originate from same individual  Optional; once or more
106      SX         Sex (gender) of cell            Optional; once
107      CA         Category                        Once
108      //         Terminator                      Once; ends an entry
109 
110     """
111 
112     def __init__(self):
113         """Initialize the class."""
114         dict.__init__(self)
115         self["ID"] = ""
116         self["AC"] = ""
117         self["AS"] = ""
118         self["SY"] = ""
119         self["DR"] = []
120         self["RX"] = []
121         self["WW"] = []
122         self["CC"] = []
123         self["ST"] = []
124         self["DI"] = []
125         self["OX"] = []
126         self["HI"] = []
127         self["OI"] = []
128         self["SX"] = ""
129         self["CA"] = ""
130 
131     def __repr__(self):
132         """Return the canonical string representation of the Record object."""
133         if self["ID"]:
134             if self["AC"]:
135                 return f"{self.__class__.__name__} ({self['ID']}, {self['AC']})"
136             else:
137                 return f"{self.__class__.__name__} ({self['ID']})"
138         else:
139             return f"{self.__class__.__name__} ( )"
140 
141     def __str__(self):
142         """Return a readable string representation of the Record object."""
143         output = "ID: " + self["ID"]
144         output += " AC: " + self["AC"]
145         output += " AS: " + self["AS"]
146         output += " SY: " + self["SY"]
147         output += " DR: " + repr(self["DR"])
148         output += " RX: " + repr(self["RX"])
149         output += " WW: " + repr(self["WW"])
150         output += " CC: " + repr(self["CC"])
151         output += " ST: " + repr(self["ST"])
152         output += " DI: " + repr(self["DI"])
153         output += " OX: " + repr(self["OX"])
154         output += " HI: " + repr(self["HI"])
155         output += " OI: " + repr(self["OI"])
156         output += " SX: " + self["SX"]
157         output += " CA: " + self["CA"]
158         return output
159 
160 
161 # Everything below is private
162 
163 
164 def __read(handle):
165     record = None
166 
167     for line in handle:
168         key, value = line[:2], line[5:].rstrip()
169         if key == "ID":
170             record = Record()
171             record["ID"] = value
172         elif key in ["AC", "AS", "SY", "SX", "CA"]:
173             record[key] += value
174         elif key in [
175             "AC",
176             "AS",
177             "SY",
178             "RX",
179             "WW",
180             "CC",
181             "ST",
182             "DI",
183             "OX",
184             "HI",
185             "OI",
186             "SX",
187             "CA",
188         ]:
189             record[key].append(value)
190         elif key == "DR":
191             k, v = value.split(";")
192             record["DR"].append((k.strip(), v.strip()))
193         elif key == "//":
194             if record:
195                 return record
196             else:
197                 continue
198     if record:
199         raise ValueError("Unexpected end of stream")
200 
201 
202 if __name__ == "__main__":
203     from Bio._utils import run_doctest
204 
205     run_doctest()
206
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/Bio/ExPASy/cellosaurus.py b/Bio/ExPASy/cellosaurus.py
--- a/Bio/ExPASy/cellosaurus.py
+++ b/Bio/ExPASy/cellosaurus.py
@@ -87,25 +87,28 @@
 
     Each record contains the following keys:
 
-     ---------  ---------------------------     ----------------------
-     Line code  Content                         Occurrence in an entry
-     ---------  ---------------------------     ----------------------
-     ID         Identifier (cell line name)     Once; starts an entry
-     AC         Accession (CVCL_xxxx)           Once
-     AS         Secondary accession number(s)   Optional; once
-     SY         Synonyms                        Optional; once
-     DR         Cross-references                Optional; once or more
-     RX         References identifiers          Optional: once or more
-     WW         Web pages                       Optional; once or more
-     CC         Comments                        Optional; once or more
-     ST         STR profile data                Optional; once or more
-     DI         Diseases                        Optional; once or more
-     OX         Species of origin               Once or more
-     HI         Hierarchy                       Optional; once or more
-     OI         Originate from same individual  Optional; once or more
-     SX         Sex (gender) of cell            Optional; once
-     CA         Category                        Once
-     //         Terminator                      Once; ends an entry
+    =========  ==============================  =======================
+    Line code  Content                         Occurrence in an entry
+    =========  ==============================  =======================
+    ID         Identifier (cell line name)     Once; starts an entry
+    AC         Accession (CVCL_xxxx)           Once
+    AS         Secondary accession number(s)   Optional; once
+    SY         Synonyms                        Optional; once
+    DR         Cross-references                Optional; once or more
+    RX         References identifiers          Optional: once or more
+    WW         Web pages                       Optional; once or more
+    CC         Comments                        Optional; once or more
+    ST         STR profile data                Optional; twice or more
+    DI         Diseases                        Optional; once or more
+    OX         Species of origin               Once or more
+    HI         Hierarchy                       Optional; once or more
+    OI         Originate from same individual  Optional; once or more
+    SX         Sex of cell                     Optional; once
+    AG         Age of donor at sampling        Optional; once
+    CA         Category                        Once
+    DT         Date (entry history)            Once
+    //         Terminator                      Once; ends an entry
+    =========  ==============================  =======================
 
     """
 
@@ -126,7 +129,9 @@
         self["HI"] = []
         self["OI"] = []
         self["SX"] = ""
+        self["AG"] = ""
         self["CA"] = ""
+        self["DT"] = ""
 
     def __repr__(self):
         """Return the canonical string representation of the Record object."""
@@ -154,7 +159,9 @@
         output += " HI: " + repr(self["HI"])
         output += " OI: " + repr(self["OI"])
         output += " SX: " + self["SX"]
+        output += " AG: " + self["AG"]
         output += " CA: " + self["CA"]
+        output += " DT: " + self["DT"]
         return output
 
 
@@ -169,12 +176,10 @@
         if key == "ID":
             record = Record()
             record["ID"] = value
-        elif key in ["AC", "AS", "SY", "SX", "CA"]:
+        elif key in ["AC", "AS", "SY", "SX", "AG", "CA", "DT"]:
             record[key] += value
         elif key in [
-            "AC",
-            "AS",
-            "SY",
+            # just append to the fields defined as lists, not to strings
             "RX",
             "WW",
             "CC",
@@ -183,8 +188,6 @@
             "OX",
             "HI",
             "OI",
-            "SX",
-            "CA",
         ]:
             record[key].append(value)
         elif key == "DR":

{"golden_diff": "diff --git a/Bio/ExPASy/cellosaurus.py b/Bio/ExPASy/cellosaurus.py\n--- a/Bio/ExPASy/cellosaurus.py\n+++ b/Bio/ExPASy/cellosaurus.py\n@@ -87,25 +87,28 @@\n \n     Each record contains the following keys:\n \n-     ---------  ---------------------------     ----------------------\n-     Line code  Content                         Occurrence in an entry\n-     ---------  ---------------------------     ----------------------\n-     ID         Identifier (cell line name)     Once; starts an entry\n-     AC         Accession (CVCL_xxxx)           Once\n-     AS         Secondary accession number(s)   Optional; once\n-     SY         Synonyms                        Optional; once\n-     DR         Cross-references                Optional; once or more\n-     RX         References identifiers          Optional: once or more\n-     WW         Web pages                       Optional; once or more\n-     CC         Comments                        Optional; once or more\n-     ST         STR profile data                Optional; once or more\n-     DI         Diseases                        Optional; once or more\n-     OX         Species of origin               Once or more\n-     HI         Hierarchy                       Optional; once or more\n-     OI         Originate from same individual  Optional; once or more\n-     SX         Sex (gender) of cell            Optional; once\n-     CA         Category                        Once\n-     //         Terminator                      Once; ends an entry\n+    =========  ==============================  =======================\n+    Line code  Content                         Occurrence in an entry\n+    =========  ==============================  =======================\n+    ID         Identifier (cell line name)     Once; starts an entry\n+    AC         Accession (CVCL_xxxx)           Once\n+    AS         Secondary accession number(s)   Optional; once\n+    SY         Synonyms                        Optional; once\n+    DR         Cross-references                Optional; once or more\n+    RX         References identifiers          Optional: once or more\n+    WW         Web pages                       Optional; once or more\n+    CC         Comments                        Optional; once or more\n+    ST         STR profile data                Optional; twice or more\n+    DI         Diseases                        Optional; once or more\n+    OX         Species of origin               Once or more\n+    HI         Hierarchy                       Optional; once or more\n+    OI         Originate from same individual  Optional; once or more\n+    SX         Sex of cell                     Optional; once\n+    AG         Age of donor at sampling        Optional; once\n+    CA         Category                        Once\n+    DT         Date (entry history)            Once\n+    //         Terminator                      Once; ends an entry\n+    =========  ==============================  =======================\n \n     \"\"\"\n \n@@ -126,7 +129,9 @@\n         self[\"HI\"] = []\n         self[\"OI\"] = []\n         self[\"SX\"] = \"\"\n+        self[\"AG\"] = \"\"\n         self[\"CA\"] = \"\"\n+        self[\"DT\"] = \"\"\n \n     def __repr__(self):\n         \"\"\"Return the canonical string representation of the Record object.\"\"\"\n@@ -154,7 +159,9 @@\n         output += \" HI: \" + repr(self[\"HI\"])\n         output += \" OI: \" + repr(self[\"OI\"])\n         output += \" SX: \" + self[\"SX\"]\n+        output += \" AG: \" + self[\"AG\"]\n         output += \" CA: \" + self[\"CA\"]\n+        output += \" DT: \" + self[\"DT\"]\n         return output\n \n \n@@ -169,12 +176,10 @@\n         if key == \"ID\":\n             record = Record()\n             record[\"ID\"] = value\n-        elif key in [\"AC\", \"AS\", \"SY\", \"SX\", \"CA\"]:\n+        elif key in [\"AC\", \"AS\", \"SY\", \"SX\", \"AG\", \"CA\", \"DT\"]:\n             record[key] += value\n         elif key in [\n-            \"AC\",\n-            \"AS\",\n-            \"SY\",\n+            # just append to the fields defined as lists, not to strings\n             \"RX\",\n             \"WW\",\n             \"CC\",\n@@ -183,8 +188,6 @@\n             \"OX\",\n             \"HI\",\n             \"OI\",\n-            \"SX\",\n-            \"CA\",\n         ]:\n             record[key].append(value)\n         elif key == \"DR\":\n", "issue": "Bio.exPASy.cellosaurus.parse misses some keys\n### Setup\r\n\r\nI am reporting a problem with Biopython version, Python version, and operating\r\nsystem as follows:\r\n\r\n```python\r\nPython 3.9.16 (main, Mar  8 2023, 04:29:44) \r\n[Clang 14.0.6 ] :: Anaconda, Inc. on darwin\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n>>> import sys; print(sys.version)\r\n3.9.16 (main, Mar  8 2023, 04:29:44) \r\n[Clang 14.0.6 ]\r\n>>> import platform; print(platform.python_implementation()); print(platform.platform())\r\nCPython\r\nmacOS-10.16-x86_64-i386-64bit\r\n>>> import Bio; print(Bio.__version__)\r\n1.81\r\n```\r\n\r\n(*Please copy and run the above in your Python, and copy-and-paste the output*)\r\n\r\n### Expected behaviour\r\nBio.exPASy.cellosaurus.parse returns a dict containing all the key/value pairs from [cellosaurus.txt](https://ftp.expasy.org/databases/cellosaurus/cellosaurus.txt) file. And, the text file lists the key at the beginning of the file as follow.\r\n---------  ---------------------------     ----------------------\r\n Line code  Content                         Occurrence in an entry\r\n ---------  ---------------------------     ----------------------\r\n ID         Identifier (cell line name)     Once; starts an entry\r\n AC         Accession (CVCL_xxxx)           Once\r\n AS         Secondary accession number(s)   Optional; once\r\n SY         Synonyms                        Optional; once\r\n DR         Cross-references                Optional; once or more\r\n RX         References identifiers          Optional: once or more\r\n WW         Web pages                       Optional; once or more\r\n CC         Comments                        Optional; once or more\r\n ST         STR profile data                Optional; twice or more\r\n DI         Diseases                        Optional; once or more\r\n OX         Species of origin               Once or more\r\n HI         Hierarchy                       Optional; once or more\r\n OI         Originate from same individual  Optional; once or more\r\n SX         Sex of cell                     Optional; once\r\n AG         Age of donor at sampling        Optional; once\r\n CA         Category                        Once\r\n DT         Date (entry history)            Once\r\n //         Terminator                      Once; ends an entry\r\n\r\n### Actual behaviour\r\n\r\nBut, actual implementation misses to parse out AG and DT.\r\n    def __init__(self):\r\n        \"\"\"Initialize the class.\"\"\"\r\n        dict.__init__(self)\r\n        self[\"ID\"] = \"\"\r\n        self[\"AC\"] = \"\"\r\n        self[\"AS\"] = \"\"\r\n        self[\"SY\"] = \"\"\r\n        self[\"DR\"] = []\r\n        self[\"RX\"] = []\r\n        self[\"WW\"] = []\r\n        self[\"CC\"] = []\r\n        self[\"ST\"] = []\r\n        self[\"DI\"] = []\r\n        self[\"OX\"] = []\r\n        self[\"HI\"] = []\r\n        self[\"OI\"] = []\r\n        self[\"SX\"] = \"\"\r\n        self[\"CA\"] = \"\"\r\n\r\n### Steps to reproduce\r\n\r\nWell, missing keys are pretty obvious. And, Age and Date wasn't returned from parse.\n", "before_files": [{"content": "# Copyright 2016 by Stephen Marshall.  All rights reserved.\n# This code is part of the Biopython distribution and governed by its\n# license.  Please see the LICENSE file that should have been included\n# as part of this package.\n\n\"\"\"Parser for the cellosaurus.txt file from ExPASy.\n\nSee https://web.expasy.org/cellosaurus/\n\nTested with the release of Version 18 (July 2016).\n\nFunctions:\n - read       Reads a file containing one cell line entry\n - parse      Reads a file containing multiple cell line entries\n\nClasses:\n - Record     Holds cell line data.\n\nExamples\n--------\nThis example downloads the Cellosaurus database and parses it. Note that\nurlopen returns a stream of bytes, while the parser expects a stream of plain\nstring, so we use TextIOWrapper to convert bytes to string using the UTF-8\nencoding. This is not needed if you download the cellosaurus.txt file in\nadvance and open it (see the comment below).\n\n    >>> from urllib.request import urlopen\n    >>> from io import TextIOWrapper\n    >>> from Bio.ExPASy import cellosaurus\n    >>> url = \"ftp://ftp.expasy.org/databases/cellosaurus/cellosaurus.txt\"\n    >>> bytestream = urlopen(url)\n    >>> textstream = TextIOWrapper(bytestream, \"UTF-8\")\n    >>> # alternatively, use\n    >>> # textstream = open(\"cellosaurus.txt\")\n    >>> # if you downloaded the cellosaurus.txt file in advance.\n    >>> records = cellosaurus.parse(textstream)\n    >>> for record in records:\n    ...     if 'Homo sapiens' in record['OX'][0]:\n    ...         print(record['ID'])  # doctest:+ELLIPSIS\n    ...\n    #15310-LN\n    #W7079\n    (L)PC6\n    0.5alpha\n    ...\n\n\"\"\"\n\n\ndef parse(handle):\n    \"\"\"Parse cell line records.\n\n    This function is for parsing cell line files containing multiple\n    records.\n\n    Arguments:\n     - handle   - handle to the file.\n\n    \"\"\"\n    while True:\n        record = __read(handle)\n        if not record:\n            break\n        yield record\n\n\ndef read(handle):\n    \"\"\"Read one cell line record.\n\n    This function is for parsing cell line files containing\n    exactly one record.\n\n    Arguments:\n     - handle   - handle to the file.\n\n    \"\"\"\n    record = __read(handle)\n    # We should have reached the end of the record by now\n    remainder = handle.read()\n    if remainder:\n        raise ValueError(\"More than one cell line record found\")\n    return record\n\n\nclass Record(dict):\n    \"\"\"Holds information from an ExPASy Cellosaurus record as a Python dictionary.\n\n    Each record contains the following keys:\n\n     ---------  ---------------------------     ----------------------\n     Line code  Content                         Occurrence in an entry\n     ---------  ---------------------------     ----------------------\n     ID         Identifier (cell line name)     Once; starts an entry\n     AC         Accession (CVCL_xxxx)           Once\n     AS         Secondary accession number(s)   Optional; once\n     SY         Synonyms                        Optional; once\n     DR         Cross-references                Optional; once or more\n     RX         References identifiers          Optional: once or more\n     WW         Web pages                       Optional; once or more\n     CC         Comments                        Optional; once or more\n     ST         STR profile data                Optional; once or more\n     DI         Diseases                        Optional; once or more\n     OX         Species of origin               Once or more\n     HI         Hierarchy                       Optional; once or more\n     OI         Originate from same individual  Optional; once or more\n     SX         Sex (gender) of cell            Optional; once\n     CA         Category                        Once\n     //         Terminator                      Once; ends an entry\n\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize the class.\"\"\"\n        dict.__init__(self)\n        self[\"ID\"] = \"\"\n        self[\"AC\"] = \"\"\n        self[\"AS\"] = \"\"\n        self[\"SY\"] = \"\"\n        self[\"DR\"] = []\n        self[\"RX\"] = []\n        self[\"WW\"] = []\n        self[\"CC\"] = []\n        self[\"ST\"] = []\n        self[\"DI\"] = []\n        self[\"OX\"] = []\n        self[\"HI\"] = []\n        self[\"OI\"] = []\n        self[\"SX\"] = \"\"\n        self[\"CA\"] = \"\"\n\n    def __repr__(self):\n        \"\"\"Return the canonical string representation of the Record object.\"\"\"\n        if self[\"ID\"]:\n            if self[\"AC\"]:\n                return f\"{self.__class__.__name__} ({self['ID']}, {self['AC']})\"\n            else:\n                return f\"{self.__class__.__name__} ({self['ID']})\"\n        else:\n            return f\"{self.__class__.__name__} ( )\"\n\n    def __str__(self):\n        \"\"\"Return a readable string representation of the Record object.\"\"\"\n        output = \"ID: \" + self[\"ID\"]\n        output += \" AC: \" + self[\"AC\"]\n        output += \" AS: \" + self[\"AS\"]\n        output += \" SY: \" + self[\"SY\"]\n        output += \" DR: \" + repr(self[\"DR\"])\n        output += \" RX: \" + repr(self[\"RX\"])\n        output += \" WW: \" + repr(self[\"WW\"])\n        output += \" CC: \" + repr(self[\"CC\"])\n        output += \" ST: \" + repr(self[\"ST\"])\n        output += \" DI: \" + repr(self[\"DI\"])\n        output += \" OX: \" + repr(self[\"OX\"])\n        output += \" HI: \" + repr(self[\"HI\"])\n        output += \" OI: \" + repr(self[\"OI\"])\n        output += \" SX: \" + self[\"SX\"]\n        output += \" CA: \" + self[\"CA\"]\n        return output\n\n\n# Everything below is private\n\n\ndef __read(handle):\n    record = None\n\n    for line in handle:\n        key, value = line[:2], line[5:].rstrip()\n        if key == \"ID\":\n            record = Record()\n            record[\"ID\"] = value\n        elif key in [\"AC\", \"AS\", \"SY\", \"SX\", \"CA\"]:\n            record[key] += value\n        elif key in [\n            \"AC\",\n            \"AS\",\n            \"SY\",\n            \"RX\",\n            \"WW\",\n            \"CC\",\n            \"ST\",\n            \"DI\",\n            \"OX\",\n            \"HI\",\n            \"OI\",\n            \"SX\",\n            \"CA\",\n        ]:\n            record[key].append(value)\n        elif key == \"DR\":\n            k, v = value.split(\";\")\n            record[\"DR\"].append((k.strip(), v.strip()))\n        elif key == \"//\":\n            if record:\n                return record\n            else:\n                continue\n    if record:\n        raise ValueError(\"Unexpected end of stream\")\n\n\nif __name__ == \"__main__\":\n    from Bio._utils import run_doctest\n\n    run_doctest()\n", "path": "Bio/ExPASy/cellosaurus.py"}], "after_files": [{"content": "# Copyright 2016 by Stephen Marshall.  All rights reserved.\n# This code is part of the Biopython distribution and governed by its\n# license.  Please see the LICENSE file that should have been included\n# as part of this package.\n\n\"\"\"Parser for the cellosaurus.txt file from ExPASy.\n\nSee https://web.expasy.org/cellosaurus/\n\nTested with the release of Version 18 (July 2016).\n\nFunctions:\n - read       Reads a file containing one cell line entry\n - parse      Reads a file containing multiple cell line entries\n\nClasses:\n - Record     Holds cell line data.\n\nExamples\n--------\nThis example downloads the Cellosaurus database and parses it. Note that\nurlopen returns a stream of bytes, while the parser expects a stream of plain\nstring, so we use TextIOWrapper to convert bytes to string using the UTF-8\nencoding. This is not needed if you download the cellosaurus.txt file in\nadvance and open it (see the comment below).\n\n    >>> from urllib.request import urlopen\n    >>> from io import TextIOWrapper\n    >>> from Bio.ExPASy import cellosaurus\n    >>> url = \"ftp://ftp.expasy.org/databases/cellosaurus/cellosaurus.txt\"\n    >>> bytestream = urlopen(url)\n    >>> textstream = TextIOWrapper(bytestream, \"UTF-8\")\n    >>> # alternatively, use\n    >>> # textstream = open(\"cellosaurus.txt\")\n    >>> # if you downloaded the cellosaurus.txt file in advance.\n    >>> records = cellosaurus.parse(textstream)\n    >>> for record in records:\n    ...     if 'Homo sapiens' in record['OX'][0]:\n    ...         print(record['ID'])  # doctest:+ELLIPSIS\n    ...\n    #15310-LN\n    #W7079\n    (L)PC6\n    0.5alpha\n    ...\n\n\"\"\"\n\n\ndef parse(handle):\n    \"\"\"Parse cell line records.\n\n    This function is for parsing cell line files containing multiple\n    records.\n\n    Arguments:\n     - handle   - handle to the file.\n\n    \"\"\"\n    while True:\n        record = __read(handle)\n        if not record:\n            break\n        yield record\n\n\ndef read(handle):\n    \"\"\"Read one cell line record.\n\n    This function is for parsing cell line files containing\n    exactly one record.\n\n    Arguments:\n     - handle   - handle to the file.\n\n    \"\"\"\n    record = __read(handle)\n    # We should have reached the end of the record by now\n    remainder = handle.read()\n    if remainder:\n        raise ValueError(\"More than one cell line record found\")\n    return record\n\n\nclass Record(dict):\n    \"\"\"Holds information from an ExPASy Cellosaurus record as a Python dictionary.\n\n    Each record contains the following keys:\n\n    =========  ==============================  =======================\n    Line code  Content                         Occurrence in an entry\n    =========  ==============================  =======================\n    ID         Identifier (cell line name)     Once; starts an entry\n    AC         Accession (CVCL_xxxx)           Once\n    AS         Secondary accession number(s)   Optional; once\n    SY         Synonyms                        Optional; once\n    DR         Cross-references                Optional; once or more\n    RX         References identifiers          Optional: once or more\n    WW         Web pages                       Optional; once or more\n    CC         Comments                        Optional; once or more\n    ST         STR profile data                Optional; twice or more\n    DI         Diseases                        Optional; once or more\n    OX         Species of origin               Once or more\n    HI         Hierarchy                       Optional; once or more\n    OI         Originate from same individual  Optional; once or more\n    SX         Sex of cell                     Optional; once\n    AG         Age of donor at sampling        Optional; once\n    CA         Category                        Once\n    DT         Date (entry history)            Once\n    //         Terminator                      Once; ends an entry\n    =========  ==============================  =======================\n\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize the class.\"\"\"\n        dict.__init__(self)\n        self[\"ID\"] = \"\"\n        self[\"AC\"] = \"\"\n        self[\"AS\"] = \"\"\n        self[\"SY\"] = \"\"\n        self[\"DR\"] = []\n        self[\"RX\"] = []\n        self[\"WW\"] = []\n        self[\"CC\"] = []\n        self[\"ST\"] = []\n        self[\"DI\"] = []\n        self[\"OX\"] = []\n        self[\"HI\"] = []\n        self[\"OI\"] = []\n        self[\"SX\"] = \"\"\n        self[\"AG\"] = \"\"\n        self[\"CA\"] = \"\"\n        self[\"DT\"] = \"\"\n\n    def __repr__(self):\n        \"\"\"Return the canonical string representation of the Record object.\"\"\"\n        if self[\"ID\"]:\n            if self[\"AC\"]:\n                return f\"{self.__class__.__name__} ({self['ID']}, {self['AC']})\"\n            else:\n                return f\"{self.__class__.__name__} ({self['ID']})\"\n        else:\n            return f\"{self.__class__.__name__} ( )\"\n\n    def __str__(self):\n        \"\"\"Return a readable string representation of the Record object.\"\"\"\n        output = \"ID: \" + self[\"ID\"]\n        output += \" AC: \" + self[\"AC\"]\n        output += \" AS: \" + self[\"AS\"]\n        output += \" SY: \" + self[\"SY\"]\n        output += \" DR: \" + repr(self[\"DR\"])\n        output += \" RX: \" + repr(self[\"RX\"])\n        output += \" WW: \" + repr(self[\"WW\"])\n        output += \" CC: \" + repr(self[\"CC\"])\n        output += \" ST: \" + repr(self[\"ST\"])\n        output += \" DI: \" + repr(self[\"DI\"])\n        output += \" OX: \" + repr(self[\"OX\"])\n        output += \" HI: \" + repr(self[\"HI\"])\n        output += \" OI: \" + repr(self[\"OI\"])\n        output += \" SX: \" + self[\"SX\"]\n        output += \" AG: \" + self[\"AG\"]\n        output += \" CA: \" + self[\"CA\"]\n        output += \" DT: \" + self[\"DT\"]\n        return output\n\n\n# Everything below is private\n\n\ndef __read(handle):\n    record = None\n\n    for line in handle:\n        key, value = line[:2], line[5:].rstrip()\n        if key == \"ID\":\n            record = Record()\n            record[\"ID\"] = value\n        elif key in [\"AC\", \"AS\", \"SY\", \"SX\", \"AG\", \"CA\", \"DT\"]:\n            record[key] += value\n        elif key in [\n            # just append to the fields defined as lists, not to strings\n            \"RX\",\n            \"WW\",\n            \"CC\",\n            \"ST\",\n            \"DI\",\n            \"OX\",\n            \"HI\",\n            \"OI\",\n        ]:\n            record[key].append(value)\n        elif key == \"DR\":\n            k, v = value.split(\";\")\n            record[\"DR\"].append((k.strip(), v.strip()))\n        elif key == \"//\":\n            if record:\n                return record\n            else:\n                continue\n    if record:\n        raise ValueError(\"Unexpected end of stream\")\n\n\nif __name__ == \"__main__\":\n    from Bio._utils import run_doctest\n\n    run_doctest()\n", "path": "Bio/ExPASy/cellosaurus.py"}]}