vlrdevapi 2.2.0__tar.gz → 2.3.0__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.
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/CHANGELOG.md +52 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/PKG-INFO +13 -2
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/README.md +12 -1
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/pyproject.toml +1 -1
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/scripts/check_mdx_examples.py +1 -1
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/scripts/download_fixtures.py +10 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/__init__.py +17 -7
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/__init__.pyi +15 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_client.py +2 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/__init__.py +5 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/article/__init__.py +4 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/article/models.py +41 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/article/namespace.py +86 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/article/namespace.pyi +19 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/article/parser.py +457 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/common.py +48 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/list/__init__.py +4 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/list/models.py +55 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/list/namespace.py +85 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/list/namespace.pyi +19 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/list/parser.py +109 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/namespace.py +127 -0
- vlrdevapi-2.3.0/src/vlrdevapi/_news/namespace.pyi +21 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_utils/paths.py +13 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/commons/__init__.py +9 -1
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/commons/datetime.py +32 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/validators.py +1 -1
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/uv.lock +1 -1
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.gitattributes +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.github/workflows/cleanup-caches.yml +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.github/workflows/docs-check.yml +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.github/workflows/publish.yml +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.github/workflows/test.yml +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.gitignore +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.python-version +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/.readthedocs.yaml +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/CODE_OF_CONDUCT.md +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/CONTRIBUTING.md +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/LICENCE +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/SECURITY.md +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/SUPPORT.md +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_base.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_cache.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/_common.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/info/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/info/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/info/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/info/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/info/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/list/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/list/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/list/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/list/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/list/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/matches/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/matches/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/matches/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/matches/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/matches/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/stages/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/stages/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/stages/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/stages/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/stages/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/standings/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/standings/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/standings/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/standings/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/standings/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/teams/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/teams/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/teams/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/teams/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_event/teams/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/common.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/completed/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/completed/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/completed/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/completed/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/completed/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/live/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/live/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/live/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/live/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/live/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/upcoming/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/upcoming/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/upcoming/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/upcoming/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_matches/upcoming/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/_common_parsing.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/agents/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/agents/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/agents/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/agents/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/agents/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/info/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/info/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/info/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/info/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/info/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/matches/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/matches/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/matches/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/matches/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/matches/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/profile/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/profile/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/profile/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/profile/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/profile/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/teams/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/teams/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/teams/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/teams/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_player/teams/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/_utils.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/economy/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/economy/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/economy/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/economy/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/economy/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/info/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/info/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/info/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/info/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/info/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/match_namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/match_namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/performance/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/performance/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/performance/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/performance/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/performance/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/players/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/players/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/players/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/players/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/players/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/rounds/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/rounds/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/rounds/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/rounds/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/rounds/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/vods/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/vods/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/vods/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/vods/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_series/vods/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/completed_matches/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/completed_matches/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/completed_matches/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/completed_matches/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/completed_matches/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/info/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/info/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/info/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/info/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/info/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/placements/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/placements/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/placements/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/placements/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/placements/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/roster/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/roster/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/roster/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/roster/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/roster/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/stats/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/stats/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/stats/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/stats/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/stats/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/transactions/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/transactions/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/transactions/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/transactions/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/transactions/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/upcoming_matches/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/upcoming_matches/models.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/upcoming_matches/namespace.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/upcoming_matches/namespace.pyi +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_team/upcoming_matches/parser.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_utils/__init__.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_utils/pagination.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_utils/team_enrichment.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/_utils/team_parsing.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/commons/countries.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/commons/mappings.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/commons/prizes.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/commons/timezone.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/exceptions.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/fetcher.py +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/src/vlrdevapi/py.typed +0 -0
- {vlrdevapi-2.2.0 → vlrdevapi-2.3.0}/wrangler.jsonc +0 -0
|
@@ -5,6 +5,57 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [2.3.0] - 2026-08-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **News namespace** — `vlrdevapi.news(page=1)` lists news items from
|
|
13
|
+
vlr.gg/news, each with `title`, `subtitle`, `link`, `country_name`,
|
|
14
|
+
`date`, and `author`, plus `has_next_page` and `page_number` on the
|
|
15
|
+
returned page.
|
|
16
|
+
- **News article content** — `vlrdevapi.news.article(article_id)` fetches
|
|
17
|
+
a single news article with its `title`, `author`, `date`, associated
|
|
18
|
+
`event_name`/`event_link`, and the full `content` body text. The body
|
|
19
|
+
is also available as Markdown via `content_md`, preserving headings,
|
|
20
|
+
lists, links, emphasis, and clip embeds.
|
|
21
|
+
- **News out-of-range pages** — requesting a news page beyond the last
|
|
22
|
+
available page (e.g. `page=176`) now raises `NotFoundError` instead of
|
|
23
|
+
silently returning an empty result.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **News article Markdown links are absolute** — links in `content_md` are
|
|
28
|
+
prefixed with `https://www.vlr.gg` when they were relative (e.g.
|
|
29
|
+
`/player/5132/mada` → `https://www.vlr.gg/player/5132/mada`). Absolute
|
|
30
|
+
and `www.`-prefixed URLs are left untouched. `NewsArticle.event_link` is
|
|
31
|
+
now an absolute URL as well.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **News article clip embeds** — `content_md` clip embeds now link to the
|
|
36
|
+
directly openable watch URL (e.g. `https://clips.twitch.tv/<slug>`) for
|
|
37
|
+
Twitch clips, Twitch VODs, YouTube, and Soop embeds, instead of the embed
|
|
38
|
+
URL which could not be opened in a new tab.
|
|
39
|
+
- **News article Markdown whitespace/emphasis** — inline emphasis and bold
|
|
40
|
+
render correctly around links: source indentation whitespace no longer
|
|
41
|
+
produces double spaces, and `*`/`**` markers no longer wrap whitespace.
|
|
42
|
+
- **News article dates are timezone-aware** — the article date is parsed
|
|
43
|
+
through the shared `commons.parse_vlr_iso_datetime` helper. Offset-aware
|
|
44
|
+
`datetime` attributes are converted to UTC as-is; naive values are
|
|
45
|
+
localised to the client's `source_tz` (or UTC) instead of the local
|
|
46
|
+
machine timezone.
|
|
47
|
+
- **Unknown article IDs raise `NotFoundError`** — vlr.gg returns a generic
|
|
48
|
+
HTTP 200 page for non-existent article IDs; `news.article()` now detects
|
|
49
|
+
the missing article card and raises `NotFoundError` instead of returning
|
|
50
|
+
an empty article.
|
|
51
|
+
- **News article Markdown fidelity** — `content_md` now renders nested
|
|
52
|
+
lists, tables (as pipe tables), and inline `code` verbatim (whitespace
|
|
53
|
+
preserved). Links whose href is `www.`-prefixed are upgraded to
|
|
54
|
+
`https://`, fixing broken relative links in Markdown.
|
|
55
|
+
- **Documentation** — news reference pages updated (article, list, index),
|
|
56
|
+
plus the getting-started guide, main docs index, and README now cover the
|
|
57
|
+
news namespace; model/parser docstrings describe the current behaviour.
|
|
58
|
+
|
|
8
59
|
## [2.2.0] - 2026-08-11
|
|
9
60
|
|
|
10
61
|
### Changed
|
|
@@ -142,6 +193,7 @@ The API surface, module structure, and type system are all new.
|
|
|
142
193
|
- `aiohttp` and `asyncio` dependencies.
|
|
143
194
|
- v1.x match/event/team parsing modules.
|
|
144
195
|
|
|
196
|
+
[2.3.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.2.0...v2.3.0
|
|
145
197
|
[2.2.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.1.0...v2.2.0
|
|
146
198
|
[2.1.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.0.1...v2.1.0
|
|
147
199
|
[2.0.1]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.0.0...v2.0.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vlrdevapi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A Python library to scrape data from vlr.gg
|
|
5
5
|
Project-URL: Homepage, https://vlrdevapi.pages.dev/
|
|
6
6
|
Project-URL: Repository, https://github.com/Vanshbordia/vlrdevapi
|
|
@@ -68,7 +68,7 @@ Access Valorant esports data from VLR.gg with a clean, type-safe Python API. Get
|
|
|
68
68
|
|
|
69
69
|
## Features
|
|
70
70
|
|
|
71
|
-
- **Complete Data Access** - Events, matches, players, teams, and
|
|
71
|
+
- **Complete Data Access** - Events, matches, players, teams, series, and news
|
|
72
72
|
- **Type-Safe** - Pydantic models with rich type hints and field descriptions
|
|
73
73
|
- **Production-Ready** - Retry logic, rate limiting, and LRU-cached enrichment
|
|
74
74
|
- **Curried Access** - Bind a player/team/series ID once, then call sub-methods without re-passing
|
|
@@ -181,6 +181,17 @@ performance = s.performance() # Advanced performance stats
|
|
|
181
181
|
economy = s.economy(game_id=1) # Economy data
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
### News
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
page = client.news(page=1) # Browse the news listing
|
|
188
|
+
for item in page.news[:5]:
|
|
189
|
+
print(item.title)
|
|
190
|
+
|
|
191
|
+
article = client.news.article(734100) # Fetch a full article
|
|
192
|
+
print(article.content_md) # Article body as Markdown
|
|
193
|
+
```
|
|
194
|
+
|
|
184
195
|
## Documentation
|
|
185
196
|
|
|
186
197
|
- **Official Docs** - [vlrdevapi.pages.dev/docs](https://vlrdevapi.pages.dev/docs)
|
|
@@ -20,7 +20,7 @@ Access Valorant esports data from VLR.gg with a clean, type-safe Python API. Get
|
|
|
20
20
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
23
|
-
- **Complete Data Access** - Events, matches, players, teams, and
|
|
23
|
+
- **Complete Data Access** - Events, matches, players, teams, series, and news
|
|
24
24
|
- **Type-Safe** - Pydantic models with rich type hints and field descriptions
|
|
25
25
|
- **Production-Ready** - Retry logic, rate limiting, and LRU-cached enrichment
|
|
26
26
|
- **Curried Access** - Bind a player/team/series ID once, then call sub-methods without re-passing
|
|
@@ -133,6 +133,17 @@ performance = s.performance() # Advanced performance stats
|
|
|
133
133
|
economy = s.economy(game_id=1) # Economy data
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
### News
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
page = client.news(page=1) # Browse the news listing
|
|
140
|
+
for item in page.news[:5]:
|
|
141
|
+
print(item.title)
|
|
142
|
+
|
|
143
|
+
article = client.news.article(734100) # Fetch a full article
|
|
144
|
+
print(article.content_md) # Article body as Markdown
|
|
145
|
+
```
|
|
146
|
+
|
|
136
147
|
## Documentation
|
|
137
148
|
|
|
138
149
|
- **Official Docs** - [vlrdevapi.pages.dev/docs](https://vlrdevapi.pages.dev/docs)
|
|
@@ -39,7 +39,7 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
39
39
|
DOCS_DIR = REPO_ROOT / "official-docs" / "content"
|
|
40
40
|
DEFAULT_FIXTURES_DIR = REPO_ROOT / ".cache" / "mdx-html"
|
|
41
41
|
|
|
42
|
-
DYNAMIC_PATHS = frozenset({"/matches", "/matches/results", "/events"})
|
|
42
|
+
DYNAMIC_PATHS = frozenset({"/matches", "/matches/results", "/events", "/news"})
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class Result(NamedTuple):
|
|
@@ -29,6 +29,16 @@ ROUTES += [
|
|
|
29
29
|
("/matches/results/?page=2", "matches/results_page2.html", {}),
|
|
30
30
|
]
|
|
31
31
|
|
|
32
|
+
# ── News ───────────────────────────────────────────────────────
|
|
33
|
+
ROUTES += [
|
|
34
|
+
("/news", "news/news.html", {}),
|
|
35
|
+
("/news/?page=2", "news/news_page2.html", {}),
|
|
36
|
+
("/news/?page=126", "news/news_page126.html", {}),
|
|
37
|
+
("/news/?page=176", "news/news_page176.html", {}),
|
|
38
|
+
("/734100", "news/article_734100.html", {}),
|
|
39
|
+
("/720033/americas-recent-victors-start-strong-in-week-1", "news/article_720033.html", {}),
|
|
40
|
+
]
|
|
41
|
+
|
|
32
42
|
# ── Events ─────────────────────────────────────────────────────
|
|
33
43
|
ROUTES += [
|
|
34
44
|
("/events", "events/events.html", {}),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""vlrdevapi — A Python library to scrape data from vlr.gg.
|
|
2
2
|
|
|
3
3
|
Provides a synchronous, typed interface for accessing match listings,
|
|
4
|
-
event data, tournament info, team/player profiles, and
|
|
5
|
-
from vlr.gg.
|
|
4
|
+
event data, tournament info, team/player profiles, statistics, and
|
|
5
|
+
news from vlr.gg.
|
|
6
6
|
|
|
7
7
|
Examples:
|
|
8
8
|
Module-level access (using a default client):
|
|
@@ -23,17 +23,25 @@ Examples:
|
|
|
23
23
|
>>> matches.matches[0].score
|
|
24
24
|
'2-1'
|
|
25
25
|
|
|
26
|
+
News listing and articles:
|
|
27
|
+
|
|
28
|
+
>>> page = vlrdevapi.news(page=1)
|
|
29
|
+
>>> article = vlrdevapi.news.article(page.news[0].id)
|
|
30
|
+
>>> article.title != ''
|
|
31
|
+
True
|
|
32
|
+
|
|
26
33
|
"""
|
|
27
34
|
|
|
28
35
|
import atexit as _atexit
|
|
36
|
+
import contextlib
|
|
29
37
|
from typing import TYPE_CHECKING
|
|
30
38
|
|
|
31
39
|
from vlrdevapi._client import VLRClient
|
|
32
|
-
import contextlib
|
|
33
40
|
|
|
34
41
|
if TYPE_CHECKING:
|
|
35
42
|
from vlrdevapi._event.namespace import EventNamespace
|
|
36
43
|
from vlrdevapi._matches.namespace import MatchesNamespace
|
|
44
|
+
from vlrdevapi._news.namespace import NewsNamespace
|
|
37
45
|
from vlrdevapi._player.namespace import PlayerNamespace
|
|
38
46
|
from vlrdevapi._series.namespace import SeriesNamespace
|
|
39
47
|
from vlrdevapi._team.namespace import TeamNamespace
|
|
@@ -43,22 +51,23 @@ if TYPE_CHECKING:
|
|
|
43
51
|
player: PlayerNamespace
|
|
44
52
|
team: TeamNamespace
|
|
45
53
|
matches: MatchesNamespace
|
|
54
|
+
news: NewsNamespace
|
|
46
55
|
client: type[VLRClient]
|
|
47
56
|
|
|
48
|
-
__version__ = "2.
|
|
57
|
+
__version__ = "2.3.0"
|
|
49
58
|
|
|
50
59
|
_default_client: "VLRClient | None" = None
|
|
51
60
|
|
|
52
61
|
|
|
53
62
|
def _get_default_client() -> "VLRClient":
|
|
54
|
-
global _default_client
|
|
63
|
+
global _default_client
|
|
55
64
|
if _default_client is None:
|
|
56
65
|
_default_client = VLRClient()
|
|
57
66
|
return _default_client
|
|
58
67
|
|
|
59
68
|
|
|
60
69
|
def _cleanup_default_client() -> None:
|
|
61
|
-
global _default_client
|
|
70
|
+
global _default_client
|
|
62
71
|
if _default_client is not None:
|
|
63
72
|
with contextlib.suppress(OSError):
|
|
64
73
|
_default_client.close()
|
|
@@ -67,7 +76,7 @@ def _cleanup_default_client() -> None:
|
|
|
67
76
|
|
|
68
77
|
_atexit.register(_cleanup_default_client)
|
|
69
78
|
|
|
70
|
-
_BOUND_NAMES = frozenset({"event", "series", "player", "team", "matches"})
|
|
79
|
+
_BOUND_NAMES = frozenset({"event", "series", "player", "team", "matches", "news"})
|
|
71
80
|
|
|
72
81
|
|
|
73
82
|
def __getattr__(name: str) -> object:
|
|
@@ -89,6 +98,7 @@ __all__ = [
|
|
|
89
98
|
"client",
|
|
90
99
|
"event",
|
|
91
100
|
"matches",
|
|
101
|
+
"news",
|
|
92
102
|
"player",
|
|
93
103
|
"series",
|
|
94
104
|
"team",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from vlrdevapi._client import VLRClient
|
|
2
2
|
from vlrdevapi._event.namespace import EventNamespace
|
|
3
3
|
from vlrdevapi._matches.namespace import MatchesNamespace
|
|
4
|
+
from vlrdevapi._news.namespace import NewsNamespace
|
|
4
5
|
from vlrdevapi._player.namespace import PlayerNamespace
|
|
5
6
|
from vlrdevapi._series.namespace import SeriesNamespace
|
|
6
7
|
from vlrdevapi._team.namespace import TeamNamespace
|
|
@@ -69,6 +70,20 @@ Examples:
|
|
|
69
70
|
>>> completed = vlrdevapi.matches.completed()
|
|
70
71
|
"""
|
|
71
72
|
|
|
73
|
+
news: NewsNamespace
|
|
74
|
+
"""Access news from vlr.gg: news listings and full article content.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
NewsNamespace instance bound to the default client. Call it with a
|
|
78
|
+
``page`` number to browse the news listing, or use ``.article()`` to
|
|
79
|
+
fetch a single article (plain text and Markdown).
|
|
80
|
+
|
|
81
|
+
Examples:
|
|
82
|
+
>>> page = vlrdevapi.news(page=1)
|
|
83
|
+
>>> article = vlrdevapi.news.article(734100)
|
|
84
|
+
>>> print(article.content_md)
|
|
85
|
+
"""
|
|
86
|
+
|
|
72
87
|
client: type[VLRClient]
|
|
73
88
|
"""Create an explicit VLRClient for connection management.
|
|
74
89
|
|
|
@@ -12,6 +12,7 @@ import httpx
|
|
|
12
12
|
from vlrdevapi.commons.timezone import REFERENCE_MATCH_PATH, detect_vlr_timezone
|
|
13
13
|
from vlrdevapi._event.namespace import EventNamespace
|
|
14
14
|
from vlrdevapi._matches.namespace import MatchesNamespace
|
|
15
|
+
from vlrdevapi._news.namespace import NewsNamespace
|
|
15
16
|
from vlrdevapi._player.namespace import PlayerNamespace
|
|
16
17
|
from vlrdevapi._series.namespace import SeriesNamespace
|
|
17
18
|
from vlrdevapi._team.namespace import TeamNamespace
|
|
@@ -111,6 +112,7 @@ class VLRClient:
|
|
|
111
112
|
self.matches = MatchesNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
|
|
112
113
|
self.team = TeamNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
|
|
113
114
|
self.event = EventNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
|
|
115
|
+
self.news = NewsNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
|
|
114
116
|
|
|
115
117
|
def _detect_timezone(self) -> ZoneInfo | tzinfo | None:
|
|
116
118
|
"""Detect the viewer timezone VLR.gg renders for this client session."""
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from datetime import datetime as datetime_
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NewsArticle(BaseModel):
|
|
7
|
+
"""A full news article from vlr.gg.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
id: Unique identifier for the news article.
|
|
11
|
+
title: Title of the news article.
|
|
12
|
+
author: Author of the news article.
|
|
13
|
+
date: Publication date as a timezone-aware UTC datetime.
|
|
14
|
+
event_name: Event the article is associated with.
|
|
15
|
+
event_link: Link to the associated event page.
|
|
16
|
+
content: Full body text of the article as plain text.
|
|
17
|
+
content_md: Full body of the article converted to Markdown, with
|
|
18
|
+
headings, nested lists, tables, links, verbatim code, emphasis,
|
|
19
|
+
and Twitch clip embeds preserved.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
model_config = ConfigDict(
|
|
23
|
+
json_schema_extra={"description": "A full news article from vlr.gg."}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
id: int = Field(default=0, description="Unique identifier for the news article")
|
|
27
|
+
title: str = Field(default="", description="Title of the news article")
|
|
28
|
+
author: str = Field(default="", description="Author of the news article")
|
|
29
|
+
date: datetime_ | None = Field(
|
|
30
|
+
default=None, description="Publication date as a timezone-aware UTC datetime"
|
|
31
|
+
)
|
|
32
|
+
event_name: str = Field(
|
|
33
|
+
default="", description="Event the article is associated with"
|
|
34
|
+
)
|
|
35
|
+
event_link: str = Field(default="", description="Link to the associated event page")
|
|
36
|
+
content: str = Field(
|
|
37
|
+
default="", description="Full text content of the news article"
|
|
38
|
+
)
|
|
39
|
+
content_md: str = Field(
|
|
40
|
+
default="", description="Full content of the news article converted to Markdown"
|
|
41
|
+
)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""News article namespace."""
|
|
2
|
+
|
|
3
|
+
from datetime import tzinfo
|
|
4
|
+
from zoneinfo import ZoneInfo
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from vlrdevapi._base import SyncNamespace
|
|
9
|
+
from vlrdevapi._news.article.models import NewsArticle
|
|
10
|
+
from vlrdevapi._news.article.parser import parse_news_article
|
|
11
|
+
from vlrdevapi._utils.paths import news_article
|
|
12
|
+
from vlrdevapi.exceptions import NotFoundError
|
|
13
|
+
from vlrdevapi.fetcher import (
|
|
14
|
+
DEFAULT_RETRY_CONFIG,
|
|
15
|
+
DEFAULT_TIMEOUT,
|
|
16
|
+
RateLimiter,
|
|
17
|
+
RetryConfig,
|
|
18
|
+
)
|
|
19
|
+
from vlrdevapi.validators import sanitize_and_validate
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NewsArticleNamespace:
|
|
23
|
+
"""Access individual news articles from vlr.gg.
|
|
24
|
+
|
|
25
|
+
Call the instance with an article ID to fetch the full article::
|
|
26
|
+
|
|
27
|
+
article = vlrdevapi.news.article(734100)
|
|
28
|
+
print(article.title)
|
|
29
|
+
|
|
30
|
+
The namespace is reachable via ``vlrdevapi.news.article``. There is
|
|
31
|
+
no need to instantiate it directly.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
__slots__ = ("_source_tz", "_sync")
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
client: httpx.Client,
|
|
39
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
40
|
+
retry_config: RetryConfig = DEFAULT_RETRY_CONFIG,
|
|
41
|
+
rate_limiter: RateLimiter | None = None,
|
|
42
|
+
extra_headers: dict[str, str] | None = None,
|
|
43
|
+
source_tz: ZoneInfo | tzinfo | None = None,
|
|
44
|
+
):
|
|
45
|
+
self._source_tz = source_tz
|
|
46
|
+
self._sync = SyncNamespace(
|
|
47
|
+
client, timeout, retry_config, rate_limiter, extra_headers
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@sanitize_and_validate
|
|
51
|
+
def __call__(self, article_id: int) -> NewsArticle:
|
|
52
|
+
"""Get a single news article from vlr.gg.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
article_id: The news article ID. Use the ``id`` field of a
|
|
56
|
+
``News`` item from a listing, or the numeric prefix of
|
|
57
|
+
its ``link``.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
NewsArticle: The article with ``title``, ``author``, ``date``,
|
|
61
|
+
``event_name``, ``event_link``, ``content`` (plain text),
|
|
62
|
+
and ``content_md`` (Markdown).
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
ValidationError: If ``article_id`` is not a valid positive integer.
|
|
66
|
+
NotFoundError: If the article does not exist. vlr.gg returns a
|
|
67
|
+
generic page (HTTP 200) for unknown article IDs, so this
|
|
68
|
+
is detected from the page content.
|
|
69
|
+
RequestError: If the HTTP request fails.
|
|
70
|
+
RateLimitError: If the rate limit is exceeded.
|
|
71
|
+
|
|
72
|
+
Examples:
|
|
73
|
+
>>> article = vlrdevapi.news.article(734100)
|
|
74
|
+
>>> article.title
|
|
75
|
+
'Gen.G, Global, VARREL, PRX bypass Pacific Stage 2 Play-Ins'
|
|
76
|
+
>>> article.content_md.startswith('The [Pacific Stage 2]')
|
|
77
|
+
True
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
html = self._sync._fetch(news_article(article_id))
|
|
81
|
+
article = parse_news_article(html, source_tz=self._source_tz)
|
|
82
|
+
if article.id == 0:
|
|
83
|
+
raise NotFoundError(
|
|
84
|
+
f"News article {article_id} not found (page contains no article content)."
|
|
85
|
+
)
|
|
86
|
+
return article
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from datetime import tzinfo
|
|
2
|
+
from zoneinfo import ZoneInfo
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from vlrdevapi._news.article.models import NewsArticle
|
|
7
|
+
from vlrdevapi.fetcher import RateLimiter, RetryConfig
|
|
8
|
+
|
|
9
|
+
class NewsArticleNamespace:
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
client: httpx.Client,
|
|
13
|
+
timeout: int = ...,
|
|
14
|
+
retry_config: RetryConfig = ...,
|
|
15
|
+
rate_limiter: RateLimiter | None = None,
|
|
16
|
+
extra_headers: dict[str, str] | None = None,
|
|
17
|
+
source_tz: ZoneInfo | tzinfo | None = None,
|
|
18
|
+
) -> None: ...
|
|
19
|
+
def __call__(self, article_id: int) -> NewsArticle: ...
|