bioversions 0.5.320__py3-none-any.whl → 0.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. bioversions/__init__.py +7 -3
  2. bioversions/__main__.py +0 -2
  3. bioversions/charts.py +3 -7
  4. bioversions/cli.py +3 -5
  5. bioversions/py.typed +0 -0
  6. bioversions/resources/__init__.py +18 -7
  7. bioversions/resources/update.py +20 -12
  8. bioversions/resources/versions.json +20536 -0
  9. bioversions/slack_client.py +2 -5
  10. bioversions/sources/__init__.py +45 -17
  11. bioversions/sources/antibodyregistry.py +9 -6
  12. bioversions/sources/bigg.py +5 -7
  13. bioversions/sources/biogrid.py +0 -2
  14. bioversions/sources/cellosaurus.py +3 -5
  15. bioversions/sources/chebi.py +1 -3
  16. bioversions/sources/chembl.py +3 -3
  17. bioversions/sources/chemidplus.py +1 -3
  18. bioversions/sources/civic.py +46 -0
  19. bioversions/sources/complexportal.py +0 -2
  20. bioversions/sources/daily.py +0 -2
  21. bioversions/sources/depmap.py +1 -3
  22. bioversions/sources/dgi.py +9 -12
  23. bioversions/sources/disgenet.py +1 -3
  24. bioversions/sources/drugbank.py +2 -4
  25. bioversions/sources/drugcentral.py +2 -4
  26. bioversions/sources/ensembl.py +4 -6
  27. bioversions/sources/expasy.py +1 -4
  28. bioversions/sources/flybase.py +0 -2
  29. bioversions/sources/gtdb.py +35 -0
  30. bioversions/sources/guidetopharmacology.py +3 -4
  31. bioversions/sources/hgnc.py +13 -14
  32. bioversions/sources/homologene.py +0 -2
  33. bioversions/sources/icd10.py +30 -0
  34. bioversions/sources/icd11.py +30 -0
  35. bioversions/sources/icf.py +30 -0
  36. bioversions/sources/intact.py +0 -2
  37. bioversions/sources/interpro.py +1 -3
  38. bioversions/sources/itis.py +8 -15
  39. bioversions/sources/kegg.py +7 -6
  40. bioversions/sources/mesh.py +10 -5
  41. bioversions/sources/mgi.py +0 -2
  42. bioversions/sources/mirbase.py +13 -7
  43. bioversions/sources/moalmanac.py +0 -2
  44. bioversions/sources/msigdb.py +0 -2
  45. bioversions/sources/ncit.py +1 -4
  46. bioversions/sources/npass.py +0 -2
  47. bioversions/sources/obo.py +4 -6
  48. bioversions/sources/ols.py +6 -8
  49. bioversions/sources/omim.py +33 -0
  50. bioversions/sources/oncotree.py +4 -4
  51. bioversions/sources/pathbank.py +0 -2
  52. bioversions/sources/pathwaycommons.py +0 -2
  53. bioversions/sources/pfam.py +0 -2
  54. bioversions/sources/pombase.py +0 -2
  55. bioversions/sources/pr.py +1 -3
  56. bioversions/sources/pubchem.py +0 -2
  57. bioversions/sources/reactome.py +0 -2
  58. bioversions/sources/rfam.py +0 -2
  59. bioversions/sources/rgd.py +1 -3
  60. bioversions/sources/rhea.py +1 -6
  61. bioversions/sources/rxnorm.py +1 -3
  62. bioversions/sources/sgd.py +0 -2
  63. bioversions/sources/signor.py +36 -0
  64. bioversions/sources/slm.py +4 -3
  65. bioversions/sources/stringdb.py +2 -4
  66. bioversions/sources/umls.py +0 -2
  67. bioversions/sources/uniprot.py +1 -3
  68. bioversions/sources/unversioned.py +0 -2
  69. bioversions/sources/wikipathways.py +0 -2
  70. bioversions/sources/zfin.py +0 -2
  71. bioversions/templates/base.html +28 -0
  72. bioversions/templates/home.html +60 -0
  73. bioversions/twitter_client.py +5 -8
  74. bioversions/utils.py +40 -32
  75. bioversions/version.py +3 -5
  76. bioversions/wsgi.py +2 -4
  77. bioversions-0.7.1.dist-info/METADATA +393 -0
  78. bioversions-0.7.1.dist-info/RECORD +81 -0
  79. bioversions-0.7.1.dist-info/WHEEL +4 -0
  80. {bioversions-0.5.320.dist-info → bioversions-0.7.1.dist-info}/entry_points.txt +1 -0
  81. {bioversions-0.5.320.dist-info → bioversions-0.7.1.dist-info/licenses}/LICENSE +1 -1
  82. bioversions-0.5.320.dist-info/METADATA +0 -166
  83. bioversions-0.5.320.dist-info/RECORD +0 -72
  84. bioversions-0.5.320.dist-info/WHEEL +0 -5
  85. bioversions-0.5.320.dist-info/top_level.txt +0 -1
