From e7e0b2fb87a36f355a597d60431e56f87a76441d Mon Sep 17 00:00:00 2001 From: Oscar Arbelaez Date: Fri, 29 Aug 2025 21:23:00 +0100 Subject: [PATCH 1/4] breaking: rename builders to sources --- src/bibx/__init__.py | 20 ++++++++++---------- src/bibx/cli.py | 2 +- src/bibx/{builders => sources}/__init__.py | 0 src/bibx/{builders => sources}/base.py | 2 +- src/bibx/{builders => sources}/openalex.py | 4 ++-- src/bibx/{builders => sources}/scopus_bib.py | 4 ++-- src/bibx/{builders => sources}/scopus_csv.py | 4 ++-- src/bibx/{builders => sources}/scopus_ris.py | 4 ++-- src/bibx/{builders => sources}/simple.py | 4 ++-- src/bibx/{builders => sources}/wos.py | 4 ++-- tests/builders/test_scopus_csv.py | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) rename src/bibx/{builders => sources}/__init__.py (100%) rename src/bibx/{builders => sources}/base.py (86%) rename src/bibx/{builders => sources}/openalex.py (98%) rename src/bibx/{builders => sources}/scopus_bib.py (97%) rename src/bibx/{builders => sources}/scopus_csv.py (98%) rename src/bibx/{builders => sources}/scopus_ris.py (98%) rename src/bibx/{builders => sources}/simple.py (82%) rename src/bibx/{builders => sources}/wos.py (99%) diff --git a/src/bibx/__init__.py b/src/bibx/__init__.py index fd4de88..4ad44f2 100644 --- a/src/bibx/__init__.py +++ b/src/bibx/__init__.py @@ -5,13 +5,13 @@ from bibx.algorithms.sap import Sap from bibx.article import Article -from bibx.builders.openalex import EnrichReferences, OpenAlexCollectionBuilder -from bibx.builders.scopus_bib import ScopusBibCollectionBuilder -from bibx.builders.scopus_csv import ScopusCsvCollectionBuilder -from bibx.builders.scopus_ris import ScopusRisCollectionBuilder -from bibx.builders.wos import WosCollectionBuilder from bibx.collection import Collection from bibx.exceptions import BibXError +from bibx.sources.openalex import EnrichReferences, OpenAlexSource +from bibx.sources.scopus_bib import ScopusBibSource +from bibx.sources.scopus_csv import ScopusCsvSource +from bibx.sources.scopus_ris import ScopusRisSource +from bibx.sources.wos import WosSource logger = logging.getLogger(__name__) @@ -37,7 +37,7 @@ def query_openalex( enrich: EnrichReferences = EnrichReferences.BASIC, ) -> Collection: """Query OpenAlex and return a collection.""" - return OpenAlexCollectionBuilder(query, limit, enrich=enrich).build() + return OpenAlexSource(query, limit, enrich=enrich).build() def read_scopus_bib(*files: TextIO) -> Collection: @@ -46,7 +46,7 @@ def read_scopus_bib(*files: TextIO) -> Collection: :param files: Scopus bib files open. :return: the collection """ - return ScopusBibCollectionBuilder(*files).build() + return ScopusBibSource(*files).build() def read_scopus_ris(*files: TextIO) -> Collection: @@ -55,7 +55,7 @@ def read_scopus_ris(*files: TextIO) -> Collection: :param files: Scopus bib files open. :return: the collection """ - return ScopusRisCollectionBuilder(*files).build() + return ScopusRisSource(*files).build() def read_scopus_csv(*files: TextIO) -> Collection: @@ -64,7 +64,7 @@ def read_scopus_csv(*files: TextIO) -> Collection: :param files: Scopus csv files open. :return: the collection """ - return ScopusCsvCollectionBuilder(*files).build() + return ScopusCsvSource(*files).build() def read_wos(*files: TextIO) -> Collection: @@ -73,7 +73,7 @@ def read_wos(*files: TextIO) -> Collection: :param files: WoS files open. :return: the collection """ - return WosCollectionBuilder(*files).build() + return WosSource(*files).build() def read_any(file: TextIO) -> Collection: diff --git a/src/bibx/cli.py b/src/bibx/cli.py index 189b3ef..a3a094d 100644 --- a/src/bibx/cli.py +++ b/src/bibx/cli.py @@ -15,7 +15,7 @@ read_wos, ) from bibx.algorithms.sap import Sap -from bibx.builders.openalex import EnrichReferences +from bibx.sources.openalex import EnrichReferences app = typer.Typer() diff --git a/src/bibx/builders/__init__.py b/src/bibx/sources/__init__.py similarity index 100% rename from src/bibx/builders/__init__.py rename to src/bibx/sources/__init__.py diff --git a/src/bibx/builders/base.py b/src/bibx/sources/base.py similarity index 86% rename from src/bibx/builders/base.py rename to src/bibx/sources/base.py index cdb7bfd..806058b 100644 --- a/src/bibx/builders/base.py +++ b/src/bibx/sources/base.py @@ -3,7 +3,7 @@ from bibx.collection import Collection -class CollectionBuilder(Protocol): +class Source(Protocol): """Protocol for classes that build collections of articles.""" def build(self) -> Collection: diff --git a/src/bibx/builders/openalex.py b/src/bibx/sources/openalex.py similarity index 98% rename from src/bibx/builders/openalex.py rename to src/bibx/sources/openalex.py index 63e608e..7a24616 100644 --- a/src/bibx/builders/openalex.py +++ b/src/bibx/sources/openalex.py @@ -8,7 +8,7 @@ from bibx.clients.openalex import OpenAlexClient, Work from bibx.collection import Collection -from .base import CollectionBuilder +from .base import Source logger = logging.getLogger(__name__) @@ -25,7 +25,7 @@ class EnrichReferences(Enum): FULL = "full" -class OpenAlexCollectionBuilder(CollectionBuilder): +class OpenAlexSource(Source): """Builder for collections of articles from the OpenAlex API.""" def __init__( diff --git a/src/bibx/builders/scopus_bib.py b/src/bibx/sources/scopus_bib.py similarity index 97% rename from src/bibx/builders/scopus_bib.py rename to src/bibx/sources/scopus_bib.py index e953f72..91c65e7 100644 --- a/src/bibx/builders/scopus_bib.py +++ b/src/bibx/sources/scopus_bib.py @@ -10,10 +10,10 @@ from bibx.collection import Collection from bibx.exceptions import MissingCriticalInformationError -from .base import CollectionBuilder +from .base import Source -class ScopusBibCollectionBuilder(CollectionBuilder): +class ScopusBibSource(Source): """Builder for collections of articles from Scopus BibTeX files.""" def __init__(self, *scopus_files: TextIO) -> None: diff --git a/src/bibx/builders/scopus_csv.py b/src/bibx/sources/scopus_csv.py similarity index 98% rename from src/bibx/builders/scopus_csv.py rename to src/bibx/sources/scopus_csv.py index 5950bc0..7aef9e7 100644 --- a/src/bibx/builders/scopus_csv.py +++ b/src/bibx/sources/scopus_csv.py @@ -11,7 +11,7 @@ from bibx.article import Article from bibx.collection import Collection -from .base import CollectionBuilder +from .base import Source logger = logging.getLogger(__name__) @@ -78,7 +78,7 @@ class Row(BaseModel): source: Annotated[str, Field(validation_alias="Source")] -class ScopusCsvCollectionBuilder(CollectionBuilder): +class ScopusCsvSource(Source): """Builder for Scopus data from CSV files.""" def __init__(self, *files: TextIO) -> None: diff --git a/src/bibx/builders/scopus_ris.py b/src/bibx/sources/scopus_ris.py similarity index 98% rename from src/bibx/builders/scopus_ris.py rename to src/bibx/sources/scopus_ris.py index da828d6..a8ee34b 100644 --- a/src/bibx/builders/scopus_ris.py +++ b/src/bibx/sources/scopus_ris.py @@ -8,7 +8,7 @@ from bibx.collection import Collection from bibx.exceptions import InvalidScopusFileError, MissingCriticalInformationError -from .base import CollectionBuilder +from .base import Source logger = logging.getLogger(__name__) @@ -37,7 +37,7 @@ def _joined(raw: Optional[list[str]]) -> Optional[str]: return " ".join(raw) -class ScopusRisCollectionBuilder(CollectionBuilder): +class ScopusRisSource(Source): """Builder for collections of articles from Scopus RIS files.""" def __init__(self, *ris_files: TextIO) -> None: diff --git a/src/bibx/builders/simple.py b/src/bibx/sources/simple.py similarity index 82% rename from src/bibx/builders/simple.py rename to src/bibx/sources/simple.py index 278c418..c18fd7c 100644 --- a/src/bibx/builders/simple.py +++ b/src/bibx/sources/simple.py @@ -1,10 +1,10 @@ from bibx.article import Article from bibx.collection import Collection -from .base import CollectionBuilder +from .base import Source -class SimpleCollectionBuilder(CollectionBuilder): +class SimpleSource(Source): """Builder for collections of articles from a list of articles.""" def __init__(self, articles: list[Article]) -> None: diff --git a/src/bibx/builders/wos.py b/src/bibx/sources/wos.py similarity index 99% rename from src/bibx/builders/wos.py rename to src/bibx/sources/wos.py index c46decb..617b8a7 100644 --- a/src/bibx/builders/wos.py +++ b/src/bibx/sources/wos.py @@ -15,7 +15,7 @@ MissingCriticalInformationError, ) -from .base import CollectionBuilder +from .base import Source logger = logging.getLogger(__name__) @@ -57,7 +57,7 @@ def parse(self, value: list[str]) -> Union[str, int, list[str]]: return self.parser(value) -class WosCollectionBuilder(CollectionBuilder): +class WosSource(Source): """Builder for collections of articles from Web of Science (WoS) ISI files.""" ISI_LINE_PATTERN = re.compile( diff --git a/tests/builders/test_scopus_csv.py b/tests/builders/test_scopus_csv.py index 8f4fe28..e1d8834 100644 --- a/tests/builders/test_scopus_csv.py +++ b/tests/builders/test_scopus_csv.py @@ -2,7 +2,7 @@ def test_scopus_csv() -> None: - """Test the ScopusCSVBuilder class.""" + """Test the ScopusCsvSource class.""" with open("docs/examples/scopus.csv") as file: collection = read_scopus_csv(file) assert collection is not None From 2593f3b23e67c0fe389517bc2c040036314cbeb0 Mon Sep 17 00:00:00 2001 From: Oscar Arbelaez Date: Fri, 29 Aug 2025 21:46:33 +0100 Subject: [PATCH 2/4] breaking: rename models --- src/bibx/__init__.py | 4 ++-- src/bibx/algorithms/sap.py | 4 ++-- src/bibx/models/__init__.py | 1 + src/bibx/{ => models}/article.py | 0 src/bibx/{ => models}/collection.py | 0 src/bibx/sources/base.py | 2 +- src/bibx/sources/openalex.py | 4 ++-- src/bibx/sources/scopus_bib.py | 4 ++-- src/bibx/sources/scopus_csv.py | 4 ++-- src/bibx/sources/scopus_ris.py | 4 ++-- src/bibx/sources/simple.py | 4 ++-- src/bibx/sources/wos.py | 4 ++-- tests/test_collection.py | 4 ++-- 13 files changed, 20 insertions(+), 19 deletions(-) create mode 100644 src/bibx/models/__init__.py rename src/bibx/{ => models}/article.py (100%) rename src/bibx/{ => models}/collection.py (100%) diff --git a/src/bibx/__init__.py b/src/bibx/__init__.py index 4ad44f2..9e476c8 100644 --- a/src/bibx/__init__.py +++ b/src/bibx/__init__.py @@ -4,9 +4,9 @@ from typing import TextIO from bibx.algorithms.sap import Sap -from bibx.article import Article -from bibx.collection import Collection from bibx.exceptions import BibXError +from bibx.models.article import Article +from bibx.models.collection import Collection from bibx.sources.openalex import EnrichReferences, OpenAlexSource from bibx.sources.scopus_bib import ScopusBibSource from bibx.sources.scopus_csv import ScopusCsvSource diff --git a/src/bibx/algorithms/sap.py b/src/bibx/algorithms/sap.py index b2d7396..81cf546 100644 --- a/src/bibx/algorithms/sap.py +++ b/src/bibx/algorithms/sap.py @@ -4,8 +4,8 @@ import networkx as nx from networkx.algorithms.community.louvain import louvain_communities -from bibx.article import Article -from bibx.collection import Collection +from bibx.models.article import Article +from bibx.models.collection import Collection YEAR = "year" LEAF = "leaf" diff --git a/src/bibx/models/__init__.py b/src/bibx/models/__init__.py new file mode 100644 index 0000000..83ea339 --- /dev/null +++ b/src/bibx/models/__init__.py @@ -0,0 +1 @@ +"""Models for the bibx package.""" diff --git a/src/bibx/article.py b/src/bibx/models/article.py similarity index 100% rename from src/bibx/article.py rename to src/bibx/models/article.py diff --git a/src/bibx/collection.py b/src/bibx/models/collection.py similarity index 100% rename from src/bibx/collection.py rename to src/bibx/models/collection.py diff --git a/src/bibx/sources/base.py b/src/bibx/sources/base.py index 806058b..d91e239 100644 --- a/src/bibx/sources/base.py +++ b/src/bibx/sources/base.py @@ -1,6 +1,6 @@ from typing import Protocol -from bibx.collection import Collection +from bibx.models.collection import Collection class Source(Protocol): diff --git a/src/bibx/sources/openalex.py b/src/bibx/sources/openalex.py index 7a24616..2677e10 100644 --- a/src/bibx/sources/openalex.py +++ b/src/bibx/sources/openalex.py @@ -4,9 +4,9 @@ from typing import Optional from urllib.parse import urlparse -from bibx.article import Article from bibx.clients.openalex import OpenAlexClient, Work -from bibx.collection import Collection +from bibx.models.article import Article +from bibx.models.collection import Collection from .base import Source diff --git a/src/bibx/sources/scopus_bib.py b/src/bibx/sources/scopus_bib.py index 91c65e7..f146e7d 100644 --- a/src/bibx/sources/scopus_bib.py +++ b/src/bibx/sources/scopus_bib.py @@ -6,9 +6,9 @@ import bibtexparser -from bibx.article import Article -from bibx.collection import Collection from bibx.exceptions import MissingCriticalInformationError +from bibx.models.article import Article +from bibx.models.collection import Collection from .base import Source diff --git a/src/bibx/sources/scopus_csv.py b/src/bibx/sources/scopus_csv.py index 7aef9e7..92c7ac8 100644 --- a/src/bibx/sources/scopus_csv.py +++ b/src/bibx/sources/scopus_csv.py @@ -8,8 +8,8 @@ from pydantic import BaseModel, Field from pydantic.functional_validators import BeforeValidator -from bibx.article import Article -from bibx.collection import Collection +from bibx.models.article import Article +from bibx.models.collection import Collection from .base import Source diff --git a/src/bibx/sources/scopus_ris.py b/src/bibx/sources/scopus_ris.py index a8ee34b..53e6b67 100644 --- a/src/bibx/sources/scopus_ris.py +++ b/src/bibx/sources/scopus_ris.py @@ -4,9 +4,9 @@ from collections.abc import Iterable from typing import Optional, TextIO -from bibx.article import Article -from bibx.collection import Collection from bibx.exceptions import InvalidScopusFileError, MissingCriticalInformationError +from bibx.models.article import Article +from bibx.models.collection import Collection from .base import Source diff --git a/src/bibx/sources/simple.py b/src/bibx/sources/simple.py index c18fd7c..d9e1cad 100644 --- a/src/bibx/sources/simple.py +++ b/src/bibx/sources/simple.py @@ -1,5 +1,5 @@ -from bibx.article import Article -from bibx.collection import Collection +from bibx.models.article import Article +from bibx.models.collection import Collection from .base import Source diff --git a/src/bibx/sources/wos.py b/src/bibx/sources/wos.py index 617b8a7..1f854cf 100644 --- a/src/bibx/sources/wos.py +++ b/src/bibx/sources/wos.py @@ -7,13 +7,13 @@ from dataclasses import dataclass from typing import Any, Callable, ClassVar, Optional, TextIO, Union -from bibx.article import Article -from bibx.collection import Collection from bibx.exceptions import ( InvalidIsiLineError, InvalidIsiReferenceError, MissingCriticalInformationError, ) +from bibx.models.article import Article +from bibx.models.collection import Collection from .base import Source diff --git a/tests/test_collection.py b/tests/test_collection.py index de9bcc5..467f550 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -1,5 +1,5 @@ -from bibx.article import Article -from bibx.collection import Collection +from bibx.models.article import Article +from bibx.models.collection import Collection articles = [ Article( From 3cd77261261b386dcdcb4781391bc997c86ca22a Mon Sep 17 00:00:00 2001 From: Oscar Arbelaez Date: Fri, 29 Aug 2025 22:54:00 +0100 Subject: [PATCH 3/4] prepare release --- src/bibx/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bibx/__init__.py b/src/bibx/__init__.py index 9e476c8..9584b61 100644 --- a/src/bibx/__init__.py +++ b/src/bibx/__init__.py @@ -28,7 +28,7 @@ "read_wos", ] -__version__ = "0.7.2" +__version__ = "0.8.0" def query_openalex( From a566bc09873bb9103bf35fb0842248d16abaa375 Mon Sep 17 00:00:00 2001 From: Oscar Arbelaez Date: Sat, 30 Aug 2025 20:44:48 +0100 Subject: [PATCH 4/4] Update all the things --- .github/workflows/cd.yml | 2 +- .github/workflows/ci.yml | 6 ++--- pyproject.toml | 12 +++++----- src/bibx/clients/openalex.py | 29 +++++++++++------------ src/bibx/models/article.py | 28 +++++++++++----------- src/bibx/models/collection.py | 4 ++-- src/bibx/sources/openalex.py | 3 +-- src/bibx/sources/scopus_bib.py | 4 ++-- src/bibx/sources/scopus_csv.py | 18 +++++++------- src/bibx/sources/scopus_ris.py | 8 +++---- src/bibx/sources/wos.py | 8 +++---- stubs/trailets/utils/warnings/__init__.py | 1 + 12 files changed, 61 insertions(+), 62 deletions(-) create mode 100644 stubs/trailets/utils/warnings/__init__.py diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 533e7e4..87fec09 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -20,7 +20,7 @@ jobs: uses: astral-sh/setup-uv@v2 with: enable-cache: true - version: "0.6.0" + version: "0.8.13" - name: Install dependencies run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7d9634..bf219ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,11 +13,11 @@ jobs: name: CI for ${{ matrix.python-version }} runs-on: ubuntu-latest env: - USING_COVERAGE: "3.9,3.13" + USING_COVERAGE: "3.11,3.13" strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 @@ -30,7 +30,7 @@ jobs: uses: astral-sh/setup-uv@v2 with: enable-cache: true - version: "0.6.0" + version: "0.8.13" - name: Run tests for ${{ matrix.python-version }} run: | diff --git a/pyproject.toml b/pyproject.toml index f962891..c069d65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,10 +12,10 @@ dependencies = [ "bibtexparser~=1.4.0", "networkx~=3.0", "pydantic~=2.11.7", - "requests~=2.32.3", - "typer~=0.16.0", + "requests~=2.32.5", + "typer~=0.17.3", ] -requires-python = ">=3.9" +requires-python = ">=3.11" classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Science/Research", @@ -39,8 +39,8 @@ dev = [ "pre-commit~=4.3.0", "ruff~=0.12.11", "mypy~=1.17.1", - "types-requests>=2.32.0.20241016", - "ipython>=8.18.1", + "types-requests~=2.32.4.20250809", + "ipython~=9.5.0", ] [project.scripts] @@ -116,7 +116,7 @@ path = "src/bibx/__init__.py" [tool.tox] requires = ["tox>=4.24.2"] -env_list = ["3.9", "3.10", "3.11", "3.12", "3.13"] +env_list = ["3.11", "3.12", "3.13"] [tool.tox.env_run_base] deps = [ diff --git a/src/bibx/clients/openalex.py b/src/bibx/clients/openalex.py index cef9830..695f8b8 100644 --- a/src/bibx/clients/openalex.py +++ b/src/bibx/clients/openalex.py @@ -1,7 +1,6 @@ import logging from concurrent.futures import ThreadPoolExecutor, as_completed, wait from enum import Enum -from typing import Optional, Union import requests from pydantic import BaseModel, ValidationError @@ -29,7 +28,7 @@ class Author(BaseModel): id: str display_name: str - orcid: Optional[str] = None + orcid: str | None = None class WorkAuthorship(BaseModel): @@ -51,10 +50,10 @@ class WorkKeyword(BaseModel): class WorkBiblio(BaseModel): """Work bibliographic information from the openalex API.""" - volume: Optional[str] = None - issue: Optional[str] = None - first_page: Optional[str] = None - last_page: Optional[str] = None + volume: str | None = None + issue: str | None = None + first_page: str | None = None + last_page: str | None = None class WorkLocationSource(BaseModel): @@ -69,9 +68,9 @@ class WorkLoacation(BaseModel): """Location of the work from the openalex API.""" is_oa: bool - landing_page_url: Optional[str] = None - pdf_url: Optional[str] = None - source: Optional[WorkLocationSource] + landing_page_url: str | None = None + pdf_url: str | None = None + source: WorkLocationSource | None class Work(BaseModel): @@ -79,15 +78,15 @@ class Work(BaseModel): id: str ids: dict[str, str] - doi: Optional[str] = None - title: Optional[str] = None + doi: str | None = None + title: str | None = None publication_year: int authorships: list[WorkAuthorship] cited_by_count: int keywords: list[WorkKeyword] referenced_works: list[str] biblio: WorkBiblio - primary_location: Optional[WorkLoacation] = None + primary_location: WorkLoacation | None = None class ResponseMeta(BaseModel): @@ -110,8 +109,8 @@ class OpenAlexClient: def __init__( self, - base_url: Optional[str] = None, - email: Optional[str] = None, + base_url: str | None = None, + email: str | None = None, ) -> None: self.base_url = base_url or "https://api.openalex.org" self.session = requests.Session() @@ -124,7 +123,7 @@ def __init__( } ) - def _fetch_works(self, params: dict[str, Union[str, int]]) -> WorkResponse: + def _fetch_works(self, params: dict[str, str | int]) -> WorkResponse: response = self.session.get( f"{self.base_url}/works", params=params, diff --git a/src/bibx/models/article.py b/src/bibx/models/article.py index c276984..210b707 100644 --- a/src/bibx/models/article.py +++ b/src/bibx/models/article.py @@ -1,6 +1,6 @@ from collections.abc import Mapping from dataclasses import dataclass, field -from typing import Optional, TypeVar, Union +from typing import TypeVar T = TypeVar("T") @@ -20,15 +20,15 @@ class Article: label: str ids: set[str] authors: list[str] = field(default_factory=list) - year: Optional[int] = None - title: Optional[str] = None - journal: Optional[str] = None - volume: Optional[str] = None - issue: Optional[str] = None - page: Optional[str] = None - doi: Optional[str] = None - _permalink: Optional[str] = None - times_cited: Optional[int] = None + year: int | None = None + title: str | None = None + journal: str | None = None + volume: str | None = None + issue: str | None = None + page: str | None = None + doi: str | None = None + _permalink: str | None = None + times_cited: int | None = None references: list["Article"] = field(default_factory=list) keywords: list[str] = field(default_factory=list) sources: set[str] = field(default_factory=set) @@ -61,7 +61,7 @@ def key(self) -> str: return next(iter(sorted(self.ids))) @property - def simple_label(self) -> Optional[str]: + def simple_label(self) -> str | None: """Return a simple label for the article.""" pieces = { "AU": self.authors[0].replace(",", "") if self.authors else None, @@ -76,7 +76,7 @@ def simple_label(self) -> Optional[str]: return ", ".join(value for value in pieces.values() if value) @property - def permalink(self) -> Optional[str]: + def permalink(self) -> str | None: """Return the permalink of the article.""" if self._permalink is not None: return self._permalink @@ -85,7 +85,7 @@ def permalink(self) -> Optional[str]: return None @property - def simple_id(self) -> Optional[str]: + def simple_id(self) -> str | None: """Return a simple ID for the article.""" if not self.authors or self.year is None: return None @@ -112,7 +112,7 @@ def set_simple_label(self) -> "Article": def info( self, - ) -> dict[str, Union[str, int, list[str], None]]: + ) -> dict[str, str | int | list[str] | None]: """Return a dictionary with the information of the article.""" return { "permalink": self.permalink, diff --git a/src/bibx/models/collection.py b/src/bibx/models/collection.py index 48c91f3..faaabe2 100644 --- a/src/bibx/models/collection.py +++ b/src/bibx/models/collection.py @@ -140,7 +140,7 @@ def published_by_year(self) -> dict[int, int]: :return: a dictionary with the number of articles published each year. """ - current_year = datetime.datetime.now(datetime.timezone.utc).year + current_year = datetime.datetime.now(datetime.UTC).year years = {} for year in range(self._first_year, current_year + 1): years[year] = 0 @@ -164,7 +164,7 @@ def cited_by_year(self) -> dict[int, int]: :return: a dictionary with the number of citations each year. """ - current_year = datetime.datetime.now(datetime.timezone.utc).year + current_year = datetime.datetime.now(datetime.UTC).year cited_items_per_year = {} for year in range(self._first_year, current_year + 1): cited_items_per_year[year] = 0 diff --git a/src/bibx/sources/openalex.py b/src/bibx/sources/openalex.py index 2677e10..7b772a3 100644 --- a/src/bibx/sources/openalex.py +++ b/src/bibx/sources/openalex.py @@ -1,7 +1,6 @@ import logging from collections import Counter from enum import Enum -from typing import Optional from urllib.parse import urlparse from bibx.clients.openalex import OpenAlexClient, Work @@ -33,7 +32,7 @@ def __init__( query: str, limit: int = 600, enrich: EnrichReferences = EnrichReferences.BASIC, - client: Optional[OpenAlexClient] = None, + client: OpenAlexClient | None = None, ) -> None: self.query = query self.limit = limit diff --git a/src/bibx/sources/scopus_bib.py b/src/bibx/sources/scopus_bib.py index f146e7d..478b91c 100644 --- a/src/bibx/sources/scopus_bib.py +++ b/src/bibx/sources/scopus_bib.py @@ -2,7 +2,7 @@ import re from collections.abc import Iterable from contextlib import suppress -from typing import Optional, TextIO +from typing import TextIO import bibtexparser @@ -69,7 +69,7 @@ def _article_from_entry(self, entry: dict) -> Article: .set_simple_label() ) - def _articles_from_references(self, references: Optional[str]) -> Iterable[Article]: + def _articles_from_references(self, references: str | None) -> Iterable[Article]: if references is None: references = "" for reference in references.split("; "): diff --git a/src/bibx/sources/scopus_csv.py b/src/bibx/sources/scopus_csv.py index 92c7ac8..9b35ad0 100644 --- a/src/bibx/sources/scopus_csv.py +++ b/src/bibx/sources/scopus_csv.py @@ -3,7 +3,7 @@ import csv import logging from collections.abc import Generator -from typing import Annotated, Optional, TextIO +from typing import Annotated, TextIO from pydantic import BaseModel, Field from pydantic.functional_validators import BeforeValidator @@ -16,11 +16,11 @@ logger = logging.getLogger(__name__) -def _str_or_none(value: Optional[str]) -> Optional[str]: +def _str_or_none(value: str | None) -> str | None: return value if value else None -def _split_str(value: Optional[str]) -> list[str]: +def _split_str(value: str | None) -> list[str]: return value.strip().split("; ") if value else [] @@ -36,27 +36,27 @@ class Row(BaseModel): title: Annotated[str, Field(validation_alias="Title")] journal: Annotated[str, Field(validation_alias="Abbreviated Source Title")] volume: Annotated[ - Optional[str], + str | None, Field(validation_alias="Volume"), BeforeValidator(_str_or_none), ] issue: Annotated[ - Optional[str], + str | None, Field(validation_alias="Issue"), BeforeValidator(_str_or_none), ] page: Annotated[ - Optional[str], + str | None, Field(validation_alias="Page start"), BeforeValidator(_str_or_none), ] doi: Annotated[ - Optional[str], + str | None, Field(validation_alias="DOI"), BeforeValidator(_str_or_none), ] cited_by: Annotated[ - Optional[int], + int | None, Field(validation_alias="Cited by"), BeforeValidator(_str_or_none), ] @@ -134,7 +134,7 @@ def _parse_file(self, file: TextIO) -> Generator[Article, None, None]: .set_simple_label() ) - def _article_from_reference(self, reference: str) -> Optional[Article]: + def _article_from_reference(self, reference: str) -> Article | None: try: *authors, journal, issue, year = reference.split(", ") if not authors: diff --git a/src/bibx/sources/scopus_ris.py b/src/bibx/sources/scopus_ris.py index 53e6b67..0fb9631 100644 --- a/src/bibx/sources/scopus_ris.py +++ b/src/bibx/sources/scopus_ris.py @@ -2,7 +2,7 @@ import re from collections import defaultdict from collections.abc import Iterable -from typing import Optional, TextIO +from typing import TextIO from bibx.exceptions import InvalidScopusFileError, MissingCriticalInformationError from bibx.models.article import Article @@ -22,7 +22,7 @@ def _size(file: TextIO) -> int: return size -def _int_or_nothing(raw: Optional[list[str]]) -> Optional[int]: +def _int_or_nothing(raw: list[str] | None) -> int | None: if not raw: return None try: @@ -31,7 +31,7 @@ def _int_or_nothing(raw: Optional[list[str]]) -> Optional[int]: return None -def _joined(raw: Optional[list[str]]) -> Optional[str]: +def _joined(raw: list[str] | None) -> str | None: if not raw: return None return " ".join(raw) @@ -86,7 +86,7 @@ def _find_volume_info(ref: str) -> tuple[dict[str, str], str]: return data, ref[last_index:] @staticmethod - def _find_doi(ref: str) -> tuple[Optional[str], str]: + def _find_doi(ref: str) -> tuple[str | None, str]: pattern = re.compile( r"((doi.org\/)|(aps.org\/doi\/)|(DOI:?)) ?(?P[^\s,;:]{5,})", re.I ) diff --git a/src/bibx/sources/wos.py b/src/bibx/sources/wos.py index 1f854cf..e281db2 100644 --- a/src/bibx/sources/wos.py +++ b/src/bibx/sources/wos.py @@ -2,10 +2,10 @@ import functools import logging import re -from collections.abc import Iterable, Mapping +from collections.abc import Callable, Iterable, Mapping from contextlib import suppress from dataclasses import dataclass -from typing import Any, Callable, ClassVar, Optional, TextIO, Union +from typing import Any, ClassVar, TextIO from bibx.exceptions import ( InvalidIsiLineError, @@ -53,7 +53,7 @@ class _IsiField: parser: Callable aliases: list[str] - def parse(self, value: list[str]) -> Union[str, int, list[str]]: + def parse(self, value: list[str]) -> str | int | list[str]: return self.parser(value) @@ -279,7 +279,7 @@ def _get_articles_from_files(self) -> Iterable[Article]: @classmethod def _get_articles_from_references( - cls, references: Optional[list[str]] + cls, references: list[str] | None ) -> Iterable[Article]: if not references: return diff --git a/stubs/trailets/utils/warnings/__init__.py b/stubs/trailets/utils/warnings/__init__.py new file mode 100644 index 0000000..b977269 --- /dev/null +++ b/stubs/trailets/utils/warnings/__init__.py @@ -0,0 +1 @@ +"""Trailets utils package."""