mapkgsutils 0.1.2__tar.gz → 0.2.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapkgsutils
3
- Version: 0.1.2
3
+ Version: 0.2.1
4
4
  Summary: Utils shared by several mapping set generation tools for biomedical databases
5
5
  Keywords: snekpack,cookiecutter
6
6
  Author: Javier Millan Acosta
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "mapkgsutils"
7
- version = "0.1.2"
7
+ version = "0.2.1"
8
8
  description = "Utils shared by several mapping set generation tools for biomedical databases"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -236,7 +236,7 @@ known-first-party = [
236
236
  docstring-code-format = true
237
237
 
238
238
  [tool.bumpversion]
239
- current_version = "0.1.2"
239
+ current_version = "0.2.1"
240
240
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(?:-(?P<release>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+(?P<build>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?"
241
241
  serialize = [
242
242
  "{major}.{minor}.{patch}-{release}+{build}",
@@ -142,29 +142,15 @@ def load_mapping_dates(cache_path: Path) -> dict[str, str]:
142
142
  cache_path: Path to the cache TSV (see :func:`write_cache`).
143
143
 
144
144
  Returns:
145
- Dict mapping each ``record_id`` to its best available ISO date
146
- string: the mapping's own per-row ``mapping_date`` when the parser
147
- produced one, else the first-seen release date. Records with no date
148
- at all are omitted, leaving their ``mapping_date`` unset rather than
149
- passing through a non-date value.
145
+ Dict mapping each ``record_id`` to the mapping's ``mapping_date``,
146
+ else the first-seen release date.
150
147
  """
151
148
  records = read_cache(cache_path)
152
149
  return {rid: date for rid, fields in records.items() if (date := _best_date(fields))}
153
150
 
154
151
 
155
152
  def _mapping_fields_json(m: Any) -> str:
156
- """JSON-encode a mapping's own fields (excluding record_id).
157
-
158
- Snapshots a mapping's current shape (subject_id, object_id,
159
- predicate_id, mapping_date, ...) so :func:`build_consolidated_mapping_set`
160
- can rebuild it into a real SSSOM mapping set later. The parser's own
161
- per-row ``mapping_date`` is kept (it's release-independent), distinct
162
- from the first-seen release version. ``default=str`` handles
163
- enum-valued fields like ``mapping_cardinality``, which round-trip
164
- cleanly back through ``Mapping(**fields)``. Returns ``"{}"`` for
165
- non-dataclass stand-ins (e.g. test doubles); callers skip those rows
166
- rather than materializing them.
167
- """
153
+ """JSON-encode a mapping's own fields (excluding record_id)."""
168
154
  from dataclasses import fields as dataclass_fields
169
155
  from dataclasses import is_dataclass
170
156
 
@@ -193,16 +179,6 @@ def consolidate(
193
179
 
194
180
  Keeps every mapping and every available piece of provenance.
195
181
 
196
- - **``list_versions`` provided** (versioned archive, e.g. ChEBI/HGNC/
197
- UniProt/Ensembl): walk every release oldest-first, calling
198
- ``run_one_version(v)`` per release and recording the first/last release
199
- each mapping was seen in. Resumes from :func:`read_meta` unless
200
- *force*. This collects the full historical set.
201
- - **``list_versions`` is ``None``** (no versioned archive, e.g. NCBI/
202
- VGNC): a single current parse via ``run_one_version(None)`` caching the
203
- *full* mapping set — every mapping kept, stamped with its own per-row
204
- date when present and left undated otherwise.
205
-
206
182
  Args:
207
183
  cache_path: Path to the cache TSV to read/write.
208
184
  meta_path: Path to the ``last_version`` sidecar to read/write.
@@ -257,8 +233,7 @@ def _consolidate_single_parse(
257
233
  pair_key = str(getattr(m, "record_id", None) or "")[-16:]
258
234
  if not pair_key:
259
235
  continue
260
- # The mapping's own per-row date survives in fields_json; first_seen_date
261
- # mirrors it so undated rows simply stay undated rather than dropping out.
236
+ # The mapping's own per-row date is taken from fields_json
262
237
  date_str = str(m.mapping_date) if getattr(m, "mapping_date", None) else ""
263
238
  records[pair_key] = {
264
239
  "first_seen_version": version_label,
@@ -170,7 +170,7 @@ def _read_sssom_to_dataframe(path: Path) -> pl.DataFrame:
170
170
  """
171
171
  # Count header lines
172
172
  header_lines = 0
173
- with path.open() as f:
173
+ with path.open(encoding="utf-8") as f:
174
174
  for line in f:
175
175
  if line.startswith("#"):
176
176
  header_lines += 1
@@ -200,7 +200,7 @@ def _extract_sssom_version(path: Path) -> str | None:
200
200
  Returns:
201
201
  Version string or None if not found.
202
202
  """
203
- with path.open() as f:
203
+ with path.open(encoding="utf-8") as f:
204
204
  for line in f:
205
205
  if not line.startswith("#"):
206
206
  break
@@ -68,6 +68,7 @@ def _is_cloudflare_blocked(response: httpx.Response) -> bool:
68
68
  # header being present, check the body.
69
69
  content_type = response.headers.get("content-type", "")
70
70
  if "text/html" in content_type:
71
+ response.read() # body is unread on a streamed response; .text needs it
71
72
  text = response.text
72
73
  if "cloudflare" in text.lower() or "cf-ray" in text.lower():
73
74
  return True
@@ -558,8 +559,7 @@ def download_datasource_with_release(
558
559
  keys: Optional list of file-key names to download.
559
560
  tar_extractors: ``{datasource_name: extractor}`` registry for
560
561
  datasources that publish a ``.tar.gz`` archive needing
561
- member-level extraction rather than a plain download (e.g.
562
- UniProt). Datasources without one are downloaded as-is.
562
+ member-level extraction.
563
563
  **kwargs: Datasource-specific knobs forwarded to the resolved hook.
564
564
 
565
565
  Returns:
@@ -5,9 +5,9 @@ from __future__ import annotations
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING, Any, TypeVar
7
7
 
8
- from sssom import MappingSetDataFrame
9
-
10
8
  if TYPE_CHECKING:
9
+ from sssom import MappingSetDataFrame
10
+
11
11
  from mapkgsutils.parsers.base import BaseMappingSet
12
12
 
13
13
  _M = TypeVar("_M", bound="BaseMappingSet")
@@ -0,0 +1,68 @@
1
+ """Logging configuration.
2
+
3
+ Provides a configured package logger. Only CRITICAL messages show by default;
4
+ call :func:`set_log_level` to raise verbosity.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import sys
11
+
12
+ __all__ = [
13
+ "LOG_LEVELS",
14
+ "get_logger",
15
+ "logger",
16
+ "set_log_level",
17
+ ]
18
+
19
+ # Map of log level names to logging constants
20
+ LOG_LEVELS: dict[str, int] = {
21
+ "critical": logging.CRITICAL,
22
+ "error": logging.ERROR,
23
+ "warning": logging.WARNING,
24
+ "warn": logging.WARNING,
25
+ "info": logging.INFO,
26
+ "debug": logging.DEBUG,
27
+ }
28
+
29
+
30
+ def get_logger(name: str) -> logging.Logger:
31
+ """Return a stderr-handled logger for *name*, defaulting to CRITICAL."""
32
+ log = logging.getLogger(name)
33
+ log.setLevel(logging.CRITICAL)
34
+ if not log.handlers:
35
+ handler = logging.StreamHandler(sys.stderr)
36
+ handler.setFormatter(
37
+ logging.Formatter(
38
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
39
+ datefmt="%Y-%m-%d %H:%M:%S",
40
+ )
41
+ )
42
+ log.addHandler(handler)
43
+ return log
44
+
45
+
46
+ logger = get_logger("mapkgsutils")
47
+
48
+
49
+ def set_log_level(level: str | int, target: logging.Logger | None = None) -> None:
50
+ """Set the log level on *target* (the mapkgsutils logger by default).
51
+
52
+ Args:
53
+ level: Level name (``"debug"``/``"info"``/``"warning"``/``"error"``/
54
+ ``"critical"``) or an integer such as ``logging.INFO``.
55
+ target: Logger to configure.
56
+
57
+ Example:
58
+ >>> from mapkgsutils.logging import set_log_level
59
+ >>> set_log_level("warning")
60
+ """
61
+ log = target if target is not None else logger
62
+ if isinstance(level, str):
63
+ level_int = LOG_LEVELS.get(level.lower())
64
+ if level_int is None:
65
+ raise ValueError(f"Unknown log level: {level}. Available: {list(LOG_LEVELS.keys())}")
66
+ log.setLevel(level_int)
67
+ else:
68
+ log.setLevel(level)