@@ -0,0 +1,36 @@
1
+ """A getter for `SIGNOR <https://signor.uniroma2.it>`_."""
2
+
3
+ import datetime
4
+ from typing import ClassVar
5
+
6
+ from bioversions.utils import Getter, VersionType, get_soup
7
+
8
+ __all__ = [
9
+ "SignorGetter",
10
+ ]
11
+
12
+ URL = "https://signor.uniroma2.it/downloads.php"
13
+ TEXT = "Click here to download the latest stable release"
14
+
15
+
16
+ class SignorGetter(Getter):
17
+ """A getter for SIGNOR."""
18
+
19
+ name = "SIGNOR"
20
+ version_type = VersionType.date
21
+ date_fmt = "%B %Y"
22
+ collection: ClassVar[list[str]] = ["signor", "signor.relation"]
23
+
24
+ def get(self) -> datetime.datetime:
25
+ """Get the latest SIGNOR version number."""
26
+ soup = get_soup(URL)
27
+ for p in soup.find_all("p"):
28
+ if TEXT in p.text:
29
+ _, _, after = p.text.partition(TEXT)
30
+ after = after.strip().lstrip("(").rstrip(")")
31
+ return datetime.datetime.strptime(after, "%B %Y")
32
+ raise RuntimeError
33
+
34
+
35
+ if __name__ == "__main__":
36
+ SignorGetter.print()
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A getter for SwissLipids."""
4
2
 
5
3
  import datetime
@@ -12,6 +10,9 @@ __all__ = [
12
10
  "SwissLipidGetter",
13
11
  ]
14
12
 
13
+ # View docs at https://www.swisslipids.org/#/api
14
+ URL = "https://www.swisslipids.org/api/index.php/downloadData"
15
+
15
16
 
16
17
  class SwissLipidGetter(Getter):
17
18
  """A getter for SwissLipids."""
@@ -22,7 +23,7 @@ class SwissLipidGetter(Getter):
22
23
 
23
24
  def get(self):
24
25
  """Get the latest SwissLipids version number."""
25
- res = requests.get("https://www.swisslipids.org/api/downloadData").json()
26
+ res = requests.get(URL, timeout=15).json()
26
27
  record = next(record for record in res if record["file"] == "lipids.tsv")
27
28
  return datetime.datetime.strptime(record["date"], "%B %d %Y")
28
29
 
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A getter for StringDB."""
4
2
 
5
3
  from bioversions.utils import Getter, VersionType, get_soup
@@ -21,9 +19,9 @@ class StringDBGetter(Getter):
21
19
  soup = get_soup("https://string-db.org/cgi/access")
22
20
  table = soup.find(**{"class": "footer_access_archive_table"})
23
21
  rows = table.find_all(**{"class": "row"})
24
- version, date, link, _summary = [row.text for row in rows[1].find_all(**{"class": "cell"})]
22
+ version, date, link, _summary = (row.text for row in rows[1].find_all(**{"class": "cell"}))
25
23
  date = date[len("current: since ") :]
26
- return dict(version=version, date=date)
24
+ return {"version": version, "date": date}
27
25
 
28
26
 
29
27
  if __name__ == "__main__":
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A getter for UMLS."""
4
2
 
5
3
  from datetime import datetime
@@ -1,8 +1,6 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A getter for UniProt."""
4
2
 
5
- from xml.etree import ElementTree # noqa:S405
3
+ from xml.etree import ElementTree
6
4
 
7
5
  import requests
8
6
 
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A collection of unversioned resources."""
4
2
 
5
3
  # TODO
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A getter for WikiPathways."""
4
2
 
5
3
  from bioversions.utils import Getter, VersionType, get_soup
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A getter for ZFIN."""
4
2
 
5
3
  from datetime import datetime
@@ -0,0 +1,28 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ {% block head %}
5
+ <!-- Required meta tags -->
6
+ <meta charset="utf-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8
+
9
+ {% block styles %}
10
+ <!-- Bootstrap CSS -->
11
+ {{ bootstrap.load_css() }}
12
+ {% endblock %}
13
+
14
+ <script src="https://kit.fontawesome.com/4c86883252.js" crossorigin="anonymous"></script>
15
+
16
+ <title>{% block title %}{% endblock %}</title>
17
+ {% endblock %}
18
+ </head>
19
+ <body>
20
+ <!-- Your page content -->
21
+ {% block content %}{% endblock %}
22
+
23
+ {% block scripts %}
24
+ <!-- Optional JavaScript -->
25
+ {{ bootstrap.load_js() }}
26
+ {% endblock %}
27
+ </body>
28
+ </html>
@@ -0,0 +1,60 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Bioversions{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container" style="margin-top: 50px; margin-bottom: 50px">
7
+ <div class="row justify-content-center">
8
+ <div class="card">
9
+ <h5 class="card-header text-center">Biological Database Latest Versions</h5>
10
+ <div class="card-body">
11
+ <p>
12
+ This web application lists the latest versions for biological databases.
13
+ It refreshes once per day.
14
+ </p>
15
+ </div>
16
+ <table class="table table-striped table-hover">
17
+ <thead>
18
+ <tr>
19
+ <th>Name</th>
20
+ <th>Version</th>
21
+ <th>Date</th>
22
+ <th class="text-center">JSON</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ {% for row in rows %}
27
+ <tr>
28
+ <td>{{ row.name }}</td>
29
+ <td>
30
+ {% if row.homepage %}
31
+ <a href="{{ row.homepage }}">{{ row.version }} <i class="fas fa-link"></i></a>
32
+ {% else %}
33
+ {{ row.version }}
34
+ {% endif %}
35
+ </td>
36
+ <td>
37
+ {% if row.date %}{{ row.date }}{% endif %}
38
+ </td>
39
+ <td class="text-center">
40
+ <a href="{{ url_for('database', name=row.name.lower().replace(" ", "")) }}">
41
+ <i class="fas fa-download"></i>
42
+ </a>
43
+ </td>
44
+ </tr>
45
+ {% endfor %}
46
+ </tbody>
47
+ </table>
48
+ <div class="card-footer">
49
+ <p style="text-align: center">
50
+ Developed with ❤️ by the <a href="https://indralab.github.io">INDRA Lab</a> in the
51
+ <a href="https://hits.harvard.edu">Harvard Program in Therapeutic Science (HiTS)</a>.<br/>
52
+ Funded by the DARPA Young Faculty Award W911NF2010255 (PI: Benjamin M. Gyori).<br/>
53
+ Point of contact: <a href="https://github.com/cthoyt">@cthoyt</a>.
54
+ (<a href="https://github.com/biopragmatics/bioversions">Source code</a>)
55
+ </p>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ {% endblock %}
@@ -1,9 +1,6 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Utilities for interacting with Twitter."""
4
2
 
5
3
  from functools import lru_cache
6
- from typing import Optional
7
4
 
8
5
  import pystow
9
6
  import tweepy
@@ -11,11 +8,11 @@ import tweepy
11
8
 
12
9
  @lru_cache(maxsize=1)
13
10
  def _get_api(
14
- consumer_key: Optional[str] = None,
15
- consumer_secret: Optional[str] = None,
16
- access_token: Optional[str] = None,
17
- access_token_secret: Optional[str] = None,
18
- ) -> Optional[tweepy.API]:
11
+ consumer_key: str | None = None,
12
+ consumer_secret: str | None = None,
13
+ access_token: str | None = None,
14
+ access_token_secret: str | None = None,
15
+ ) -> tweepy.API | None:
19
16
  consumer_key = pystow.get_config("bioversions", "consumer_key", passthrough=consumer_key)
20
17
  consumer_secret = pystow.get_config(
21
18
  "bioversions", "consumer_secret", passthrough=consumer_secret
bioversions/utils.py CHANGED
@@ -1,18 +1,16 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Utilities and implementation for bioversions."""
4
2
 
5
3
  import datetime
6
4
  import enum
7
5
  import ftplib
8
6
  import os
9
- from typing import Any, ClassVar, Mapping, Optional, Union
7
+ from collections.abc import Mapping
8
+ from typing import Any, ClassVar
10
9
 
11
10
  import bioregistry
12
11
  import pydantic
13
12
  import pystow
14
13
  import requests
15
- import requests_ftp
16
14
  from bs4 import BeautifulSoup
17
15
  from cachier import cachier
18
16
 
@@ -21,8 +19,6 @@ HERE = os.path.abspath(os.path.dirname(__file__))
21
19
  DOCS = os.path.abspath(os.path.join(HERE, os.pardir, os.pardir, "docs"))
22
20
  IMG = os.path.join(DOCS, "img")
23
21
 
24
- requests_ftp.monkeypatch_session()
25
-
26
22
 
27
23
  class VersionType(enum.Enum):
28
24
  """Different types of versions."""
@@ -47,7 +43,9 @@ def norm(s: str) -> str:
47
43
  return s.lower().replace(" ", "").replace("-", "").replace(".", "")
48
44
 
49
45
 
50
- def get_soup(url: str, verify: bool = True, timeout: Optional[int] = None) -> BeautifulSoup:
46
+ def get_soup(
47
+ url: str, verify: bool = True, timeout: int | None = None, user_agent: str | None = None
48
+ ) -> BeautifulSoup:
51
49
  """Get a beautiful soup parsed version of the given web page.
52
50
 
53
51
  :param url: The URL to download and parse with BeautifulSoup
@@ -55,9 +53,13 @@ def get_soup(url: str, verify: bool = True, timeout: Optional[int] = None) -> Be
55
53
  except for Ensembl, which makes a big pain
56
54
  :param timeout: How many integer seconds to wait for a response?
57
55
  Defaults to 15 if none given.
56
+ :param user_agent: A custom user-agent to set, e.g., to avoid anti-crawling mechanisms
58
57
  :returns: A BeautifulSoup object
59
58
  """
60
- res = requests.get(url, verify=verify, timeout=timeout or 15)
59
+ headers = {}
60
+ if user_agent:
61
+ headers["User-Agent"] = user_agent
62
+ res = requests.get(url, verify=verify, timeout=timeout or 15, headers=headers)
61
63
  soup = BeautifulSoup(res.text, features="html.parser")
62
64
  return soup
63
65
 
@@ -76,9 +78,9 @@ class MetaGetter(type):
76
78
 
77
79
  _cache = None
78
80
 
79
- date_fmt: Optional[str]
80
- date_version_fmt: Optional[str]
81
- homepage_fmt: Optional[str]
81
+ date_fmt: str | None
82
+ date_version_fmt: str | None
83
+ homepage_fmt: str | None
82
84
 
83
85
  @property
84
86
  def _cache_prop(cls):
@@ -99,7 +101,7 @@ class MetaGetter(type):
99
101
  raise TypeError(f"_cache_prop was a {type(cls._cache_prop)}")
100
102
 
101
103
  @property
102
- def date(cls) -> Optional[datetime.date]:
104
+ def date(cls) -> datetime.date | None:
103
105
  """Get the date if it's set."""
104
106
  vp = cls.version_date_parsed
105
107
  if vp is not None:
@@ -116,10 +118,12 @@ class MetaGetter(type):
116
118
  try:
117
119
  return datetime.datetime.strptime(date, cls.date_fmt).date()
118
120
  except ValueError:
119
- raise ValueError(f"Issue in {cls.__name__} with date {date} and fmt {cls.date_fmt}")
121
+ raise ValueError(
122
+ f"Issue in {cls.__name__} with date {date} and fmt {cls.date_fmt}"
123
+ ) from None
120
124
 
121
125
  @property
122
- def version_date_parsed(cls) -> Optional[datetime.date]:
126
+ def version_date_parsed(cls) -> datetime.date | None:
123
127
  """Get the date as a parsed class there's a format string."""
124
128
  if cls.date_version_fmt is None:
125
129
  return None
@@ -127,11 +131,12 @@ class MetaGetter(type):
127
131
  return datetime.datetime.strptime(cls.version, cls.date_version_fmt).date()
128
132
  except ValueError:
129
133
  raise ValueError(
130
- f"Issue parsing {cls.__name__} version {cls.version} with fmt {cls.date_version_fmt}"
131
- )
134
+ f"Issue parsing {cls.__name__} version {cls.version} "
135
+ f"with fmt {cls.date_version_fmt}"
136
+ ) from None
132
137
 
133
138
  @property
134
- def homepage(cls) -> Optional[str]:
139
+ def homepage(cls) -> str | None:
135
140
  """Get the homepage's URL if a format string was specified."""
136
141
  if cls.homepage_fmt is None:
137
142
  return None
@@ -157,11 +162,11 @@ class Bioversion(pydantic.BaseModel):
157
162
  #: The version type
158
163
  vtype: VersionType
159
164
  #: The date of the current release
160
- date: Optional[datetime.date]
165
+ date: datetime.date | None
161
166
  #: The URL for the homepage of the specific version of the database
162
- homepage: Optional[str]
167
+ homepage: str | None
163
168
  #: The database prefix
164
- bioregistry_id: Optional[str]
169
+ bioregistry_id: str | None
165
170
 
166
171
 
167
172
  class Getter(metaclass=MetaGetter):
@@ -173,32 +178,35 @@ class Getter(metaclass=MetaGetter):
173
178
  version_type: ClassVar[VersionType]
174
179
 
175
180
  #: The URL with `{version}` to format in the version. Specify this in the inheriting class.
176
- homepage_fmt: ClassVar[Optional[str]] = None
181
+ homepage_fmt: ClassVar[str | None] = None
177
182
 
178
- date_fmt: ClassVar[Optional[str]] = None
183
+ date_fmt: ClassVar[str | None] = None
179
184
 
180
- date_version_fmt: ClassVar[Optional[str]] = None
185
+ date_version_fmt: ClassVar[str | None] = None
181
186
 
182
- bioregistry_id: ClassVar[Optional[str]] = None
187
+ bioregistry_id: ClassVar[str | None] = None
183
188
 
184
189
  # The following are automatically calculated based on the metaclass
185
190
  version: ClassVar[str]
186
191
  date: ClassVar[str]
187
192
  homepage: ClassVar[str]
188
193
 
189
- def get(self) -> Union[str, Mapping[str, str], datetime.datetime]:
194
+ #: Prefixes this getter works for
195
+ collection: ClassVar[list[str] | None] = None
196
+
197
+ def get(self) -> str | Mapping[str, str] | datetime.datetime:
190
198
  """Get the latest of this database."""
191
199
  raise NotImplementedError
192
200
 
193
201
  @classmethod
194
- def print(cls, sep: str = "\t", file=None): # noqa:T202
202
+ def print(cls, sep: str = "\t", file=None):
195
203
  """Print the latest version of this database."""
196
204
  x = [cls.bioregistry_id, cls.name, cls.version]
197
205
  if cls.date:
198
206
  x.append(f"({cls.date})")
199
207
  if cls.homepage:
200
208
  x.append(cls.homepage)
201
- print(*x, sep=sep, file=file) # noqa:T201
209
+ print(*x, sep=sep, file=file)
202
210
 
203
211
  @classmethod
204
212
  def resolve(cls) -> Bioversion:
@@ -216,7 +224,7 @@ class Getter(metaclass=MetaGetter):
216
224
  @classmethod
217
225
  def to_dict(cls) -> Mapping[str, Any]:
218
226
  """Get a dict with the data for this database."""
219
- return cls.resolve().to_dict()
227
+ return cls.resolve().model_dump()
220
228
 
221
229
 
222
230
  class DailyGetter(Getter):
@@ -224,7 +232,7 @@ class DailyGetter(Getter):
224
232
 
225
233
  version_type = VersionType.daily
226
234
 
227
- def get(self) -> Union[str, Mapping[str, str]]:
235
+ def get(self) -> str | Mapping[str, str]:
228
236
  """Return a constant "daily" string."""
229
237
  return "daily"
230
238
 
@@ -237,14 +245,14 @@ class UnversionedGetter(Getter):
237
245
  #: Has this database been apparently abandoned (true) or is it still updated (false)
238
246
  abandoned: ClassVar[bool]
239
247
 
240
- def get(self) -> Union[str, Mapping[str, str]]:
248
+ def get(self) -> str | Mapping[str, str]:
241
249
  """Return a constant unversioned string."""
242
250
  return "unversioned"
243
251
 
244
252
 
245
253
  def get_obo_version(url: str) -> str:
246
254
  """Get the data version from an OBO file."""
247
- with requests.get(url, stream=True) as res:
255
+ with requests.get(url, stream=True, timeout=60) as res:
248
256
  for line in res.iter_lines():
249
257
  line = line.decode("utf-8")
250
258
  if line.startswith("data-version:"):
@@ -270,7 +278,7 @@ class OBOFoundryGetter(Getter):
270
278
 
271
279
  def get(self) -> str:
272
280
  """Get the OBO version."""
273
- url = f"http://purl.obolibrary.org/obo/{self.key}.obo"
281
+ url = f"https://purl.obolibrary.org/obo/{self.key}.obo"
274
282
  return self.process(get_obo_version(url))
275
283
 
276
284
  def process(self, version: str) -> str:
bioversions/version.py CHANGED
@@ -1,22 +1,20 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """Version information for bioversions."""
4
2
 
5
3
  import os
6
- from subprocess import CalledProcessError, check_output # noqa: S404
4
+ from subprocess import CalledProcessError, check_output
7
5
 
8
6
  __all__ = [
9
7
  "VERSION",
10
8
  ]
11
9
 
12
- VERSION = "0.5.320"
10
+ VERSION = "0.7.1"
13
11
 
14
12
 
15
13
  def get_git_hash() -> str:
16
14
  """Get the bioversions git hash."""
17
15
  with open(os.devnull, "w") as devnull:
18
16
  try:
19
- ret = check_output( # noqa: S603,S607
17
+ ret = check_output(
20
18
  ["git", "rev-parse", "HEAD"],
21
19
  cwd=os.path.dirname(__file__),
22
20
  stderr=devnull,
bioversions/wsgi.py CHANGED
@@ -1,8 +1,6 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  """A web application for listing database versions."""
4
2
 
5
- from typing import Any, Dict
3
+ from typing import Any
6
4
 
7
5
  import flask
8
6
  from flask_bootstrap import Bootstrap
@@ -22,7 +20,7 @@ def home():
22
20
  @app.route("/database/<name>.json")
23
21
  def database(name: str):
24
22
  """Resolve information about a given database."""
25
- rv: Dict[str, Any] = dict(query=name)
23
+ rv: dict[str, Any] = {"query": name}
26
24
  try:
27
25
  bioversion = resolve(name)
28
26
  except KeyError: